Base URL
All API requests use the following base URL:
https://verilock.io/api/v1
Authentication
Include your API key in the Authorization header as a Bearer token:
Authorization: Bearer qi_live_...
Content-Type: application/json
Keep your API key secret. Never expose it in client-side code, mobile apps, or public repositories. All API calls must originate from your backend server.
| Key Prefix | Environment |
|---|
qi_live_ | Production — real verifications, charges apply |
qi_test_ | Sandbox — simulated results, free to use |
Create a Verification Session
curl -X POST https://verilock.io/api/v1/sessions \
-H "Authorization: Bearer qi_live_..." \
-H "Content-Type: application/json" \
-d '{
"profile_id": "01234567-89ab-cdef-0123-456789abcdef",
"applicant_email": "john@example.com",
"external_id": "user_12345",
"redirect_url": "https://example.com/callback",
"metadata": {
"plan": "premium"
}
}'
Response:
{
"id": "ses_abc123",
"status": "created",
"session_token": "tok_abc123",
"session_url": "https://verilock.io/verify/tok_abc123",
"expires_at": "2026-03-18T12:00:00Z"
}
Use session_token to initialize the Web or Flutter SDK. Use session_url for redirect-based flows.
Upload a Document (Multipart)
curl -X POST https://verilock.io/api/v1/sessions/ses_abc123/documents \
-H "Authorization: Bearer qi_live_..." \
-F "file=@/path/to/id_front.jpg" \
-F "side=front" \
-F "document_type=passport"
Response:
{
"id": "doc_xyz789",
"session_id": "ses_abc123",
"side": "front",
"document_type": "passport",
"status": "uploaded",
"created_at": "2026-03-17T12:01:00Z"
}
| Field | Required | Values |
|---|
file | Yes | JPG, PNG, or PDF (max 10MB) |
side | Yes | front or back |
document_type | No | passport, id_card, driving_license, residence_permit |
Upload a selfie with the same pattern:
curl -X POST https://verilock.io/api/v1/sessions/ses_abc123/selfie \
-H "Authorization: Bearer qi_live_..." \
-F "file=@/path/to/selfie.jpg"
AML Screening
Screen a person against global sanctions lists, PEP databases, and watchlists.
curl -X POST https://verilock.io/api/v1/aml/screen \
-H "Authorization: Bearer qi_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"date_of_birth": "1990-01-15",
"nationality": "US"
}'
Response:
{
"id": "aml_abc123",
"status": "completed",
"risk_level": "low",
"matches": [],
"screened_lists": ["ofac_sdn", "un_sanctions", "eu_sanctions", "pep_global"],
"created_at": "2026-03-17T12:02:00Z"
}
Retrieve a previous screening:
curl https://verilock.io/api/v1/aml/screen/aml_abc123 \
-H "Authorization: Bearer qi_live_..."
Transaction Screening
Screen transactions for AML/CFT risk indicators.
curl -X POST https://verilock.io/api/v1/transactions/screen \
-H "Authorization: Bearer qi_live_..." \
-H "Content-Type: application/json" \
-d '{
"transaction_ref": "TX-001",
"transaction_type": "wire_transfer",
"sender_name": "Alice Smith",
"sender_country": "US",
"receiver_name": "Bob Johnson",
"receiver_country": "NG",
"amount": 5000,
"currency": "USD"
}'
Response:
{
"id": "txs_abc123",
"status": "completed",
"risk_level": "medium",
"risk_score": 62,
"flags": ["cross_border", "high_risk_jurisdiction"],
"transaction_ref": "TX-001",
"created_at": "2026-03-17T12:03:00Z"
}
Batch screening
Screen up to 100 transactions in a single request:
curl -X POST https://verilock.io/api/v1/transactions/screen/batch \
-H "Authorization: Bearer qi_live_..." \
-H "Content-Type: application/json" \
-d '{
"transactions": [
{ "transaction_ref": "TX-002", "sender_name": "Alice", "receiver_name": "Charlie", "amount": 200, "currency": "EUR" },
{ "transaction_ref": "TX-003", "sender_name": "Alice", "receiver_name": "Diana", "amount": 15000, "currency": "USD" }
]
}'
Wallet Screening
Screen crypto wallet addresses against known illicit activity databases.
curl -X POST https://verilock.io/api/v1/wallet/screen \
-H "Authorization: Bearer qi_live_..." \
-H "Content-Type: application/json" \
-d '{
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD08",
"network": "ethereum"
}'
Response:
{
"id": "wlt_abc123",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD08",
"network": "ethereum",
"risk_level": "low",
"risk_score": 12,
"flags": [],
"labels": [],
"created_at": "2026-03-17T12:04:00Z"
}
Supported networks: ethereum, bitcoin, polygon, bsc, solana, tron, arbitrum, optimism.
Error Responses
All errors follow a consistent JSON structure:
{
"error": "error_code",
"message": "Human-readable description of the problem.",
"details": null
}
| Status | Error Code | Description |
|---|
401 | authentication_error | Missing or invalid API key |
402 | insufficient_credits | Account has no remaining credits for this operation |
404 | not_found | The requested resource does not exist |
409 | session_completed | Session already has a final decision |
410 | session_expired | Session token has expired |
422 | validation_error | Request body failed validation. Check details for field-level errors |
429 | rate_limited | Too many requests. Retry after the Retry-After header value |
500 | server_error | Internal error on our end. Retry with backoff |
Validation error example
{
"error": "validation_error",
"message": "The given data was invalid.",
"details": {
"name": ["The name field is required."],
"amount": ["The amount must be a number."]
}
}
Rate Limiting
Every response includes rate-limit headers:
| Header | Description |
|---|
X-RateLimit-Limit | Maximum requests per minute |
X-RateLimit-Remaining | Requests remaining in the current window |
Retry-After | Seconds until the window resets (only on 429 responses) |
Default rate limit is 120 requests/minute per API key. Contact us if you need higher limits for production workloads.
When you receive a 429, wait for the number of seconds specified in Retry-After before retrying. Our SDKs handle this automatically with exponential backoff.
List endpoints return paginated results using a data / meta pattern:
{
"data": [
{ "id": "ses_abc123", "status": "completed", "..." : "..." },
{ "id": "ses_def456", "status": "pending", "..." : "..." }
],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 142,
"last_page": 8
}
}
| Parameter | Default | Description |
|---|
page | 1 | Page number |
per_page | 20 | Items per page (max 100) |
curl "https://verilock.io/api/v1/sessions?page=2&per_page=50&status=completed" \
-H "Authorization: Bearer qi_live_..."
Webhook Verification
When you configure webhooks, Verilock signs every payload with HMAC-SHA256 so you can verify authenticity.
Each webhook request includes a X-Verilock-Signature header:
X-Verilock-Signature: sha256=a1b2c3d4e5f6...
Verify the signature
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
// In your webhook handler:
const isValid = verifyWebhook(rawBody, req.headers['x-verilock-signature'], webhookSecret);
Always use constant-time comparison (timingSafeEqual, compare_digest, hash_equals) to prevent timing attacks.
Webhook payload structure
{
"event": "session.completed",
"data": {
"id": "ses_abc123",
"status": "completed",
"decision": "approved",
"risk_score": 15,
"completed_at": "2026-03-17T12:05:00Z"
},
"created_at": "2026-03-17T12:05:01Z"
}
Common event types: session.completed, session.expired, aml.completed, transaction.screened, wallet.screened.