API Reference
Complete ZYKAY API documentation.
URLs
| Service | URL |
|---|---|
| Widget (script tag) | https://widget-app.zykay.com/v4/loader.min.js |
| API Partner | https://api.zykay.com |
Endpoints
POST /v1/exchange
Exchanges a grant_code for an pass_token and the verification results.
Quick example:
curl -X POST https://api.zykay.com/v1/exchange \
-H "Content-Type: application/json" \
-H "X-Partner-ID: pk_live_xxx" \
-H "X-Partner-Timestamp: 1700000000" \
-H "X-Partner-Nonce: 550e8400-e29b-41d4-a716-446655440000" \
-H "X-Partner-Signature: IiqKqzxLThjE4anzR2UGycy5JrJKSjjD2m3R81RxsW8" \
-d '{"grant_code":"g_abc123..."}'Answer :
{
"pass_token": "p_xyz789...",
"expires_in": 14400,
"age_over_18": true,
"scopes": ["isAdult"],
"attributes": {
"age_over_18": true
}
}POST /v1/introspect
Checks the validity of a pass_token and returns the verification attributes.
Quick example:
curl -X POST https://api.zykay.com/v1/introspect \
-H "Content-Type: application/json" \
-H "X-Partner-ID: pk_live_xxx" \
-H "X-Partner-Timestamp: 1700000000" \
-H "X-Partner-Nonce: 550e8400-e29b-41d4-a716-446655440000" \
-H "X-Partner-Signature: ..." \
-d '{"pass_token":"p_xyz789..."}'Answer :
{
"active": true,
"scope": "multi_scope_verification",
"attributes": {
"age_over_18": true,
"is_french": true
},
"scopes_verified": ["isAdult", "isFrench"]
}HMAC authentication
All requests to the API must be signed with HMAC-SHA256.
Required headers
| Header | Description |
|---|---|
X-Partner-ID | Your Partner ID (pk_live_xxx) |
X-Partner-Timestamp | Unix timestamp in seconds |
X-Partner-Nonce | Unique UUID v4 per request |
X-Partner-Signature | Signature HMAC-SHA256 (base64url) |
Signature generation
const crypto = require('crypto');
function base64url(buffer) {
return buffer.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
// 1. Hash du body (SHA256)
const bodyHash = base64url(
crypto.createHash('sha256').update(JSON.stringify(body)).digest()
);
// 2. Chaîne canonique
const canonical = `${bodyHash}.${timestamp}.${partnerId}.${nonce}`;
// 3. IMPORTANT: Décoder le secret base64
const secretBytes = Buffer.from(partnerSecret, 'base64');
// 4. Signature HMAC
const signature = base64url(
crypto.createHmac('sha256', secretBytes).update(canonical).digest()
);⚠️
The Partner Secret is base64 encoded. You MUST decode it before using it for HMAC.
Scopes available
| Scope | Kind | Result | EUDI Wallet | |
|---|---|---|---|---|
isAdult | Binary | age_over_18: true | Yes | Yes |
isFrench | Binary | is_french: true | Yes | Yes |
isEU | Binary | is_eu: true | Yes | Yes |
isMale | Binary | is_male: true | Yes | No |
isFemale | Binary | is_female: true | Yes | No |
isUnique | Nullify | nullifier: "0x..." | Yes | Yes |
revealNationality | Disclosure | nationality: "FRA" | Yes | No |
revealBirthYear | Disclosure | birth_year: 1990 | Yes | No |
⚠️
isMale and isFemale are mutually exclusive. Scopes marked No for the EUDI Wallet are not available with data-client-proof-mode="true".
Error codes
| Code | HTTP | Description |
|---|---|---|
MISSING_HEADERS | 401 | Missing required headers |
INVALID_PARTNER | 403 | Partner ID not recognized |
TIMESTAMP_SKEW | 401 | Timestamp offset > 5 minutes |
INVALID_SIGNATURE | 401 | Incorrect HMAC signature |
REPLAY_DETECTED | 401 | Nonce already used |
INVALID_GRANT | 400 | Grant code invalid or expired |
→ Complete list of error codes
Rate limits
The published limits and retry strategy are documented here: