API reference
Error Reference
Always check the HTTP status code first - it's reliable across every endpoint. The error body shape varies by endpoint (see below), so don't build logic that assumes a single fixed error structure.
Error response shapes
The policy-generation endpoints (/policies/generate, /policies/bulk, /policies) return a flat string message:
{
"error": "jurisdiction is required"
}Newer endpoints (/policies/{id}/publish, /consent/logs) return a structured object with a lower-case, snake_case code:
{
"error": {
"code": "not_found",
"message": "Policy not found"
}
}HTTP status codes
OK
Request succeeded. Body contains the result.
Bad Request
Malformed JSON, a missing required field, or an invalid value (e.g. bad policy_type or jurisdiction).
Unauthorized
Missing, invalid, or revoked API key.
Payment Required
Your subscription is inactive, or you've hit your plan's policy generation limit.
Forbidden
Key lacks the required scope, or your plan doesn't include API access.
Not Found
The requested resource does not exist, or isn't owned by this API key.
Too Many Requests
Rate limit exceeded. See the Retry-After header.
Internal Server Error
Unexpected server error. Contact support if it persists.
Structured error codes
Only the newer, structured-error endpoints return a machine-readable code:
| Code | Status | Meaning |
|---|---|---|
| unauthorized | 401 | API key missing or invalid |
| not_found | 404 | Resource with the given ID does not exist, or you don't own it |
| invalid_param | 400 | A query or body parameter has an invalid format |
| server_error | 500 | Unexpected server error |
On the flat-string endpoints, match on the HTTP status code instead - the message text is for logging/display, not branching logic.
Recommended error handling
async function callPolicify(endpoint, options) {
const res = await fetch(`https://policifyai.com/api/v1${endpoint}`, options)
if (res.ok) return res.json()
const body = await res.json()
// 'error' is either a string or { code, message } depending on the endpoint
const message = typeof body.error === 'string' ? body.error : body.error.message
switch (res.status) {
case 429: {
const retryAfter = parseInt(res.headers.get('Retry-After') ?? '60')
await sleep(retryAfter * 1000)
return callPolicify(endpoint, options) // retry once
}
case 401:
throw new Error('Invalid API key - check your environment variable')
case 402:
throw new Error('Policy quota exhausted or subscription inactive - upgrade your plan')
default:
throw new Error(`API error ${res.status}: ${message}`)
}
}