API
Handle API errors
Classify device and app API failures and correlate requests without parsing human-readable messages.
Error contract
Device and app API errors include a top-level machine-readable code. Existing
detail, message, error, and validation errors fields remain available;
their shape depends on the endpoint. Branch on code, not message text.
{
"detail": "Device not connected",
"code": "device_not_connected"
}This contract covers /api/v1/execution/start_device, device-proxy routes,
device-session routes, streaming worker-connection routes, and /api/v1/apps
routes, including app creation, build upload/finalization, and remote-build runner
availability. The legacy /api/v1/builds routes use the same contract.
The partner connections API retains its separate nested error.code envelope.
Do not treat a product error as a partner connection error.
Request correlation
Send a UUID in X-Request-ID to correlate your request with Revyl diagnostics.
Revyl echoes its canonical UUID spelling on success and error responses. If the
header is missing or invalid, Revyl generates a UUID. Record the returned header
when reporting a failure. A request ID is not an idempotency key and does not
make a repeated mutation safe.
Codes and recovery
| Code | Meaning and action |
|---|---|
device_not_connected | The worker reports that its device is not connected. Re-check session readiness before sending another action. |
upload_expired | The multipart upload no longer exists. Discard its saved upload state and start a new upload. |
worker_gone | The session's worker route is missing or the worker cannot be reached. Re-check the authorized session state; do not assume a mutation was never applied. |
app_exists | An active app with that name already exists in your organization. The 409 includes existing_app_id; use that ID instead of creating another app. |
no_runner | Build admission verified that no eligible runner is active. Wait for capacity before retrying a build submission. |
credits_exhausted | Build admission was denied by the compute-credit gate. Check the payer's credits before retrying. |
billing_denied | Device admission was denied by the billing gate. Resolve the payer's entitlement before retrying. |
billing_unavailable | Billing verification could not complete. Retry only when the requested operation is safe to repeat. |
authentication_required | Provide valid credentials. |
access_denied | The authenticated caller cannot perform this operation. |
not_found | The resource is missing or inaccessible to this caller. The response does not distinguish other organizations' resources. |
invalid_request | Correct the request, including any validation errors, before retrying. |
conflict | Resolve the conflicting state before repeating the operation. |
rate_limited | Respect Retry-After when present. |
request_timeout | The request timed out. Read the current state before repeating a mutation. |
temporarily_unavailable | A dependency is temporarily unavailable; preserve the request ID for diagnostics. |
worker_error | The worker request failed without a more specific classification. |
internal_error | Revyl could not complete the request. Preserve the request ID; do not automatically replay a mutation. |
Existing feature-specific codes are preserved. Handle unknown codes as errors using the HTTP status and retain their request IDs; clients should not require a closed list of codes.
Retry-After, when supplied, is a delay in seconds. It is not a promise that
capacity will be available afterwards. No retry delay is invented when Revyl
does not know one.
An ordinary full device pool can return 200 with an initializing or queued
session. Likewise, runner availability can return 200 with available: false. These are status results, not error responses; no slots_full error is
implied.
Recover an existing app
For an app-name conflict, the response retains its existing human-readable detail and adds the organization-scoped ID:
{
"detail": "An app named \"My app\" already exists in this organization.",
"code": "app_exists",
"existing_app_id": "00000000-0000-0000-0000-000000000123"
}The ID comes only from the caller's organization and excludes deleted apps.