Evalgent
Back to Blog
Voice AI Testing

Drive-Thru AI Upselling: When the Pitch Breaks the Order

Deepesh Jayal
16 min read
Drive-Thru AI Upselling: When the Pitch Breaks the Order
On this page

Most teams track one number for drive thru ai upselling: how often guests say yes. Whether it is ai drive thru upsell logic or a phone line, the habit is the same. That number counts a "yes" as revenue even when the ticket behind it is wrong. A pitch is not a sentence the agent says. It is a write to the order. It can add a line, swap a line, reprice a combo or drop a modifier.

This post is for the team that built a voice ordering agent into its own QSR or restaurant POS product. It runs in lanes and on phone lines across many stores, and every prompt change reaches every store. It covers six pitch breaks, a metric that counts the damage, code for a pitch gate and a test plan. For the wider QA program, see QA for drive-thru voice AI across 500 stores.

79.2%
AI visits with a suggestive sell, vs 59.2% at the same brands (Intouch Insight 2026)
29.1%
AI visits where the guest repeated part of the order, vs 20.4% (Intouch Insight 2026)
90.0%
AI visits where the confirmed order was correct, n=70 (Intouch Insight 2026)
43%
Inaccurate orders with a customization miss (Intouch Insight 2026)

Why the pitch is a write, not a sentence

A voice order is a short workflow. The guest speaks. The agent captures items and writes lines to the point of sale. It reads back an order total. The kitchen display shows the ticket. The receipt prints.

A pitch touches every step in that chain. The agent decides to speak. The guest answers in noise. The agent binds the answer to a pitch, then edits the cart. Each hop can fail silently. A transcript can look perfect while the ticket holds two sodas.

That is why one metric is not enough for voice ai upselling. One vendor glossary defines upsell conversion as "upsells accepted / upsells offered". That fits the sales motion. It says nothing about whether the order was right afterward.

What the 2026 mystery-shop data does and does not say

The best public data is a mystery shop study on drive thru voice ai. Intouch Insight's 26th annual drive-thru study ran 2,145 core visits across 13 US chains in June and July 2026. It also ran 120 visits at locations reported to use voice AI, 40 at each of three chains.

At those AI locations, a suggestive sell was offered on 79.2% of visits. The same three chains scored 59.2% overall. Order accuracy was 90.8% at AI locations and 86.7% at the same brands. In 29.1% of AI visits the guest had to repeat part of the order, versus 20.4%. The report states that AI locations "were selected, not randomized."

Read this carefully. Voice AI pitches more often, and drive thru ai order accuracy in this sample was not worse. That is good news. But the study does not link the two. It does not say how many wrong orders followed a pitch. It does not measure whether an accepted upsell reached the ticket intact. The study measured suggestive selling and accuracy separately. The core study found that customization drove 43% of inaccurate orders. Upsell edits are customization edits made by the agent, and ai order taking now makes them on every pitch.

An earlier 2025 Intouch study shows the tension. Shops in January 2025 found voice AI suggested items on 81% of visits, against a 64% traditional benchmark. Its accuracy was 83%, against 89% for traditional lanes, and 65% of voice AI errors involved customizations. Accuracy improved by 2026.

Two cautions. These are mystery shops of a few dozen visits per brand, not call-level audits. And nothing here says any chain's agent fails. The pitch is simply where an audit should look first.

Six ways a pitch breaks the order

Use this copyable framework, the six pitch breaks. Each has a trigger, a stored result and a test that catches it.

Grid of six drive thru upsell failure types, each with what the guest says, what the POS stores, and the check that catches it

1. The late pitch

The guest says "that's all." The agent pitches anyway. This is the failure guests notice first. One widely shared 2025 clip, reported by Dexerto, showed an AI asking what drink a guest wanted right after a large Mountain Dew was ordered. That is the same class of bug.

The cause is usually a race. The pitch is planned when the cart looks incomplete. The closing phrase arrives a few hundred milliseconds later, and speech-to-text only finalizes it after a pause. The pitch audio is already queued.

