Errors & limits
Every error code the Partner API returns, and every cap it enforces.
Every failure has the same shape. Branch on error.error_code; log description for a human; never match on its text. error.error_attributes carries the details a code defines, and is null for the rest.
{
"description": "story_document: the link serves \"text/html\", which this input does not accept. Point at the file itself.",
"code": "url_type_not_allowed",
"error": {
"id": null,
"error_code": "url_type_not_allowed",
"error_attributes": null,
"user_readable_message": null,
"locale": "en"
}
}code repeats error.error_code for older clients; read error.error_code.
Error codes
| HTTP | error_code | Means | What to do |
|---|---|---|---|
| 400 | invalid_request | a body or input value is wrong; the description names the input | fix it and resend. Never retry unchanged |
| 400 | url_not_fetchable | a link could not be fetched, broke one of the link rules, or served an empty file | check it against Passing files by URL |
| 400 | url_type_not_allowed | the link served a content type this input does not take, or bytes that are not the type it claimed | serve the file itself, with its real type |
| 400 | url_too_large | the file is over the input's max_bytes, or over what the run has left of its byte budget | send a smaller file |
| 401 | unauthorized | the key is missing, malformed, revoked or expired | stop and alert someone. A retry cannot help |
| 403 | api_key_credit_limit_reached | the key is at one of its own credit limits; error_attributes.period says which | ask an admin to raise the key's limit in Studio, or wait for a daily or monthly limit to reset |
| 403 | credits_exhausted | the organization has no credits left | top up the organization |
| 403 | access_denied | org_id is not the key's organization, the key lacks the scope, or it may not run this workflow | check the key's scopes and workflow allowlist in Studio |
| 404 | entity_not_found | no such workflow, project, run or webhook endpoint in your organization — or a run of a workflow outside the key's allowlist | check the id |
| 409 | idempotency_conflict | the same idempotency_key arrived with a different body | use a new key for new work |
| 429 | too_many_requests | a read limit was reached | wait Retry-After, then resend |
| 500 | internal_error | ours | retry with backoff; if it persists, send us the x-morphic-trace-id |
Limits
| Limit | Default | Notes |
|---|---|---|
| Credits | the key's own limits | a total every key has, and optional daily and monthly caps — see Credit limits |
| Reads of one run | 30 per minute, per key | one read a minute per run is plenty; wait=45 is cheaper still |
| Reads of all runs | 1,200 per minute, per key | room to follow hundreds of runs at once |
| Links per run | 10 | across all file inputs together |
| Bytes per run | 500 MB | across all links together |
| Bytes per file | the input's max_bytes | documents up to 100 MB; media caps are per input |
| Run lifetime | 24 hours | a run not finished by expires_at becomes expired |
| Webhook endpoints | 10 per organization | remove one before adding another |
Starting runs is never rate-limited: a key's credit limits bound what a retry loop can spend, and an idempotency_key turns a retry into a replay. The read limits are raised on request for a volume we have discussed.
Billing
Credits for a run started over the API are billed to the organization that owns the key, and counted against the key that started it. An admin sees the spend under Billing & usage → Credit activity in Studio, and each key's limits on the API keys page.
A run keeps counting against the key that started it even if somebody steps into the chat in Studio and takes it over, so the key's remaining budget is the budget for the whole run.
A run refused before it starts — a bad link, or a key or organization without credit — costs nothing. A run that fails partway through has consumed whatever its completed steps cost; credits_used on the run says how much.