Skip to main content

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 PrefixEnvironment
qi_live_Production — real verifications, charges apply
qi_test_Sandbox — simulated results, free to use

Create a Verification Session

cURL
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:
201 Created
{
  "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
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:
200 OK
{
  "id": "doc_xyz789",
  "session_id": "ses_abc123",
  "side": "front",
  "document_type": "passport",
  "status": "uploaded",
  "created_at": "2026-03-17T12:01:00Z"
}
FieldRequiredValues
fileYesJPG, PNG, or PDF (max 10MB)
sideYesfront or back
document_typeNopassport, id_card, driving_license, residence_permit
Upload a selfie with the same pattern:
cURL
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
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:
200 OK
{
  "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
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:
200 OK
{
  "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
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
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:
200 OK
{
  "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
}
StatusError CodeDescription
401authentication_errorMissing or invalid API key
402insufficient_creditsAccount has no remaining credits for this operation
404not_foundThe requested resource does not exist
409session_completedSession already has a final decision
410session_expiredSession token has expired
422validation_errorRequest body failed validation. Check details for field-level errors
429rate_limitedToo many requests. Retry after the Retry-After header value
500server_errorInternal error on our end. Retry with backoff

Validation error example

422 Unprocessable Entity
{
  "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:
HeaderDescription
X-RateLimit-LimitMaximum requests per minute
X-RateLimit-RemainingRequests remaining in the current window
Retry-AfterSeconds 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.

Pagination

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
  }
}
ParameterDefaultDescription
page1Page number
per_page20Items 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.