API Reference
The Xident API allows you to verify ages, manage users, and integrate advanced features. All endpoints are served from api.xident.io under versioned path prefixes.
Base URL
https://api.xident.io Verification widget endpoints use the /verify/v1/ prefix. Dashboard and admin endpoints use their own prefixes.
Authentication
Xident uses a Stripe-style dual key model. All API requests require the X-API-Key header:
Public Key vs Secret Key
| Key Type | Prefix | Use In | Can Access |
|---|---|---|---|
| Public Key | pk_live_ / pk_test_ | Browser (JS SDK) | Widget-facing endpoints: requirements, liveness, OCR, feedback |
| Secret Key | sk_live_ / sk_test_ | Server only (Node, PHP, Go, Python) | All endpoints, including POST /verify/v1/init and GET /verify/v1/status/:token |
Public Key (frontend)
X-API-Key: pk_live_your_public_key Secret Key (backend)
X-API-Key: sk_live_your_secret_key Never expose your secret key in client-side code. The public key is safe to embed in your HTML or JavaScript.
Response Envelope
All API responses use a consistent envelope format:
{
"success": true,
"data": { ... },
"meta": {
"request_id": "req_xyz789",
"timestamp": "2026-03-30T10:30:00Z"
}
} Error responses set success: false and include an error object instead of data.
Verification Endpoints
Initialize Session
Create a new verification session. Requires secret key.
POST /verify/v1/init Request Body
{
"callback_url": "https://yoursite.com/verified",
"metadata": {
"user_id": "your-user-123"
}
} Response
{
"success": true,
"data": {
"session_id": "ses_abc123",
"verify_url": "https://verify.xident.io/ses_abc123"
},
"meta": {
"request_id": "req_xyz789",
"timestamp": "2026-03-30T10:30:00Z"
}
} Get Verification Status
Retrieve the result of a completed verification. Requires secret key.
GET /verify/v1/status/:token Response
{
"success": true,
"data": {
"verified": true,
"age_threshold": 18,
"above_threshold": true,
"country": "US",
"xident_id": "xid_abc123",
"methods_completed": ["liveness", "age_estimation"],
"created_at": "2026-03-30T10:30:00Z",
"metadata": {
"user_id": "your-user-123"
}
},
"meta": {
"request_id": "req_xyz789",
"timestamp": "2026-03-30T10:30:05Z"
}
} Response Fields
| Field | Type | Description |
|---|---|---|
verified | boolean | Whether verification was successful |
age_threshold | number | The age threshold that was checked (e.g. 18) |
above_threshold | boolean | Whether the user is above the required age threshold |
country | string | ISO country code |
xident_id | string | User's Xident ID (if they have one) |
methods_completed | array | Verification methods used |
metadata | object | Custom data passed in init() |
Check Requirements
Get verification requirements for your project. Uses public key.
GET /verify/v1/requirements/check Response
{
"success": true,
"data": {
"requirements": {
"min_age": 18,
"liveness_required": true,
"document_required": false,
"wallet_supported": true
},
"project_name": "My Website"
},
"meta": {
"request_id": "req_abc456",
"timestamp": "2026-03-30T10:30:00Z"
}
} Feedback
Submit Widget Feedback
Allow users to submit feedback about the verification experience. Uses public key.
POST /verify/v1/feedback Request Body
{
"session_id": "ses_abc123",
"rating": 4,
"comment": "Quick and easy verification process."
} Response
{
"success": true,
"data": {
"id": "fb_abc123",
"created_at": "2026-03-30T10:35:00Z"
},
"meta": {
"request_id": "req_def789",
"timestamp": "2026-03-30T10:35:00Z"
}
} Request Human Review
Allow users to request a human review after a failed verification. Uses public key.
POST /verify/v1/review-request Request Body
{
"session_id": "ses_abc123",
"reason": "Age was incorrectly estimated"
} Response
{
"success": true,
"data": {
"review_id": "rev_abc123",
"status": "pending",
"created_at": "2026-03-30T10:40:00Z"
},
"meta": {
"request_id": "req_ghi012",
"timestamp": "2026-03-30T10:40:00Z"
}
} EU Wallet (Path E)
Verify age using EU Digital Identity Wallet credentials (mso_mdoc attestations). Uses the W3C Digital Credentials API and OpenID4VP protocol.
Validate Wallet Attestation
Validate an mso_mdoc age attestation directly. Uses public key.
POST /verify/v1/wallet/validate Request Body
{
"mso_mdoc": "base64_encoded_mso_mdoc_attestation...",
"age_threshold": 18
} Response
{
"success": true,
"data": {
"above_threshold": true,
"age_threshold": 18,
"issuer": "DE",
"wallet_provider": "eu-id-wallet"
},
"meta": {
"request_id": "req_wal123",
"timestamp": "2026-03-30T10:30:00Z"
}
} Start Wallet Transaction
Start an OpenID4VP cross-device verification flow. Returns a QR code URL for the user to scan with their wallet app. Uses public key.
POST /verify/v1/wallet/transaction Request Body
{
"age_threshold": 18,
"callback_url": "https://yoursite.com/wallet-verified"
} Response
{
"success": true,
"data": {
"transaction_id": "wtx_abc123",
"qr_code_url": "https://api.xident.io/verify/v1/wallet/transaction/wtx_abc123/qr",
"openid4vp_url": "openid4vp://authorize?request_uri=...",
"expires_at": "2026-03-30T10:35:00Z"
},
"meta": {
"request_id": "req_wal456",
"timestamp": "2026-03-30T10:30:00Z"
}
} Poll Wallet Transaction Status
Poll for the status of a cross-device wallet transaction. Uses public key.
GET /verify/v1/wallet/transaction/:id/status Response
{
"success": true,
"data": {
"transaction_id": "wtx_abc123",
"status": "completed",
"above_threshold": true,
"age_threshold": 18
},
"meta": {
"request_id": "req_wal789",
"timestamp": "2026-03-30T10:32:00Z"
}
} Face 2FA
Register Face
Enroll a user's face for 2FA on your site.
POST /verify/v1/face-2fa/register Request Body
{
"user_id": "your-user-123",
"face_image": "base64_encoded_image..."
} Response
{
"success": true,
"data": {
"face_id": "face_abc123",
"enrolled_at": "2026-03-30T10:30:00Z"
},
"meta": {
"request_id": "req_f2a123",
"timestamp": "2026-03-30T10:30:00Z"
}
} Verify Face
Authenticate a user via face match.
POST /verify/v1/face-2fa/verify Request Body
{
"user_id": "your-user-123",
"face_image": "base64_encoded_image..."
} Response
{
"success": true,
"data": {
"verified": true,
"confidence": 0.97,
"face_id": "face_abc123"
},
"meta": {
"request_id": "req_f2a456",
"timestamp": "2026-03-30T10:30:00Z"
}
} Blacklist
Check Blacklist
Check if a face matches any in your blacklist.
POST /verify/v1/blacklist/check Request Body
{
"face_image": "base64_encoded_image..."
} Response (No Match)
{
"success": true,
"data": {
"match_found": false,
"matches": []
},
"meta": {
"request_id": "req_bl123",
"timestamp": "2026-03-30T10:30:00Z"
}
} Response (Match Found)
{
"success": true,
"data": {
"match_found": true,
"matches": [
{
"blacklist_id": "bl_abc123",
"similarity": 0.94,
"reason": "fraud",
"added_at": "2026-01-10T08:00:00Z"
}
]
},
"meta": {
"request_id": "req_bl456",
"timestamp": "2026-03-30T10:30:00Z"
}
} Add to Blacklist
Add a face to your blacklist.
POST /verify/v1/blacklist/add Request Body
{
"face_image": "base64_encoded_image...",
"reason": "fraud",
"metadata": {
"original_user_id": "user-456"
}
} Response
{
"success": true,
"data": {
"blacklist_id": "bl_abc123"
},
"meta": {
"request_id": "req_bl789",
"timestamp": "2026-03-30T10:30:00Z"
}
} Usage
Get Usage Statistics
Retrieve usage data for your project.
GET /dashboard/v1/usage Query Parameters
| Parameter | Type | Description |
|---|---|---|
start_date | string | Start date (ISO 8601) |
end_date | string | End date (ISO 8601) |
Response
{
"success": true,
"data": {
"period": {
"start": "2026-03-01T00:00:00Z",
"end": "2026-03-31T23:59:59Z"
},
"summary": {
"total_verifications": 1523,
"full_verifications": 892,
"token_verifications": 631,
"total_cost": 45.67
},
"by_type": [
{
"type": "full",
"count": 892,
"cost": 35.68
},
{
"type": "token",
"count": 631,
"cost": 9.47
}
]
},
"meta": {
"request_id": "req_usg123",
"timestamp": "2026-03-30T10:30:00Z"
}
} Error Responses
All errors follow this format:
{
"success": false,
"error": {
"code": "invalid_token",
"message": "The token has expired or already been used"
},
"meta": {
"request_id": "req_err123",
"timestamp": "2026-03-30T10:30:00Z"
}
} Error Codes
| Code | HTTP Status | Description |
|---|---|---|
invalid_token | 400 | Token is invalid, expired, or already used |
unauthorized | 401 | Invalid or missing API key |
forbidden | 403 | API key doesn't have permission |
not_found | 404 | Resource not found |
rate_limited | 429 | Too many requests |
internal_error | 500 | Server error |
Rate Limits
| Endpoint | Limit |
|---|---|
/verify/v1/init | 100 requests/minute |
/verify/v1/status/* | 100 requests/minute |
/verify/v1/face-2fa/* | 60 requests/minute |
/verify/v1/blacklist/* | 30 requests/minute |
/verify/v1/wallet/* | 60 requests/minute |
/verify/v1/feedback | 10 requests/minute |
/verify/v1/review-request | 10 requests/minute |
/dashboard/v1/usage | 10 requests/minute |
Rate limit headers are included in responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705315200 Webhooks
Configure webhooks in your Dashboard to receive real-time events.
Event Types
| Event | Description |
|---|---|
verification.completed | User completed verification |
verification.failed | Verification failed |
token.verified | Token was verified |
Webhook Payload
{
"id": "evt_abc123",
"type": "verification.completed",
"created_at": "2026-03-30T10:30:00Z",
"data": {
"verification_id": "ver_xyz789",
"user_id": "your-user-123",
"above_threshold": true,
"age_threshold": 18,
"methods_completed": ["liveness", "age_estimation"]
}
} Signature Verification
Verify webhook signatures to ensure authenticity:
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
} Next Steps
- Getting Started - Quick setup guide
- Integration Guide - Frontend SDK usage
- Concepts - Understanding Xident