Automated Phone Calls: When Completed Doesn't Mean Delivered

Automated phone calls for business fail in a way that does not look like failure: the platform reports the call as completed, the campaign report shows a normal

Automated phone calls for business fail in a way that does not look like failure: the platform reports the call as completed, the campaign report shows a normal success rate, and the customer heard nothing useful. Twilio's own documentation is explicit that a completed call "indicates that a connection was established, and audio data was transferred" — not that anything was understood, and not that the message landed.

Our position is that this single gap is the reason outbound calling breaks quietly. An inbound outage announces itself: callers redial, the queue backs up, somebody escalates within minutes. An outbound outage is silent, because a customer who was never told to expect a call has no way to report that it did not come. That asymmetry is the whole operational problem, and it is not solved by picking a better model.

One practitioner solved it privately and, in doing so, described the fix precisely. Writing on Hacker News on 8 June 2026, logicallee described a personal reminder system built on Twilio: it "keeps calling me until I receive the message which it reads me", and the recipient has to say "message received" before the notification is marked delivered (Hacker News comment 48452998). The worked example given in that comment is a reminder for a doctor's appointment. What that mechanism actually is, stripped of the personal framing, is an application-level delivery receipt — built because the telephony layer does not provide one.

The short answer: Treat completed as "a connection happened", never as "the message was delivered" — every outbound calling deployment needs a delivery signal captured inside the conversation, a retry policy that keys on that signal rather than on call status, and a named owner who can stop a running campaign.

Last updated: July 31, 2026.

Diagram of three ledgers for an outbound calling campaign — the campaign runner, the telephony provider and the system of record — showing where each one disagrees about whether a message was delivered

The campaign runner knows what it attempted. The carrier knows what connected. Only the system of record knows whether the thing you called about actually happened.

We should be straightforward about the basis for what follows: we have not operated a production outbound calling campaign ourselves. Every mechanism below is read from vendor and standards documentation that we fetched and quote directly, plus arithmetic we work through in full so you can check it. Where a number is an illustration rather than a measurement, it says so on the line.

What automated phone calls for business actually means here

Automated phone calls for business are calls placed or answered by software rather than by a person. Historically that meant a recorded message played by a dialer. Increasingly it means an AI voice agent built from speech recognition, a language model and speech synthesis. This article is about the outbound half: appointment reminders, delivery and order notifications, payment and renewal prompts, service-outage alerts, survey and follow-up calls. It is about what happens to those campaigns when the software underneath them degrades halfway through.

That scope matters because outbound and inbound fail differently and are governed differently. On an inbound line the caller is present, waiting, and impatient. The failure is visible in real time and the recovery question is "what does this caller hear next". On an outbound campaign, the failure is a batch problem: several hundred rows of a list were processed while the system was misbehaving, the list has moved on, and by the time anyone notices, the window in which the reminder was useful has closed.

The vocabulary is worth pinning down, because vendors use these words loosely and your runbook cannot afford to:

TermWhat it means preciselyWhat it is often confused with
AttemptThe campaign runner handed one row to the telephony layerA call
ConnectA media path was established to the far endA person answering
AnswerSomething picked up — human, voicemail, IVR, faxA human answering
DispositionThe status code the platform assigns when the call endsThe outcome of the call
DeliveryThe recipient received the information the call existed to conveyDisposition completed
OutcomeThe business event the call was supposed to causeDelivery

Every row in that table is a place where an outbound calling deployment can silently substitute the left column for the right one. The rest of this article is about keeping them apart.

Adjacent ground, deliberately left to other work: which number the call comes from, how it is signed, and whether it gets labelled as spam is a separate problem set, and it is the subject of our earlier analysis of caller provenance and number ownership. None of what follows will help if the call never connects because the number is blocked.

Why an outbound outage is silent

An outbound calling failure produces no complaint, because the affected customers do not know a call was owed to them. This inverts the normal detection path for a customer-facing system, where users are the fastest and cheapest outage detector you have. On outbound, the customer is not a sensor.

Google's SRE book defines the errors golden signal in a way that names this class exactly. Errors are "the rate of requests that fail, either explicitly (e.g., HTTP 500s), implicitly (for example, an HTTP 200 success response, but coupled with the wrong content), or by policy" (Google SRE, Monitoring Distributed Systems). A voice agent whose model call times out mid-sentence, leaving the recipient holding a silent line until they hang up, is the telephony version of a 200 with the wrong content. The transport succeeded. The payload was garbage. Nothing in the transport-level status will tell you.

Three properties make this worse on the phone than in almost any other channel:

