Errors

HTTP status codes, error envelope, and error codes returned by the API.

Error envelope

Most errors return a JSON error object with a machine‑readable code, a human‑readable message, and an optional details array, alongside the request meta:

{
  "error": {
    "code": "agent_unavailable",
    "message": "Agent is currently disabled.",
    "details": []
  },
  "meta": {
    "request_id": "b7f0b9c2-1a2b-4c3d-8e9f-0a1b2c3d4e5f"
  }
}

A few failures bypass the envelope and return Laravel's bare { "message": "..." } shape. Branch on the HTTP status code first, then read error.code when the body carries an error object:

  • 401 Unauthenticated — a missing, malformed, revoked, or expired bearer key: { "message": "Unauthenticated." }.
  • 403 / 404 from ownership or route binding — an agent (or other resource) you don't own, or a UUID that doesn't resolve: { "message": "This action is unauthorized." }.

Everything else uses the envelope, including validation (422 validation_failed), a missing scope, an expired key, and a missing premium plan.

Validation errors

When a request body or query parameter fails validation, the API returns 422 with the standard envelope, code: "validation_failed", and a details array naming each invalid field:

{
  "error": {
    "code": "validation_failed",
    "message": "The content field is required.",
    "details": [
      { "field": "content", "message": "The content field is required." }
    ]
  },
  "meta": { "request_id": "b7f0b9c2-1a2b-4c3d-8e9f-0a1b2c3d4e5f" }
}

message repeats the first field error for convenience; iterate details — each entry is { field, message } — to surface every problem at once.

This is not Laravel's default { "message", "errors": { "field": [...] } } validation shape. The API normalizes validation into the same error.code / error.details envelope as every other error, so read error.details[].field and error.details[].message.

Status codes

StatusMeaning
200 OKRequest succeeded.
201 CreatedResource created.
202 AcceptedAsynchronous job accepted (crawl started).
204 No ContentSucceeded with no body (delete).
401 UnauthorizedMissing/invalid key, or the key has expired.
403 ForbiddenNot the owner, missing scope, or no premium plan.
404 Not FoundThe agent, source, or training job does not exist, or the source/job does not belong to the agent in the path. A resource that exists but you don't own returns 403, not 404.
422 Unprocessable EntityValidation failed or a subscription limit was reached.
423 LockedThe agent is disabled and cannot accept the operation.
429 Too Many RequestsRate limit exceeded — see Rate Limits.
502 Bad GatewayAn upstream dependency (storage or the training service) failed.

Error codes

These appear as error.code in the envelope:

codeStatusMeaning
token_expired401The API key passed its expiry date.
premium_required403Organization lacks an active premium/AppSumo plan.
insufficient_scope403The key is missing the scope this endpoint requires.
forbidden403The key's user has no organization, or is not the organization owner (returned by source creation when the owner-only check fails).
resource_not_found404The source or training job was not found for this agent.
validation_failed422A request body or query parameter failed validation; details[] lists each { field, message }. Also covers domain-rule failures such as an invalid source type on retrain.
subscription_limit_reached422An agent‑count limit on the plan was reached.
subscription_limit_exceeded422A training quota (text, URLs, or document size) was exceeded.
premium_plan_required422A premium‑only model or setting was requested on a non‑premium plan.
agent_unavailable423The agent is disabled, so source/model writes are blocked.
upstream_failure502Storage upload, crawl, retrain, or status lookup failed upstream.

Examples

403 insufficient_scope

{
  "error": {
    "code": "insufficient_scope",
    "message": "Token is missing required scope: source:create",
    "details": []
  },
  "meta": { "request_id": "b7f0b9c2-1a2b-4c3d-8e9f-0a1b2c3d4e5f" }
}

422 validation_failed

{
  "error": {
    "code": "validation_failed",
    "message": "The content field is required.",
    "details": [
      { "field": "content", "message": "The content field is required." }
    ]
  },
  "meta": { "request_id": "b7f0b9c2-1a2b-4c3d-8e9f-0a1b2c3d4e5f" }
}

423 agent_unavailable

{
  "error": {
    "code": "agent_unavailable",
    "message": "Agent is currently disabled.",
    "details": []
  },
  "meta": { "request_id": "b7f0b9c2-1a2b-4c3d-8e9f-0a1b2c3d4e5f" }
}

Include the X-Request-ID response header (also in meta.request_id) when contacting support — it lets us trace the exact request in our logs.