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/introspect

Request

Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
X-Partner-IDYesYour Partner ID
X-Partner-TimestampYesUnix timestamp in seconds
X-Partner-NonceYesUnique UUID v4 per request
X-Partner-SignatureYesSignature HMAC-SHA256 (base64url)

The HMAC signature is calculated in the same way as for the Exchange API.

Body

{
  "pass_token": "p_xyz789abc123..."
}
FieldKindRequiredDescription
pass_tokenstringYesPass 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
  }
}
FieldKindDescription
activebooleantrue if the token is valid and not expired
scopestringVerification type ("age_verification", "identity_verification", "multi_scope_verification")
expnumberExpiry timestamp (milliseconds)
iatnumberCreation timestamp (milliseconds)
substringVerification flow identifier
attributesobjectVerification attributes (same fields as exchange response)
scopes_verifiedstring[]List of checked scopes
proof_metadataobjectProof 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

CodeHTTPDescription
INVALID_REQUEST400pass_token missing or invalid
INTERNAL_ERROR500Server 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)