PolicifyAI
Dashboard

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

200

OK

Request succeeded. Body contains the result.

400

Bad Request

Malformed JSON, a missing required field, or an invalid value (e.g. bad policy_type or jurisdiction).

401

Unauthorized

Missing, invalid, or revoked API key.

402

Payment Required

Your subscription is inactive, or you've hit your plan's policy generation limit.

403

Forbidden

Key lacks the required scope, or your plan doesn't include API access.

404

Not Found

The requested resource does not exist, or isn't owned by this API key.

429

Too Many Requests

Rate limit exceeded. See the Retry-After header.

500

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:

CodeStatusMeaning
unauthorized401API key missing or invalid
not_found404Resource with the given ID does not exist, or you don't own it
invalid_param400A query or body parameter has an invalid format
server_error500Unexpected 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}`)
  }
}