Age Verification
/v1/age/verificationsConfirm that a person has reached a minimum age, using the date of birth held by the RDCPASS national digital identity instead of a self-declared birthday or a checkbox. Send an RDCPASS ID or an identity document number and the age threshold you must enforce; RDCPASS answers with over_threshold and the person’s basic KYC. Every call requires the authentication stack described in Authentication.
At a glance
- Endpoints:
POST /v1/age/verifications(single) andPOST /v1/age/verifications/batches(batch). - Input: an
identifier—rdcpass_idor a document type (passport,ceni,driving_licence,national_id) — and athresholdin whole years (default18). - Output:
result(verifiedornot_found),over_threshold,age,accountandkyc.basic. - Basic KYC only — additional KYC scopes never apply to this service.
- Declared
purposeis alwaysage_gating. - Single, batch, synchronous and asynchronous modes; async results arrive on the
age_verification.completedwebhook.
This service returns basic KYC only
Age Verification never returns documents, addresses, phone numbers, biometrics or any other additional scope — even if your organization subscribes to them and your application has selected them. The request takes noscopes or document_delivery field, and scopes_applied is always ["kyc.basic"] on a verified result. If you need more than basic identity data, use KYC Validation instead. See KYC scopes & subscriptions for how basic KYC is defined.Use cases
Age Verification is designed for any business that must keep minors out of a product or channel, and must be able to prove it did so. Set threshold to the minimum age that applies to your activity under Congolese law, your regulator’s rules or your own policy.
| Industry | Typical threshold | Where it fits |
|---|---|---|
| Gaming & betting | 18 | Account opening on sports-betting and online-casino platforms, before the first deposit, and again before a withdrawal to an unverified wallet. |
| Alcohol & tobacco e-commerce | 18 or 21 | At checkout, before an order containing restricted products is accepted — and at delivery, by checking the customer’s CENI card number against the order. |
| Social media & digital platforms | 13, 16 or 18 | Sign-up age assurance, access to age-restricted content or features, and eligibility for creator monetization and payouts. |
| Telecommunications | Minimum age for SIM registration | SIM card registration at a shop or agent, and bulk re-checks of an existing subscriber base during a regulatory registration campaign. |
How the check works
RDCPASS resolves the identifier to a certified RDCPASS account, computes the person’s age in whole years from their registered date of birth on the date of the request, and compares it with threshold. over_threshold is true when age is greater than or equal to threshold. A person who turns 18 today is over_threshold for a threshold of 18.
Only verified + over_threshold: true is a pass
Grant access only whenresult is verified and over_threshold is true. A not_found result carries over_threshold: null and must be treated as a failed check — ask the person to register with RDCPASS or to use another identifier.Request body
Plaintext JSON shape, before encryption.
| Field | Type | Required | Description |
|---|---|---|---|
| identifier | object | Yes | The person to check, { "type", "value" }. type is rdcpass_id (e.g. COD-2103-0214-4937) or a document type: passport (e.g. OB1234567), ceni (voter card, e.g. 1234567890123), driving_licence or national_id. |
| threshold | integer | No | Minimum age in whole years, from 1 to 120. Default 18. |
| purpose | string enum | Yes | Always age_gating, which must be among the purposes approved for your application. |
| reference | string | In batches | Your own identifier for this check, echoed back in the response and webhooks. Optional for single calls, required for every batch item. |
{
"identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" },
"threshold": 18,
"purpose": "age_gating",
"reference": "player-KIN-55012"
}{
"identifier": { "type": "ceni", "value": "1234567890123" },
"threshold": 21,
"purpose": "age_gating",
"reference": "order-LUB-2026-00931"
}Response body
A successful call returns 200 OK. Both verified and not_found are successful calls, not errors.
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier of the check, prefixed age_. |
| object | string | Always age_verification. |
| livemode | boolean | true in production, false in the sandbox. |
| reference | string | null | The reference you sent, or null. |
| status | string enum | Always completed for synchronous calls. Async and batch items can be failed. |
| result | string enum | verified or not_found. See the table below. |
| over_threshold | boolean | null | true if age ≥ threshold, false otherwise. null when result is not_found. |
| threshold | integer | The threshold applied to this check. |
| age | integer | null | Age in whole years on the date of the request. null when result is not_found. |
| identifier | object | The identifier you sent. |
| account | object | Verified only. RDCPASS account: rdcpass_id, status, certified, created_at, level_of_assurance. |
| kyc.basic | object | Verified only. Basic KYC: full_name, first_name, last_name, date_of_birth, age, gender, nationality. No other kyc block is ever present. |
| scopes_applied | string[] | ["kyc.basic"] when verified, empty when not_found. |
| scopes_withheld | string[] | Always empty for this service. |
| purpose | string | The purpose you declared (age_gating). |
| created_at | string | When the check was performed (ISO 8601, UTC). |
| result | Meaning | Data returned |
|---|---|---|
| verified | A certified RDCPASS account exists for the identifier; its date of birth was used to compute age. | over_threshold, age, account, kyc.basic. |
| not_found | No certified RDCPASS account matches the identifier. | None — over_threshold and age are null. |
Keep only what you need
Basic KYC is returned so you can link the check to the right customer record. If your obligation is only to prove an age check, store the checkid, result, over_threshold and threshold, and discard the rest.{
"id": "age_5f1b8c3d27",
"object": "age_verification",
"livemode": true,
"reference": "player-KIN-55012",
"status": "completed",
"result": "verified",
"over_threshold": true,
"threshold": 18,
"age": 38,
"identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" },
"account": {
"rdcpass_id": "COD-2103-0214-4937",
"status": "active",
"certified": true,
"created_at": "2025-03-14T09:22:41Z",
"level_of_assurance": "LOA3"
},
"kyc": {
"basic": {
"full_name": "Kabeya Mwamba Tshisekedi",
"first_name": "Kabeya",
"last_name": "Tshisekedi",
"date_of_birth": "1988-04-12",
"age": 38,
"gender": "male",
"nationality": "COD"
}
},
"scopes_applied": ["kyc.basic"],
"scopes_withheld": [],
"purpose": "age_gating",
"created_at": "2026-09-26T10:15:02Z"
}{
"id": "age_2e9d4a7c80",
"object": "age_verification",
"livemode": true,
"reference": "player-GOM-10877",
"status": "completed",
"result": "verified",
"over_threshold": false,
"threshold": 18,
"age": 16,
"identifier": { "type": "rdcpass_id", "value": "COD-0910-7734-2051" },
"account": {
"rdcpass_id": "COD-0910-7734-2051",
"status": "active",
"certified": true,
"created_at": "2026-01-08T14:03:19Z",
"level_of_assurance": "LOA3"
},
"kyc": {
"basic": {
"full_name": "Mbuyi Ilunga Nsimba",
"first_name": "Mbuyi",
"last_name": "Nsimba",
"date_of_birth": "2010-02-19",
"age": 16,
"gender": "female",
"nationality": "COD"
}
},
"scopes_applied": ["kyc.basic"],
"scopes_withheld": [],
"purpose": "age_gating",
"created_at": "2026-09-26T10:17:45Z"
}{
"id": "age_9c4e1f6b32",
"object": "age_verification",
"livemode": true,
"reference": "player-KIN-55013",
"status": "completed",
"result": "not_found",
"over_threshold": null,
"threshold": 18,
"age": null,
"identifier": { "type": "rdcpass_id", "value": "COD-4417-0092-6618" },
"scopes_applied": [],
"scopes_withheld": [],
"purpose": "age_gating",
"created_at": "2026-09-26T10:18:10Z"
}Example request
The same check in eight languages. Header values are illustrative — see Authentication to compute a real signature, encrypt the body and set X-RDCPASS-IV.
# Plaintext body shown for readability - encrypt and sign it as described in /docs/authentication before sending.
curl https://api.rdcpass.cd/v1/age/verifications \
-X POST \
-H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
-H "X-RDCPASS-Timestamp: 1735689600" \
-H "X-RDCPASS-Nonce: 7a3c9e1b-5d2f-4b86-a0e4-1c8f6d2b9a73" \
-H "X-RDCPASS-IV: Zt4Kp1Qw8Rm3Xv6N" \
-H "X-RDCPASS-Signature: 1d6f2a...b48e" \
-H "Content-Type: application/json" \
-d '{
"identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" },
"threshold": 18,
"purpose": "age_gating",
"reference": "player-KIN-55012"
}'{
"id": "age_5f1b8c3d27",
"object": "age_verification",
"livemode": true,
"reference": "player-KIN-55012",
"status": "completed",
"result": "verified",
"over_threshold": true,
"threshold": 18,
"age": 38,
"identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" },
"account": {
"rdcpass_id": "COD-2103-0214-4937",
"status": "active",
"certified": true,
"created_at": "2025-03-14T09:22:41Z",
"level_of_assurance": "LOA3"
},
"kyc": {
"basic": {
"full_name": "Kabeya Mwamba Tshisekedi",
"first_name": "Kabeya",
"last_name": "Tshisekedi",
"date_of_birth": "1988-04-12",
"age": 38,
"gender": "male",
"nationality": "COD"
}
},
"scopes_applied": ["kyc.basic"],
"scopes_withheld": [],
"purpose": "age_gating",
"created_at": "2026-09-26T10:15:02Z"
}Request modes
Age Verification supports all four request modes. Add Prefer: respond-async to run asynchronously and Idempotency-Key to retry safely. Details in Single, batch & async.
| Synchronous (default) | Asynchronous (Prefer: respond-async) | |
|---|---|---|
Single — POST /v1/age/verifications | 200 with the result. | 202 with a job; result via the age_verification.completed webhook or GET /v1/jobs/{job_id}. |
Batch — POST /v1/age/verifications/batches | 200 with all results, up to 50 items. | 202 with a batch, up to 10,000 items; results via webhook or GET /v1/batches/{batch_id}/results. |
Batch example
Typical for re-checking an existing player or subscriber base. Batch-level purpose and threshold apply to every item unless an item overrides them. Each item returns its own result; an invalid item fails on its own without failing the batch.
curl https://api.rdcpass.cd/v1/age/verifications/batches \
-X POST \
-H "X-RDCPASS-Key-Id: key_live_8f2a1c0e9b" \
-H "X-RDCPASS-Timestamp: 1735689600" \
-H "X-RDCPASS-Nonce: 7a3c9e1b-5d2f-4b86-a0e4-1c8f6d2b9a73" \
-H "X-RDCPASS-IV: Zt4Kp1Qw8Rm3Xv6N" \
-H "X-RDCPASS-Signature: 1d6f2a...b48e" \
-H "Idempotency-Key: 3f8a1d6c-92e4-4b07-8c5a-e1d9b2f7a604" \
-H "Content-Type: application/json" \
--data-binary @batch.json # Plaintext body shown for readability - encrypt and sign it as described in /docs/authentication before sending.{
"purpose": "age_gating",
"threshold": 18,
"items": [
{ "reference": "player-KIN-55012", "identifier": { "type": "rdcpass_id", "value": "COD-2103-0214-4937" } },
{ "reference": "player-GOM-10877", "identifier": { "type": "rdcpass_id", "value": "COD-0910-7734-2051" } },
{ "reference": "player-LUB-20411", "identifier": { "type": "passport", "value": "OB1234567" } },
{ "reference": "player-KIN-55014", "identifier": { "type": "rdcpass_id", "value": "COD-21O3-0214" } }
]
}{
"object": "list",
"data": [
{
"reference": "player-KIN-55012",
"status": "succeeded",
"result": {
"id": "age_5f1b8c3d27",
"object": "age_verification",
"result": "verified",
"over_threshold": true,
"threshold": 18,
"age": 38,
"account": { "rdcpass_id": "COD-2103-0214-4937", "status": "active", "certified": true, "...": "..." },
"kyc": { "basic": { "full_name": "Kabeya Mwamba Tshisekedi", "...": "..." } },
"scopes_applied": ["kyc.basic"],
"scopes_withheld": []
}
},
{
"reference": "player-GOM-10877",
"status": "succeeded",
"result": {
"id": "age_2e9d4a7c80",
"object": "age_verification",
"result": "verified",
"over_threshold": false,
"threshold": 18,
"age": 16,
"account": { "rdcpass_id": "COD-0910-7734-2051", "...": "..." },
"kyc": { "basic": { "full_name": "Mbuyi Ilunga Nsimba", "...": "..." } },
"scopes_applied": ["kyc.basic"],
"scopes_withheld": []
}
},
{
"reference": "player-LUB-20411",
"status": "succeeded",
"result": {
"id": "age_7b3f0e2a91",
"object": "age_verification",
"result": "not_found",
"over_threshold": null,
"threshold": 18,
"age": null,
"scopes_applied": [],
"scopes_withheld": []
}
},
{
"reference": "player-KIN-55014",
"status": "failed",
"error": {
"code": "invalid_identifier",
"message": "identifier.value is not a valid rdcpass_id (expected COD-XXXX-XXXX-XXXX)."
}
}
],
"next_cursor": null
}Asynchronous example
Send the same body with Prefer: respond-async. RDCPASS answers immediately with 202 Accepted and a job; when the check completes, the age_verification.completed event is delivered to your application’s webhook URL with the result embedded. You can also poll GET /v1/jobs/{job_id}. Asynchronous batches of up to 10,000 items return a batch object and emit batch.completed or batch.completed_with_errors. See Webhooks for signature verification.
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"id": "job_6a2d9f4e1c",
"object": "job",
"service": "age_verification",
"status": "pending",
"created_at": "2026-09-26T10:15:00Z",
"result_url": "/v1/jobs/job_6a2d9f4e1c"
}{
"id": "evt_1c7e4a9d3b2f8065",
"object": "event",
"type": "age_verification.completed",
"livemode": true,
"created_at": "2026-09-26T10:15:01Z",
"data": {
"object": {
"id": "job_6a2d9f4e1c",
"object": "job",
"service": "age_verification",
"status": "completed",
"created_at": "2026-09-26T10:15:00Z",
"result_url": "/v1/jobs/job_6a2d9f4e1c",
"result": {
"id": "age_5f1b8c3d27",
"object": "age_verification",
"reference": "player-KIN-55012",
"status": "completed",
"result": "verified",
"over_threshold": true,
"threshold": 18,
"age": 38,
"account": { "rdcpass_id": "COD-2103-0214-4937", "...": "..." },
"kyc": { "basic": { "...": "..." } },
"scopes_applied": ["kyc.basic"],
"scopes_withheld": []
}
}
}
}Billing
Each check is billed at the Age Verification base price — there is never a scope surcharge, because no additional scope applies. not_found results are billed at the base rate; requests rejected with an error are not billed. Batch items are billed individually. See Billing.
Errors
Errors use the standard format described in Errors. A threshold outside 1–120 or a missing identifier is rejected with 400 Bad Request.
| Code | HTTP | Cause | What to do |
|---|---|---|---|
| invalid_identifier | 400 | The identifier value is malformed for its type. | Check the format (e.g. COD-2103-0214-4937 for an RDCPASS ID). |
| unsupported_identifier_type | 400 | The identifier type is not accepted. | Use rdcpass_id, passport, ceni, driving_licence or national_id. |
| purpose_not_approved | 403 | age_gating is not an approved purpose for your application. | Add it to the application’s declared purposes in the console. |
| service_not_enabled | 403 | Age Verification is not enabled on this application. | Enable the service on the application in the console. |
| production_access_required | 403 | Production keys used before the application was approved for production. | Complete the process described in Going to production. |
| batch_too_large | 413 | A synchronous batch contains more than 50 items. | Send it with Prefer: respond-async (up to 10,000 items). |
| insufficient_balance | 402 | Prepaid wallet is empty. | Top up the wallet. |
| credit_limit_reached | 402 | Postpaid usage is over the credit limit. | Contact your financial officer to settle or raise the limit. |
| quota_exceeded | 429 | The plan’s quota is used up. | Wait for the reset or upgrade the plan. |
| rate_limited | 429 | Burst rate limit exceeded. | Back off per retry_after and retry with the same Idempotency-Key. |
Next steps
- KYC scopes & subscriptions — what basic KYC contains.
- KYC Validation — full identity verification with additional scopes.
- Single, batch & async — batches, jobs and polling in detail.
- Going to production — get production keys for your application.