Endpointing makes this worse. Deepgram's endpointing docs say the feature is on by default and "set to 10 milliseconds." A short pause after "and a medium fries" can end the turn, so the agent speaks into what the guest meant as a breath. See best endpointing for voice agents for tuning.

Timeline of a late pitch: the agent plans a drink pitch at 1.0 seconds, the guest says that's all, the pitch audio plays over it, and the closing phrase is only finalized at 2.0 seconds

Barge-in adds a twist: if the guest talks over the pitch, the agent stops, and the end of turn is unclear. The fix is a gate that runs at speak time, not plan time. Check the cart version, the closing flag and any open question again right before audio starts. The code section below shows one.

2. The duplicate or replacement

The agent offers what the cart already holds. It pitches a drink with a combo that includes one. It offers "a large" for an item already upsized. Or it replaces a line instead of adding one, so the guest's second burger vanishes.

The trigger is a cart model the pitch logic does not share with the order logic. Combos hide items inside one line, so a rule that looks for a loose "drink" line misses the drink inside "Combo 1." Build carts with every combo, size and bundle on the menu and ask the gate for a verdict on each.

3. The misheard accept or decline

Short answers are the riskiest audio in a drive-thru, and speech recognition has almost no context for them. "Yes," "no," "sure," "nah" and "that's fine" are one syllable, spoken over an engine, a passenger and a speaker post.

There are three variants:

  • Phantom yes. Noise or a passenger's voice becomes an acceptance. A study of Whisper hallucinations on non-speech audio found hallucinated text in 40.3% of 301,317 non-speech files, led by "thank you" at 24.76% of outputs. Your engine may not be Whisper, but noise can become words. Run the same probe on yours.
  • Swallowed no. The guest declines while the agent is already speaking, and the decline is clipped.
  • Misbound answer. The guest answers the previous question. "Yes" meant "yes, ketchup." The agent binds it to the pitch.

The third one is a state bug. Tag every question the agent asks with an ID. Bind an answer only to the open question, and ask again if two are open. Our post on STT entity accuracy covers why short tokens fail more than names do.

4. The combo repricing

"Make it a combo?" is the most valuable pitch on most menus and the most dangerous. Accepting it should replace two or three lines with one. If the agent adds the combo line and keeps the old lines, the guest pays twice.

One of the 2026 Intouch shoppers reported that the AI defaulted to fries as a combo side and an extra side was then charged. The page presents it as one shopper's account, not a rate.

Here is a worked example with illustrative prices. The guest ordered a burger and fries. The agent pitches the combo and the guest says yes.

Outcome after "yes"CartTotal
CorrectCombo 1 (burger, fries, medium drink)$10.99
Old lines keptBurger, fries, Combo 1$20.67
Side duplicatedCombo 1 plus fries$14.18
Modifier droppedCombo 1, "no pickles" missing$10.99
Default drink never askedCombo 1 with default cola$10.99

Only the first row is right. The last two rows have a correct total and a wrong order. A total check alone will pass them. You need a line-level check against what the guest meant.

5. The modifier and allergy carry-over

Every upsell that swaps an item rebuilds its modifiers. Upsizing a drink may reset "no ice." A combo conversion may lose "no pickles." A larger sandwich may drop a note that matters for a guest's health.

Allergy handling has its own failure modes, covered in AI phone ordering for restaurants: allergy and add-on errors. The upsell-specific test is narrow. Record an order with a modifier or allergy flag, accept an upsell on that line, and compare modifiers before and after. They must match unless the guest changed them. Rebuilds also break half-and-half pizzas, as in AI pizza phone ordering.

6. The total, ticket and receipt mismatch

The agent says "$12.40." The kitchen display shows the right food. The receipt says $13.15. Three numbers from three places.

The cause is usually that the agent does its own arithmetic. Let the POS be the source of truth for the order total. Many POS APIs can price a cart without saving it. Square's CalculateOrder endpoint, for example, "enables applications to preview order pricing without creating an order." Its response carries `total_money`, tax and discount totals. A voice agent should read back only a total returned by a call like this. Square also snapshots item prices when an order is created. That is an example only. Check how your POS behaves.

