Error codes
Reference for all error codes returned by ZYKAY APIs.
Exchange API Errors
| Code | HTTP | Description | Solution |
|---|---|---|---|
MISSING_HEADERS | 401 | One or more required headers are missing | Include all headers: X-Partner-ID, X-Partner-Timestamp, X-Partner-Nonce, X-Partner-Signature |
INVALID_PARTNER | 403 | Partner ID not recognized | Check your Partner ID |
TIMESTAMP_SKEW | 401 | Timestamp shifted by more than 5 minutes | Use current Unix timestamp in seconds, synchronize server clock |
INVALID_SIGNATURE | 401 | HMAC signature verification failed | Check the signature calculation. IMPORTANT: The partner secret is base64 and must be decoded before use |
REPLAY_DETECTED | 401 | The nonce has already been used | Use a unique UUID v4 for each request |
INVALID_REQUEST | 400 | Invalid request body or missing grant_code | Check the JSON format and the presence of grant_code |
INVALID_GRANT | 400 | Invalid grant format | The grant_code must start with g_ |
GRANT_INVALID | 401 | Grant code nonexistent or expired | Grants expire in 5 minutes and are single use |
INTERNAL_ERROR | 500 | Server side error | Try again with exponential backoff |
Introspection API errors
| Code | HTTP | Description | Solution |
|---|---|---|---|
INVALID_REQUEST | 400 | pass_token missing or invalid | Check that the body contains {"pass_token": "p_..."} |
INTERNAL_ERROR | 500 | Server side error | Try again with exponential backoff |
HMAC signature errors (MISSING_HEADERS, INVALID_SIGNATURE, etc.) are the same as the Exchange API above.
An expired or invalid token returns a status 200 with {"active": false} (not an HTTP error), according to RFC 7662.
Billing Session Errors (ADULT_BLIND)
These errors are returned by POST /api/billing/session only.
| Code | HTTP | Description | Fix |
|---|---|---|---|
FORBIDDEN_RAIL | 403 | Organization is not on the ADULT_BLIND rail | Contact tech@zykay.com to enable the rail |
MISSING_BLIND_APP_ID | 400 | blindAppId not configured for the organization | Contact ZYKAY support |
MISSING_ORIGIN | 400 | origin field missing from body | Include "origin": "https://yoursite.com" in the body |
INVALID_SCOPES | 400 | Scopes not supported for your tier | Check available scopes for your level |
INVALID_JSON | 400 | Malformed JSON body | Check the body format |
HMAC errors (MISSING_HEADERS, INVALID_PARTNER, INVALID_SIGNATURE, etc.) are the same as the Exchange API.
Loader v4 errors (frontend)
The v4 loader does not use an error postMessage. Errors go through the onError callback:
| Code | Description | Action |
|---|---|---|
INIT_FAILED | Failed to create verify-request | Check data-partner-id, connectivity and CORS |
SERVER_VERIFY_TIMEOUT | server-verify mode timeout | Check your endpoint data-server-verify-endpoint |
HTTP Status Codes
| Status | Meaning |
|---|---|
200 | Success |
400 | Bad request - Check request body |
401 | Unauthorized - Check credentials/signature |
404 | Not found - Check endpoint URL |
429 | Too many requests - Rate limit reached (see published policy) |
500 | Internal Server Error - Try Again Later |
Policy 429 (summary)
GET /api/w/verify-status/:requestId?pt=...: 30 req / 60s, returnsRetry-After: 60POST /api/partner/verify-request: 60 req / 60sGET /api/partner/verify-status/:requestId: 60 req / 60sPOST /api/billing/session(pre-auth IP): 30 req / 60sPOST /api/billing/session(post-auth partner): 100 req / 60s
Full reference: Rate-Limit Policy
Error handling (recommended integration)
async function callWithRetry(makeCall) {
const delaysMs = [1000, 2000, 4000];
for (let i = 0; i < delaysMs.length; i++) {
const res = await makeCall();
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get('Retry-After') || '0');
const waitMs = retryAfter > 0 ? retryAfter * 1000 : delaysMs[i];
await new Promise((r) => setTimeout(r, waitMs));
}
throw new Error('Rate limit exceeded after retries');
}