AI agent actions and APIs: how to let a support agent do things safely
Communicate.so
How AI agent actions work: idempotency, confirmation steps, scoped permissions, and the blast radius of a wrong write in customer support.
TL;DR: An AI agent that only answers questions is safe by default, because a wrong sentence is embarrassing but reversible. An AI agent that can also act, issuing a refund, changing a subscription, updating an address, crosses into a different risk class, because a wrong write changes a system of record and a customer's money. This guide covers what an action actually is under the hood, why every write needs an idempotency key, why some actions need a confirmation step and others do not, and how to scope permissions so a support agent can help without becoming an admin panel. It also covers the failure modes that actually happen: double refunds from network retries, an agent that argues itself into approving a return it should have escalated, and a broken webhook that silently drops confirmations. The angle is concrete: idempotency patterns, permission scopes, and a worked example, not general advice about being careful.
Most write-ups about AI agents in support stop at answering. The harder, more valuable step is letting the agent act on a customer's account through AI agent actions, not just describe what the customer should do next themselves.
This guide is for the engineer or support lead deciding which actions to expose to an agent and how to build the plumbing underneath them. It assumes you already have a working answer layer and are now asking a different question: what happens the first time the agent is wrong about a write instead of a sentence. For the guardrails that keep answers grounded before you ever reach this stage, see AI agent guardrails.
What an action is, mechanically
An action is a function call the agent can invoke with structured arguments, wired to a real API endpoint on your side: your order system, your billing provider, your CRM. The model does not touch your database directly. It emits a structured request, your backend validates and executes it, and the result comes back into the conversation as a new fact the model can reference.
The three-layer split matters because each layer has a different failure mode. The model can misread intent or hallucinate an argument. The orchestration layer can retry a call that already succeeded.
The backend can reject a call that violates a business rule. Treat all three as independent points of failure, not one combined risk.
A refund action, for example, takes an order ID and an amount, calls your payment provider through Stripe or a similar processor, and returns a confirmation or an error. The agent never holds a payment credential. It holds a scoped token that can call one endpoint with bounded arguments, and your backend enforces the actual limits.
This separation is what makes actions auditable. Every call that goes through your backend can be logged with the same request ID, the same customer, and the same timestamp as the conversation turn that triggered it, so a support lead can reconstruct exactly what happened and why without trusting the model's own account of events.
Communicate.soIdempotency: the one rule that is not optional
Every write action needs an idempotency key, full stop. A model call can be retried by the orchestration layer, by a flaky network connection, or by a user who clicks submit twice while the assistant is thinking. If the underlying action is not idempotent, a retry does the action again.
The standard pattern, documented by Stripe among others, is to generate a unique key per logical action attempt, not per HTTP call, and pass it along with the request. The server stores the key with the result of the first execution and returns that same result for any duplicate key, instead of executing again.
For an AI agent, the key should be derived from something stable in the conversation: the turn ID plus the action name plus a hash of the arguments. That way, if the same turn triggers the same action twice because of a retry, the key matches and the second call is a no-op that returns the first result.
A concrete failure this prevents: a customer asks for a refund, the agent calls the refund endpoint, the network times out before the response arrives, the orchestration layer retries, and without an idempotency key the customer gets refunded twice. With a key, the second call finds the stored result and returns it unchanged, and the payment processor never sees a duplicate charge reversal.
| Idempotency approach | Prevents duplicate writes | Survives orchestration retries | Requires backend storage |
|---|---|---|---|
| No idempotency key | ✗ | ✗ | ✗ |
| Client-generated key per HTTP call | ✗ | ✗ | ✓ |
| Stable key per logical action attempt | ✓ | ✓ | ✓ |
| Database unique constraint on the action | ✓ | ✓ | ✓ |
The bottom two rows are not mutually exclusive. A stable key stops most duplicates before they reach your database, and a unique constraint on the action table is the backstop for the case where two requests race each other and both pass the key check before either writes, a pattern AWS documents in its idempotency whitepaper for distributed systems generally.
Confirmation steps: when to ask before acting
Not every action needs a confirmation step, and adding one everywhere trains customers to click confirm without reading, which defeats the purpose. The useful rule is to gate confirmation on reversibility and blast radius, not on how the action feels.
A password reset email is fully reversible, low blast radius, and safe to fire without confirmation. A full account deletion is irreversible, high blast radius, and should always require an explicit second step, ideally one the agent cannot complete on its own.
Refunds sit in the middle and deserve a closer look. A refund under a threshold you set, say the value of a typical order, can often go through automatically because the financial exposure per mistake is small and the volume savings are real. A refund above that threshold, or one that contradicts your stated policy, should pause for either a customer confirmation or a human review, a pattern close to what Zendesk describes in its guidance on automation thresholds for financial actions.
The confirmation itself should restate the specific action in plain terms: the order number, the amount, and what will happen next. A generic 'are you sure' teaches nothing and gets clicked through as reflexively as the action it was meant to gate.
The design failure to avoid is a confirmation that only exists in the chat transcript with no server-side check. If the backend will execute whatever the model claims was confirmed, the confirmation is theater. The backend should require a signed token or a matching flag set only after the actual confirmation event, not trust a sentence in the conversation history.
Communicate.soScoping permissions so the agent cannot become an admin panel
Scope every action token to the narrowest permission that accomplishes the task. An agent that can issue a refund does not need the ability to change a customer's email address, and an agent that can update a shipping address does not need refund authority. Bundling every capability into one broad token turns a single prompt injection or reasoning error into an account takeover.
The practical pattern is a permission scope per action, enforced server-side, independent of what the model claims it intends to do. If the model calls an endpoint outside its granted scope, the backend rejects the call before it touches any customer data, and that rejection is itself a signal worth logging and reviewing.
Rate limits belong at the same layer. A single conversation should not be able to trigger fifty refund attempts in a minute, even if every individual call looks legitimate, because that pattern is either a bug in the orchestration loop or an attempted abuse of the AI agent. A per-conversation and per-customer ceiling catches both.
Field-level scoping matters as much as endpoint-level scoping. An action that updates a customer record should only be allowed to touch an explicit allowlist of fields, such as shipping address or phone number, never an open-ended patch that could also modify a role, a permission flag, or a payment method on file.
Treat every action definition as a contract the model cannot renegotiate. The model proposes arguments, the backend validates them against the scope, and nothing the model says in its reasoning changes what the backend is willing to execute. This is the same trust boundary discipline the OWASP top ten for LLM applications recommends for excessive agency, treated as a named risk category rather than an edge case.
The blast radius of a wrong write
A wrong answer in chat is bad and reversible: the customer reads it, maybe acts on bad information, and a follow-up message can correct the record. A wrong write is bad and sometimes not reversible: money has moved, a subscription has changed tier, an account has been deleted.
Rank actions by blast radius before you expose any of them. A password reset is near-zero blast radius. A refund is bounded blast radius, capped at the order value.
An account deletion or a bulk data export is unbounded blast radius, because the damage scales with how much data or money was in scope, not with a single transaction.
Cursor's support incident is a useful real-world reference point even though it involved an answer rather than a write: the company's cofounder publicly acknowledged an incorrect response from a front-line AI support bot that told customers a real policy did not exist. Apply that same failure mode to a write action and the cost is not a wrong sentence, it is a wrong refund or a wrong cancellation that already executed.
DPD's chatbot incident shows a different angle on blast radius: a badly behaved agent was disabled within a day once it started swearing at customers, according to reporting in The Register. A misbehaving answer agent can be turned off fast because the damage stops the moment it goes quiet. A misbehaving action agent has already left artifacts in your systems of record that outlive the shutdown.
The design implication is to size your confirmation and rollback investment to the blast radius, not to the frequency of the action. A rare but catastrophic action, like a full account deletion, deserves more friction than a common but bounded one, like a small refund, even though the common action will trigger far more often in production.
Building a rollback path before you ship the action
Every write action needs a rollback plan defined before it ships, not discovered after the first incident. For a refund, the rollback is usually another payment operation, a re-charge with clear customer notice, and it should be exactly as scoped and logged as the original action.
For a subscription change, the rollback is reverting to the prior tier and prior billing date, and that requires storing the previous state before the change executes, not reconstructing it from logs after a customer complains. Store the pre-action snapshot as part of the same transaction that performs the action.
For actions with no clean technical rollback, like a shipped order or a sent email, the rollback is procedural: a documented escalation path to a human who can issue a manual correction, a discount, or an apology. The agent should know these actions are one-directional and should weight its confidence threshold higher before firing one, a distinction the ai agent guardrails approach to refusal thresholds covers in more depth.
Test the rollback path the same way you test the action itself. A rollback that only exists as a runbook nobody has executed is a rollback you do not actually have, and the first real incident is the wrong time to discover a missing permission or a broken script.
Communicate.soA worked example: a bounded refund action end to end
Communicate.soWalk through one action fully to see how the pieces fit. A customer messages that an order arrived damaged and asks for a refund. The agent, grounded in your policy documents through RAG for customer support, confirms the order qualifies under your stated return window and damage policy.
The agent proposes a refund action with the order ID and the order amount as arguments. The orchestration layer generates an idempotency key from the turn ID, the action name, and a hash of those arguments, then calls your backend.
Your backend checks three things before touching the payment provider: the token's scope allows refunds, the amount is under the automatic-approval threshold you configured, and the idempotency key has not been seen before. If all three pass, it calls Stripe with the same idempotency key, stores the result, and returns a confirmation to the agent.
The agent tells the customer the refund is processing and states the timeline your payment provider actually gives for funds to appear, rather than a generic promise. If the amount exceeds the threshold, the backend instead creates a pending approval and the agent tells the customer a person will confirm shortly, which routes into your existing escalation workflow.
Every step in this sequence writes to the same log line: turn ID, idempotency key, action name, arguments, and outcome. When something goes wrong three weeks later, a support lead pulls that log and sees exactly what happened, instead of reconstructing it from a customer's memory of a chat, a discipline that ties directly into how you measure and report on agent behavior through analytics.
Where communicate fits, honestly
Communicate's actions layer lets a grounded agent call scoped functions against your systems, refunds, order lookups, account updates, with the idempotency and permission-scoping patterns described above enforced server-side, not left to the model's discretion.
The agent runs on gpt-4o-mini through OpenRouter with response and prompt caching, and every action call is logged against the same conversation record the customer sees, so a review of what happened does not require trusting the model's own summary of events.
The honest limits: actions are configured per workspace and require you to define the scope, the threshold, and the confirmation rule for each one, there is no default set of pre-built financial integrations beyond what you wire up. Entry is a one-time $1 activation with 100 test credits, and the pricing page has the details. Questions go to [email protected].
Key takeaways
- Every write action needs an idempotency key derived from the conversation turn, not the HTTP call, so a retry cannot execute the same action twice.
- Gate confirmation steps on reversibility and blast radius, not on how risky an action feels, and enforce confirmation server-side, never by trusting the chat transcript.
- Scope every action token to the narrowest permission and field set that accomplishes the task, with rate limits enforced independently of what the model claims it intends.
- Rank actions by blast radius and define a rollback path before shipping, including a documented human escalation for actions with no clean technical reversal.
- Log every action call with the same request ID as the conversation turn so a review of what happened does not depend on the model's own account of events.
Ready to add scoped, auditable actions to your support agent? Start with a one-dollar account activation that includes 100 test credits, wire up an action against a real endpoint, and test the idempotency and confirmation paths before any customer sees them. The ai agent guardrails guide covers the refusal and escalation logic that should sit upstream of every action call.
Frequently asked questions
What is an AI agent action, technically?
An action is a structured function call the model can invoke with defined arguments, wired to a real backend endpoint that performs the write. The model never touches your database directly, it emits a request that your backend validates and executes, then returns a result the model can reference. This separation, covered in the AI agent actions section above, is what makes actions auditable and safe to scope.
Why does every write action need an idempotency key?
Because network retries, orchestration retries, and duplicate user clicks can all cause the same logical action to be attempted twice. Without a stable key that identifies the attempt, a retry executes the action again, which for a refund means a customer gets paid twice and for an account update means a change gets applied twice with unpredictable results.
How should the idempotency key be generated?
Derive it from the conversation turn ID, the action name, and a hash of the arguments, not from the HTTP request itself. That way any retry of the same logical attempt produces the same key, and the backend can return the stored result from the first execution instead of running the action again.
Which actions need a confirmation step before they execute?
Gate confirmation on reversibility and blast radius. Low-risk, reversible actions like a password reset email can fire automatically. High-risk or irreversible actions like an account deletion or a refund above a set threshold should pause for explicit confirmation, ideally enforced server-side rather than trusted from the chat transcript, a threshold pattern covered in the confirmation section above and in ai agent guardrails.
Can a confirmation step be faked by a clever prompt?
Not if the backend enforces it correctly. A confirmation that only exists as a line in the chat transcript is not a real gate, because the model could claim confirmation happened when it did not. The backend should require a signed token or a server-recorded flag set only by the actual confirmation event, independent of anything the model says.
How narrow should an action's permission scope be?
As narrow as the task requires. An action that issues refunds should not also be able to change a customer's email or payment method. Bundling capabilities into one broad token means a single reasoning error or prompt injection can reach far more than intended, a risk OWASP names explicitly as excessive agency in its top ten for LLM applications.
What is field-level scoping and why does it matter?
Field-level scoping restricts an update action to an explicit allowlist of fields, such as shipping address or phone number, rather than allowing an open-ended patch to a customer record. Without it, an action meant to update a shipping address could technically also modify a role or a payment method, because the backend never restricted which fields the call was allowed to touch.
Should rate limits apply per conversation or per customer?
Both. A per-conversation limit catches a runaway loop within a single chat, and a per-customer limit catches abuse or bugs that span multiple conversations from the same account. Neither limit should depend on the model's own judgment about whether it has called an action too many times.
What does blast radius mean for an AI agent action?
Blast radius is the scope of damage a single wrong execution can cause. A password reset has near-zero blast radius. A bounded refund has blast radius capped at the order value.
An account deletion or bulk data export has unbounded blast radius, because the damage scales with how much was in scope, not with a single call, a distinction covered in the blast radius section above.
How does a wrong action differ from a wrong chat answer?
A wrong chat answer is embarrassing but reversible with a follow-up message. A wrong action can leave a permanent artifact in a system of record, like a duplicate refund or an unwanted subscription change, that a correction cannot simply undo. Cursor's cofounder publicly acknowledged an incorrect response from a front-line AI support bot, illustrating the reputational cost even for an answer-only failure.
Do all refunds need human approval?
No. A refund under a threshold you set, sized to your typical order value and risk tolerance, can process automatically because the exposure per mistake is small. A refund above that threshold, or one that contradicts stated policy, should route to a pending human approval instead of executing immediately.
What should happen when an action call fails midway?
The backend should treat a failed call as not-yet-executed and safe to retry with the same idempotency key, which returns either the stored prior result if it did complete, or executes fresh if it did not. The agent should tell the customer the action is being retried or has failed, rather than guessing at an outcome it cannot confirm.
How do you build a rollback path for an action that has no clean reversal?
For actions like a shipped order or a sent email that cannot be undone technically, the rollback is procedural: a documented escalation to a human who can issue a manual correction. The agent should recognize these as one-directional and weight its confidence threshold higher before firing one, a pattern the ai agent guardrails approach to refusal thresholds extends to actions.
Should the pre-action state be stored before an action executes?
Yes, as part of the same transaction that performs the action. Reconstructing a customer's prior subscription tier or address from logs after a complaint is slower and less reliable than storing the pre-action snapshot up front, and it is the only way to build a dependable rollback for actions that do support reversal.
What logging does an action call need?
Every action call should log the turn ID, the idempotency key, the action name, the arguments, and the outcome, tied to the same conversation record the customer sees. This lets a support lead reconstruct exactly what happened weeks later without relying on the model's own summary of events, which may be incomplete or wrong.
Can an AI agent be trusted to decide when to escalate instead of act?
Only within the boundaries the backend enforces. The agent's judgment about whether to act or escalate should be a proposal, not the final gate, because the backend independently checks scope, threshold, and idempotency regardless of what the agent decided. This mirrors the grounding discipline in reduce AI hallucinations in support, where the model's confidence is never the sole safeguard.
How does action scoping relate to prompt injection risk?
Scoping is the actual defense. A prompt injection that tricks the model into proposing an action outside its intended use still hits the same backend scope check as a legitimate request, and a correctly narrow scope means the worst outcome is a rejected call, not an unauthorized write. This is the excessive agency risk OWASP documents for LLM applications generally.
Should every action be reversible before it ships?
No, but every action needs an explicit rollback plan, technical or procedural, defined before it ships. Some actions, like a sent email, genuinely cannot be undone, and the plan for those is a documented human escalation, not a technical reversal that does not exist.
How do you test an action's rollback path?
The same way you test the action itself: run it in a staging environment against real dependencies, verify the rollback restores the prior state correctly, and repeat after any change to the action's logic. A rollback that has never actually been executed is a rollback you do not reliably have.
What is the single biggest mistake teams make when adding actions to a support agent?
Treating the action layer as an extension of the chat layer's trust model. A chat answer that is wrong costs a correction message. A write that is wrong costs a reversal, a customer's trust, or money that has already moved, and the backend needs to enforce that difference independently of the model's confidence.