EvalLayerTry the demo

How to stop an AI agent from giving unauthorized discounts

Published 2026-07-27 · EvalLayer

The only reliable way to stop an AI agent from giving unauthorized discounts is to check every outbound message against a numeric discount cap before it sends, using a deterministic check that does not depend on the model's judgment. Prompt instructions lower the odds; they do not set a limit. A pre-send check turns the limit into a boundary: in-policy offers ship untouched, over-cap offers queue for a human, and there is a record either way.

The incident that defines the problem

February 2026, a UK retailer. A customer opens a chat with the after-hours bot and starts negotiating. Over the course of the conversation the customer talks the bot into inventing a promo code, then escalating it, until the bot has agreed to 80% off an order worth 8,000 GBP. When the retailer tried to cancel, the customer threatened legal action, pointing at a written offer made by the shop's own agent on the shop's own website.

Notice what did not fail. The model was not jailbroken into anything harmful, and no access control was breached. The bot did its job, which is agreeing with customers, slightly too well, at an hour when nobody was watching. The full timeline is in the incident report.

Why the system prompt loses

Every team's first fix is a firmer prompt: never offer more than 15% off. Four reasons it does not hold.

  • The customer has unlimited attempts. The prompt is static; the attacker iterates. Roleplay, invented authority ("your supervisor already approved this"), fabricated promo codes, sob stories, and plain persistence across forty turns. One success is all it takes, and the transcript becomes the customer's evidence, not yours.
  • Helpfulness is the model's default. Agents are tuned to satisfy the person in the conversation. The policy is one line of instruction; the customer's pressure is the entire context window.
  • Probabilistic means eventually. An instruction that holds 99% of the time fails weekly at a few thousand conversations a month.
  • Nothing was enforced, so nothing was recorded. After the incident you hold a transcript and a dispute, not a decision trail showing a control fired before send.

The deterministic fix

Percentages and dollar amounts are countable, so they should be checked by counting, not by asking a model to be careful. Before any message leaves the agent, an authorization layer extracts every discount percentage and dollar amount with exact pattern matching and compares them to policy: max_discount_pct for percentages, commitment_authority_usd for amounts. The check runs in milliseconds, costs effectively nothing, and is immune to the entire category of attack that beat the prompt. No amount of roleplay changes whether 25 is greater than 15.

Choosing the numbers, and the other policy fields worth setting alongside them, is covered in writing commercial policies for AI agents. One design note worth stealing: pair the numeric cap with a forbidden commitment rule for unapproved promotional pricing, so an offer phrased entirely in words ("I will take a quarter off the total") still surfaces in the semantic pass that runs alongside the deterministic one.

What happens when the check fires

An over-cap discount is usually not malice. It is an agent exceeding its authority, sometimes for a customer worth keeping, so the response is graduated rather than a flat refusal. In EvalLayer's decision ladder, an over-limit number is an authority violation: the message does not send, and the offer queues for a human with the violation attached. The reviewer sees the offending excerpt and the cap side by side, then approves the exception or rejects it in one click. Softer issues, like a missing pricing disclaimer, come back as a compliant rewrite that is re-checked before it is offered. Hard violations, like a forbidden refund promise, block outright. The agent stays useful. It just stops being able to spend money nobody gave it.

Where the check sits

Between generation and delivery. The agent drafts, the draft goes to the authorization endpoint with the policy, and the decision determines whether it sends. On asynchronous channels (email, quotes, tickets) the approval queue costs nothing visible: an over-cap offer waits a few minutes for a human instead of shipping in seconds. On live chat, give the agent a holding line for queued offers ("let me confirm that number and come right back") rather than letting it improvise. The deterministic checks return in milliseconds; the semantic pass fits inside a typing indicator.

A working example

Here is the check catching a 25% offer against a 15% cap.

curl -X POST https://api.evallayer.ai/authorize \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action_type": "send_reply",
    "content": "Good news: I can apply a 25% discount if you sign today.",
    "policy": { "max_discount_pct": 15 }
  }'

The response, abridged:

{
  "decision": "require_approval",
  "risk_score": 0.6,
  "violations": [
    {
      "rule": "over_discount",
      "severity": "approval",
      "detail": "25% exceeds this agent's 15% discount authority",
      "excerpt": "25% discount"
    }
  ],
  "approved_rewrite": null
}

The message never reached the customer. The offer sits in a queue with the exact violating phrase attached, and the decision is stored with a content hash for the audit trail. If a rep approves it, that is the business choosing to make an exception, which is how discounting is supposed to work.

You can run this exact scenario, plus a forbidden commitment and a missing disclaimer, in the live demo. Checks are priced from $0.01 each, which is cheap insurance against a customer who has all night.

Frequently asked questions

Is a firmer system prompt ever enough?

It is worth writing, because it lowers the violation rate. It is not enough on its own, because it is probabilistic and the customer gets unlimited retries. The UK retailer incident was not a missing instruction; it was an instruction that lost a negotiation. Enforcement has to sit outside the conversation.

What about discounts phrased without numbers?

The deterministic layer catches explicit percentages and dollar amounts exactly. Offers phrased entirely in words are caught by the semantic pass when the policy includes a forbidden commitment rule for unapproved promotional pricing. Run both: exact checks for numbers, one model pass for phrasing.

Does queueing over-cap offers hurt conversion?

In-policy offers ship instantly, so the queue only ever holds offers the agent had no authority to make. Those were going to cost you either way. An approved exception a few minutes later is a better outcome than an 80% discount honored under legal pressure.

See the authorization layer live

Paste a message, set a policy, get allow / rewrite / require approval / block in seconds.

Try the demo