Developers

Start with one request.

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

Public signup, personal access and reviewer sign-in are not yet open. These examples describe integration contracts; they do not enable a live account. Check account availability before testing.

Quickstart

As the relying party you create a verification session in one call. Show the returned QR token to the person, they continue through the approved capture and review workflow, 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" \
  -H "Content-Type: application/json" \
  -d '{"client_reference":"order-42"}'

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

Authentication

Relying-party API calls carry a server-side bearer credential. Discuss workspace access before integrating. Once company registration is available, an active workspace owner will be able to issue and revoke integration keys after email verification, passkey enrolment and payment confirmation. Subject and operator routes require their own authenticated actor. Keep relying-party keys on your server.

# Use the server-side API key issued during workspace provisioning for relying-party calls.
Authorization: Bearer <your issued API key>
# Request key rotation through the authorised provisioning process. Revoked keys fail closed.

Verify

You create the session and poll its result; the person completes the approved capture and review workflow. The review workflow is implemented, but public identity enrolment is not yet open. A successful IDV remains reusable for three calendar months from its final completion. Each company requests specific details and the person explicitly authorises access. Reuse keeps the original expiry. After expiry, fresh IDV updates the same personal profile.

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..." }
}

Identity sharing

One person keeps one profile across company relationships. Create a company request with POST /v1/identity/requests. An invitation is claimed in the personal account; known clients receive a request in their inbox. Read only the explicitly approved fields from your own request. A pending, declined or expired request releases no verified details.

The personal account supports GET /v1/me/identity, GET /v1/me/identity/requests and an explicit decision for an exact request. The server rechecks the IDV validity at approval. Raw captures, internal scores, personal vault contents and unrelated company records are excluded.

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" \
  -H "Content-Type: application/json" \
  -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" \
  -H "Content-Type: application/json" \
  -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 designed for zero knowledge: encryption must happen on the client, and the API stores opaque ciphertext. Personal Vault and Authenticator access requires the individual's own £25 monthly membership. Company bundles pay for IDV and signature usage and grant no personal premium access.

# 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 after an approved capture and review workflow has completed. Native apps are not yet publicly released.

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.