The failure is invisible on both ends. The customer hears silence and assumes it was a wrong number or a bad connection. The campaign runner sees a call that was answered and then ended, which is what a successful call also looks like. The two views agree with each other and both are wrong.

The window closes. A reminder for a 9am appointment has no value at 2pm. Unlike a failed email or a failed webhook, a late outbound call is not a delayed success; it is often a new and slightly strange event for the recipient. This is why detection lag is the dominant term in the cost of an outbound incident, rather than fix time.

The retry makes it worse before it makes it better. The natural engineering instinct — retry everything that did not obviously succeed — dials the wrong subset, which we work through below.

The SRE book's data-integrity chapter makes the general form of the argument: "to prevent data quality from degrading before users' eyes, and to detect low-grade data corruption or data loss scenarios before they become unrecoverable, a system of out-of-band checks and balances is needed both within and between an application's datastores" (Google SRE, Data Integrity). For outbound calling, the out-of-band check is the one thing no telephony vendor can build for you, because it lives in your system of record rather than theirs.

Rod Rivera of Rasa put the demo-versus-production gap plainly in a conference session description, noting that voice agents "demo beautifully and fail strangely" and that the instinct is to blame the model while the evidence points elsewhere (Voice AI Space Conference NYC, May 2026). Strange failure is exactly the hard kind to alert on, because it does not look like an error anywhere in the stack.

This fireside session from the same conference covers observability and testing for voice agents in production, which is the practical companion to this section:

Play video

Completed is not delivered: reading the disposition list honestly

A call disposition tells you what the network did, not what the recipient received. Twilio publishes eight status values for a call, and the definitions are worth reading as literally as they are written (Twilio, Call resource, fetched July 31, 2026):

StatusTwilio's definition, verbatimWhat it does not tell you
queued"The call is ready and waiting in line before dialing."How long it will wait
ringing"The call is currently ringing."Whether anyone is near the phone
in-progress"The call was answered and is currently in progress."Who or what answered
completed"The call was answered and has ended normally."Whether anything was communicated
busy"The caller received a busy signal."Whether the number is right
no-answer"There was no answer or the call was rejected."Which of those two happened
failed"The call could not be completed as dialed, most likely because the provided number was invalid."Whether the number is permanently bad
canceled"The call was canceled via the REST API while it was ringing."Nothing — this one is honest

The load-bearing sentence is the clarification Twilio adds underneath: a completed call "indicates that a connection was established, and audio data was transferred. This can occur when a call is answered by a person, an IVR phone tree menu, or even a voicemail." Three quite different business outcomes share one status: a customer who heard you, a machine that recorded you, and a phone tree that swallowed you.

The same documentation notes that the duration field "is empty for busy, failed, unanswered, or ongoing calls", which is a small detail with a large consequence: duration exists only for the calls you already believed succeeded, so it cannot be used to distinguish a failed attempt from a successful one. It can only be used to distinguish a good completed from a bad one, which is a different and more useful job that we come back to under instrumentation.

Answering machine detection is the usual proposed fix, and it is genuinely useful, but it is a probability rather than a fact. With MachineDetection=Enable, Twilio documents that results "can be: machine_start, human, fax, unknown"; with DetectMessageEnd the set expands to machine_end_beep, machine_end_silence, machine_end_other, human, fax, unknown. The documentation is candid about the limit: because "not all humans and not all voicemail greetings follow similar patterns in answering calls, it's possible that AMD will not always return the right answer" (Twilio, Answering Machine Detection, fetched July 31, 2026).

There is a timing cost attached to that probability. MachineDetectionTimeout defaults to 30 seconds, after which the call proceeds with AnsweredBy of unknown; the speech-activity threshold defaults to 2400 ms and the initial-silence timeout to 5000 ms. Shorten those and you get more unknown results; lengthen them and the human who answered is listening to silence while the detector makes up its mind. The Twilio guidance frames it as a direct trade: you may decrease the default "if you have time constraints for taking an action", but "not giving enough time will result in more unknown results."

Amazon's outbound campaign product exposes the same branch as a first-class design step rather than an optional flag: setting up Connect outbound campaigns requires you to "create and publish a flow that includes a Check call progress block", which "enables you to branch based on whether a person or a machine answered a call" (AWS, Set up outbound campaigns, fetched July 31, 2026). The platforms agree that the machine-versus-human branch is unavoidable. They do not promise it is correct.

So the honest reading of any disposition list is: it is a network-layer record with a business-layer name. completed means the phone company did its job.

The Three Ledgers, and where they disagree

