API Reference

Complete ZYKAY API documentation.

URLs

ServiceURL
Widget (script tag)https://widget-app.zykay.com/v4/loader.min.js
API Partnerhttps://api.zykay.com

Endpoints

POST /v1/exchange

Exchanges a grant_code for an pass_token and the verification results.

Full documentation

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.

Full documentation

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

HeaderDescription
X-Partner-IDYour Partner ID (pk_live_xxx)
X-Partner-TimestampUnix timestamp in seconds
X-Partner-NonceUnique UUID v4 per request
X-Partner-SignatureSignature 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

ScopeKindResultPDFEUDI Wallet
isAdultBinaryage_over_18: trueYesYes
isFrenchBinaryis_french: trueYesYes
isEUBinaryis_eu: trueYesYes
isMaleBinaryis_male: trueYesNo
isFemaleBinaryis_female: trueYesNo
isUniqueNullifynullifier: "0x..."YesYes
revealNationalityDisclosurenationality: "FRA"YesNo
revealBirthYearDisclosurebirth_year: 1990YesNo
⚠️

isMale and isFemale are mutually exclusive. Scopes marked No for the EUDI Wallet are not available with data-client-proof-mode="true".

Complete scope reference

Error codes

CodeHTTPDescription
MISSING_HEADERS401Missing required headers
INVALID_PARTNER403Partner ID not recognized
TIMESTAMP_SKEW401Timestamp offset > 5 minutes
INVALID_SIGNATURE401Incorrect HMAC signature
REPLAY_DETECTED401Nonce already used
INVALID_GRANT400Grant code invalid or expired

Complete list of error codes

Rate limits

The published limits and retry strategy are documented here:

Rate-Limit Policy

Documentation