> ## Documentation Index
> Fetch the complete documentation index at: https://docs.offloadapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Human In The Loop

> How Offload pauses for input, what your app receives, and how to resume safely.

Offload moves a case to `INPUT_NEEDED` when the worker decides it cannot continue safely without a human answer from your application.

## What You Receive

If `clientWebhookUrl` is configured, you receive `case.input_needed` with:

* `inputRequest`
* `inputRequestId`
* `inputRequestStatus`
* `inputRequestedAt`

Example:

```json theme={null}
{
  "event_id": "evt_4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f_case_input_needed_1774131900300",
  "event_type": "case.input_needed",
  "timestamp": 1774131900300,
  "data": {
    "caseId": "4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f",
    "clientReferenceId": "vendor_2048",
    "status": "INPUT_NEEDED",
    "channel": "EMAIL",
    "attemptCount": 1,
    "attachments": [],
    "inputRequest": "The vendor asked whether a signed PDF is acceptable. How should I respond?",
    "inputRequestId": "c8c6a7ca-a913-4d13-8cd8-5887efb0531c",
    "inputRequestStatus": "PENDING",
    "inputRequestedAt": 1774131900300
  }
}
```

You can also fetch the same active question from `GET /cases/{id}` while the request is still present on the case.

## Resume The Case

Submit the answer with the same `inputRequestId`:

```bash theme={null}
curl -X POST "$OFFLOAD_BASE_URL/cases/4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f/input" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $OFFLOAD_API_KEY" \
  -d '{
    "inputRequestId": "c8c6a7ca-a913-4d13-8cd8-5887efb0531c",
    "providedContext": "Yes. Signed PDFs are acceptable. Ask them to send it before Friday at 5 PM PT."
  }'
```

Successful acceptance:

```json theme={null}
{
  "ok": true,
  "data": {
    "caseId": "4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f",
    "inputRequestId": "c8c6a7ca-a913-4d13-8cd8-5887efb0531c",
    "accepted": true
  }
}
```

## What `202 Accepted` Means

The current implementation performs two steps:

1. enqueue an internal `INPUT_PROVIDED` event
2. immediately update `inputRequestStatus` to `RESOLVED`

That means:

* your answer was accepted for async processing
* the case may still briefly read as `INPUT_NEEDED`
* the worker has not necessarily replied to the counterparty yet

## When The API Returns `409`

`POST /cases/{id}/input` returns `409` when:

* the case is not currently `INPUT_NEEDED`
* the active request is no longer `PENDING`
* the provided `inputRequestId` is stale or mismatched

## Recommended App Behavior

* Persist `inputRequestId` from the webhook.
* Show `inputRequest` to the human or system that can answer it.
* Keep `providedContext` direct and decision-oriented.
* Treat `202` as queued work, not a final state transition.
* Watch for the next webhook or poll the case to confirm the new state.

## What Happens Next

After the worker processes your answer, it can:

* send the next reply and return to `RUNNING`
* complete the case
* fail the case
* ask for more input again

When the answer is added, the worker appends it into `goal.knowledge.additionalHumanContext` before re-evaluating the case.