Every outbound calling deployment keeps three separate records of the same campaign, they are written by three different parties, and reconciling them is somebody's job whether or not that person has been named. We call this the Three Ledgers, and the practical rule that falls out of it is short: a campaign is not finished when the queue is empty; it is finished when the record of truth has moved.

LedgerWritten byAuthoritative forCannot answer
The campaign runnerYour dialer or voice-agent platformWhich rows were attempted, in what order, with which retry counter, and what the queue state is nowWhether the recipient understood anything
The telephony recordThe carrier or CPaaS providerWhether a media path was established, for how long, and what the termination cause was — this is also the ledger you are billed againstWhether the audio was intelligible or the agent coherent
The system of recordYour business — CRM, scheduling system, billing, ticketingWhether the outcome happened: the appointment was confirmed, the invoice was paid, the delivery slot was acceptedWhich attempt caused it, unless you write that down

The three disagree constantly, and each disagreement has a distinct meaning:

  • Runner says attempted, carrier has no record. The call never left. Typically a credential, quota or provider-side rejection. Of the three disagreements this is the one with the clearest signature, because the attempt rate drops against the campaign plan.
  • Carrier says completed, runner says failed. Your callback never arrived or arrived late. Twilio imposes "a hard upper timeout of 15 seconds" on all call-related HTTP requests because of "the real-time nature of voice calls", and webhook retry behaviour is a configurable policy — retry on 4xx, 5xx, connect and TLS failures (ct), or read timeouts (rt) — with a default connect timeout of 5000 ms and read timeout of 15000 ms (Twilio, webhooks connection overrides, fetched July 31, 2026). A campaign runner that treats a missing callback as a failed call will re-dial people who were already called.
  • Carrier says completed, system of record shows nothing. This is the one that matters. Either the call genuinely failed to deliver, or delivery happened and you did not write it down. Those two require opposite responses, and no ledger on its own can tell them apart.

The third row is where the Three Ledgers framing earns its keep, because it converts a vague operational worry into a specific engineering requirement: the conversation has to write something into the system of record. Not the campaign runner writing "call completed". The conversation itself, at the point where the recipient does the thing the call existed for. A confirmation keypress. A spoken acknowledgement, as the Hacker News reminder system does. A callback URL hit by the agent when it reaches the step that matters. Anything that is generated by the recipient's behaviour rather than by the network's.

Absent that, your delivery number is an inference, and during a degradation it is a wrong inference in the flattering direction.

The Un-Ring Rule: why a phone call cannot be made idempotent

Distributed systems solved the duplicate-delivery problem a long time ago, and the solution does not transfer to the telephone. That is the core of what makes outbound calling architecture unusual, and it is why copying retry patterns from your message queue produces a system that annoys customers.

Google Cloud Pub/Sub states the standard position plainly: "by default, Pub/Sub offers at-least-once delivery with no ordering guarantees on all subscription types", it "might redeliver a message even after an acknowledgment request for the message returns successfully", and therefore "accommodating more-than-once delivery requires your subscriber to be idempotent when processing messages" (Google Cloud, Subscription overview, fetched July 31, 2026).

That last sentence is the one that breaks. Idempotency works because the receiver can detect a repeat and discard it. A person cannot discard a phone call they have already answered. Ringing someone's phone is a side effect with no undo and no dedupe, executed in the recipient's attention rather than in your database. We call this the Un-Ring Rule: at-least-once delivery is free at the transport layer and expensive at the human layer, so the deduplication has to happen before the dial, not after.

Everything about retry design follows from that inversion:

Standard distributed-systems practiceWhat it costs on an outbound callWhat to do instead
Retry aggressively on any non-successRepeat calls to people who already got the messageRetry only on dispositions that provably mean "no contact"
Make the operation idempotent at the receiverImpossible — the phone has already rungIdempotency key held in the system of record, checked before each attempt
Exponential backoff with jitterReasonable, but backoff can push the call outside the useful windowBackoff bounded by a business deadline, after which the row is abandoned rather than delayed
At-least-once by defaultCustomer complaints and dialing-reputation damageAt-most-once by default, with a deliberate exception list
Retry until successUnbounded calls to a number that never confirmsHard attempt cap per row per campaign, set as a business rule

The practical mechanism is an idempotency key that lives outside the calling platform. Before any attempt, the runner asks the system of record whether the outcome for this row is already satisfied. After any attempt that produced a delivery signal, it writes that fact back. The key is not the call SID and not the row number; it is the business fact — this customer, this appointment, this billing period.

