A2P SMS Deduplication: How to Prevent Repeat Messages Without Blocking Legitimate Notifications
An operational guide to separating technical duplicates, controlled retries, and intentionally repeated messages, using idempotency keys, use-case windows, and auditable traceability.

What Deduplication Solves and What It Must Not Do
A2P SMS message deduplication reduces repeated sends caused by duplicate requests, network retries, concurrent events, or uncertainty after an incomplete API response. Its purpose is not to prevent every repetition: it should stop the same business intent from producing multiple unwanted SMS messages without blocking a legitimate resend, a new alert, or a consented communication with a different purpose.
The technical principle is idempotency. An idempotent operation maintains the same intended effect whether it is processed once or multiple times. In messaging, this requires the application to recognize that two requests represent the same intent before creating two independent sends.
A disconnection before receiving the platform response does not prove that the send was not created. In that situation, creating another message without reconciling the status may cause a duplicate. The policy should retain its own idempotency identifier, persist it, and use it to query or reconcile the result before issuing a second send.
- Do not treat deduplication as a global block by phone number.
- Do not assume that an API acknowledgement, queued status, or sent event confirms delivery to the handset.
- Apply the decision to the business intent and lifecycle status, not only to the SMS text.
- Define explicit, auditable exceptions for requested resends, material content changes, or incidents.

The Three Cases That Must Be Separated
A useful policy begins by classifying the reason for repetition. The three cases may look identical in a recipient's history, but they require different decisions.
A technical duplicate occurs when the same intent is processed more than once: for example, two concurrent requests with the same key, a retry after losing the response, or an event replay from a queue. It should normally be suppressed if an active or already-created send exists for the same intent.
A controlled retry is a deliberate action within a defined policy. It may be necessary when the previous status indicates failure, non-delivery, or an uncertain outcome, but it must use the same business correlation, frequency limits, and eligibility rules. It must not be confused with blindly running a request again.
A legitimately repeated message corresponds to a new purpose, event, challenge, or authorization. An order confirmation and a later alert about an order change may go to the same number and have similar text, but they are not the same business event.
- Technical duplicate: same intent, same key, unwanted repetition.
- Controlled retry: same intent, new action permitted by a status and timing rule.
- Legitimate repetition: new intent or verifiable material change.

