API v1 preview

# Errors and retries

Recognize failures and recover without duplicating resources or charges.

## Read the response

API errors use an HTTP status and a JSON body with code, message and optional details. Branch on code; message is explanatory and can change. A missing path ID returns 404 with a resource_not_found code, such as model_not_found. A missing body reference returns 400 with that resource code and details[].field naming the reference.

Example error

json

```
{
  "code": "invalid_argument",
  "message": "Supply a five-digit ZIP code.",
  "details": [
    {
      "field": "shipping_address.postal_code",
      "message": "Supply a five-digit ZIP code."
    }
  ]
}
```

Before parsing JSON, check Content-Type. A network failure has no HTTP response; CDN or proxy rejections can return HTML, text or an empty body. Direct storage errors use S3 XML. Preserve the HTTP status and a sanitized response for support, without API keys, upload grants or payment URLs.

## Choose when to retry

| Outcome | Action |
| --- | --- |
| 400 / 401 / 403 / 404 / 412 / 413 / 415 | Resolve the indicated problem before retrying. See the code index below. |
| 409 idempotency_request_in_progress | Wait Retry-After, then retry the same key and body. |
| 409 model_not_ready / model_repair_estimate_pending | Wait until the model status is ready (poll or model.ready), then retry the same request. |
| Other 409 | Resolve the indicated conflict before retrying. |
| 429 | Wait at least Retry-After seconds (currently 60); check account quotas. |
| Network failure / 500 / 503 | Honor Retry-After on dependency 503s, then use bounded exponential backoff. Reuse the saved key and body for POST retries. |
| HTTP 200 with a failed workflow | Read the resource’s error; repeating GET or pricing does not restart processing. |

[Idempotent requests](https://ottomfg.com/docs/idempotency.md)

[Account and request limits](https://ottomfg.com/docs/limits.md)

## Error code index

| HTTP | Code | What to do |
| --- | --- | --- |
| 400 Bad Request | `file_not_found` | No file with this ID exists in the account, or it expired before a model used it. Register and upload the file again. |
| 400 Bad Request | `file_not_ready` | The file bytes have not finished uploading, or their size differs from the registration. Finish the upload, then retry. |
| 400 Bad Request | `file_too_large` | The file is over the upload limit. Reduce it before you register it. |
| 400 Bad Request | `filename_required` | The file registration has no filename. Send the original filename with its extension. |
| 400 Bad Request | `invalid_argument` | A field, query parameter or header is invalid. Fix the field named in details, then send the request again. |
| 400 Bad Request | `invalid_file_size` | size_bytes is not a positive whole number. Send the file's exact byte count. |
| 400 Bad Request, 404 Not Found | `model_not_found` | No model with this ID exists in the account, or it was deleted. Check the ID. |
| 400 Bad Request | `payment_method_not_found` | No saved card with this ID is approved for the business. Choose a card from List payment methods. |
| 400 Bad Request | `pricing_unavailable` | The model cannot be priced, usually because processing failed. Read the model's status_reasons; repeating the request does not rerun inspection. |
| 400 Bad Request, 404 Not Found | `quote_not_found` | No quote with this ID exists in the account, or it is past retention. Check the ID. |
| 400 Bad Request | `shipping_unavailable` | No shipping service accepts this destination or basket. Check the address, or split an oversized basket. |
| 400 Bad Request | `size_bytes_required` | The file registration has no size_bytes. Send the file's exact byte count. |
| 400 Bad Request | `unsupported_file_extension` | The file type is not supported. Use a GLB, OBJ or ZIP file. |
| 401 Unauthorized | `unauthenticated` | The API key is missing, invalid or revoked. Send a valid business API key in Authorization: Bearer. |
| 403 Forbidden | `permission_denied` | The key lacks the endpoint's scope, or payments lack spending consent. Check the scope listed on the endpoint. |
| 404 Not Found | `not_found` | Any route (HTTP 404): no API route matches the path. Check the method, path and version prefix. |
| 404 Not Found | `order_not_found` | No order with this ID exists in the account. Check the ID. |
| 405 Method Not Allowed | `method_not_allowed` | Any route (HTTP 405): the route does not accept this HTTP method. Use a method listed in the reference. |
| 406 Not Acceptable | `not_acceptable` | Any route (HTTP 406): the Accept header excludes application/json. Accept application/json. |
| 409 Conflict | `cancellation_unavailable` | The order can no longer be cancelled, or other orders rely on its paid repair. Retrieve it to see its status. |
| 409 Conflict | `idempotency_key_reused` | This Idempotency-Key was already used with a different body. Retry the original body, or use a new key for a new operation. |
| 409 Conflict | `idempotency_request_in_progress` | A request with this Idempotency-Key is still running. Wait Retry-After seconds, then retry the same key and body. |
| 409 Conflict | `invoice_unavailable` | The order has no paid invoice yet. Pay the order, then download the invoice. |
| 409 Conflict | `model_in_use` | An order uses this model, so it cannot be deleted. |
| 409 Conflict | `model_not_ready` | The model is still processing. Wait until its status is ready, then retry. |
| 409 Conflict | `model_repair_estimate_pending` | The repair fee is still being estimated. Wait until the model status is ready, then retry. |
| 409 Conflict | `payment_in_progress` | A payment with a different method is still in progress. Wait for its result before you try another method. |
| 409 Conflict | `payment_method_unavailable` | The saved card cannot be charged, for example because its billing address is invalid. Update it, then retry. |
| 409 Conflict | `payment_reconciliation_required` | The payment outcome is unknown and needs review. Contact support with the order ID; do not pay again. |
| 409 Conflict | `payment_unavailable` | The order cannot take a payment now. Retrieve it and check its status and expires_at. |
| 409 Conflict | `quote_accepted` | An order already uses this quote. Find it with List orders and quote_id; do not create another. |
| 409 Conflict | `quote_expired` | The quote is older than 30 minutes. Create a new quote, then order it with a new Idempotency-Key. |
| 409 Conflict | `quote_unavailable` | A model, its price or its repair changed after quoting. Create and review a new quote. |
| 409 Conflict | `repair_payment_pending` | Another order has an unpaid repair fee for this file revision. Pay or cancel that order, then quote again. |
| 412 Precondition Failed | `precondition_failed` | The model changed since you read its ETag. Read it again, review the change, then retry with the new ETag in If-Match. |
| 413 Content Too Large | `request_too_large` | The JSON body is over this endpoint's size limit. Send a smaller body. |
| 415 Unsupported Media Type | `unsupported_media_type` | The body is not JSON. Send Content-Type: application/json. |
| 429 Too Many Requests | `resource_exhausted` | A rate or account limit was reached. Wait at least Retry-After seconds, then retry. |
| 500 Internal Server Error | `internal` | The server failed unexpectedly. Retry with backoff; contact support with X-Request-ID if it persists. |
| 503 Service Unavailable | `shipping_rates_unavailable` | Shipping rates could not be fetched. Retry with backoff and the same Idempotency-Key. |
| 503 Service Unavailable | `unavailable` | A required service is briefly unavailable. Retry with backoff, reusing the same Idempotency-Key. |

## Retry a storage upload

Retry transfer with the same signed instructions while valid. If they expire, register a new file with a new key. Replaying the old key does not renew permission. Storage success is HTTP 204 with no body.

## Contact support

Send your own X-Request-ID using 8–64 ASCII letters, digits or hyphens, or let the API generate one. Do not include secrets or account IDs in that value.

[What to include in a support request](https://ottomfg.com/docs/support.md)

API v1 preview

Source: https://ottomfg.com/docs/errors
