application/problem+json:
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.
type links directly to that code’s entry on this page, so the URL in an
error always takes you to its explanation.Authentication (401)
missing_api_key
No Authorization header on the request.
Fix: Send Authorization: Bearer ck_live_... (or ck_sandbox_...).
invalid_api_key
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.
revoked_api_key
The key was revoked.
Fix: Use a current key, or ask us to issue a replacement.
expired_api_key
The key passed its expiry date.
Fix: Ask us for a fresh key.
partner_disabled
The account itself is disabled — no key will work.
Fix: Contact api@trycactus.com.
Access (403)
The key authenticated, but can’t reach this product. The code tells you who can unblock it. See Product access for which endpoints belong to which product.product_not_enabled
Your account isn’t enabled for that product.
Fix: Contact api@trycactus.com — only Cactus
can add a product to an account.
missing_scope
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.
Request (400)
invalid_request
Malformed body or parameters. The errors[] array lists each offending
field with a field, code, and message.
Fix: Correct the fields named in errors[].
Not found (404)
not_found
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.
Method not allowed (405)
method_not_allowed
The path exists but not for that HTTP method.
Fix: Check the method against the API reference.
Conflict (409)
The resource exists but isn’t in a state that allows this call. All of these become retryable once the underlying condition changes.document_not_uploaded
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.
document_in_use
The document is attached to an extraction or underwriting still in flight.
Fix: Wait for that job to reach a terminal status, then retry.
extraction_not_completed
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.
underwriting_inputs_not_completed
You asked for the result before the job finished.
Fix: Poll until status is completed, or use a webhook.
Rejected at acceptance (422)
Validation failures. None of these are billed — a document is only charged once it passes acceptance.document_not_found
A referenced document id doesn’t exist.
Fix: Check the id from the POST /v1/documents response.
document_deleted
A referenced document has been deleted.
Fix: Re-upload it.
duplicate_document
The same document id appears more than once in one request.
Fix: De-duplicate document_ids.
document_over_size_cap
Over 500 pages or 100 MB. Rejected outright and not charged.
Fix: Split the file and submit the parts separately.
document_type_unresolvable
We couldn’t determine the document’s type.
Fix: Set declared_type explicitly. See
Preparing documents.
unsupported_asset_class
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.
end_user_ref_required
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.
rate_unavailable
No rate-card line exists for that document type on your account.
Fix: Contact api@trycactus.com — this is a
configuration gap on our side.
Rate limit (429)
rate_limited
Too many requests.
Fix: Wait the number of seconds in the Retry-After header, then retry.
Server (500)
internal_error
Something failed on our side.
Fix: Retry. If it persists, send us the request_id from the response.
Jobs that fail after being billed are refunded in full, automatically.
A failed extraction or underwriting never costs you anything.