Skip to main content
Talario delivers each webhook at least once and retries for 24 hours. Your endpoint decides with its answer whether Talario may stop retrying. A reliable endpoint follows four rules:
  1. Verify the signature before anything else.
  2. Store durably, then answer 2xx. Only answer 2xx when the delivery survives a crash or restart of your application.
  3. Deduplicate by delivery_id, and process each message and recipient once.
  4. Process asynchronously: download files and run your business logic in a queued job, not in the HTTP request.

Durable acceptance

Answering 2xx tells Talario that your application has taken over the message. If you answer 2xx and then lose the delivery, Talario does not send it again. Durable means, for example, a row in your database and a job in a persistent queue (database, Redis with persistence, SQS). An in-memory queue, a sync queue or a job dispatched after the response does not count. What you must store before answering depends on whether your application has an API key with receiving access: We recommend an API key that may Read received messages. Your endpoint can then answer quickly, and a failing download does not cause webhook retries. If storing fails, answer with 5xx. Talario retries later.

Deduplication

Use two keys: The external Message-ID header is chosen by the sender. It can be missing, repeated or forged; never use it as the only key. Deduplication must hold for parallel requests. Use unique database constraints, not a cache entry or a check before the insert.

Laravel example

This example stores each delivery in a table, answers 204, and processes the message in a queued job. It handles payload version 1; the changes for version 2 are noted at the end. It uses the VerifyTalarioWebhook middleware and the configuration from the signature guide.

Migration

database/migrations/2026_10_21_000000_create_talario_deliveries_table.php

Controller

app/Http/Controllers/TalarioWebhookController.php
createOrFirst relies on the unique index, so parallel deliveries with the same delivery_id store one row. Dispatching again for a stored but unprocessed delivery is harmless because the job is idempotent.
Use a persistent queue connection such as database or redis, not sync. Dispatching stores the job before the response is sent; if the queue is not available, the request fails with 5xx and Talario retries.

Model

app/Models/TalarioDelivery.php

Job

app/Jobs/ProcessTalarioDelivery.php
A failed download makes the job fail and retry with Laravel’s backoff, without involving Talario. The download link is used until it expires; afterwards the job requests a new one. Requests for a new link fail with 410 message_expired after the retention period, so process messages well within 7 days.

Processing payload version 2

To process payload version 2, adapt the example:
  • The message fields are at the top level of the payload instead of below message (the controller above already handles both).
  • Store subject, text, HTML and headers from the payload instead of parsing the .eml.
  • Download each entry of attachments with its url, check size and sha256, and request a new link with POST /receiving/messages/{id}/attachments/{attachment_id}/download-url when it expired.
  • If content_omitted is true, read the full content with GET /content and the recipient of the delivery.
  • If parse_status is failed, process the message from the original in download.

Errors in your application

If your endpoint was down for longer than 24 hours, deliveries are failed. List them with GET /receiving/messages and retry them, or download the messages directly with the API.