Concurrency is the other place where the transport layer quietly sets policy for you. Vapi documents that "if your org has a concurrency limit of 10, a maximum of 10 calls will be started at a single time. The rest of the calls will be queued and retried a few minutes later as your concurrency slots become available" (Vapi, Outbound campaigns overview, fetched July 31, 2026). That is a sensible default, and it means the platform is already retrying on your behalf under one specific condition. You need to know which retries are the platform's and which are yours, because two independent retry loops on the same row produce duplicate calls that neither log explains.

Amazon Connect makes the capacity constraint explicit in the opposite direction: "by default, the service quota for concurrent campaign calls is 0", and raising it requires a support ticket (AWS, Set up outbound campaigns); event-driven mass notifications such as "severe weather warnings, evacuation notices, disaster response communications, or utility disruptions" require pre-authorisation, because of "the impact on carrier networks for voice or SMS communications". Capacity for outbound calling is not elastic in the way cloud capacity usually is, which changes what "catch up after an outage" can mean. You frequently cannot simply dial faster afterwards.

A degraded campaign, worked through with numbers

Here is the arithmetic that makes the case concrete. These figures are an illustration built on stated assumptions, not a measurement of any real deployment — the point is the shape of the discrepancy, and every number below can be recomputed from the assumptions.

Assume a 5,000-row appointment-reminder campaign, a concurrency limit of 20, and roughly one call per slot per minute once ringing, conversation and inter-call overhead are counted. That is 1,200 attempts per hour, so the full list takes a little over four hours. Now assume the language model behind the voice agent degrades for 20 minutes — not an outage, just latency high enough that the agent's turns time out after the greeting. During that window the runner places 400 attempts.

Assume the carrier leg is entirely healthy, because it is: the model is the degraded component, and the network does not know that. Of the 400 attempts, take a plausible split of 150 that nobody answers (no-answer or busy) and 250 that are answered.

SegmentCallsDisposition recordedReality
Not answered150no-answer / busyNothing lost; normal retry applies
Answered, message completed90completedDelivered
Answered, agent timed out after greeting160completedRecipient heard a greeting, then silence
Total attempts in the window400250 completed90 delivered

The campaign report at the end of the day says 250 of 400 calls in that window completed — a 62.5% completion rate, which is unremarkable. The true delivery rate for the window is 90 of 400, or 22.5%. The report overstates delivery by 160 customers, and it does so using a metric nobody would call wrong.

Now apply the default retry policy, which retries what did not connect:

GroupSizeRetried by a status-keyed policy?Should be retried?
no-answer / busy150YesYes — but they were never the incident
completed, delivered90NoNo
completed, silent failure160NoYes — and this is the entire incident

The retry policy re-dials 150 people it was always going to re-dial, and permanently skips the 160 people the outage actually harmed. The incident is not merely undetected; the recovery mechanism is aimed at the wrong population. This is the single most important consequence of keying retries on call status.

One signal does move. Assume a healthy full message runs about 52 seconds and a truncated one about 14 seconds. The mean answered-call duration for the window is then (90 × 52 + 160 × 14) ÷ 250 ≈ 27.7 seconds, against roughly 52 seconds when the agent is working. That is a 47% drop in a number your telephony provider already computes, on data it already has, in a window where every other metric looks normal.

Mean answered-call duration is therefore the cheapest available proxy for delivery, and it is not one of the four golden signals as they are usually implemented — it is neither latency, traffic, errors nor saturation. It is closest to the "implicitly, wrong content" case that the SRE book's errors definition explicitly includes and that a default telephony dashboard will not surface for you. If you take one instrumentation change from this article, take that one.

Two honest caveats on the proxy. Duration moves for legitimate reasons — a script change, a different segment of the list, a shift in the human-versus-voicemail mix — so alert on the deviation against the same window on prior runs, not on an absolute threshold. And duration cannot distinguish a truncated message from a short successful one, which is why it is a detector, not a ledger. The ledger still has to be the confirmation signal from the recipient.

Chart comparing recorded call dispositions against real delivery for a 400-call degradation window, showing 250 calls completed but only 90 delivered, and the retry policy targeting the wrong 150

Every number here is recomputable from the assumptions stated in this section. It is an illustration, not a measurement.

Stop, drain, or continue: ending a campaign that has gone wrong

When a running campaign is suspected of degrading, there are exactly three exits and each has a different cost. The decision has to be made in minutes, which means it has to be made in advance.

Decision tree for an outbound campaign that has degraded mid-run, branching on whether the defect is delivery-affecting, whether it is detectable per call, and whether the list can be safely re-dialled, ending in stop, drain or continue

