Introspection API
Check the validity of a pass_token and retrieve the verification attributes.
Introspection is optional. Since v2.6.2, POST /v1/exchange returns verification attributes directly. Introspection is useful for re-verifying an existing pass_token in a different context (for example, during a user reconnection).
Endpoint
POST https://api.zykay.com/v1/introspectRequest
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | application/json |
X-Partner-ID | Yes | Your Partner ID |
X-Partner-Timestamp | Yes | Unix timestamp in seconds |
X-Partner-Nonce | Yes | Unique UUID v4 per request |
X-Partner-Signature | Yes | Signature HMAC-SHA256 (base64url) |
The HMAC signature is calculated in the same way as for the Exchange API.
Body
{
"pass_token": "p_xyz789abc123..."
}| Field | Kind | Required | Description |
|---|---|---|---|
pass_token | string | Yes | Pass token received during exchange (starts with p_) |
Answer
Active token (200)
{
"active": true,
"scope": "multi_scope_verification",
"exp": 1739548800000,
"iat": 1739534400000,
"sub": "fid_abc123...",
"attributes": {
"age_over_18": true,
"is_french": true,
"nullifier": "0x7a3b9c...",
"verification_method": "france_identite",
"verified_at": 1739534400000
},
"scopes_verified": ["isAdult", "isFrench", "isUnique"],
"proof_metadata": {
"proof_count": 1,
"total_generation_time_ms": 2500
}
}| Field | Kind | Description |
|---|---|---|
active | boolean | true if the token is valid and not expired |
scope | string | Verification type ("age_verification", "identity_verification", "multi_scope_verification") |
exp | number | Expiry timestamp (milliseconds) |
iat | number | Creation timestamp (milliseconds) |
sub | string | Verification flow identifier |
attributes | object | Verification attributes (same fields as exchange response) |
scopes_verified | string[] | List of checked scopes |
proof_metadata | object | Proof metadata (number of proofs, generation time) |
Inactive token (200)
An expired, invalid or unknown token returns a status 200 with active: false:
{
"active": false
}According to the RFC 7662 convention, an invalid token returns an HTTP status 200 (not 401 or 404). Always check the active field in the response.
Errors
| Code | HTTP | Description |
|---|---|---|
INVALID_REQUEST | 400 | pass_token missing or invalid |
INTERNAL_ERROR | 500 | Server error |
HMAC signature errors (MISSING_HEADERS, INVALID_SIGNATURE, etc.) are the same as Exchange API.
Examples
const crypto = require('crypto');
function base64url(buffer) {
return buffer.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
async function introspect(partnerId, partnerSecret, passToken) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const body = JSON.stringify({ pass_token: passToken });
const bodyHash = base64url(
crypto.createHash('sha256').update(body, 'utf8').digest()
);
const canonical = `${bodyHash}.${timestamp}.${partnerId}.${nonce}`;
const secretBuffer = Buffer.from(partnerSecret, 'base64');
const signature = base64url(
crypto.createHmac('sha256', secretBuffer).update(canonical).digest()
);
const response = await fetch('https://api.zykay.com/v1/introspect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Partner-ID': partnerId,
'X-Partner-Timestamp': timestamp,
'X-Partner-Nonce': nonce,
'X-Partner-Signature': signature
},
body
});
const data = await response.json();
if (data.active) {
console.log('Token valide, scopes:', data.scopes_verified);
console.log('Attributs:', data.attributes);
} else {
console.log('Token expiré ou invalide');
}
return data;
}Common pitfall: The Partner Secret is base64 encoded. You MUST decode it before using it as an HMAC key. See the signing guide for details.
Use cases
- Session re-verification: verify that a pass_token stored in session is still active
- Backend side verification: validate a pass_token transmitted by the frontend
- Audit: consult the proof metadata (number of proofs, generation time)