Developers

One small API.
Ship in a day.

A small REST API and an issued key. Create a session over your key, then poll for the decision. Every mutating call takes an Idempotency-Key.

Quickstart

As the relying party you create a verification session in one call. Show the returned QR token to the person, they complete capture in the Magic Auth app, and you poll the result.

POST/v1/verification-sessions
# 1. Create a verification session (mutating call, so an Idempotency-Key is required)
curl -X POST https://api.magicauth.ai/v1/verification-sessions \
  -H "Authorization: Bearer <your issued API key>" \
  -H "Idempotency-Key: 8a1c-1f2e-42" \
  -d '{"client_reference":"order-42"}'

# => { "session_id": "vs_8a1c...", "qr": { "token": "...", "expires_at": "..." }, "status": "pending" }

Authentication

Every request carries the opaque API key Magic Auth issued you at onboarding, as a bearer credential. Keys are scoped to the products you enable, and you rotate them from the portal. A revoked key fails closed. There is no self-serve key format and no public sandbox.

# Every request carries the opaque API key Magic Auth issued you at onboarding.
Authorization: Bearer <your issued API key>
# Rotate from the portal. A revoked key fails closed.

Verify

You create the session and poll its result; the person completes document and biometric capture in the app. A decided session returns its decision, the assurance target, and a receipt id that the other products accept in place of a fresh check.

GET/v1/verification-sessions/{id}/result
GET /v1/verification-sessions/vs_8a1c.../result

# =>
{
  "session_id": "vs_8a1c...",
  "state": "decided",
  "decision": "pass",
  "assurance": { "target": "IAL2_BUILD_TO_STANDARD", "status": "not_certified_yet" },
  "receipt": { "receipt_id": "rcpt_2b7f..." }
}

Sign

Signing is a two-step flow. First you create a signing request over the document hash, so the document never leaves your systems. Then the person's device signs the action hash and the call returns a long-term-validation bundle. The signature covers the hash, so it stays tamper-evident.

POST/v1/signing-requests
# Step 1. Create a signing request over the document hash. The server never sees the document.
curl -X POST https://api.magicauth.ai/v1/signing-requests \
  -H "Authorization: Bearer <your issued API key>" \
  -H "Idempotency-Key: sign-7c3a-01" \
  -d '{"subject_id":"sub_1a2b","action_type":"loan_agreement","document_sha256":"9f86d0..."}'

# => { "signing_request_id": "sr_5d21...", "action_hash": "3b1f...", "expires_at": "..." }
POST/v1/signing-requests/{id}/complete
# Step 2. The signer's device signs the action hash and returns the long-term-validation bundle.
curl -X POST https://api.magicauth.ai/v1/signing-requests/sr_5d21.../complete \
  -H "Authorization: Bearer <your issued API key>" \
  -H "Idempotency-Key: sign-7c3a-02" \
  -d '{"device_key_id":"dk_9f...","device_signature":"MEUCIQ..."}'

# => long-term-validation bundle: device signature, trusted timestamp, signing receipt

Authenticate

Magic Auth is a standard OIDC provider. Use the authorization code flow with PKCE (S256 is mandatory), exchange the code at the token endpoint with a private_key_jwt client assertion, and verify id tokens against the JWKS. The id token carries both identity assurance and authenticator assurance, so you can gate sensitive actions on how the person proved themselves.

GET/v1/authorize
POST/v1/token
GET/v1/jwks
# Authorization code flow, PKCE with S256 is mandatory.
GET https://api.magicauth.ai/v1/authorize?response_type=code
  &client_id=YOUR_CLIENT&redirect_uri=https://you/callback
  &code_challenge=...&code_challenge_method=S256&scope=openid

# Exchange the code at POST /v1/token (client auth: private_key_jwt).
# Verify id tokens against the keys at GET /v1/jwks.

Vault

Vault is zero-knowledge. Encryption happens on the device under a key derived from the user, and the API only ever stores and returns ciphertext. There is no server-side endpoint that returns plaintext, by design.

# Zero-knowledge. The API only ever stores and returns opaque ciphertext.
PUT /v1/vault/items  # store an encrypted item (Idempotency-Key required)
GET /v1/vault/items    # list item metadata for sync
GET /v1/vault/items/{id} # fetch one item's ciphertext

Webhooks

Outbound webhooks to your endpoint are on the roadmap and not yet available. Today you read a verification decision by polling GET /v1/verification-sessions/{id}/result once the person has completed capture in the app.

Errors

The API uses conventional HTTP status codes. 4xx for a problem with your request, 429 when you exceed a rate limit, 5xx for a fault on our side. Error bodies carry a stable code and a human-readable message, never an internal stack trace.