Three exits, and the wrong one is expensive in a different way each time.

Stop. Halt dialing immediately, freeze the list, and reconcile before resuming. Correct when the defect is delivery-affecting and you cannot tell per call which rows were hit. The cost is the untouched remainder of the list plus whatever the closing window costs you. The prerequisite is that someone has the authority and the mechanism to stop it — see below.

Drain. Let the calls already in flight finish, place no new ones, keep the queue intact. Correct when the defect is intermittent or you are not yet sure it is real. This is the lowest-regret default in an ambiguous first two minutes, because it stops the harm growing without discarding queue state.

Continue. Keep dialing, flag the affected window for reconciliation afterwards. Correct only when the defect is not delivery-affecting, such as a wrong caller-ID name, a cosmetic script issue or a logging fault, or when the message is time-critical enough that a degraded call beats no call at all. That last judgement belongs to the business owner of the campaign, not to whoever is on call.

The mechanism question is the one worth answering explicitly, in writing, before the first campaign runs. NIST's AI Risk Management Framework states MANAGE 2.4 as: "Mechanisms are in place and applied, responsibilities are assigned and understood to supersede, disengage, or deactivate AI systems that demonstrate performance or outcomes inconsistent with intended use" (NIST AI RMF Playbook, Manage). Read that as two separate requirements, because it is: a mechanism, and an assignment of responsibility. A voice platform with a pause button nobody on the night shift has permission to press satisfies neither.

The companion subcategory, MANAGE 4.1, requires that "post-deployment AI system monitoring plans are implemented, including mechanisms for capturing and evaluating input from users and other relevant AI actors, appeal and override, decommissioning, incident response, recovery, and change management." Recovery is named there alongside incident response, which is the right pairing for outbound calling: stopping the campaign is incident response, and reconciling it is recovery, and they are different pieces of work with different owners.

Four questions worth answering before the first campaign runs, written down where the on-call engineer can find them at 3am:

  1. Who can stop a campaign? Name roles, not people, and make sure at least one of them is awake at every hour the campaign can dial.
  2. What does stop actually do to in-flight calls? Drop them mid-sentence, or let them finish? Do not assume the same button does the same thing on every platform — test it in a pilot rather than reading it off a feature list.
  3. What happens to the queue? Preserved for resume, or discarded? If preserved, does the retry counter survive?
  4. Who decides between stop, drain and continue? If the answer is "the engineer who noticed", you have delegated a business decision to the person least equipped to make it at that moment.

The general principle here is the same one we argued in our analysis of when human approval is genuinely a control rather than a rubber stamp: an approval or an override is only a control if the person holding it has the information and the authority to use it in the time available.

The morning-after reconciliation

Reconciliation is the out-of-band check that the campaign runner cannot perform on itself, and it is the step that turns a silent incident into a known one. It runs after every campaign, not only after a suspected failure, because a check you only run during incidents is a check that has never been tested.

The procedure is a three-way join across the Three Ledgers, and it produces four buckets:

BucketRunnerCarrierSystem of recordMeaningAction
CleanAttemptedCompletedOutcome presentWorkedNone
Silent failureAttemptedCompletedNo outcomeProbably not deliveredRe-dial under a policy, or fall back to another channel
Phantom attemptAttemptedNo recordNo outcomeCall never leftRe-dial; investigate the runner
Unexplained successNot attempted, or attempted and failedAnyOutcome presentSomething else caused itDo not re-dial; fix attribution

The fourth bucket is the one people are surprised by and the one that protects your customers. Outcomes arrive by routes your campaign does not control. The customer saw the SMS, called back on their own, or confirmed on the web. If reconciliation only asks "which calls failed", it will re-dial those customers too.

The silent-failure bucket is where the judgement sits, and it should not be resolved by a rule that says "re-dial everything". Sensible criteria, in the order they should be applied:

  1. Is the window still open? If the appointment is in 40 minutes, a call is worth more than a perfect record. If it was yesterday, the correct action is an apology through a different channel, not a re-dial.
  2. Is there a cheaper channel? Text costs the recipient far less attention than a second phone call, and it carries a delivery signal the sender can actually read: SMS returns a carrier delivery report, and email at minimum records a bounce. For a message that fits in text, falling back beats calling twice.
  3. What is the attempt count on this row already? The Un-Ring Rule applies: the cap is a business rule, not an engineering default, and reconciliation must respect it rather than reset it.
  4. Was this row in the degradation window? Rows outside it are ordinary retries and should not be swept up in an incident recovery.