Design a Deduplication Key Based on Intent
The key should represent a specific unit of business intent. The destination number is necessary but not sufficient. It should be stored and compared in a normalized international representation compliant with the E.164 numbering plan, preventing local formatting differences, spaces, hyphens, or prefixes from producing different keys for the same destination.
As a baseline, a key can combine the normalized destination, purpose, business event identifier, sender or sending profile, and a correlation identifier. The template or its version can be added when it helps distinguish materially different communications. In distributed systems, it is also advisable to store an application-generated idempotency key that remains stable across retries of the same operation.
Do not use the SMS body as the only key. Dynamic messages change based on amounts, dates, names, or references. In authentication, the text may contain different secrets for the same transaction or similar messages for different challenges. Two similar-looking OTPs must not be treated as equivalent without knowing the challenge, attempt, or authentication event to which they belong.
- Normalized destination in international format.
- Purpose: authentication, alert, confirmation, campaign, or another defined domain.
- Event identifier: order, incident, session, challenge, or transaction.
- Sender or sending profile when it forms part of the intent.
- Template version or content classification, if relevant.
- Persistent correlation and idempotency identifier.
Apply Time Windows by Use Case, Not One Universal Window
The deduplication window is the period during which two requests with the same intent are considered candidates for suppression or reconciliation. There is no universally correct duration: it should be defined by purpose, risk, user experience, and the expected message lifecycle.
For OTPs and other authentication secrets, the policy should be tied to the authentication transaction and the specific secret. A secret should be accepted only once during its validity period. NIST states that an out-of-band authentication must be considered invalid if it is not completed within 10 minutes; this limit does not require the operational suppression window to be 10 minutes, but it prevents authentication from being treated as an indefinitely open process.
For transactional alerts, confirmations, and consented communications, the window should reflect the event. A repeated confirmation for the same order may be suppressed while the same event and version are pending or have already been processed. However, a material order update, a new incident, or a verifiable resend request should be evaluated as a new condition, not as an automatic duplicate.
- OTPs: tie the decision to the challenge and secret, not only to the recipient or text.
- Alerts: use the incident, status, or event identifier that triggered the notification.
- Confirmations: distinguish the first confirmation from a later change in the same process.
- Consented campaigns: separate event and frequency rules from technical deduplication.
Use Decision States and a State Machine
Safe deduplication is not limited to returning yes or no. It is useful to use explicit decision states: allow, suppress, review, and replace a pending message. Each state should be supported by a state machine that distinguishes, at a minimum, new requests, messages pending send, sent, delivered, undelivered, failed, and unknown-result messages.
Allow means that no equivalent match exists within the applicable policy or that an authorized exception makes the request a new intent. Suppress means that an equivalent operation already exists and its effect should be preserved. Review is reserved for data conflicts, high-risk exceptions, or ambiguous results that should not be resolved through an automatic rule.
Replace pending may be used only when the policy permits replacing a message that has not yet left the pending state and the platform or architecture reliably controls that transition. It must not be assumed that an already sent message, including one marked as sent by a provider, can be withdrawn or replaced.
- Allow: new intent or valid exception.
- Suppress: same intent within the window and in a status that prevents another send.
- Review: correlation conflict, incomplete data, or a risk exception.
- Replace pending: only before effective sending and with state control.
Resolve Concurrency, Uncertainty, and Asynchronous Callbacks
Two identical requests can arrive at the same time from different processes. Protection must occur before creating two messages: use an atomic reservation or a uniqueness constraint on the deduplication key and applicable window. The operation should return the decision and a reference to the existing record where applicable.
When an API response is lost or a disconnection occurs, retain the result as uncertain. Before sending again, query or reconcile using the idempotency key, correlation identifier, or available external identifier. If the outcome cannot be determined, apply a documented risk rule rather than assuming the first attempt failed.
Status callbacks are asynchronous lifecycle events. They may arrive late or in an order different from what was expected. They should be validated according to the mechanism offered by the platform and processed in a way that tolerates changes in parameters and sequences. A queued status indicates acceptance for processing; a sent status is also not a uniform confirmation of delivery to the handset. Even a delivery DLR should be interpreted according to the contractual and technical semantics of the route, without presenting it as independent proof that the user read the message.
- Reserve the key before creating the send to prevent races.
- Keep the status unknown when the response is uncertain.
- Make callback processing idempotent.
- Do not downgrade terminal states because of delayed or reordered events without an explicit rule.
- Distinguish API acceptance, processing, sending, and reported delivery.
Log Every Suppression So It Can Be Explained and Reviewed
A suppression that cannot be explained becomes an operational risk. The record should make it possible to reconstruct which request was received, which message or intent it was compared against, which rule made the decision, and when the window preventing a new creation will expire.
Store the idempotency and deduplication keys, internal and external correlation identifiers, purpose, normalized destination, previous and new status, applied rule, timestamp, window expiry, and reference to the existing message. When an exception or manual review is involved, record the responsible party, justification, and result.
Audit data should follow appropriate access, minimization, and retention policies. Avoid logging authentication secrets in plain text. For OTPs, it is particularly important that traceability makes it possible to relate the event without exposing the out-of-band secret.
- Reason for allowing, suppressing, reviewing, or replacing.
- Key and correlation for the request and existing message.
- Known lifecycle status and the source of that status.
- Applied rule, policy version, and expiry.
- Responsible party and justification for manual exceptions.
- Minimum data required for diagnosis and audit.
Define Exceptions Without Turning Them Into a Bypass Route
Exceptions must be predefined, verifiable, and traceable. A material content change, a new business event, an authorized channel change, an operational incident, or a verifiable resend request may justify not treating a request as a duplicate.
In authentication, generating a new secret and resending an existing secret are different decisions. The accepted secret must be usable only once while it is valid. In addition, generating a new secret must not reset failed-attempt controls when they apply. Deduplication must not weaken security controls or replace attempt limiting.
Do not use exceptions to bypass frequency limits, conceal integration failures, or repeat commercial messages without a consent basis and an applicable policy. Technical deduplication must coexist with consent, opt-out, frequency, and content rules, but it must not replace them.
- Verifiable material change in content or status.
- New purpose or new business event.
- Authorized and traceable channel change.
- User-requested and verifiable resend.
- Incident with an approval procedure and recorded evidence.
- Never reset attempt controls merely by creating a new OTP.
Frequently asked questions
Is a message with the same text always a duplicate?
No. The same text can belong to different events, and different texts can represent the same intent. The decision should be based on the normalized destination, purpose, business event, correlation, status, and applicable window.
Does an API acknowledgement confirm that the user received the SMS?
No. Acceptance of a request or a queued status confirms that the message has entered processing, not that it has reached the handset. Later statuses must be interpreted according to their semantics and do not amount to read confirmation in SMS.
How should a resent OTP be handled?
It should be tied to the authentication transaction and the specific secret. Resending the same secret and generating a new one are different policies. An accepted secret must be usable only once during its validity period.
What happens if the sending platform response is lost?
Keep the result uncertain, retain the idempotency key, and reconcile the status before creating another send. A lost response does not prove that the original request was not applied.
Which metrics help detect a poor configuration?
Review the suppression rate by purpose, observed duplicates, failed retries, suppressed messages that later required resending, complaints, correlation errors, and manual review cases. Analyze both false positives, where a legitimate message was blocked, and false negatives, where a duplicate was allowed.
Sources consulted
- E.164: The international public telecommunication numbering planInternational Telecommunication Union (ITU-T)
- NIST SP 800-63B-4: AuthenticatorsNational Institute of Standards and Technology (NIST)
- RFC 9110: HTTP SemanticsIETF / RFC Editor
- Messages resourceTwilio Documentation
- Outbound Message Status in Status CallbacksTwilio Documentation
- Message Status StreamTwilio Documentation
- Authentication Cheat SheetOWASP Foundation