Log three values for every order: spoken total, POS total at order confirmation and receipt total. Add a POS ticket ID so you can join them. Any difference is a defect, whatever the cause.

Measure clean acceptance, not raw acceptance

Raw acceptance counts yeses. Clean acceptance counts yeses that produced the right order. Define five numbers per period, per store and per pitch type:

  • Offer rate = orders with a pitch / eligible orders
  • Raw accept rate = accepted offers / offers
  • Clean accept rate = accepted offers whose final ticket matches the intended order / offers
  • Corruption rate = orders where the ticket differs from intent after a pitch / orders with a pitch
  • Phantom add rate = declined offers where an item still appeared / declined offers

"Intended order" needs a reference. For simulated calls you wrote the script, so you know it. For live calls, a judge compares the audio to the ticket. That is why transcript-only evaluation misses these cases. A phantom yes can read as a clean "yes" in the transcript.

Here is a worked example with illustrative numbers, not measured data. Take 1,000 offers.

  • 240 accepted, so raw accept rate is 24.0%.
  • 17 of those 240 orders were corrupted. Clean accepts are 223, so clean accept rate is 22.3%.
  • 8 of the 760 declines still got the item added. Phantom add rate is 1.1%.
  • Each accepted item adds $2.10. Each corrupted order costs $7.00 in remakes, refunds and goodwill (assumption).

Naive revenue is 240 × $2.10 = $504. Clean revenue is 223 × $2.10 = $468.30. Fix costs are 25 bad orders × $7.00 = $175. Net value is $293.30. The raw number overstates the benefit by 72%.

Waterfall chart of an illustrative upsell program: $504 naive revenue, minus $35.70 for corrupted accepts, minus $175 for remake and refund cost, leaving $293.30 net

Do not read these dollars as benchmarks. A pitch that sells well but corrupts 7% of accepts can lose to a quieter pitch with a lower raw rate.

A pitch gate and a ticket reconciler

Two small functions cover most of the six breaks. A gate runs right before the pitch is spoken. A reconciler compares intended and stored lines. This is simplified Python; adapt names to your cart model.

from dataclasses import dataclass, field
import time

CLOSING = ("that's all", "that is all", "that's it", "that'll be it", "nothing else")

@dataclass
class Cart:
    version: int = 0
    lines: list = field(default_factory=list)   # {"sku", "qty", "mods"}
    closed: bool = False                        # closing intent seen
    last_closing_ms: int | None = None
    pitches_made: int = 0
    pending_q: str | None = None                # id of the question awaiting an answer

def may_pitch(cart, item, now_ms, built_for_version, menu, max_pitches=1):
    """Return (ok, reason). Run again at speak time, not only at plan time."""
    if cart.version != built_for_version:
        return False, "stale_cart"              # cart changed after the pitch was planned
    if cart.closed or (cart.last_closing_ms is not None
                       and now_ms - cart.last_closing_ms < 4000):
        return False, "after_close"
    if cart.pending_q is not None:
        return False, "question_open"           # do not stack a second question
    if cart.pitches_made >= max_pitches:
        return False, "pitch_budget"
    if not menu.get(item, {}).get("available", False):
        return False, "item_unavailable"
    if any(l["sku"] == item for l in cart.lines):
        return False, "already_in_cart"
    for l in cart.lines:                        # combos already include sides/drinks
        if item in menu.get(l["sku"], {}).get("includes", ()):
            return False, "included_in_line"
    return True, "ok"