Detection time deserves to be a metric in its own right. DORA defines failed deployment recovery time as "the time it takes to recover from a deployment that fails and requires immediate intervention" (DORA, the four keys). Outbound calling needs the same clock started earlier: measure from the first affected call to the moment a human knew, and separately from that moment to the moment the affected rows were resolved. In a channel whose failures do not announce themselves, expect the first of those two intervals to dominate — and it is the one instrumentation can actually shrink.

Flow of the morning-after reconciliation, joining the campaign runner, the telephony record and the system of record into four buckets — clean, silent failure, phantom attempt and unexplained success — with the action for each

The join runs after every campaign, not only after a suspected incident.

What to instrument, and what should actually page someone

The instrumentation question has two halves worth keeping separate: what to record, and what is worth waking a person for. The SRE book's rule is strict and worth keeping strict — "every page should be actionable", and "every page response should require intelligence. If a page merely merits a robotic response, it shouldn't be a page" (Google SRE, Monitoring Distributed Systems).

SignalWhat it catchesWhere it comes fromPage, or dashboard?
Attempt rate against the campaign planRunner stalled, quota exhausted, credentials expiredCampaign runnerPage — it is unambiguous and actionable
Connect rateCarrier or number-reputation problemTelephony recordPage on a sharp drop
Mean answered-call duration vs the same window on prior runsThe agent failing mid-call while dispositions look normalTelephony recordPage — this is the silent-failure detector
Delivery-signal rate (confirmations per answered call)The actual thing you care aboutSystem of recordPage — but only once the signal exists and is trusted
unknown rate from answering-machine detectionDetection thresholds mistuned, or an unusual listTelephony recordDashboard
Retry counter distribution across rowsA retry loop that is not convergingCampaign runnerDashboard, with a hard cap that pages
Callback delivery failuresYour ledger diverging from the carrier'sYour endpointPage if sustained
Reconciliation bucket sizes per campaignEverything the live signals missedThe joinDashboard, reviewed daily

Two design notes on that table.

The delivery-signal rate is the only row that measures the thing the campaign exists to do, and it is the only row that cannot be bought from a vendor. It requires the conversation to write to your system of record at the moment of delivery. That is a single integration point, and without it every other row on this table is a proxy.

Alerting on rate-of-change against the same window on prior runs, rather than on absolute thresholds, matters more on outbound than on inbound, because outbound traffic is intrinsically bursty and scheduled. An absolute connect-rate threshold will fire every time the list composition changes, and an alert the team stops trusting trains it to wait for a second signal — which, in a channel with no complaining customers, may never come.

The engineer's version of this argument came from a practitioner who has worked at one of the frontier voice AI companies, writing on Hacker News in March 2026: "enterprises care about reliability and liability", and they need to see what the agent heard before it acts on the transcription and what it is about to say before it is synthesised (Hacker News comment 47228784). That is an argument for a cascaded architecture, and it is also an argument for instrumentation at the seams between the components — which is precisely where a mid-call timeout occurs.

We have made the general case for recording refusals and non-events, not just successful actions, in our earlier work on AI observability and audit trails. Outbound calling is the sharpest instance of it we have looked at, because the non-event — the message that was not delivered — is invisible in every log that the calling platform produces by default.

Five facts your calling platform must be able to answer

This is a procurement filter rather than a feature checklist. Each item is a question that has a factual answer, that the vendor can demonstrate rather than assert, and that determines whether the operational model above is buildable on their platform at all.

1. Can the conversation write to an external system mid-call, and what happens to the call if that write fails? This is the delivery-signal requirement. If the agent can only report outcomes after the call ends, you cannot distinguish delivered from truncated. Ask to see the failure behaviour, not the happy path: if your endpoint is slow, does the caller hear silence? Twilio's 15-second hard timeout on call-related HTTP requests (Twilio, webhooks connection overrides) is a concrete example of the kind of constraint worth asking every vendor to state explicitly.

2. What exactly does the stop button do to calls already in progress, and who can press it? Test it in a pilot. The difference between "no new calls" and "everything drops now" is the difference between an orderly halt and several hundred people hung up on mid-sentence.

3. Which retries are yours and which are the platform's? Concurrency-driven requeueing, carrier-level reattempts and your own business retries are three different loops. Get them enumerated. Two loops that do not know about each other produce duplicate calls that neither log explains.

4. Is the list state durable and resumable, and does the retry counter survive a restart? A campaign that resumes with counters reset will re-dial rows that already hit their cap. This is a question about the runner's persistence model and it deserves a straight answer.

