Open your account →

Rox Verify API

Integrate KYC & KYB identity verification into your application. REST + JSON, authenticated with HMAC request signing.

Base URL — https://platform.roxverify.io/api/v1

API keys

Keys are created inside your Rox Verify account.

Sign in at platform.roxverify.ioIntegration, and generate a credential. Each credential is a public Key + a Secret. The secret is shown once — store it securely; it never travels over the network and is used only to sign requests. Credentials are scoped to the abilities you select (kyc:create, kyc:read, kyc:pdf, and the kyb:* equivalents).

Authentication (HMAC request signing)

Send these headers on every request:

X-Api-Key:        <your key>
X-Api-Timestamp:  <unix seconds>        # must be within 5 minutes
X-Api-Signature:  <hex HMAC-SHA256>

The signature is HMAC-SHA256 of the following string, using your secret as the key:

{METHOD}\n{REQUEST_PATH}\n{TIMESTAMP}\n{sha256_hex(body)}

# e.g. POST /api/v1/sessions
POST\n/api/v1/sessions\n1750000000\n<sha256 of the JSON body>

Ready-to-use Node.js helper:

const crypto = require('crypto');

const KEY    = 'rk_live_...';   // X-Api-Key
const SECRET = 'rsk_...';       // never sent — signs requests
const BASE   = 'https://platform.roxverify.io/api/v1';

// Build the signed headers for a request
function sign(method, path, body = '') {
  const ts = Math.floor(Date.now() / 1000).toString();
  const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
  const signingString = [method.toUpperCase(), path, ts, bodyHash].join('\n');
  const signature = crypto.createHmac('sha256', SECRET).update(signingString).digest('hex');
  return {
    'X-Api-Key': KEY,
    'X-Api-Timestamp': ts,
    'X-Api-Signature': signature,
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };
}

// Create a verification session
const body = JSON.stringify({ client_name: 'Jane Cooper', vendor_data: 'user-123' });
const path = '/api/v1/sessions';
fetch(BASE + '/sessions', { method: 'POST', headers: sign('POST', path, body), body })
  .then(r => r.json()).then(console.log);

Requests with a missing/invalid signature, or a timestamp older than 5 minutes, are rejected with 401 — so an intercepted request can’t be replayed or modified.

1 · Create a verification session

POST/sessions kyc:create

Creates a user (KYC) session and returns a hosted URL to send to your client.

{ "client_name": "Jane Cooper", "client_email": "jane@example.com", "vendor_data": "user-123" }
→ { "id": 42, "type": "user", "status": "Not Started", "session_url": "https://verify.roxverify.io/session/AbC123" }

To open a business verification (KYB) session instead, pass "type": "business" (requires the kyb:create ability):

{ "type": "business", "company_name": "Sellsides Ltd", "company_website": "sellsides.com", "vendor_data": "company-123" }
→ { "id": 51, "type": "business", "status": "Not Started", "session_url": "https://verify.roxverify.io/session/XyZ789" }

company_website (or a company email) is optional — it’s used to show the business logo on the verification.

2 · Get status & extracted data

GET/sessions/{id} kyc:read

Overall status, per-check results, and the extracted identity once your client finishes — so you get more than just Approved / Declined.

{
  "id": 42, "type": "user", "status": "Approved",
  "checks": { "id_verification": "Approved", "liveness": "Approved", "face_match": "Approved",
              "aml": "Approved", "ip_analysis": "Approved",
              "proof_of_address": "Approved", "email": "Approved", "phone": "Approved" },

  "identity": {
    "first_name": "James", "last_name": "Whitfield", "full_name": "James Whitfield",
    "date_of_birth": "1988-04-17", "nationality": "GBR",
    "country": "United Kingdom", "country_code": "GBR",
    "country_code_alpha2": "GB", "country_flag": "🇬🇧",
    "document_type": "Passport",
    "document_number": "533401372",
    "id_number_type": "passport_number",   // national_id_number | residence_number | ...
    "passport_number": "533401372",        // same value, under its type-specific key
    "address": "221B Baker Street, London, NW1 6XE"
  },

  // The three blocks below appear ONLY when your workflow runs that step:
  "address_verification": { "verified": true, "status": "Approved",
                            "address": "221B Baker Street, London, NW1 6XE",
                            "document_type": "Bank Statement" },
  "email_verification":   { "email": "james@example.com", "verified": true, "status": "Approved" },
  "phone_verification":   { "phone_number": "+44 7700 900123", "verified": true, "status": "Approved" },

  "charged_services": ["kyc","liveness","aml","ip"]
}
  • identity is returned as soon as an ID document has been read.
  • id_number_type tells you whether the ID number is a passport_number, national_id_number, residence_number or driving_license_number; the same value is also repeated under that key.
  • country_flag (emoji) and country_code_alpha2 are derived from the issuing country for easy display.
  • address_verification, email_verification and phone_verification are present only when your workflow includes those steps — each carries a boolean verified flag.

3 · Get the full decision

GET/sessions/{id}/decision kyc:read

Everything from the status call above (identity + the verification blocks) plus the complete raw decision — document data, biometric scores, AML hits, IP / device.

4 · Download the report PDF

GET/sessions/{id}/pdf kyc:pdf

Returns the official report as a PDF (application/pdf).

Webhooks (optional)

Instead of polling, Rox Verify will POST the verified event to your endpoint when a session reaches a final status. Configure your receiving URL in your account under Integration → Webhooks.

Each webhook is signed the same way as API responses, so you can verify it genuinely came from Rox Verify before trusting it.

KYB (kyb:*) endpoints mirror the KYC ones and are available when business verification is enabled on your account.