If you provide clientWebhookUrl when creating a case, Offload sends JSON POST requests when the case reaches one of its outward-facing async states.
Events You Can Receive
| Event | When it is sent |
|---|
case.completed | The worker marked the case COMPLETED. |
case.failed | The worker or AgentMail event handler marked the case FAILED. |
case.input_needed | The worker paused the case and needs input from your app. |
There are no webhook events for:
- case creation
- case accepted
- case resumed after input
- case currently running
Delivery Behavior
The current implementation sends webhooks like this:
- method:
POST
- header:
content-type: application/json
- body: serialized JSON event envelope
- success rule: any HTTP
2xx
- retry behavior: none in application code
- signing: none
If the request fails or returns a non-2xx, Offload logs the failure and drops it.
This is the most important webhook constraint in the current implementation: delivery is best-effort, single-attempt, and unsigned. Use HTTPS, dedupe with event_id, and run a polling reconciliation job for important workflows.
Event Envelope
{
"event_id": "evt_4fc5904d-0e44-4ed3-aa57-c4a47ad0ae4f_case_completed_1774131900000",
"event_type": "case.completed",
"timestamp": 1774131900000,
"data": {}
}
event_id is deterministic for a specific case update:
- it includes the case ID
- it includes the event type
- it includes the case
updatedAt timestamp
That makes it a good dedupe key on your side.
Recommended Handler Pattern
export async function POST(request: Request) {
const event = await request.json();
// 1. Deduplicate by event_id
// 2. Persist the full payload
// 3. Queue your own downstream work
// 4. Return a fast 2xx
return new Response(null, { status: 204 });
}
Attachment Handling
Webhook payloads include attachment metadata in data.attachments, but not a durable file URL.
If you need the file:
- read
attachmentId from the webhook payload
- call
GET /cases/{id}/attachments/{attachmentId}
- use the returned
downloadUrl before expiresAt
If you passed metadata when creating the case, Offload includes it in webhook payloads. This is useful because GET /cases/{id} does not currently return metadata.
Reconciliation Strategy
For production use, a reliable pattern is:
- use webhooks for low-latency updates
- store
event_id
- periodically poll active or recently updated cases
- repair any missed terminal or input-needed transitions from polling results