The same message went out twice
A retried delivery is not a duplicate — retries are deduplicated at the point they arrive, and a 4xx is treated as an answer and never retried. Two copies mean either two separate intents, or one intent sent again by a person after an uncertain result. Establish which before changing anything, because the two have opposite fixes.
What you are looking at#
The recipient has two of the same message, or your thread shows two outbound rows a minute apart, or one number was rung twice. The word 'duplicate' hides two very different events: the same action delivered twice, and two actions that happened to say the same thing. Connect prevents the first structurally and cannot prevent the second at all, because from the inside a second decision is a legitimate one.
The structural protection is worth knowing precisely. Every normalised provider event is stored once, with the signature nonce under a partial unique index — that index *is* the replay protection, not a comparison somebody remembered to write. Requests a call cannot afford to lose (arrived, brief, end) are retried through a deploy restart or a proxy 502, and a 4xx is an answer, so it is never retried.
Causes, most likely first#
| Cause | How to recognise it | What it really is |
|---|---|---|
| Two intents | Two rows with different actors — one a person, one the engine — or a follow-up that fired beside a manual send | Not a duplicate. Two decisions were taken, and both were carried out |
| A resend after an uncertain result | The first send never reached sent; somebody sent again | The one genuine duplicate the system can produce, and it needs a person to complete it |
| Two threads for one conversation | One outbound row in each thread; the recipient received one message | A display problem, not a sending one |
| A repeated inbound | The same incoming message appears twice, and any reply to each looks like a duplicate reply | Canonicalisation on the way in; the outbound side is behaving correctly |
| A follow-up that was not closed | Two rows on the follow-ups screen with the same reason | Two commitments, so two actions. Closing one after it is drained is too late |
The second row is the important one. A send that has been handed to a provider but has not come back with the provider's own acknowledgement is *uncertain*, not *sent* — and that distinction exists precisely so nobody assumes failure. Sending again from an uncertain state is the classic way a real duplicate is created, by a person acting reasonably on an incomplete answer.
What Connect completed#
Both messages were handed to the provider and both carry the provider's own evidence of acceptance. Each is recorded separately with its actor — the engine, or the person who approved it — under the rule it was taken under, so the record answers 'who sent the second one' rather than leaving it to inference. Cost and allowance were charged for both.
What Connect did not complete#
Nothing was recalled, and nothing can be. Once a message has provider evidence it exists outside Connect. No apology was sent either: Connect does not write to a customer about its own duplicate on its own initiative, because deciding whether the second copy is worth mentioning is a judgement about the relationship, not about the send.
For a call, the second attempt was a real second call and was charged as one. There is no automatic suppression of a repeat call to the same number within a window.
What you can do#
Open both outbound rows and compare the actors and the times.
Result Two different actors means two intents; the same actor twice within seconds means a resend.
If it was a resend after an uncertain result, note that the first one did arrive.
Result You now know the recipient has two, and can decide whether to say so. It also tells you the uncertainty was in the acknowledgement, not in the delivery.
If it was two intents, close the follow-up that produced the second.
Result An open follow-up with a due time will fire again. Closing it is the only thing that stops the pattern repeating tomorrow.
If duplicates keep appearing on one channel, set autonomy for that contact or endpoint to ask before sending.
Result Autonomy resolves at four scopes and the narrowest wins, so you can slow one relationship down without slowing the workspace.
What an administrator can do#
- Check whether the mailbox reconnected recently. A cursor that restarts can re-present inbound mail, and a reply to each copy looks like duplicated outbound.
- Check the follow-ups screen for two rows with the same reason against one contact, which is the commonest source of two genuine intents.
- Check whether a person and the engine both hold work on the same thread — an approval queue that a person also answers from the conversation screen can produce one send from each.
When to escalate#
Escalate if two sends carry the *same* provider acknowledgement, or if the same normalised provider event appears twice against one call. Either would mean the deduplication itself failed, which has a different remedy from anything above and should be looked at with the correlation id in hand. Escalate too if a duplicate went to a recipient on the suppression list — that is a compliance question rather than a delivery one.
Questions#
Does Connect deduplicate by message content?
No, and deliberately. Two identical sentences to the same person can be two correct actions — a reminder and a confirmation often read the same. Deduplication is on the event, using the provider's nonce, where sameness is a fact rather than a guess.
A retry happened during a deploy. Could that have sent twice?
The requests that are retried through a restart are the ones a call cannot lose, and each is stored once on arrival. A retry that reaches an already recorded event changes nothing; the unique index refuses the second write.
Can I stop follow-ups from firing while I investigate?
Set the channel's autonomy to ask before sending, or draft_only if you want Connect to keep preparing without asking. Both hold the outbound; the follow-up stays on the record either way, so nothing is quietly dropped.