def reconcile(intended, ticket, prices, spoken_total_cents):
    """Compare what the guest meant with what the POS stored."""
    def key(lines):
        return sorted((l["sku"], l["qty"], tuple(sorted(l.get("mods", ())))) for l in lines)
    flags = []
    if key(intended) != key(ticket):
        flags.append("lines_differ")
    pos_total = sum(prices[l["sku"]] * l["qty"] for l in ticket)
    if pos_total != spoken_total_cents:
        flags.append("spoken_total_mismatch")
    return flags, pos_total```

Three details matter. The gate runs again at speak time, so a stale plan dies. It uses `built_for_version`, so a cart edit between plan and speech blocks the pitch. And the reconciler compares lines and modifiers, so it catches the rows in the combo table that a total check would pass.

Run the gate on live traffic too and log every `reason`. A rising `after_close` count means the race is still there. A zero count across a long test set means the set is thin. For tool-call failures that never raise errors, see [detecting silent tool failures](/blog/detect-silent-tool-failures-voice-agents) and [tool argument accuracy](/blog/tool-argument-accuracy-voice-agents).

## A/B testing upsell prompts without sending bad orders to stores

A/B testing a pitch prompt has two goals that pull apart. You want a lift in clean accepts. You must not raise corruption. Live traffic is the slowest and costliest place to learn about corruption. Our general guide to [A/B testing voice agents](/blog/ab-testing-voice-agents) covers the basics. Here is what changes for upsells.

**Sample size for the lift.** For a two-sided test at 5% significance and 80% power, the per-arm sample is roughly n = (1.96 + 0.84)² × [p₁(1−p₁) + p₂(1−p₂)] / (p₂ − p₁)². Say clean accept is 24% and you hope for 27%. That needs about 3,300 offers per arm. A store with 60 pitched orders a day gets there in about 55 store-days per arm. Small lifts take many stores or many weeks.

**Sample size for the harm.** Detecting a rise in corruption from 1.5% to 3.0% of pitched orders needs about 1,500 per arm by the same formula. Smaller rises will not show at this scale, so set the guard at a level that matters.

**Use the rule of three before live traffic.** If 300 simulated calls produce zero corrupted tickets, the 95% upper bound on the true rate is about 1 − 0.05^(1/300), or 0.99%. With 100 calls it is 2.95%. A few hand-run calls prove little.

A staged path for drive thru ai upselling changes works well:

1. **Simulated calls** with the full break matrix. Zero tolerance for late pitches and duplicates.
2. **Shadow mode.** The new prompt plans pitches on live calls but does not speak them. Compare what it would have said with the cart.
3. **Canary lanes.** A small share of stores or lanes get the new prompt, with automatic rollback if corruption crosses its threshold.
4. **Full rollout** only after the canary holds for a set number of offers.

Menus, combos and availability differ by store. See [multi-tenant voice agent testing](/blog/multi-tenant-voice-agent-testing) for how to build the matrix.

## What to put in the test set

The matrix is break × condition. Every row below should appear in simulated calls before a pitch change ships.

| Break | Scenario to script | Conditions to vary |
|---|---|---|
| Late pitch | "That's all" spoken at 0.3, 0.8 and 1.5 seconds after the last item | Fast pace, slow pace, mid-sentence pause |
| Duplicate | Order a combo, then wait for a pitch for a side or drink | Every combo and bundle on the menu |
| Misheard accept | Decline a pitch while a passenger says "yes" | Engine noise, accent, cross-talk, interruption |
| Misbound answer | Answer "yes" to the question before the pitch | Delayed answer, overlapping answer |
| Combo repricing | Accept a combo upgrade with modifiers on the base item | Every size, side choice and drink choice |
| Modifier carry-over | Upsize after "no ice" and an allergy note | Several modified lines in one order |
| Total mismatch | Read back, then compare with POS and receipt | Discounts, tax rules, rounding |

Accents matter on one-word answers. See [voice agent accent robustness](/blog/voice-agent-accent-robustness) for that slice.

## How to test a drive thru ai upselling change before it ships

1. **Write the pitch policy as testable rules.** Examples: never pitch after a closing phrase, one pitch per order, never pitch an item in the cart or a combo. Evalgent generates test scenarios from an agent's prompt and flows, including hard constraints and policies, so rules like these become scenarios.
2. **Build the break matrix.** Use the table above. Script each row with an expected final cart, not only an expected phrase.
3. **Vary the caller.** Use accent, speech pace, interruption level, response delay and background noise. Evalgent's caller profiles cover these, and its simulator reaches the agent over phone, SIP or WebRTC.
4. **Judge from the audio and the ticket.** Compare what the guest said with what the POS stored. Add custom metrics such as pitch after close, duplicate line and modifier loss, and run them on every call.
5. **Gate the release.** Require zero late pitches and zero duplicate lines in the matrix, and a corruption upper bound you can defend, such as the rule-of-three figure.
6. **Roll out in stages.** Shadow mode, then canary lanes, with a rollback threshold set before launch.
7. **Monitor live calls.** Push recordings so calls are transcribed, mapped to scenarios and judged. Evalgent's production monitoring flags tool-call failures and policy failures with fix recommendations. Score every call, not a sample, as in [score every call, not a sample](/blog/score-every-call-not-a-sample).

Also walk the [AI phone agent go-live checklist](/blog/ai-phone-agent-go-live-checklist) and the [2026 QA playbook for AI phone ordering](/blog/voice-agent-testing-food-ordering).

## What this does not cover

The public data above measures how often chains pitch, not what a pitch earns. We found no primary source for a voice AI upsell acceptance benchmark, so we left it out. Measure your own. We also did not verify any state price-display rule, so ask counsel before changing how prices are spoken or shown.

## Frequently asked questions

### Does AI drive-thru upselling increase average ticket size?

It can, but public evidence is thin. Mystery-shop data shows voice AI offers suggestive sells more often, 79.2% of visits versus 59.2% at the same chains in 2026. No primary source we found ties that to a larger check. Measure clean acceptance and net revenue in your own stores.

### Why does a drive-thru AI keep upselling after I say that's all?

The pitch was usually planned before the closing phrase was finalized. Speech-to-text needs a pause to finish, and the queued pitch plays in the gap. A gate that rechecks the cart version and a closing flag at speak time stops it.

### How do you measure upsell acceptance for a drive-thru voice agent?

Count clean accepts: accepted offers whose final ticket matches what the guest meant, divided by offers. Track raw accept rate, corruption rate and phantom add rate beside it. Raw acceptance alone counts a misheard yes or a duplicated side as a win.

### How accurate is AI drive-thru order taking when it upsells?

We found no public study that separates the two. Intouch Insight's 2026 shops found 90.8% order accuracy at AI locations and 90.0% correct on the order the AI confirmed (n=70). That sample was selected, not random. Audit pitched orders separately from unpitched orders to see your own rate.

### How do you A/B test upsell prompts without sending wrong orders to stores?

Test in simulated calls first, then run the new prompt in shadow mode, then canary lanes with automatic rollback. Set a corruption guard before launch. With zero bad tickets in 300 simulated calls, the 95% upper bound on the true rate is about 1%.

### What happens to modifiers and allergy notes when a guest accepts a combo upgrade?

They can be lost. A combo conversion rebuilds the line, and "no pickles" or an allergy note may not carry over. Test by recording modifiers before the pitch and comparing them after acceptance. They should match unless the guest changed them. Escalate any allergy flag to a person.

### How can a voice agent mishear a yes or no on an upsell?

One-word answers have little context for the engine to use. Noise, a passenger or an interruption can become "yes". Research shows speech models can produce text from non-speech audio. Tag each question with an ID, bind answers to the open question, and test with noise and cross-talk.

### How many test calls do you need before changing an upsell prompt?

For a lift from 24% to 27% clean acceptance, about 3,300 offers per arm. For a safety check, 300 simulated calls with zero corrupted tickets bound the true rate under about 1% at 95% confidence. Use both numbers, and cover every break type in the set.

## The bottom line

A drive thru ai upselling pitch is a write to the order, so it must be tested like one. Count clean acceptance, gate every pitch at speak time, and prove a prompt change in simulated calls before any store hears it.

Related Articles