LegitFlash

API

API

Create a verification session, send the applicant to the hosted link, receive the signed webhook, fetch the result. The machine-readable reference is GET /api/v1/openapi.json (OpenAPI 3.1, webhooks included); the readable one is the API reference.

Tokens: live and test

  • Personal tokens. A token acts as its owner in a team (X-Team-Id), so an integration stops working when that user leaves the team. Use a dedicated user for production integrations.
  • Abilities: sessions:read, sessions:write, flows:read, webhooks:read (plus the account ones, read/write). The token's team role still applies: erasing a session needs owner or admin.
  • Mode (chosen at creation, Account, API tokens): tid_live_... works on real sessions only; tid_test_... works on test sessions only (livemode: false). A session id of the other mode is a 404.
  • Test mode: the hosted link shows a TEST banner and a simulator (approve, reject with a reason, request a resubmission) instead of real captures. Test sessions never enter the review queue, never reach Pumble, are never billed, send no email, and the record is deleted 24 hours after its last activity. Webhooks fire normally with "livemode": false.
  • The dashboard has a Live / Test switch on Sessions and shows a TEST badge.

Quick start

Copied
# 1. Create a session (test token while you integrate)
curl -sS https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions \
  -H "Authorization: Bearer $TRUSTID_TOKEN" \
  -H "Idempotency-Key: order-1234-verification" \
  -H "Content-Type: application/json" \
  -d '{"flow_id":"01J...","external_reference":"order-1234","metadata":{"order":"1234"},"redirect_url":"https://shop.example/verified"}'
# -> 201 {"data":{"id":"01J...","status":"created","url":"https://YOUR-LEGITFLASH-DOMAIN/v/...","qr_svg_url":"...","embed":{...}}}

# 2. Redirect the applicant to data.url (or show the QR, or embed it), then wait for the webhook.
# 3. Fetch the result
curl -sS https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions/01J... -H "Authorization: Bearer $TRUSTID_TOKEN"
Copied
// Node 22
const res = await fetch('https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TRUSTID_TOKEN}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': `order-${order.id}-verification`,
  },
  body: JSON.stringify({ flow_id: process.env.TRUSTID_FLOW, external_reference: String(order.id) }),
});
const { data } = await res.json();
redirect(data.url); // shown only in this response: store data.id, not the link
Copied
// PHP (Laravel HTTP client)
$session = Http::withToken(config('services.trustid.token'))
    ->withHeaders(['Idempotency-Key' => "order-{$order->id}-verification"])
    ->post('https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions', [
        'flow_id' => config('services.trustid.flow'),
        'external_reference' => (string) $order->id,
    ])->throw()->json('data');
return redirect($session['url']);

Webhooks: see Webhooks (signature, retries, payload; the body also carries livemode and your metadata).

Endpoints

Method Path Ability Notes
POST /sessions sessions:write 201 + the session and url, qr_svg_url (signed, 15 min), embed. Idempotency-Key honoured
GET /sessions sessions:read cursor pagination; filters status, flow_id, external_reference, `created[gte
GET /sessions/{id} sessions:read status, decision, reason codes, result (per the flow's result detail), check flag names, timeline, media_purge_at. Never media
POST /sessions/{id}/cancel sessions:write only before submission; else 409 not_cancellable
POST /sessions/{id}/link sessions:write new hosted link, the old one dies; 409 not_relinkable once decided/expired
DELETE /sessions/{id} sessions:write data-subject erasure now (media, record, webhook payloads), 204
GET /sessions/{id}/export sessions:read access-request export (JSON, no media binaries, no internal notes)
GET /flows, /flows/{id} flows:read `archived=0
GET /usage sessions:read period, included (the plan's verifications per month, or the trial allowance), used, overage (extra ones beyond included), cap (most extra verifications the team's overage cap allows, null = no cap), overage_allowed
GET /webhook-deliveries webhooks:read session_id, status; delivery status only, never payloads

Create body: flow_id (required), external_reference, applicant.email, locale (en|es), send_invite (live mode only), redirect_url (its host must be one of the hosts of the flow's redirect URLs), metadata (up to 10 string pairs, 500 bytes each, keys [A-Za-z0-9_.-]{1,40}). Metadata must never contain personal data: it is echoed in responses and webhooks and kept as plain text.

Idempotency

Send Idempotency-Key (at most 255 characters) on any POST. Per team and token mode, for 24 hours: the same key with the same request replays the first 2xx response (header Idempotent-Replayed: true, even the one-time url); the same key with a different request is 422 idempotency_key_reused. Failed requests are not stored, so a corrected retry just runs. Only a hash of the key is stored and the stored response is encrypted.

Errors, pagination, limits, versioning

  • Errors are RFC 9457 application/problem+json with a stable code (validation_failed, missing_ability, not_found, flow_not_ready, quota_exceeded (HTTP 402: no plan, trial used up, included used up with overage off, or overage cap reached; quota.reason and quota.used/included say which), not_cancellable, not_relinkable, idempotency_key_reused, idempotency_key_invalid, rate_limited, ...). More in Errors.
  • Lists use cursor pagination: follow links.next until it is null.
  • Rate limit per token by plan (limits.api_rpm): X-RateLimit-Limit/Remaining, 429 + Retry-After.
  • A token in the query string is a 400; always use the Authorization header.
  • Versioning: changes inside v1 are additive (new fields, endpoints, events, enum values: ignore what you do not know). A breaking change ships as /api/v2 with a 12-month overlap, announced by Deprecation and Sunset headers on v1.
  • Developers, API in the dashboard shows the last 100 calls of the team (method, path, status, duration, token name; no bodies).