The receipt sends. The fourth identical copy to the same buyer, inside the same 60 minutes, does not. That copy is held, the answer is approval_required, and the fix names the call a person uses to release it. This is a walkthrough of that path for a store whose order agent sends receipts, using AgentiSend’s published behaviour: the email that goes out, and the one that is stopped.

A receipt is the mail a store cannot get casually wrong. The order was paid, the buyer wants the record, and a second copy that arrives because a job retried does not feel like a courtesy. It feels like the store lost track. Shipping notices and password resets have the same shape: a person asked for one message, and a job is capable of sending four.

The one that sends

The agent that writes the receipt holds a key that can send and nothing else. It cannot raise its own ceiling, and it does not hold the key the store uses to administer the account. The receipt itself is ordinary. Who paid, what the order was, where a reply goes. One recipient. The budget on the key is checked before the send, the call succeeds, the message is accepted, and the buyer gets the email. That is the whole of the happy path, and it is the path every real order should take.

Nothing in that call is clever. The point of the setup is the next call, the one a retry makes when the first request timed out and the agent did not keep an idempotency key, or when a loop sends the same body again because the order still looks unpaid to a buggy check. One repeat is not the published tripwire. Two are not either. The published line is the fourth near-identical message to the same recipient inside 60 minutes.

That is worth stating precisely, because “the duplicate was blocked” would be a tidier claim and a false one. The first receipt is delivered. A single retry can be delivered too. The guard is for the loop, not for the first echo. The idempotency key, kept for 7 days, is what stops the first echo.

The one that is refused

The fourth copy is not delivered. The agent receives approval_required. The public example of that answer says the action requires human approval before it executes, and the fix says to approve the pending action, then retry. Like every refusal it carries code, message, fix, docs_url and retryable, and here retryable is false. The code is not a failure in the sense of “this address is bad” or “try again in a minute.” It says the send is waiting, and that the agent retrying will not help. An agent that treats the refusal as a dropped request and submits again creates a second held row. It does not create a delivery, and it does make a queue.

What a person sees is a row, not a log line. The row carries the subject, how many recipients, the first three addresses, which key asked, and why it was held. The message body is rendered, because approving a send nobody has read is not approval. Approving runs the held receipt through the same path any other send takes. Rejecting keeps a reason on the row. Both decisions record who made them.

The fix is the part that matters to whoever is on call for the store. A refusal that only says no leaves the agent, and the person on call, to guess. A refusal that names the approve call means the on-call step is “open the row, read the receipt, let it go or don’t.” If the fourth copy is a genuine retry of a receipt the buyer never got, a person approves it. If the fourth copy is a loop, a person rejects it and the job gets fixed. The held message is how a store tells those two cases apart. A delivered fourth copy is how it fails to.

If the loop is bigger than one buyer

The same guard has a second line: a window of 20 or more sends that has collapsed to fewer than 3 recipients is held the same way. And when something is wrong across every job at once, POST /limits/kill-all pauses every key on the account in one request. Nothing resumes on its own. A person brings the keys back.

What the guard does not do

The limit is easy to skip in a walkthrough with a clean refusal. The guard does not read the receipt and decide it is accurate. A wrong total, sent once, goes out. A wrong total, sent four times in an hour, gets held on the fourth. Accuracy is still the store’s job. The queue is there so a loop cannot keep being accurate at the buyer’s expense, or inaccurate four times, without a person seeing the fourth.

It is also not a way to reach people who did not buy anything. A receipt goes to the buyer of that order. There is no version of this setup whose job is to find new recipients.

To watch the refusal without a real buyer on the other end, send the loop to an address ending in @simulator.agentisend.com. Those sends cost nothing, reach nobody and work before a domain is verified.

The held-send behaviour, including the approval_required answer and the fix, is on the approvals page. The product is at agentisend.com.

Leave a Reply

Your email address will not be published. Required fields are marked *