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

# Webhooks

> How Talario delivers received messages to your application, and how retries work.

A route with a **Webhook** action delivers every received message to an HTTPS endpoint of your application. Talario sends a signed `POST` request with a JSON body and retries until your endpoint confirms it.

## Endpoints

Manage endpoints in the dashboard under **Webhooks**. An endpoint URL must:

* use `https` on port 443 with a valid, publicly trusted certificate,
* resolve to a public IP address,
* not contain a user name, password, query string or fragment.

Talario does not follow redirects. When you change the URL of an endpoint or disable it, open deliveries to it are cancelled; they are not redirected to the new URL. You can [retry](#manual-retries) them afterwards.

## Deliveries

For every message, recipient and endpoint there is exactly one **delivery** with a stable `delivery_id`. A message with two recipients that both match a webhook route therefore results in two deliveries, each naming its own `recipient`.

Each delivery can have several **attempts**. All attempts and manual retries of a delivery keep the same `delivery_id` and the same payload version, but get a new timestamp, new signature and new download links.

### Request

```http theme={null}
POST /webhooks/talario HTTP/1.1
Host: app.example.com
Content-Type: application/json
X-Talario-Delivery-Id: 019a0520-0000-7000-8000-000000000001
X-Talario-Timestamp: 1792598400
X-Talario-Key-Id: 01k7xq3n4b5c6d7e8f9g0h1j2k
X-Talario-Signature: v1=5d41402abc4b2a76b9719d911017c592…

{"schema_version":1,"event":"message.received", …}
```

| Header | Content |
| - | - |
| `X-Talario-Delivery-Id` | Stable ID of the delivery |
| `X-Talario-Timestamp` | Unix time of this attempt, in seconds |
| `X-Talario-Key-Id` | ID of the signing secret used |
| `X-Talario-Signature` | `v1=` followed by the HMAC-SHA256 signature as hex |

[Verify the signature](/webhooks/signatures) before you process the request.

### Payload

The body depends on the payload version of the endpoint:

| Version | Content | Status |
| - | - | - |
| [Version 1](/webhooks/payload-v1) | Metadata and a download link for the original message | Available |
| [Version 2](/webhooks/payload-v2) | Parsed content: subject, text, HTML, headers, attachment links | Available |

Read `schema_version` in the body to tell the formats apart. A change of the payload version applies to messages received afterwards.

## Responding

Answer with any `2xx` status **after** you have stored the delivery durably; see [Process webhooks reliably](/webhooks/processing). The response body is ignored.

Every other outcome is a failed attempt and is retried:

* any status outside `2xx`, including redirects (`3xx`) and `4xx`,
* a connection that takes longer than 10 seconds to establish,
* an attempt that takes longer than 120 seconds in total,
* network and TLS errors.

A single `4xx` does not disable your endpoint.

## Automatic retries

| Attempt | Time |
| - | - |
| 1 | immediately after the message was stored |
| 2 | 1 minute after the previous failure |
| 3 | 5 minutes later |
| 4 | 15 minutes later |
| 5 | 60 minutes later |
| 6 and later | every 3 hours |

Each delay is extended by a random 0 to 20 %. With `429` or `503`, Talario respects a `Retry-After` header (seconds or HTTP date) of up to 3 hours.

Automatic retries end **24 hours after the message was received**. The delivery is then `failed`; the message stays available until `expires_at`.

## Manual retries

You can request one more attempt of a `failed` or `cancelled` delivery until the message expires: in the dashboard under **Inbound**, or with the API and a key that may **Retry webhooks**:

```bash theme={null}
curl -X POST https://talario.eu/api/v1/receiving/deliveries/019a0520-0000-7000-8000-000000000001/retry \
  -H "Authorization: Bearer $TALARIO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

```json theme={null}
{
  "delivery_id": "019a0520-0000-7000-8000-000000000001",
  "status": "retry_requested"
}
```

The answer is `202 Accepted`; the mail server makes the attempt shortly afterwards. The `Idempotency-Key` header is required (1 to 255 visible ASCII characters, for example a UUID). Repeating a request with the same key returns the same answer with the header `Idempotent-Replayed: true`.

A manual retry does not start a new 24-hour period of automatic retries. A delivery that your endpoint already confirmed cannot be retried (`409 delivery_not_retryable`): download the message again instead.

## Delivery guarantees

Delivery is **at least once**. A lost response, a timeout after your endpoint stored the delivery or a manual retry can deliver the same delivery again. Your endpoint must recognize repeated deliveries by `delivery_id`; see [deduplication](/webhooks/processing#deduplication).

Deliveries can arrive in any order, and several can arrive at the same time.

## Delivery state

The API shows the state of each delivery in [`GET /receiving/messages/{id}`](/api-reference/introduction):

| `status` | Meaning |
| - | - |
| `pending` | Waiting for the first or next attempt |
| `succeeded` | Your endpoint answered with `2xx` |
| `failed` | No successful attempt within 24 hours |
| `cancelled` | Stopped because the endpoint URL changed, the endpoint was disabled or deleted, or the domain or project was suspended |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.