5. What is exportable, at what latency, and can it be joined to our records? Reconciliation needs per-attempt records with a stable identifier you can match against your own rows. A dashboard you can look at is not the same as data you can join. Ask specifically whether records are available within the hour, because a nightly export sets a floor on your detection time.

A note on what we could not verify: several conversational-voice vendors publish campaign features without publishing the state machine behind them. The Vapi outbound campaigns overview we fetched on July 31, 2026 documents concurrency-driven queuing clearly but does not, on that page, set out campaign state values or a failed-call retry policy. That is not a criticism of one vendor — it is the general reason questions 3 and 4 have to be asked directly rather than researched.

What this article deliberately does not cover

Outbound calling sits next to a substantial body of law, and this article is not the place to interpret it. Consent and do-not-call obligations, abandoned-call and ring-duration rules, call recording, disclosure that the caller is not human, and the treatment of voice as biometric data are all real constraints on any outbound programme, and several of them differ by jurisdiction and are actively contested. Take them to counsel, not to a blog post. Nothing in the operational design above changes what the law requires of you, and none of it should be read as advice on that.

Two adjacent constraints are worth flagging only because they interact with the mechanics discussed here. Amazon's guidance for outbound campaigns notes that "regulations may require unanswered calls to ring for a minimum amount of time, such as 15 seconds, so the customer has time to pick up the call", which interacts directly with the ring-timeout setting your throughput model depends on — Twilio documents its ring timeout default as "60 seconds and the maximum is 600 seconds" (Twilio, Call resource). And the caller-identity question, which determines whether the call connects at all, we have treated separately in the caller-provenance article linked earlier.

Our own scope limit is worth stating too: we have written about outbound campaigns here. The inbound question — what a caller hears when the agent is unavailable, and how a human queue absorbs deflected volume — is a different problem with different arithmetic, and it is not covered by anything above.

Where LeapForce fits, and where it does not

LeapForce is not a dialer and does not place phone calls; the telephony layer in everything above stays with your CPaaS or contact-centre vendor. What LeapForce governs is the layer between the campaign and the models and systems it touches — which is where the failures in this article originate.

Three pieces are relevant. Workflows provides durable, resumable runs with retries and approval gates that pause for a named person, which is the shape the stop/drain/continue decision needs: a run that halts on a condition and waits for a human rather than continuing on a default. Observability and audit records what was refused as well as what ran, which is the same principle as counting the message that was never delivered rather than only the call that connected. The campaign runner is also a non-human identity that needs an owner, a scope and an expiry, which we set out in our earlier analysis of non-human identity for AI agents — the credential that dials your customers should not outlive the person who requested it.

Our rollout method is the same one we apply to gateway deployments generally: Observe first. Enforce second. Optimize third. For an outbound calling programme that translates cleanly. Run the reconciliation join and the duration metric before you change any policy, so you know what normal looks like. Then enforce the attempt caps, the stop authority and the delivery-signal requirement. Only then tune throughput, concurrency and cost.

Honest limits and open questions

We have not run a production outbound calling campaign. The mechanisms above come from vendor and standards documentation we fetched and quoted, not from our own operational logs. The worked example is arithmetic on stated assumptions and is labelled as such wherever it appears. If you have real numbers from a campaign that degraded, they beat everything in this article.

The illustrative split is the weakest assumption. We assumed 150 unanswered and 250 answered out of 400 attempts, and a 90/160 delivered/truncated split among the answered. Those proportions are plausible for an appointment-reminder list and are not derived from data. Change them and the discrepancy between recorded completion and real delivery changes in size — but not in direction, which is the part of the argument that matters.

Duration as a delivery proxy is unvalidated at scale. We can show the arithmetic that makes it move; we cannot tell you what false-positive rate it produces on a real list, and it will vary with script length and list composition more than we would like. Treat it as the cheapest detector available rather than a solved one.

Platform behaviour changes. Every vendor detail here carries a fetch date of July 31, 2026. Timeouts, defaults, quotas and campaign semantics are exactly the kind of thing that changes between releases without an announcement. Re-check them against the current documentation before you design against them.

What we could not settle. We found no vendor-published data on how frequently a completed disposition corresponds to a failed delivery, which is the single number that would size this problem for the industry. It is measurable — it requires only a delivery signal and a large enough campaign — and if any platform publishes it, that would be a genuine contribution to the field. Until then, each deployment has to measure its own.

 FAQ

Frequently asked questions

A completed call means the phone network established a connection and audio flowed. Twilio's documentation states that a completed call "indicates that a connection was established, and audio data was transferred", and that this "can occur when a call is answered by a person, an IVR phone tree menu, or even a voicemail". Delivery means the recipient actually received the information the call existed to convey. The gap between them is where automated phone calls for business fail silently: a call whose agent timed out after the greeting is recorded as completed, exactly like a call that worked.

