# Reliability

> The guarantees every job has, and what your client does.

You do not configure reliability per request. Every job is queued, held against your balance, settled
against what it delivered, and reported with one error shape. This page lists those guarantees and
the few things your client has to do itself.

## Queue

A submit is accepted at once and the job waits in the queue (`lifecycle: queued`) until it can
start. Use `X-Refabric-Start-Timeout` to bound the wait: a job that has not started in time ends with `start_timeout`,
without running and without a charge ([Start deadline](https://docs.refabric.com/task-apis/calling-tasks/asynchronous-jobs#start-deadline)).

## Never charged for failed work

A job holds its estimate when it is submitted and is charged only for what it delivered. A failed or
cancelled job returns the rest of its hold; a job that delivered nothing costs nothing. A job that
delivers part of what it planned succeeds with a warning for each missing output, and the missing
outputs are not charged ([Pricing](https://docs.refabric.com/task-apis/pricing#what-is-charged),
[Task errors](https://docs.refabric.com/task-apis/errors/task-errors#warnings)).

## What you should retry

Every error — an answer to a request or a failed job — says whether trying again can help:

| `retryable` | What to do |
|---|---|
| `true` | retry with backoff; resend a submit with the same `Idempotency-Key` |
| `false` | do not retry the same request; fix it, using `code` and `field` |

An error that is not ours — a proxy's or a load balancer's page without our error object — and a
network failure are retried on `429`, `500`, `502`, `503` and `504`
([Task errors](https://docs.refabric.com/task-apis/errors/task-errors#retrying)).

:::warning
Never resubmit to find out how a job is doing. Every submit without the same `Idempotency-Key` is a
new job. Read the job with its `job_id` instead.
:::

## Lost answers

If a submit's answer never reached you, send the same request again with the same
`Idempotency-Key`: you get the first job back, and nothing new is held
([Idempotency](https://docs.refabric.com/api-reference/platform/idempotency)). Without a key, find the job with
`GET /v1/jobs`, which lists your jobs newest first.

## Webhook deliveries

A delivery your server answers with `408`, `429`, a `5xx` or not at all is retried, up to
31 attempts; any other `3xx` or `4xx` is final. Every attempt is in the
delivery log. If all of them fail, the job's result is still
readable with `GET /v1/jobs/{job_id}/result` ([Webhooks](https://docs.refabric.com/task-apis/calling-tasks/webhooks#delivery-and-retries)).

## Summary

| Situation | What Refabric does | What you do |
|---|---|---|
| Busy queue | the job waits in `queued` | set `X-Refabric-Start-Timeout` if it must not start late |
| Job fails | returns the unused hold; reports `error` | retry only if `retryable` is `true` |
| Some outputs missing | charges only what was delivered; adds a warning per missing output | read `summary.warnings` |
| Answer lost | keeps one job per `Idempotency-Key` | resend with the same key |
| Webhook answered `5xx` or not at all | retries the delivery | dedupe by the event's `id` |

## Related

::::cards
:::card{title="Asynchronous jobs" href="/task-apis/calling-tasks/asynchronous-jobs"}
How a job moves from queued to terminal.
:::
:::card{title="Task errors" href="/task-apis/errors/task-errors"}
The error shape and every code.
:::
:::card{title="Idempotency" href="/api-reference/platform/idempotency"}
Safe retries for a submit.
:::
::::
