KYC Verification
Add identity and age verification to your platform in minutes. ProntoID handles document scanning, biometric liveness, and compliance — you receive a webhook with the result.
Introduction
The ProntoID KYC API lets your platform verify users' real-world identities without handling any raw identity data yourself. When your backend calls create-kyc-verification, ProntoID returns a short-lived verification URL. You redirect your user there; ProntoID runs the checks; and then POSTs the structured result to your webhook endpoint.
Each KYC service you create (on the Configure page) gets its own independent pk_live_… / sk_live_… key pair, scoped to a single business name and set of verification requirements. This means you can run separate services for different products, environments, or compliance regions with full isolation.
How it works
client_reference to the ProntoID API using your service credentials. You receive back a verification_url.verification_url. ProntoID guides them through document capture, liveness detection, and age checks — depending on your service configuration.webhook_url. Your server validates and acts on it — never trust the return URL alone.return_url, users land back on your site after verification. Use this to show a confirmation screen — but always validate status from the webhook, not the URL.release_required: true, release_type, and an optional community slug when creating the verification. After KYC completes, ProntoID's completion screen redirects the user to sign a legally-binding release affidavit on secure.prontoid.com before returning to your platform.Create a service
Each integration starts with a KYC service. Go to Configure KYC Service, fill in your details, and submit. The page will display your API key and secret — copy the secret immediately, it won't be shown again.
A service stores: your business name (shown to users during verification), your requirements (identity, age, liveness, face match), compliance flags (2257, GDPR, payment processor), your webhook URL, an optional return URL, and the set of allowed origins for CORS.
API credentials
After creating a service you receive two credentials:
| Key | Format | Used in |
|---|---|---|
api_key |
pk_live_… or pk_test_… |
X-API-Key header |
api_secret |
sk_live_… or sk_test_… |
X-API-Secret header |
api_secret is only returned at creation time. Store it in an environment variable or secrets manager immediately. If lost, rotate via your KYC dashboard.Create a verification
When a user needs to be verified, your backend makes a POST to the verification endpoint. Pass your user's internal ID as client_reference — this is echoed back in the webhook so you can match results to your users.
https://75o3us32d7.execute-api.us-east-1.amazonaws.com/production/create-kyc-verification
require_once 'ProntoIDKYCClient.php'; // Credentials from your KYC service $apiEndpoint = 'https://75o3us32d7.execute-api.us-east-1.amazonaws.com/production/create-kyc-verification'; $apiKey = 'pk_live_…'; $apiSecret = 'sk_live_…'; $client = new ProntoIDKYCClient($apiEndpoint, $apiKey, $apiSecret); // $userId = your internal user identifier $result = $client->createVerification($userId, [ 'redirect_url' => 'https://yoursite.com/verified', // optional 'state' => 'order_8821', // optional passthrough // ProntoRelease — omit if no release form required 'release_required' => true, // require affidavit after KYC 'release_type' => 'creator_model', // 'creator_model' | 'creator' 'community' => 'malibustrings', // optional community slug ]); if ($result) { // Redirect the user to start verification header('Location: ' . $result['verification_url']); } else { $error = $client->getLastError(); // Handle error… }
curl -X POST \ https://75o3us32d7.execute-api.us-east-1.amazonaws.com/production/create-kyc-verification \ -H 'Content-Type: application/json' \ -H 'X-API-Key: pk_live_…' \ -H 'X-API-Secret: sk_live_…' \ -d '{ "client_reference": "user_abc123", "redirect_url": "https://yoursite.com/verified", "state": "order_8821", "release_required": true, "release_type": "creator_model", "community": "malibustrings" }'
Success response 200
{
"success": true,
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"verification_token": "vt_…",
"verification_url": "https://verify.prontoid.com/start?token=vt_…",
"expires_at": 1735430400,
"client_reference": "user_abc123"
}
Redirect the user
Send the user to the verification_url returned from the API. The URL is single-use and expires — do not cache it. ProntoID will guide the user through the configured checks (document capture, liveness, age gate) and then redirect to your return_url if one is set.
return_url only signals that the flow ended — it carries no verification result. Always wait for the webhook before granting access or marking a user as verified.Webhook
ProntoID POSTs verification results to the webhook_url you set when creating the service. Your endpoint must respond with HTTP 200; any other status or a timeout triggers a retry.
Example webhook handler — PHP
// kyc-webhook.php $payload = json_decode(file_get_contents('php://input'), true); if ($payload['status'] === 'approved') { $userId = $payload['client_reference']; // your internal user ID // Mark user as verified in your database markUserVerified($userId); } // Always respond 200 to acknowledge receipt http_response_code(200); echo json_encode(['received' => true]);
PHP client library
Drop ProntoIDKYCClient.php into your project. It handles request signing, JSON encoding, cURL transport, and error surfacing. No Composer dependency required.
require_once 'ProntoIDKYCClient.php'; $client = new ProntoIDKYCClient( 'https://75o3us32d7.execute-api.us-east-1.amazonaws.com/production/create-kyc-verification', $_ENV['PRONTOID_API_KEY'], $_ENV['PRONTOID_API_SECRET'] ); $result = $client->createVerification($userId, [ 'redirect_url' => 'https://yoursite.com/verified', 'state' => 'order_8821', 'metadata' => ['plan' => 'premium'], // ProntoRelease (omit if no release form required) 'release_required' => true, 'release_type' => 'creator_model', // 'creator_model' | 'creator' 'community' => 'malibustrings', // optional community slug ]); if ($result) { header('Location: ' . $result['verification_url']); exit; } // Error handling $error = $client->getLastError(); // human-readable message $httpCode = $client->getLastHttpCode(); // raw HTTP status
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
client_reference |
string | Required | Your internal user ID. Echoed back in the webhook so you can match results. |
redirect_url |
string (URL) | Optional | Override the return_url set on the service for this specific request. |
webhook_url |
string (URL) | Optional | Override the service-level webhook URL for this request only. |
state |
string | Optional | Arbitrary string passed through the flow and included in the webhook payload. Useful for correlating verifications with orders, sessions, etc. |
metadata |
object | Optional | Key-value pairs attached to the verification record. Not exposed to the end user. |
release_required |
boolean | Optional | Set to true to require the user to sign a ProntoRelease affidavit after KYC completes. When true, the completion screen replaces the normal "Go to website" button with a "Sign Release Form" CTA that routes to secure.prontoid.com/release-form.php. Defaults to false. |
release_type |
string | Optional |
Required when release_required is true. Controls which affidavit form is presented:creator_model — the user is both subject and producer (self-posted content, selfie-style). Signs a combined performer + producer release.creator — the user is a photographer or videographer. Signs a producer-only release including a structured records-keeping address and model warrant.
|
community |
string | Optional | Community slug used to skin the release form and set the post-signing redirect. For example, malibustrings loads the MalibuStrings-branded affidavit and redirects to malibustrings.bentbox.co/dashboard on completion. Omit to use the platform-level default config. |
Response fields
| Field | Type | Description |
|---|---|---|
success | boolean | true when the verification session was created successfully. |
request_id | string (UUID) | Unique ID for this verification request. Log this for support queries. |
verification_token | string | Short-lived token embedded in verification_url. Single-use. |
verification_url | string (URL) | The URL to redirect your user to. Expires at expires_at. |
expires_at | integer (Unix) | Unix timestamp after which the URL is invalid. Typically 30 minutes from creation. |
client_reference | string | Echo of the client_reference you submitted. |
Webhook payload
ProntoID sends a POST with Content-Type: application/json to your webhook URL. Respond with HTTP 200 to acknowledge.
{
"event": "verification.completed",
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"client_reference": "user_abc123",
"status": "approved", // "approved" | "rejected" | "pending_review"
"state": "order_8821",
"completed_at": 1735430400,
"checks": {
"identity_verification": "passed",
"age_verification": "passed", // present if enabled
"liveness_check": "passed", // present if premium+
"face_match": "passed" // present if premium+
},
"metadata": { "plan": "premium" }
}
Service tiers
| Feature | Basic | Premium | Enterprise |
|---|---|---|---|
| Identity verification | ✓ | ✓ | ✓ |
| Age verification | ✓ | ✓ | ✓ |
| Liveness detection | — | Premium | Enterprise |
| Face matching | — | Premium | Enterprise |
| Document authentication | — | — | Enterprise |
| Watchlist screening | — | — | Enterprise |
| Pricing | $0.50 / verification | $1.00 / verification + $29/mo | Custom — contact sales |
Error codes
| HTTP | Message | Cause & fix |
|---|---|---|
400 |
Missing required field | A required parameter was omitted. Check client_reference, headers. |
401 |
Authentication required | Missing or invalid X-API-Key / X-API-Secret headers. |
403 |
Permission denied | The credentials do not belong to this service, or the service is inactive. |
409 |
Service ID conflict | Duplicate service creation race condition — retry once. |
429 |
Rate limit exceeded | Too many requests. Back off and retry with exponential delay. |
500 |
Server error | Transient server-side error. Log request_id and contact support if it persists. |
Overview
ProntoRelease extends the KYC flow with a legally-binding digital affidavit signed by the user immediately after their identity is verified. It is designed for platforms hosting user-generated content — particularly adult or rights-sensitive material — where both identity and authorisation must be established in a single, tamper-evident record.
ProntoRelease handles two distinct legal relationships: a Creator / Model release for users who are both the subject and producer of their content (self-posted material), and a Photographer / Videographer release for users who produce content featuring other performers. Both release types generate a SHA-256-anchored PDF affidavit stored on prontoid-private S3 with AES-256 encryption, retained for seven years.
Where requires_2257 is enabled on the release service config, the affidavit includes the mandatory 18 U.S.C. § 2257 custodian clause, naming Brooks & Keitt Sàrl (Place du Midi 30, 1950 Sion, Switzerland) as the designated custodian of age-verification records.
Release parameters
The three ProntoRelease parameters are passed alongside the standard create-kyc-verification request body. They are stored on the verification session and read by the ProntoID completion screen to decide whether to show the release form CTA.
| Parameter | Type | Required | Description |
|---|---|---|---|
release_required |
boolean | Required | Must be true to trigger the release flow. When false (default), the normal KYC-only flow runs and the user is sent to return_url as usual. |
release_type |
string | Required |
creator_model — the user is both the subject and the producer of their content (e.g. selfie-style posts). The affidavit combines a performer sworn statement and a producer identification clause.creator — the user is a photographer or videographer uploading content featuring other performers. The affidavit is producer-only and includes a structured records-keeping address field and a mandatory model records warrant when § 2257 is enabled.
|
community |
string | Optional |
A community slug registered in prontoid-release-services. Controls the branding of the affidavit form, the platform and community names shown on the PDF, and the post-signing redirect URL. If omitted, the platform-level default config is used (identified by the _platform sentinel slug).Example: malibustrings renders the MalibuStrings affidavit and redirects to malibustrings.bentbox.co/dashboard after signing.
|
community slug must match a record in your prontoid-release-services table that is linked to the same kyc_service_id as the session. A mismatch returns a 403 from the release form fetch Lambda.Code example
The example below shows a typical BentBox-style integration where the user selects their role (Model or Photographer) on your site before KYC begins. The role maps directly to release_type.
// User selected "Verify as Model" on your platform $result = $client->createVerification($userId, [ 'release_required' => true, 'release_type' => 'creator_model', 'community' => 'malibustrings', // optional ]); // After KYC: user sees "Identity Verified — One More Step" // with a "Sign Release Form →" button instead of "Go to website". // Clicking it routes to: // secure.prontoid.com/release-form.php?access_token=…&community=malibustrings
// User selected "Verify as Photographer" on your platform $result = $client->createVerification($userId, [ 'release_required' => true, 'release_type' => 'creator', 'community' => 'malibustrings', // optional ]); // After KYC: user sees "Sign Photographer Release →" button. // The release form collects a structured records-keeping address // and renders the producer-only affidavit with § 2257 model warrant.
// Standard KYC — no release form (release_required defaults to false) $result = $client->createVerification($userId, [ 'redirect_url' => 'https://yoursite.com/verified', 'state' => 'order_8821', ]); // After KYC: user is sent to return_url as normal.
Post-KYC flow
release_type: creator_model or release_type: creator in the API call.release_required: true, release_type, and optionally community. These are stored on the verification session in KycConfig.release_required and shows "Sign Release Form →" instead of the normal return button.secure.prontoid.com/release-form.php, the form is pre-filled with verified identity data (name, DOB, nationality). The user adds aliases or a records address, types their signature, and submits. A SHA-256-anchored PDF is generated and stored in S3.return_url configured on the release service (community-level or platform-level). A release_token and document_hash are displayed on the success screen as permanent references.verification_token in prontoid-release-records and retained until the retain_until date.verification_token-index GSI before writing — a duplicate submission returns the existing release_token gracefully.