Watch mean answered-call duration against the same window on previous runs. It is the one signal that moves when an agent fails mid-call while the answered calls still read completed. In the worked example in this article, a 20-minute degradation drops mean duration from about 52 seconds to about 28 seconds while the completion rate stays at an unremarkable 62.5%. Pair it with a delivery-signal rate — confirmations written to your system of record by the conversation itself — which is the only measurement of the thing you actually care about.

Only on dispositions that provably mean no contact was made, and only under a hard attempt cap set as a business rule. Retrying on call status alone re-dials the people who did not answer and skips the people whose call connected and delivered nothing. Because a phone call cannot be un-rung, at-least-once retry semantics that are free in a message queue are expensive here — the deduplication has to happen before the dial, against an idempotency key held in your system of record rather than in the calling platform.

The business owner of the campaign owns the policy; engineering owns its implementation. Attempt caps, the deadline after which a row is abandoned rather than delayed, and the choice to fall back to SMS instead of calling twice are all judgements about customer experience and cost, not about infrastructure. What goes wrong when this is unassigned is that the retry policy defaults to whatever the platform does, and the platform's default was designed for throughput.

That depends on where the degradation is. If the campaign runner stalls, the queue usually survives and the attempt rate drops visibly — the easiest failure to detect. If the model behind the AI voice agent degrades while telephony stays healthy, the queue keeps draining at full speed and the answered calls are recorded as normal, which is the dangerous case. Establish in a pilot what your platform's stop does to in-flight calls, whether queue state and retry counters survive a restart, and whether concurrency-driven requeueing (Vapi documents queued calls being "retried a few minutes later as your concurrency slots become available") is happening alongside your own retries.

It is useful and it is probabilistic, so branch on it but do not treat it as ground truth. Twilio's documentation is direct that because "not all humans and not all voicemail greetings follow similar patterns in answering calls, it's possible that AMD will not always return the right answer", and it returns unknown as a legitimate outcome. There is a timing cost too: the detection timeout defaults to 30 seconds, and shortening it produces more unknown results while lengthening it leaves a human listening to silence. Design the unknown branch deliberately — it is not an error case, it is a normal one.

Join three records: what the campaign runner attempted, what the telephony provider says connected, and what your system of record shows as the outcome. That produces four buckets — clean, silent failure (completed but no outcome), phantom attempt (attempted but no carrier record), and unexplained success (outcome present without a successful call). Only the first three are candidates for action, and the fourth exists specifically to stop you re-dialling customers who already resolved the matter another way. Run the join after every campaign, not only after an incident, so it is a tested procedure rather than an improvised one.

Someone who can both diagnose the failure and stop the campaign, or two people who together can. NIST's AI Risk Management Framework, MANAGE 2.4, requires that "mechanisms are in place and applied, responsibilities are assigned and understood to supersede, disengage, or deactivate AI systems that demonstrate performance or outcomes inconsistent with intended use" — which is a mechanism requirement and a responsibility requirement, and both have to be satisfied before the first campaign dials. Follow the SRE rule for what earns a page: it must be actionable and require judgement, otherwise it belongs on a dashboard.

Dispositions, call progress detection, concurrency management and per-attempt records are the vendor's job, and the platforms cited in this article document all four. The delivery signal, the reconciliation join, the attempt cap as a business rule, and the decision authority to stop a campaign are yours, because all four depend on your system of record and your organisation. No vendor can tell you whether the appointment was confirmed; only your scheduling system knows that. Expect to build one integration point for the delivery signal and a scheduled job for the reconciliation join.

When the message fits in text, a phone call is the worse channel. SMS returns a carrier delivery report, both text channels cost the recipient less attention, and a duplicate text is a minor annoyance where a duplicate call is an interruption. Calling wins when the recipient must act immediately, when the message needs a spoken confirmation, or when the population genuinely does not read messages. It is also the wrong tool when nobody in the organisation will own the reconciliation: an outbound calling programme with no owner for the morning-after join is a system that will fail silently for months and then be discovered by a customer complaint about something else.

Ready to Govern Your AI?

Talk to LeapForce — one controlled layer for every AI tool, connector, model, and agent.

Thirty minutes · No pitch deck

Ready to turn AI experiments into measurable ROI?

Bring one outcome you'd like AI to move. We'll help you scope a pilot you can actually measure — and tell you honestly if it's not worth doing yet.

Comments