Errors
Failed requests under /v1/integrations use a common JSON envelope. Branch on
HTTP status and stable code, not the human-readable message, and retain the
request ID for logs and support.
See the API Reference for endpoint-specific error schemas.
Error envelope
Code
Code
| Field | Use |
|---|---|
statusCode | Numeric HTTP status. |
message | Diagnostic text; do not branch on it. |
timestamp | Error timestamp. |
path | Requested path. |
code | Stable, case-sensitive error category. |
details | Error-specific context. |
request_id | Identifier matching X-Request-Id. |
For an unexpected failure, provide the request ID, method, path, time, and relevant order identifier to support. Never include an API key or sensitive photo URL.
HTTP status handling
| Status | Meaning | Client action |
|---|---|---|
400 Bad Request | Invalid body, query, identifier, catalog choice, photo selection, or webhook URL. | Correct the request; do not retry unchanged. |
401 Unauthorized | Credentials could not authenticate the request. | Check the Authorization header and API key. |
402 Payment Required | The account lacks credits for order creation. | Add credits, then reconcile before creating again. |
403 Forbidden | The request is not authorized for this operation. | Check the required scope. |
404 Not Found | The resource or route is unavailable in the authenticated context. | Verify the identifier and request path. |
409 Conflict | The operation conflicts with current order state. | Fetch current order detail before acting. |
413 Payload Too Large | An uploaded file is too large. | Resize or recompress it. |
429 Too Many Requests | An endpoint rate limit was exceeded. | Honor Retry-After; retry with backoff and jitter. |
5xx | The service encountered a transient failure. | Retry only safe operations; reconcile ambiguous writes first. |
See Authentication for scope rules.
Common and order codes
| Status | Code | Meaning or useful details |
|---|---|---|
400 | validation-error | Request validation failed; inspect details.errors. |
400 | bad-request | The request is malformed or violates general request rules. |
400 | answer-time-unavailable | Inspect available_answer_times and next_available_at. |
400 | mock-outcome-required | Test keys must send one of the values in allowed_mock_outcomes; Live keys must omit mock_outcome. |
400 | photo-index-not-found | The index is invalid for the selected catalog context. |
400 | missing-required-photo-indexes | Inspect missing_index_codes. |
400 | photo-limit-exceeded | Inspect index_code, max_photos, and received_photos. |
400 | unsupported-photo-url | Use only a managed URL returned by POST /photos. |
402 | not-enough-credits | The account cannot fund the order. |
404 | not-found | A catalog entity was not found. |
404 | order-not-found | The order is unavailable in the authenticated context. |
Refresh Catalog data before correcting catalog codes.
Photo errors
| Status | Code | Meaning or useful details |
|---|---|---|
400 | photo-upload-limit-exceeded | The request contains more than 10 files. |
400 | empty-photo-file | One of the supplied files is empty. |
400 | INVALID_FILE_TYPE | The uploaded image format is unsupported. |
400 | RESOLUTION_TOO_LARGE | An image exceeds 8,000 pixels on one side or 50 megapixels. |
400 | RESOLUTION_MISSING | Image dimensions could not be read. |
413 | FILE_TOO_LARGE | The file exceeds the active per-file limit. |
409 | order-not-awaiting-update-photos | The order is not accepting updated photos; details.expected_status is update-photos. |
400 | invalid-update-photo | A resolution violates the documented photo-update rules. |
400 | photo-index-not-requested | Inspect requested_index_codes. |
400 | missing-requested-photo-indexes | Inspect missing_index_codes. |
400 | photo-limit-exceeded | Too many photos were submitted for an index. |
400 | unsupported-photo-url | Upload the replacement through POST /photos first. |
Updated-photo submissions have no idempotency key. After a timeout, conflict, or stale response, retrieve the order before submitting again.
Webhook errors
| Status | Code | Meaning |
|---|---|---|
400 | validation-error | Invalid URL or event fields. |
400 | unsafe-webhook-url | The destination failed the checks documented in Webhooks. |
400 | bad-request | The endpoint identifier is invalid. |
409 | webhook-already-exists | The same destination URL is already registered for this API key. |
409 | webhook-limit-reached | The API key already has the maximum 10 endpoints. |
404 | not-found | The endpoint is unavailable in the authenticated context. |
Audit errors
GET /audit/events returns 400 with invalid-audit-date-range when from is
later than to. Invalid timestamps, filters, or pagination values return the
normal validation-error response.
Rate limits and retries
Authenticated LegitGrails API responses expose X-RateLimit-Limit and
X-RateLimit-Remaining. The API-key category limiter uses code
rate-limit-exceeded and includes Retry-After in seconds; the same value is
available as details.retry_after_seconds. Additional abuse protection can
also return 429, so always branch on the HTTP status and honor Retry-After.
Do not use X-RateLimit-Reset to calculate the API-key category window; it is
not produced by that limiter.
Treat the response headers as authoritative, wait at least the requested interval, and add capped exponential backoff with random jitter. The exact per-category limits and endpoint mapping are documented in Rate limits.
Use these rules for timeouts and transient failures:
| Operation | Safe recovery |
|---|---|
Any GET | Retry with capped backoff; honor Retry-After. |
POST /orders | Reconcile an ambiguous result before retrying; do not retry automatically. |
POST /photos | This operation is not idempotent; reuse a known successful response and avoid automatic retries. |
POST /orders/{id}/update_photos | Fetch authoritative order detail and its latest request before another submission. |
DELETE /webhooks/{id} | A repeat can return 404; treat an already absent endpoint as reconciled. |
Do not retry 400, 401, 402, 403, 404, 409, or 413 unchanged. For
5xx and transport failures, retry only idempotent or reconciled operations,
with a cap and jitter. If failures persist, retain the most recent
request_id.