Authentication
Every request to the AIdenID API must be authenticated with an API key and scoped to a specific organization and project.
All authentication examples on this page are backend integration examples. Do not copy API-key or bearer-token flows into browser JavaScript, extensions, or mobile client bundles.
API keys
API keys are the primary authentication mechanism. Each key is bound to an organization and grants access to one or more projects. API keys follow the format aid_*.
You can generate and manage API keys from the Dashboard > Settings page.
Required headers
Every API request must include these three headers:
| Header | Description | Example |
|---|---|---|
Authorization | Bearer token with your API key | Bearer <SERVER_INJECTED_API_KEY> |
X-Org-Id | Your organization identifier | org_abc123 |
X-Project-Id | The project to scope the request to | proj_def456 |
Scope note: The Authorization: Bearer ... header in this section applies only to server-side API key requests. Consumer endpoints use cookie-backed sessions and do not accept consumer bearer session headers.
Treat X-Org-Id and X-Project-Id as requested scope hints, not trusted identity claims. The backend must derive authorized org/project scope from the credential, reject mismatches with a 403 error, and include a request ID in the error envelope.
Backend enforcement is implemented before route handlers in require_auth and require_auth_without_scope: API keys and exchanged access tokens are resolved to their stored project scope, and mismatched tenant headers return 403. Regression coverage includes forged project access and access-token/header mismatch cases in the API contract suite.
curl https://api.aidenid.com/v1/identities \
-H "Authorization: Bearer <SERVER_INJECTED_API_KEY>" \
-H "X-Org-Id: org_abc123" \
-H "X-Project-Id: proj_def456"API key scopes
API keys support granular scopes that control which operations are permitted:
| Scope | Permissions |
|---|---|
identities:read | List and retrieve identities, events, and extractions |
identities:write | Create, extend, squash identities and manage children |
webhooks:read | List webhook endpoints |
webhooks:write | Create, update, delete webhook endpoints and rotate secrets |
reliability:write | Create and manage auth flow test runs |
domains:read | List registered domains |
domains:write | Register and verify custom domains |
Token exchange
For short-lived access, you can exchange your API key for a temporary access token:
Mutation safety: include a unique Idempotency-Key header on every token-exchange request. The service replays the original response for duplicate keys instead of minting extra tokens.
Request-side transport and logging controls: never log inbound Authorization headers at your gateway or reverse proxy, enforce HTTPS-only transport, and apply explicit auth-header redaction rules in edge/service logs before requests reach the token exchange handler.
curl -X POST https://api.aidenid.com/v1/auth/tokens \
-H "Authorization: Bearer <SERVER_INJECTED_API_KEY>" \
-H "X-Org-Id: org_abc123" \
-H "X-Project-Id: proj_def456" \
-H "Idempotency-Key: <GENERATED_UNIQUE_KEY>" \
-H "Content-Type: application/json" \
-d '{
"scopes": ["identities:read", "identities:write"],
"ttl_seconds": 3600
}'Use a high-entropy idempotency key per mutation attempt. Never reuse predictable values like counters, timestamps, or static strings.
The token-exchange route must enforce tenant scope at runtime: resolve org/project from the authenticated principal, compare to X-Org-Id and X-Project-Id , and return 403 scope_mismatch with a request ID on divergence.
Treat exchanged bearer tokens as highly sensitive credentials. Return them only from server-side handlers with Cache-Control: no-store, max-age=0 and Pragma: no-cache, never persist them in browser storage, and redact them from logs and traces.
HTTP/1.1 200 OK
Cache-Control: no-store, max-age=0
Pragma: no-cache
Content-Type: application/json
{
"access_token": "<omitted-in-docs>",
"expires_at": "2026-04-06T16:10:00Z",
"scopes": ["identities:read", "identities:write"]
}The real bearer token value is intentionally omitted in docs examples. Handle token material only in server memory and never emit it to logs, fixtures, screenshots, or browser-visible telemetry.
Get current profile
Returns the organization, project, and scopes associated with the current API key or access token.
{
"organization_id": "org_abc123",
"project_id": "proj_def456",
"scopes": ["identities:read", "identities:write", "webhooks:read"]
}Consumer authentication (magic link)
Human consumers authenticate using passwordless magic links. This flow does not use API keys — instead, a magic link is sent to the consumer's personal email, and clicking it creates a session.
Signup flow
Run these requests only from trusted backend services or operator terminals. Do not embed API keys, bearer tokens, or auth-flow mutation calls in browser bundles or untrusted client code.
# 1. Request signup
curl -X POST https://api.aidenid.com/v1/consumer/signup \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <GENERATED_UNIQUE_KEY>" \
-d '{ "email": "user@example.com" }'
# 2. User clicks magic link in email
# 3. Verify the token from the magic link
curl -X POST https://api.aidenid.com/v1/consumer/verify \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <GENERATED_UNIQUE_KEY>" \
-d '{ "token": "<REDACTED_MAGIC_LINK_TOKEN>" }'
# Response sets an httpOnly session cookie
# { "accountId": "cac_...", "verified": true, ... }Login flow
# Request a magic link
curl -X POST https://api.aidenid.com/v1/consumer/login \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <GENERATED_UNIQUE_KEY>" \
-d '{ "email": "user@example.com" }'
# User clicks the link, then verify as aboveOnce authenticated, consumer API requests use the httpOnlyaidenid_consumer_session cookie. Browser clients should not send consumer bearer tokens. See the Consumer API for all available endpoints.
Free tier authentication (anonymous grant token)
The agent free tier uses a server-issued anonymous grant token tied to IP reputation and abuse controls. Requests without a valid grant token are rejected.
Treat one-time grant or polling tokens as secret material: return them only from server handlers with Cache-Control: no-store, max-age=0, never log token values, and redact token-shaped fields before writing request/response bodies to traces.
# No API key required; X-Anon-Grant is required
curl -X POST https://api.aidenid.com/v1/free/identity \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <GENERATED_UNIQUE_KEY>" \
-H "X-Anon-Grant: <REDACTED_ANON_GRANT>"
# Response shown in docs intentionally omits secret token fields
# { "id": "fi_<REDACTED_ID>", "email": "agent@example.test", "status": "ACTIVE" }# Example server-side auth log shaping (allowlist-only)
# Do not recursively log arbitrary metadata payloads.
const toSafeAuthLog = (payload: Record<string, unknown>) => ({
event: typeof payload.event === "string" ? payload.event : "consumer_auth",
requestId: typeof payload.requestId === "string" ? payload.requestId : "req_unknown",
outcome: typeof payload.outcome === "string" ? payload.outcome : "unknown",
});
// Optional: explicit allowlist for a few non-sensitive metadata keys.
const toSafeAuthMetadata = (payload: Record<string, unknown>) => ({
flow: typeof payload.flow === "string" ? payload.flow : undefined,
status: typeof payload.status === "string" ? payload.status : undefined,
retryable: typeof payload.retryable === "boolean" ? payload.retryable : undefined,
});# Missing grant token
# HTTP 401
# { "error": { "code": "unauthorized", "message": "Missing X-Anon-Grant header.", "details": { "requestId": "req_..." } }, "requestId": "req_..." }
# Expired grant token
# HTTP 403
# { "error": { "code": "grant_expired", "message": "Anonymous grant expired.", "details": { "requestId": "req_..." } }, "requestId": "req_..." }
# Replayed one-time grant token
# HTTP 409
# { "error": { "code": "grant_replayed", "message": "Anonymous grant already consumed.", "details": { "requestId": "req_..." } }, "requestId": "req_..." }
# Invalid grant token format/signature
# HTTP 403
# { "error": { "code": "forbidden", "message": "Anonymous grant is invalid.", "details": { "requestId": "req_..." } }, "requestId": "req_..." }Accept X-Anon-Grant only over HTTPS, enforce short TTL and audience/org/project binding, require one-time/replay-protected jti semantics, and never log raw grant tokens. Return canonical errors for replay (grant_replayed) versus expiry (grant_expired) to keep abuse handling deterministic.
The returned token is a sensitive bearer credential used for subsequent polling requests. Treat it as opaque bytes: do not trim, normalize, decode, or transform before sending it back. It is scoped to the specific identity and cannot be reused. See the Free Tier API for details.
Never log raw polling tokens, never place them in URLs, and store responses with Cache-Control: no-store semantics in intermediate services.
Security best practices
- Never expose API keys in client-side code. API keys should only be used in server-side applications or secure agent runtimes.
- Use minimal scopes. Grant only the scopes each agent or service actually needs.
- Rotate keys regularly. Generate new API keys periodically and revoke old ones.
- Use idempotency keys. All mutation requests require an
Idempotency-Keyheader to prevent duplicate operations on retry.
Error responses
| Code | Status | Description |
|---|---|---|
unauthorized | 401 | Missing or invalid API key |
forbidden | 403 | API key lacks the required scope for this operation |