HTTP success means Epho accepted or read the request. A run can still settle as failed, timed out, or canceled, so asynchronous clients must inspect the turn status.
HTTP status codes
| Status | Meaning |
|---|---|
200 | Successful read or synchronous SSE stream |
201 | Signup or device OAuth ticket created |
202 | Turn queued or cancellation accepted |
401 | Missing or invalid Epho bearer token |
402 | Team has no positive compute credit |
403 | Invalid device OAuth polling secret |
404 | Resource not found, outside the token's team, or unsupported OAuth provider |
409 | Another turn is already running in this chat |
422 | Request validation failed |
429 | Signup or OAuth polling rate limit exceeded |
Validation errors
Laravel validation responses use an errors object:
{
"message": "The given data was invalid.",
"errors": {
"model": [
"Unsupported model for the selected harness."
]
}
}
Common causes include:
- missing
prompt,harness, ormodel; - an unsupported harness/model pair;
- a missing provider key for a paid model;
- an unsupported CPU or memory value;
- invalid environment-variable names or limits;
- malformed input files;
- duplicate or reserved MCP server names;
- sending
envormcp_serverswhile resuming a chat.
Turn statuses
| Status | Terminal | Meaning |
|---|---|---|
processing | no | Queued, provisioning, resuming, or running |
completed | yes | Agent finished successfully |
failed | yes | Launch or agent execution failed |
timed_out | yes | Agent timed out or stopped heartbeating |
canceled | yes | Client requested cancellation |
Read the error field when a terminal status is not completed.
{
"turn_id": "01K...",
"status": "failed",
"output": null,
"error": "Failed to start the run.",
"artifacts": []
}
Retry guidance
- Retry
429only after respecting the response's rate-limit timing. - Do not retry
422without changing the request. - For
409, wait for the active turn or use a different chat. - A submission that returned
202should be tracked through its existingturn_id; do not create a duplicate turn just because polling is quiet. - Lifecycle events and SSE keepalives distinguish queued or quiet work from a disconnected client.