Authentication

Every request to the AIdenID API must be authenticated with an API key and scoped to a specific organization and project.

Server-side only examples

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:

HeaderDescriptionExample
AuthorizationBearer token with your API keyBearer <SERVER_INJECTED_API_KEY>
X-Org-IdYour organization identifierorg_abc123
X-Project-IdThe project to scope the request toproj_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.

Tenant header anti-spoofing

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:

ScopePermissions
identities:readList and retrieve identities, events, and extractions
identities:writeCreate, extend, squash identities and manage children
webhooks:readList webhook endpoints
webhooks:writeCreate, update, delete webhook endpoints and rotate secrets
reliability:writeCreate and manage auth flow test runs
domains:readList registered domains
domains:writeRegister 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.

POST/v1/auth/tokens
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.

Token exchange response handling

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

GET/v1/me

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

Server-side only

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 above

Once 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.

Token safety

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_..." }
X-Anon-Grant hardening requirements

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.

Token handling requirements

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-Key header to prevent duplicate operations on retry.

Error responses

CodeStatusDescription
unauthorized401Missing or invalid API key
forbidden403API key lacks the required scope for this operation