# Detached orders

> The Markdown form of https://openfill.ai/docs/detached. The index of every docs page is https://openfill.ai/llms.txt, and every page in one file is https://openfill.ai/llms-full.txt.

To avoid holding a connection while a low price waits, detach the request. You get 202 and an order id at once, the order waits on your behalf, and you collect the result when it is done.

Set shell variables such as ORDER_ID, FILE_ID and BATCH_ID to ids returned by the API before running the corresponding command.

## Submitting

Pass `detached: true` on a chat or completion request. Disconnecting never cancels a detached order. Detaching does not change how long an order waits: a price-locked order still expires with its window (10 seconds), so leave the lock off for a request that must wait for a low level. `stream` and `detached` cannot be combined.

**submit**

```bash
curl https://api.openfill.ai/v1/chat/completions \
  -H "Authorization: Bearer $OPENFILL_API_KEY" -H "Content-Type: application/json" \
  -d '{"messages": [{"role":"user","content":"hello"}],
       "model": "market-preview",
       "price_limit": 0.05, "price_lock": false, "max_wait": 600, "detached": true}'
```

**Response**

```json
202 {"id": "<order_id>", "object": "order", "status": "queued", "expires_at": "...",
     "result_url": "/v1/orders/<order_id>/result",
     "result_stored": true, "result_retention_days": 7}
```

| Field | Meaning |
| --- | --- |
| id | The order id. Every later call names it. |
| status | queued on a fresh submission. On an idempotent replay, the original order's current status. |
| expires_at | When the order leaves the book unexecuted: your timeout, or the lock window. |
| result_url | Where the body will be. |
| result_stored | Whether a result will be kept. false when storage is off for this request. |
| result_retention_days | How long it is kept, or 0. |
| warning | Present only when result_stored is false, saying the result URL will answer 410. |

> Caution
>
> Data storage is **off by default**, and with it off a detached result is never stored, so the result endpoint returns `410` after successful completion. A failed, canceled or expired order returns its terminal state with `200`. Turn it on in [Settings](https://openfill.ai/settings) if you intend to collect results later. The [storage page](https://openfill.ai/docs/storage) has the rules.

A detached request asking to keep a result is refused with 503 before admission when object storage is unavailable.

A gateway restart restores queued detached work only when a result can be retained. With storage off, that restored order is canceled without a charge.

## Collecting the result

**collect**

```bash
curl "https://api.openfill.ai/v1/orders/$ORDER_ID/result" \
  -H "Authorization: Bearer $OPENFILL_API_KEY"
```

**Response**

```json
{"status": "done", "result": { ...the full chat completion... }}
{"status": "done", "result": null, "result_pending": true}   # not written yet, retry
{"status": "expired", "error": "...", "result": null}        # terminal, stop polling
```

Branch on the HTTP status first, then on `status`, the field that says whether the order has finished. Every `200` carries `id`, `status`, `created_at`, `expires_at` and `result_expires_at`, then one of the shapes below; a `410` is the error envelope, with the reason in its message.

| Order status | Answer |
| --- | --- |
| queued | 200 with result: null and queue_position, the order's place in the book. |
| executing | 200 with result: null. |
| done, result stored | 200 with result, the full completion body. Repeated reads are allowed until expiry, within the request and download limits. |
| done, still being written | 200 with result: null and result_pending: true, plus a Retry-After header. Poll again. |
| done, stored body temporarily unreadable | 503 with Retry-After. Retry the same result URL; sending the inference request again creates new work. |
| done, nothing stored | 410 result_unavailable. The message says why: storage was off for the account or for this request, the result aged out or was evicted under the storage cap, you deleted it, or its durable write did not complete. |
| error, canceled, expired | 200 with result: null and a populated error. Final; nothing further will arrive. |

Poll at a gentle interval. The result endpoint shares the per-account order budget on [rate limits](https://openfill.ai/docs/rate-limits), and each body read counts against the hourly download allowance there.

## Deleting a stored result

`DELETE /v1/orders/{id}/result` removes the body from storage ahead of your retention period and answers `deleted: true`. Deleting is idempotent, so a retry after a dropped response is not an error, and it does not change what the request was billed: the order and its charge stay in your history. Deleting before the order has run marks it, and the result is never stored.

Two cases are refused.

| Case | What happens |
| --- | --- |
| The durable write has not landed yet | Refused with 409 result_write_pending until it has, with Retry-After. Deleting mid-write would leave the order pointing at a body that is gone. |
| The result belongs to a batch that is still running | Refused with 409 batch_in_progress until the batch finishes, since its output file needs it. |

## Per-request storage controls

A chat, completion or Messages request may send `"store": false` to retain nothing durably for that call, or `"retention_days": N` to keep it for a shorter window than your account setting. Both can only shorten storage: your account policy is the outer bound. Lowering your account setting also shortens data already stored. On Responses, use retention_days: 0; store is only echoed.

**Detached and idempotent**

A detached request that carries an `Idempotency-Key` is safe to resend: the replay answers `202` again with the same order id and the order's current status, and a resend with a different body is refused with `409`. During the key's lifetime, one order exists for it while waiting, running or done; an order that expired, failed or was cancelled releases its key, and the next resend creates a new one.

### Following an order without its body

`GET /v1/orders/{id}` reports status, queue position, what the order cleared at and any error, whatever your storage setting. It is the call to make while an order waits.

Previous: [Orders](https://openfill.ai/docs/orders). Next: [Streaming](https://openfill.ai/docs/streaming).
