Verification flow (v1)
Verification in Loom API v1 is a three-step flow: you start a session, the user completes identity verification via iDenfy's hosted UI, then you poll status or receive a webhook and optionally validate tokens.
Overview
- POST /verify/start — Create a session; receive
verificationIdandsessionUrl. - GET /verify/status — Poll verification status by
verificationId. - POST /tokens/validate — Validate a JWT token to confirm the result in your backend.
All endpoints require the tenant API key in the x-tenant-api-key header.
POST /verify/start
Start a new verification session. Returns a verificationId and a sessionUrl to redirect or embed for the user.
Request
POST https://api.loomapi.com/verify/start
Headers
| Header | Required | Description |
|---|---|---|
x-tenant-api-key | Yes | Your tenant API key |
Content-Type | Yes | application/json |
Body
The request body is optional. Accepted fields:
| Field | Type | Description |
|---|---|---|
userAgent | string (optional) | User agent string of the end user's browser |
ip | string (optional) | IP address of the end user |
Unknown fields are rejected — the schema uses strict validation. Do not send metadata, callback_url, user_id, session_id, or similar; the API will return a 400.
{}
Or with optional fields:
{
"userAgent": "Mozilla/5.0 ...",
"ip": "203.0.113.42"
}
Response (201)
{
"verificationId": "cjld2cjxh0000qzrmn831i7rn",
"status": "started",
"provider": "idenfy",
"sessionUrl": "https://ui.idenfy.com/?authToken=...",
"config": {
"pollingIntervalMs": 3000,
"tokenType": "jwt"
}
}
| Field | Description |
|---|---|
verificationId | Use this to poll status or correlate webhooks |
status | Always started on creation |
provider | Always idenfy in v1 |
sessionUrl | Send the user here to complete identity verification |
config.pollingIntervalMs | Recommended polling interval if not using webhooks |
config.tokenType | Token format issued on approval (jwt) |
GET /verify/status
Check the status of a verification session.
Request
GET https://api.loomapi.com/verify/status?verificationId=cjld2cjxh0000qzrmn831i7rn
Headers
| Header | Required | Description |
|---|---|---|
x-tenant-api-key | Yes | Your tenant API key |
Query parameters
| Parameter | Required | Description |
|---|---|---|
verificationId | Yes | The verificationId from the start response |
Response (200)
{
"verificationId": "cjld2cjxh0000qzrmn831i7rn",
"status": "approved",
"confidence": 0.95,
"createdAt": "2024-01-15T14:30:00.000Z",
"completedAt": "2024-01-15T14:31:00.000Z"
}
Status values
| Status | Meaning |
|---|---|
pending | Session created, user has not started |
started | User has opened the iDenfy flow |
submitted | User submitted documents; iDenfy processing |
approved | Verification passed |
denied | Verification failed — documents rejected |
resubmission | iDenfy requested re-submission of documents |
rejected | Permanently rejected |
Note: When using iDenfy,
POST /verify/completeis not available — iDenfy is webhook-only. Results arrive via the iDenfy callback to/verify/callback. Poll/verify/statusor use outbound webhooks.
POST /tokens/validate
Validate a JWT token to confirm the user's verification result on your backend.
Request
POST https://api.loomapi.com/tokens/validate
Headers
| Header | Required | Description |
|---|---|---|
x-tenant-api-key | Yes | Your tenant API key |
Content-Type | Yes | application/json |
Body
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response (200)
Valid token:
{ "valid": true, "over18": true, "reason": "OK" }
Invalid or expired token:
{ "valid": false, "over18": false, "reason": "TOKEN_EXPIRED" }
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the token is valid |
over18 | boolean | Whether the verified person is 18 or over |
reason | string | See reason codes below |
Reason codes
| Reason | Meaning |
|---|---|
OK | Token is valid |
TOKEN_EXPIRED | Token has passed its expiry time |
TOKEN_REVOKED | Token was explicitly revoked |
TOKEN_NOT_FOUND | Token does not exist in the system |
UNDERAGE_OR_UNKNOWN | Verification completed but age could not be confirmed as 18+ |
Example: minimal flow
# 1. Start verification
RESP=$(curl -s -X POST https://api.loomapi.com/verify/start \
-H "x-tenant-api-key: $LOOM_TENANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}')
VERIFICATION_ID=$(echo "$RESP" | jq -r '.verificationId')
SESSION_URL=$(echo "$RESP" | jq -r '.sessionUrl')
echo "Send user to: $SESSION_URL"
# 2. Poll status (or use webhooks instead)
curl -s "https://api.loomapi.com/verify/status?verificationId=$VERIFICATION_ID" \
-H "x-tenant-api-key: $LOOM_TENANT_API_KEY"
# 3. When you have a token from the session, validate it
curl -s -X POST https://api.loomapi.com/tokens/validate \
-H "x-tenant-api-key: $LOOM_TENANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"token": "YOUR_JWT_TOKEN"}'
Webhooks
Instead of polling, configure a webhook URL in the dashboard to receive verification.completed events. See Outbound Webhooks for payload format and signature verification.
Error responses
| Code | Meaning |
|---|---|
400 | Invalid request body (unknown fields, bad format) |
401 | Invalid or missing x-tenant-api-key |
404 | verificationId not found |
429 | Rate limit exceeded — see Retry-After header |
Full error format in Errors.
Next steps
- Quickstart — run the full flow in under 10 minutes
- Outbound Webhooks — event payload and signature verification
- Billing & Usage — how verification usage is counted