> ## 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.

# Quickstart

> Create your first Offload case, handle webhook events, and resume paused work.

This is the shortest implementation path that matches the current code:

1. create a case
2. store the returned `caseId`
3. receive `case.completed`, `case.failed`, or `case.input_needed`
4. resume paused work with `POST /cases/{id}/input`
5. poll `GET /cases/{id}` or fetch attachments when needed

## 1. Configure Your API Host And Key

The codebase does not hardcode a single public hostname. Use the Offload API host for your environment.

```bash theme={null}
export OFFLOAD_BASE_URL="https://<your-offload-api-host>"
export OFFLOAD_API_KEY="your-api-key"
```

All public routes use the same header:

```http theme={null}
x-api-key: your-api-key
```

## 2. Create A Case

This example asks Offload to collect a signed W-9 over email and return structured output when the case completes.

```bash theme={null}
curl -X POST "$OFFLOAD_BASE_URL/cases" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $OFFLOAD_API_KEY" \
  -d '{
    "clientReferenceId": "vendor_2048",
    "channel": "EMAIL",
    "maxAttempts": 3,
    "followUpDelayHours": 72,
    "goal": {
      "objective": "Collect a signed W-9 from the vendor",
      "knowledge": {
        "vendorName": "Northstar Supplies",
        "reason": "AP onboarding",
        "dueDate": "2026-04-10"
      }
    },
    "counterparty": {
      "address": "ap@northstar.example",
      "name": "Maya Chen",
      "company": "Northstar Supplies",
      "timezone": "America/Los_Angeles",
      "customData": {
        "vendorId": "vendor_2048"
      }
    },
    "senderPersona": {
      "name": "Avery",
      "role": "Accounts Payable",
      "company": "Example Corp",
      "tone": "friendly, concise"
    },
    "constraints": [
      "Email only",
      "Do not mention legal action"
    ],
    "resultSchema": {
      "type": "object",
      "properties": {
        "signedW9Received": { "type": "boolean" },
        "taxId": { "type": "string" },
        "receivedAt": { "type": "string" }
      },
      "required": ["signedW9Received"]
    },
    "clientWebhookUrl": "https://your-app.example/webhooks/offload",
    "metadata": {
      "vendorId": "vendor_2048",
      "source": "ap-onboarding"
    }
  }'
```

Successful response:

```json theme={null}
{
  "ok": true,
  "data": {
    "caseId": "4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f"
  }
}
```

### Important Notes About Creation

* `201` means the case was stored and queued. The first outbound email is sent asynchronously.
* `maxAttempts` is the number of automated follow-up attempts after the initial outreach.
* `resultSchema` only needs to be an object at API validation time. The API does not fully validate that it is a strict JSON Schema document.
* Repeating the same `POST /cases` call creates another case. There is no idempotency key on this route.

## 3. Create A Case From TypeScript

```ts theme={null}
const response = await fetch(`${process.env.OFFLOAD_BASE_URL}/cases`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.OFFLOAD_API_KEY!,
  },
  body: JSON.stringify({
    channel: "EMAIL",
    goal: {
      objective: "Collect a signed W-9 from the vendor",
    },
    counterparty: {
      address: "ap@northstar.example",
      name: "Maya Chen",
    },
    senderPersona: {
      name: "Avery",
    },
    clientWebhookUrl: "https://your-app.example/webhooks/offload",
  }),
});

const body = await response.json();

if (!response.ok || !body.ok) {
  throw new Error(body.error?.message ?? "Failed to create case");
}

const caseId: string = body.data.caseId;
```

## 4. Handle Webhook Events

If you set `clientWebhookUrl`, Offload sends a JSON `POST` when the case:

* completes
* fails
* pauses for human input

Example handler:

```ts theme={null}
type OffloadWebhookEvent =
  | {
      event_type: "case.completed";
      event_id: string;
      timestamp: number;
      data: {
        caseId: string;
        resultStatus?: string;
        result?: Record<string, unknown>;
        attachments: Array<{ attachmentId: string }>;
      };
    }
  | {
      event_type: "case.failed";
      event_id: string;
      timestamp: number;
      data: {
        caseId: string;
        resultStatus?: string;
        failureReason?: string | null;
        attachments: Array<{ attachmentId: string }>;
      };
    }
  | {
      event_type: "case.input_needed";
      event_id: string;
      timestamp: number;
      data: {
        caseId: string;
        inputRequest: string | null;
        inputRequestId: string | null;
        inputRequestStatus: string | null;
        attachments: Array<{ attachmentId: string }>;
      };
    };

export async function POST(request: Request) {
  const event = (await request.json()) as OffloadWebhookEvent;

  switch (event.event_type) {
    case "case.completed":
      // Persist event_id for dedupe, then consume event.data.result
      break;
    case "case.failed":
      // Persist failure details and decide whether your app should retry manually
      break;
    case "case.input_needed":
      // Store inputRequestId and surface event.data.inputRequest to an operator
      break;
  }

  return new Response(null, { status: 204 });
}
```

<Warning>
  The current implementation sends webhooks with `content-type: application/json`, but it does not sign them and it does not retry failed deliveries. Use HTTPS, dedupe on `event_id`, and consider periodic polling reconciliation for important cases.
</Warning>

## 5. Resume A Paused Case

When Offload needs a human decision, it sends `case.input_needed` and sets the case to `INPUT_NEEDED`.

Resume the case with the exact `inputRequestId` from the webhook:

```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, a signed PDF is acceptable. Ask them to send it by Friday."
  }'
```

Accepted response:

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

### Important Notes About Input Submission

* `202` means the input was queued, not that the case has already resumed.
* The handler immediately marks `inputRequestStatus` as `RESOLVED` before the worker processes the queued event.
* In that short window, `GET /cases/{id}` can still show `status: "INPUT_NEEDED"` together with `inputRequest.status: "RESOLVED"`.
* There is no webhook for "input accepted" or "case resumed". You only get the next terminal or paused event.

## 6. Poll A Case

Use polling when:

* you did not set `clientWebhookUrl`
* you want to reconcile missed webhook deliveries
* you want to fetch the plain-text transcript on demand

```bash theme={null}
curl -H "x-api-key: $OFFLOAD_API_KEY" \
  "$OFFLOAD_BASE_URL/cases/4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f"
```

Fetch the transcript:

```bash theme={null}
curl -H "x-api-key: $OFFLOAD_API_KEY" \
  "$OFFLOAD_BASE_URL/cases/4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f?includeTranscript=true"
```

`includeTranscript` only enables the transcript when the query value is the literal string `true` after trimming and lowercasing.

## 7. Fetch An Attachment

Webhook events and `GET /cases/{id}` return attachment metadata, not a permanent file URL.

```bash theme={null}
curl -H "x-api-key: $OFFLOAD_API_KEY" \
  "$OFFLOAD_BASE_URL/cases/4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f/attachments/63016fee-124b-46d8-a3e5-616f187a6c41"
```

The response includes a fresh `downloadUrl` plus an `expiresAt` timestamp. Request the link only when you are ready to fetch the file.

## Next Pages

* [Cases](/concepts/cases)
* [Case Lifecycle](/concepts/lifecycle)
* [Webhook Events](/api-reference/webhook-events)
* [Error Handling](/guides/error-handling)
