# Headers

> The request headers that change how a task call is handled, and the headers every answer carries.

These are request options, not task inputs. A task's input is the JSON body, checked against its
schema; a header changes how the call is handled — whether it is safe to retry, how long to wait,
when to give up. The two stay separate: a **header** is sent with the HTTP request, a **task
input** is a field of the body.

## Idempotency-Key

Makes a retried submit safe: the same key with the same body answers the first job again instead of
starting a second one.

| | |
|---|---|
| Header | `Idempotency-Key` |
| Default | none — every submit starts a new job |
| Format | an opaque string; use a UUID v4 |
| Where it applies | `POST /v1/tasks/{name}` only; other operations ignore it |

::::code-group
```python
import uuid
key = str(uuid.uuid4())  # once per operation, kept with it
s.post(f"{API}/tasks/image.generate", json=body, headers={"Idempotency-Key": key})
```

```javascript
const key = crypto.randomUUID(); // once per operation, kept with it
await fetch(`${API}/tasks/image.generate`, { method: "POST", headers: { ...headers, "Idempotency-Key": key }, body: JSON.stringify(body) });
```

```bash
curl -s -X POST "https://api.refabric.com/v1/tasks/image.generate" \
  -H "Authorization: Key $REFABRIC_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a7e-4b0d-4c1e-9a55-0d2f8b1e7c33" -d @body.json
```
::::

:::note
The same key with a different body answers `409 idempotency_key_reused`; nothing runs.
:::

[Idempotency →](https://docs.refabric.com/api-reference/platform/idempotency)

## Prefer: wait

Holds the submit's answer until the job ends, up to `N` seconds.

| | |
|---|---|
| Header | `Prefer: wait=N` |
| Default | absent: the submit answers `202` at once |
| Unit | seconds, a whole number |
| Max | 60; a larger `N` is cut to it |
| Where it applies | `POST /v1/tasks/{name}` |

```http
Prefer: wait=30
```

:::note
A job that ends within `N` seconds answers `200` with its result; otherwise you get the usual `202`
and the job keeps running. A malformed `Prefer` is treated as no wait. Set your HTTP client's timeout
longer than `N`.
:::

[Synchronous →](https://docs.refabric.com/task-apis/calling-tasks/synchronous)

## X-Refabric-Start-Timeout

Despite the name, this limits time-to-start, not the run: a job that has not started within this
many seconds of the submit ends without running.

| | |
|---|---|
| Header | `X-Refabric-Start-Timeout` |
| Default | absent: the job waits in the queue as long as it takes |
| Unit | seconds; decimals allowed |
| Min | 0.1 |
| Max | 86400 |
| Values | a value outside the range, or not a number, answers `422 invalid_request` naming the header |
| Where it applies | `POST /v1/tasks/{name}` |

```http
X-Refabric-Start-Timeout: 30
```

:::note
A job that misses its start deadline fails with `start_timeout`, releases its credit hold, and the
answer about it carries `X-Refabric-Start-Timeout-Type: user`.
:::

[Start deadline →](https://docs.refabric.com/task-apis/calling-tasks/asynchronous-jobs#start-deadline)

## Refabric-Version

Pins the dated contract version you wrote against.

| | |
|---|---|
| Header | `Refabric-Version` |
| Default | absent: the current version |
| Format | a date, `YYYY-MM-DD` |
| Values | the published versions; any other value answers `400 invalid_request` |
| Where it applies | every operation; every answer echoes the version it followed |

```http
Refabric-Version: <a published version date>
```

[Versioning →](https://docs.refabric.com/api-reference/platform/versioning)

## X-Request-ID

Your own id for a request, logged next to ours.

| | |
|---|---|
| Header | `X-Request-ID` |
| Default | absent |
| Format | a short id of letters, digits and `. _ : -`; the exact rule is in [Conventions](https://docs.refabric.com/api-reference/platform/conventions#headers) |
| Where it applies | every operation |

```http
X-Request-ID: order-4711-render
```

:::note
Every answer carries OUR id in `X-Request-ID` (`req_…`) — quote it to support. Yours comes back as
`X-Client-Request-ID`. An id that does not fit the format is dropped, never refused.
:::

[Support →](https://docs.refabric.com/support)

## webhook_url

A query parameter, not a header: the address we `POST` the job's terminal event to.

| | |
|---|---|
| Parameter | `?webhook_url=https://…` |
| Default | absent: no per-job callback |
| Format | an `https://` URL that resolves to a public address |
| Where it applies | `POST /v1/tasks/{name}` |

```bash
curl -s -X POST "https://api.refabric.com/v1/tasks/image.generate?webhook_url=https://example.com/hooks/refabric" \
  -H "Authorization: Key $REFABRIC_API_KEY" -H "Content-Type: application/json" -d @body.json
```

:::note
A `webhook_url` we will not deliver to is refused at submit, before anything is held. It receives
the terminal events only; registered endpoints can receive more.
:::

[Webhooks →](https://docs.refabric.com/task-apis/calling-tasks/webhooks)

## Response headers

| Header | On | Meaning |
|---|---|---|
| `X-Request-ID` | every answer | our id for the request (`req_…`) |
| `X-Client-Request-ID` | answers to a request that sent a valid `X-Request-ID` | your id, echoed |
| `Refabric-Version` | every answer | the contract version the answer follows |
| `X-Refabric-Credits` | a submit of a task that costs credits | the credits held for the job |
| `X-Refabric-Credit-Type` | the same answers | the credit type of that hold |
| `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` | answers to a request made with a key | the state of the key's rate window ([Limits](https://docs.refabric.com/task-apis/limits#rate-limits)) |
| `Retry-After` | `429` | seconds until you may call again |
| `X-Refabric-Error-Type` | every error answer | the error's `type`, the same as `error.type` |
| `X-Refabric-Start-Timeout-Type` | an answer about a job that missed its start deadline | `user`: the deadline was yours |

## Related

::::cards
:::card{title="Conventions" href="/api-reference/platform/conventions"}
The rules every operation follows.
:::
:::card{title="Asynchronous jobs" href="/task-apis/calling-tasks/asynchronous-jobs"}
What the submit answers and how a job runs.
:::
::::
