> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycactus.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every error code the API returns, and what to do about each one.

Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem
documents, served as `application/problem+json`:

```json theme={null}
{
  "type": "https://docs.trycactus.com/errors#product_not_enabled",
  "title": "Product not enabled for this account",
  "status": 403,
  "detail": "Your account is not enabled for underwriting. Contact api@trycactus.com to enable it.",
  "code": "product_not_enabled",
  "request_id": "req_01J9Z6X2QK8N4M"
}
```

Branch on **`code`**, never on `title` or `detail` — those are prose and may
be reworded. `code` is stable. Every response, success or failure, also
carries a `Request-Id` header; quote it when you contact support.

<Note>
  `type` links directly to that code's entry on this page, so the URL in an
  error always takes you to its explanation.
</Note>

<h2 id="authentication-401">
  Authentication (401)
</h2>

<h3 id="missing_api_key">
  `missing_api_key`
</h3>

No `Authorization` header on the request.

**Fix:** Send `Authorization: Bearer ck_live_...` (or `ck_sandbox_...`).

<h3 id="invalid_api_key">
  `invalid_api_key`
</h3>

The header is malformed, or the key isn't one we recognize. Also returned
when the scheme isn't `Bearer`, or the key doesn't start with `ck_live_` /
`ck_sandbox_`.

**Fix:** Check the key was copied whole — they're shown once at issuance.

<h3 id="revoked_api_key">
  `revoked_api_key`
</h3>

The key was revoked.

**Fix:** Use a current key, or ask us to issue a replacement.

<h3 id="expired_api_key">
  `expired_api_key`
</h3>

The key passed its expiry date.

**Fix:** Ask us for a fresh key.

<h3 id="partner_disabled">
  `partner_disabled`
</h3>

The account itself is disabled — no key will work.

**Fix:** Contact [api@trycactus.com](mailto:api@trycactus.com).

<h2 id="access-403">
  Access (403)
</h2>

The key authenticated, but can't reach this product. The code tells you who
can unblock it. See [Product access](/authentication#product-access) for
which endpoints belong to which product.

<h3 id="product_not_enabled">
  `product_not_enabled`
</h3>

Your account isn't enabled for that product.

**Fix:** Contact [api@trycactus.com](mailto:api@trycactus.com) — only Cactus
can add a product to an account.

<h3 id="missing_scope">
  `missing_scope`
</h3>

Your account has the product, but the key you used was issued without it.

**Fix:** Use a key that carries the scope. Ask us if you're not sure which
of your keys does.

<h2 id="request-400">
  Request (400)
</h2>

<h3 id="invalid_request">
  `invalid_request`
</h3>

Malformed body or parameters. The `errors[]` array lists each offending
field with a `field`, `code`, and `message`.

**Fix:** Correct the fields named in `errors[]`.

<h2 id="not-found-404">
  Not found (404)
</h2>

<h3 id="not_found">
  `not_found`
</h3>

No such resource — or it belongs to another account. The two are
deliberately indistinguishable, so one account can't probe another's ids.

**Fix:** Check the id, and that it was created with the same account.

<h2 id="method-not-allowed-405">
  Method not allowed (405)
</h2>

<h3 id="method_not_allowed">
  `method_not_allowed`
</h3>

The path exists but not for that HTTP method.

**Fix:** Check the method against the API reference.

<h2 id="conflict-409">
  Conflict (409)
</h2>

The resource exists but isn't in a state that allows this call. All of these
become retryable once the underlying condition changes.

<h3 id="document_not_uploaded">
  `document_not_uploaded`
</h3>

The document was registered, but the file bytes never arrived.

**Fix:** `PUT` the file to the upload URL from `POST /v1/documents` before
referencing it.

<h3 id="document_in_use">
  `document_in_use`
</h3>

The document is attached to an extraction or underwriting still in flight.

**Fix:** Wait for that job to reach a terminal status, then retry.

<h3 id="extraction_not_completed">
  `extraction_not_completed`
</h3>

You asked for the result before the job finished.

**Fix:** Poll the extraction (or use `?wait=`) until `status` is
`completed`. Better still, register a webhook endpoint.

<h3 id="underwriting_inputs_not_completed">
  `underwriting_inputs_not_completed`
</h3>

You asked for the result before the job finished.

**Fix:** Poll until `status` is `completed`, or use a webhook.

<h2 id="rejected-at-acceptance-422">
  Rejected at acceptance (422)
</h2>

Validation failures. **None of these are billed** — a document is only
charged once it passes acceptance.

<h3 id="document_not_found">
  `document_not_found`
</h3>

A referenced document id doesn't exist.

**Fix:** Check the id from the `POST /v1/documents` response.

<h3 id="document_deleted">
  `document_deleted`
</h3>

A referenced document has been deleted.

**Fix:** Re-upload it.

<h3 id="duplicate_document">
  `duplicate_document`
</h3>

The same document id appears more than once in one request.

**Fix:** De-duplicate `document_ids`.

<h3 id="document_over_size_cap">
  `document_over_size_cap`
</h3>

Over 500 pages or 100 MB. Rejected outright and not charged.

**Fix:** Split the file and submit the parts separately.

<h3 id="document_type_unresolvable">
  `document_type_unresolvable`
</h3>

We couldn't determine the document's type.

**Fix:** Set `declared_type` explicitly. See
[Preparing documents](/preparing-documents).

<h3 id="unsupported_asset_class">
  `unsupported_asset_class`
</h3>

There's no underwriting model for that asset class yet. The message lists
the supported ones.

**Fix:** Use a supported asset class, or ask us about the one you need.

<h3 id="end_user_ref_required">
  `end_user_ref_required`
</h3>

Your account requires `end_user_ref` on every run, so each of your users'
analyses lands in its own isolated workspace.

**Fix:** Pass `end_user_ref` — your own opaque identifier for that user.

<h3 id="rate_unavailable">
  `rate_unavailable`
</h3>

No rate-card line exists for that document type on your account.

**Fix:** Contact [api@trycactus.com](mailto:api@trycactus.com) — this is a
configuration gap on our side.

<h2 id="rate-limit-429">
  Rate limit (429)
</h2>

<h3 id="rate_limited">
  `rate_limited`
</h3>

Too many requests.

**Fix:** Wait the number of seconds in the `Retry-After` header, then retry.

<h2 id="server-500">
  Server (500)
</h2>

<h3 id="internal_error">
  `internal_error`
</h3>

Something failed on our side.

**Fix:** Retry. If it persists, send us the `request_id` from the response.

<Note>
  Jobs that fail after being billed are **refunded in full**, automatically.
  A failed extraction or underwriting never costs you anything.
</Note>
