Errors

The API returns conventional HTTP status codes plus a structured JSON body so clients can render field-specific errors.

Status codes

Status Meaning
200 Success
201 Created (POST)
204 No content (typically DELETE)
400 Validation failed — body has field-level errors
401 Missing or invalid auth token
403 Authenticated but not authorized for this action or record
404 Record does not exist, or exists in another tenant (we return 404 rather than 403 to avoid leaking existence across orgs)
409 Conflict — e.g. duplicate unique key
422 Semantically invalid (e.g. illegal state transition)
429 Rate limit exceeded
5xx Server error

Validation error shape

{
  "error": true,
  "errors": {
    "email": "Enter a valid email address.",
    "custom_fields": {
      "bant_score": "must be a number",
      "industry": "must be one of ['saas', 'manufacturing']"
    }
  }
}
  • Top-level keys are field names.
  • For nested objects (custom_fields), the value is itself a { key: message } map.
  • A single field can have a list of messages if multiple validators fire.

Auth error shape

{
  "detail": "Authentication credentials were not provided."
}

Or for an expired access token:

{
  "detail": "Token expired.",
  "code": "token_not_valid"
}

When you see code: token_not_valid, call POST /api/auth/refresh/ and retry the original request.

Rate limiting

{
  "detail": "Request was throttled. Expected available in 12 seconds.",
  "retry_after": 12
}

The Retry-After HTTP header is also set. Default limits: 1000 requests/hour per token for read endpoints, 200 for write endpoints. Self-hosted deployments can change this via REST_FRAMEWORK['DEFAULT_THROTTLE_RATES'] in settings.py.