- Verify the signature before anything else.
- Store durably, then answer
2xx. Only answer2xxwhen the delivery survives a crash or restart of your application. - Deduplicate by
delivery_id, and process each message and recipient once. - Process asynchronously: download files and run your business logic in a queued job, not in the HTTP request.
Durable acceptance
Answering2xx 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, answers204, 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
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
attachmentswith itsurl, checksizeandsha256, and request a new link withPOST /receiving/messages/{id}/attachments/{attachment_id}/download-urlwhen it expired. - If
content_omittedistrue, read the full content withGET /contentand therecipientof the delivery. - If
parse_statusisfailed, process the message from the original indownload.
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.