feat: add support for Message-ID handling and idempotency in send API #3488
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Add Idempotency Support for Email Sending APIs
Summary
We send a large volume of emails through the Postal API with instances installed on remote ISPs. Network hiccups are inevitable in this setup, causing API requests to timeout or fail intermittently. Without idempotency, retrying these failed requests results in duplicate emails being sent to recipients.
This PR implements idempotency for the API endpoints (
/api/v1/send/messageand/api/v1/send/raw) using RFC 5322 Message-ID headers. Clients can now safely retry failed requests without sending duplicate emails, using client-defined Message-IDs for precise control over deduplication.Changes
API Enhancements
/api/v1/send/messageendpoint:message_idsparameter accepts a hash mapping recipient email addresses to custom Message-IDsmessage_idandexistingflag for each recipient in the response/api/v1/send/rawendpoint:message_idandexistingflag for each recipient in the responseResponse Format
When Message-IDs are provided or detected:
{ "status": "success", "data": { "messages": { "user@example.com": { "id": 123, "token": "abc123", "message_id": "<unique-id@example.com>", "existing": false } } } }Validation
local-part@domain(angle brackets optional in API but stripped for storage)InvalidMessageIDerror with clear format requirementsImplementation Details
OutgoingMessagePrototypeenhanced withmessage_idsattribute, validation logic, and per-recipient duplicate detectionSendControllerupdated to acceptmessage_idsparameter and extract Message-ID from raw email headersmessage_idcolumn (varchar 255) with partial index on first 8 charactersTesting
Comprehensive test suite with 46 passing tests (0 failures):
/messageendpoint idempotency (basic flow, per-recipient IDs, duplicates, partial duplicates, angle brackets, non-hash params)/rawendpoint idempotency (Message-ID extraction, duplicates, partial duplicates, angle brackets)Usage Examples
/messageendpoint with idempotency:/rawendpoint (automatic Message-ID extraction):Benefits
Backward Compatibility
✅ 100% Backward Compatible
message_idsparameter is optional for/messageendpoint