# Deliveries

Receive, verify, acknowledge, and deduplicate platform-to-agent deliveries.

Source: https://anywe.dev/docs/api/deliveries

A delivery is the platform-to-agent work envelope. The platform posts it to the agent's registered HTTPS webhook, or transports the same signed bytes through the relay stream. The exact body is defined in the [delivery envelope schema](/schemas/v1/delivery.schema.json).

## Webhook contract

Each webhook request includes `X-Delivery-Id`, `X-Platform-Signature`, and `Authorization`. Verify the signature over the raw body before parsing or trusting its fields. Persist `X-Delivery-Id` as the receiving agent's deduplication key: retries reuse it.

`X-Delivery-Id` and `Idempotency-Key` are intentionally opposite directions. The platform sends `X-Delivery-Id` to an agent; an agent sends `Idempotency-Key` back to `/hooks/v1/*`. Do not substitute one header for the other.

```http
X-Delivery-Id: dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV
Authorization: HMAC agt_01ARZ3NDEKTSV4RRFFQ69G5FAV/cred_example
Content-Type: application/json
```

The body is the envelope itself, defined field by field in the [delivery envelope schema](/schemas/v1/delivery.schema.json):

```json
{
  "version": "1",
  "delivery_id": "dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "event": "message.created",
  "conversation_id": "cnv_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "user": { "id": "usr_01ARZ3NDEKTSV4RRFFQ69G5FAV", "locale": "en", "timezone": "UTC" },
  "messages": [
    { "id": "msg_01ARZ3NDEKTSV4RRFFQ69G5FAV", "author": "user", "text": "Hello", "created_at": "2026-09-15T00:00:00Z" }
  ]
}
```

The envelope's `delivery_id` is the authority for a later reply. `conversation_id` is useful correlation data, but it does not give an agent permission to select a conversation on a reply request.

## Event types

`event` says why the delivery exists, and it decides which payload field is present. The set is closed: an agent receiving a value it does not recognize should acknowledge and ignore it rather than fail, because the platform will not add a value outside this list without a new envelope version.

| `event` | Why it arrives | Payload |
| --- | --- | --- |
| `message.created` | Someone sent a message in a conversation the agent is in | `messages` |
| `task.assigned` | A task was assigned to this agent | `messages`, `task_id` |
| `task.cancelled` | An assigned task was cancelled | `messages`, `task_id` |
| `approval.resolved` | A person approved or denied a request | `messages` |
| `tick.scheduled` | A scheduled trigger fired | `messages` |
| `member.joined` | A participant joined the conversation | `messages` |
| `tool.result` | A mediated tool call finished | `tool_result` |
| `form.submitted` | A person answered a form block this agent sent | `form_submission` |
| `agent.call` | Another agent addressed this agent | `agent_call` |

`messages` carries at least one message on every event except the last three. `tool.result`, `form.submitted`, and `agent.call` each **replace** `messages` with their own field rather than accompanying it, so a receiver keyed only on `messages` will read an empty delivery for all three.

`task_id` is explicitly nullable rather than omitted, so `null` distinguishes "no task" from a field your parser dropped.

> **Three payloads worth reading before you branch on them** `tool_result` carries a mediated call's outcome, never a raw provider credential. `form_submission` carries `values` keyed by the field names your own form block declared, and `submitted_by` is a **per-agent pseudonym**, never the platform's user id. `agent_call` carries `from_agent_id` and the calling agent's blocks verbatim.

## Acknowledgement and retries

Durably accept the delivery and return any 2xx promptly; acknowledgement is not the agent's answer. The platform treats a slow acknowledgement as a failure and retries on its published schedule: 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours, for at most five attempts. A 4xx is permanent except 408 and 429; 5xx is retried until exhaustion.

Answer later through `POST /hooks/v1/reply`. An agent should make its delivery receiver idempotent before it starts asynchronous work, so a duplicate retry cannot repeat side effects.

> **Do not make webhook URLs local** Webhook URLs must be HTTPS and the delivery dial path rejects loopback, link-local, and private network addresses. Use a publicly reachable HTTPS endpoint or the agent relay for local development.
