Duplicate message protection
A duplicate message is the second and every further message with the same text sent to the same number. It rarely happens on purpose — more often it comes from repeating an API call after a timeout, from a double click on a button, or from one number ending up in the recipient list several times. BulkGate protects you against it, but the transactional and promotional routes handle it differently.
How the two protections differ
| Transactional message | Promotional message | |
|---|---|---|
| What it watches | the same message repeated over time | duplicate recipients within one list |
| Reach | across requests, a 5-minute window | only within a single request |
| Default state | off | off |
| What you will see | the message fails with duplicity_message |
the recipient quietly drops off the list |
Transactional messages: the same message repeated over time
You turn it on with the duplicates_check parameter in the transactional or simple transactional API. It accepts on and off (it also understands true/false, 1/0, yes/no), and the default is off.
When it is on, BulkGate remembers this combination for every message sent:
| Part of the fingerprint | Note |
|---|---|
| Account | applies across every application (application_id) under one wallet |
| Channel | the same text over SMS and over Viber are two different messages |
| Phone number | in international format |
| Message text | must match character by character — a different name, time or code makes it a different message |
If a message with the same fingerprint arrives within five minutes, BulkGate discards it and it never reaches the operator.
The five-minute window starts with the first message and discarded attempts do not extend it. Once it expires, the same message goes through again.
For a cascade message, the fingerprint uses the main cascade text, not the texts of the individual channels.
What you will see in the response
- A single message — the API returns a
duplicity_messageerror with code400. See the list of error types. - A bulk transactional message — the discarded message gets the
duplicity_messagestatus and counts towardstotal.status.duplicity_message. The rest of the batch is sent as usual.
Think twice before using the protection for one-time passwords. If the code changes with every attempt, it never applies. If you send a fixed text, you will discard the second send within five minutes — and the user who pressed “Resend” gets no message at all.
Promotional messages: duplicate recipients within one batch
On the promotional route nothing is remembered between requests. BulkGate simply goes through the recipient list built from a single request before sending and keeps the first occurrence. The duplicates_check parameter in the promotional or simple promotional API has three values:
| Value | What it removes |
|---|---|
same_text |
the same message repeated to the same number |
same_number |
any further message to the same number, even with a different text |
| not set | nothing — the list is sent exactly as you submitted it |
The removal is silent — the response carries no status and no duplicate counter. The only trace is a lower recipient count than the number you submitted. If nobody is left after the removal, the request fails with no_recipients.
Two consecutive requests do not see each other. Send the same recipient in two batches and both messages go out.
Campaigns in the portal
A campaign goes the promotional route and runs the duplicate check on its own — once the recipient list is built, it shows you how many it found under the Duplicate message warning heading:
- Same number and same text — the campaign always removes these, with no choice offered.
- Same number, different text — this one is up to you. Tick Remove duplicate recipients and each number gets a single message; leave it unticked and all of them go out.
In an A/B tested campaign duplicate recipients are always removed, otherwise one person would receive several tested variants and the test result would be worthless.
What to watch out for
- The same parameter means two different things. On the transactional route
duplicates_checkis a switch that protects across requests over time; on the promotional route it selects a method and applies only within one request. - A shared account means a shared window. Several integrations under one wallet can discard each other's messages if they send the same texts to the same numbers.
- A discarded message is not sent, so it is neither delivered nor recorded among sent messages.
- Check the counts in the response. The promotional route reports no removals — if you compare deliveries against your input list, expect the difference.
Thank you for your feedback.