When the Caller Is an Agent: Action Boundaries for AI-Driven Backends

A confident model output is not admission to mutate Tier‑1 state. How to design action boundaries when callers are humans or agents — proposal vs admission, risk gates, idempotency, sync vs async, and fail-closed assurance.

In the previous post I argued that wire fraud after deepfakes is not primarily a detection problem. It is an authorization design problem: the meeting was treated as the approval. The same failure is now showing up in a new place — not only in human workflows, but in systems where an AI agent can propose the next privileged action.

A model drafts a tool call. A workflow engine retries. A RAG agent chains three API mutations before a human notices. From the product’s point of view it can look like “AI assistance.” From the backend’s point of view it is a high-frequency caller that never gets tired, never feels urgency the way a person does, and will happily replay a side effect if your contract allows it.

This post is about that shift. Not another model-vendor risk survey — that is in DeepSeek and enterprise AI. Not another threat landscape piece — that is in The Quiet War and wire fraud controls. Here the focus is architecture: how to design action boundaries so that a valid session, a valid API key, or a confident model output is never mistaken for permission to mutate Tier‑1 state.

AI did not invent unsafe writes — it scaled them

Backend teams have always had dangerous endpoints: booking confirmation, listing publish, payout instruction change, admin override. Humans hit them through UIs. Integrations hit them through batch jobs. The control problem was already real.

What changed is the caller profile. An LLM-backed agent can:

  • Propose mutations faster than a human operator can review them.
  • Retry aggressively when latency or timeouts look like failure.
  • Chain tools so one “helpful” plan becomes five writes across services.
  • Sound authoritative in logs and tickets even when the plan is wrong.

That is the deepfake lesson in software form. Synthetic media broke the habit of treating a conversation as authorization. Agents break the habit of treating a successful model step as admission to change production state.

If your platform is adding copilots, auto-triage, or tool-calling workflows — and most serious SaaS platforms are — the write path has to assume the caller may be automated. Staff-level backend design is how you keep correctness when intelligence is no longer only on the human side of the request.

The unit of safety is still the action, not the session

Most API stacks authenticate at the edge and authorize with a coarse check: valid JWT, correct tenant, role includes write. Necessary. Not sufficient for customer-facing transaction systems — and especially not sufficient when the subject is an agent acting on behalf of a user.

A session answers: who is calling?
A role or scope answers: are they generally allowed to touch this resource class?
A model score answers: how confident is the plan?
An action boundary answers: is this specific mutation allowed right now, with this payload, under these invariants, and what happens if we try it twice?

Those are different questions. Conflating them is how you get a session that authenticated cleanly at 9 a.m. and mutated irreversible state at 2 p.m. because a tool call “looked right” — the same way a finance employee authorized a transfer because the CFO “looked right” on video.

Four layers that keep getting mixed up

When teams say “we secured the AI feature,” they often mean only the first two layers:

  • Authentication — proof of identity for this request (user token, service identity, signed device JWT).
  • Authorization — policy over resources and roles (RBAC, scopes, tenant isolation).
  • Model / risk judgment — retrieval grounding, policy prompts, fraud or anomaly scores, allow/monitor/challenge/block.
  • Action admission — whether this business command may proceed: schema validation, invariants, idempotency, step-up, rate limits, and side-effect contracts.

Login MFA and gateway JWT checks sit in the first layer. Tenant middleware sits in the second. Guardrails and risk models sit in the third. Marketplace posting, booking, beneficiary changes, and privileged agent tools fail in production when the fourth layer is missing.

A strong model does not replace admission. A weak model does not excuse skipping it. The backend owns the write.

A concrete flow: agent proposes, system admits

Take a generic high-stakes write: something other parties will rely on — a listing, a booking request, a standing instruction update. Whether a human clicked Submit or an agent emitted a tool call, the HTTP shape can look the same:

POST /v1/resources/{id}/mutations
Idempotency-Key: <client-or-agent-generated-uuid>
Authorization: Bearer <access-token>
X-Actor-Type: agent   # or user / service

{
  "type": "UPDATE_DESTINATION",
  "payload": { ... },
  "proposed_by": "copilot",
  "confidence": 0.81
}

What should happen before any durable side effect?

  1. Authenticate the caller and bind the request to a tenant and subject.
  2. Authorize that this subject may propose this mutation type on this resource — including whether agents are allowed at all for this action class.
  3. Separate proposal from admission — treat model output as a proposal, never as a committed decision.
  4. Score risk if you have signals: device, velocity, amount, destination novelty, retrieval citations, historical abuse. Map to allow / monitor / challenge / block.
  5. Validate the payload against a versioned schema — reject unknown fields for sensitive commands. Tool schemas are API contracts.
  6. Check invariants in the system of record: resource state, ownership, cooling periods, dual-control requirements, velocity limits.
  7. Admit, reject, or challenge with a stable decision. Challenge may mean step-up MFA for the human principal behind the agent.
  8. Record the decision with an audit trail that includes actor type, proposal source, and reason codes — independent of the model’s narrative.
  9. Emit work only after admission — sync response for the decision, async for fan-out.

Steps 1–2 are identity. Step 4 is where AI/ML earns its keep on the write path. Steps 5–9 are classical backend architecture. Most “AI incidents” that look like model failures are actually missing steps 3 and 7: the system executed the proposal.

Risk at the boundary, not vibes in the prompt

Prompt instructions like “do not call dangerous tools” are useful. They are not a control plane.

Production systems need a gate that turns signals into a decision before mutation:

  • allow — proceed to invariant checks and admission.
  • monitor — proceed, but elevate logging and review.
  • challenge — require step-up or human confirmation before admission.
  • block — reject; do not write.

This is the same shape as adaptive authentication, applied to actions instead of only to login. It is also where ML belongs in a serious backend: not as a replacement for invariants, but as a ranked input to the gate. Rule floors catch obvious cases (blocked country, unknown payee, agent forbidden on this action class). Models catch subtler velocity and behavior patterns. Threshold changes should roll out report-only first — log what would have happened before blocking real users or agents.

I spend a lot of engineering time on this layer in identity and agentic systems: evaluate at the action boundary, step up before execution, scope any elevation to that operation alone, and fail closed when step-up cannot complete. The channel may be a human UI or a tool call. The design requirement does not change.

Idempotency is mandatory when agents retry

Humans double-click. Agents double-call. Orchestrators replay nodes. Load balancers retry after the server already committed. If your mutation endpoint is not idempotent, you do not have a Tier‑1 API — you have a raffle with a smarter player.

An action boundary should treat the Idempotency-Key (or a natural business key) as part of admission:

  • Same key + same body → return the original decision.
  • Same key + different body → conflict, do not silently apply the second body.
  • New key → new admission attempt, subject to risk and invariants.

Store the admission record before fan-out. If the process crashes after commit and before the response, the retry must find the same decision. That is how you keep correctness when both the network and the agent lie about what already happened.

This is also where event-driven systems start. Once you admit a mutation, downstream consumers will see the event at least once. If admission was not idempotent, every consumer inherits the bug — and an agent may have created the duplicate in the first place. That is the next article in this series.

Sync decision, async consequences

A common design mistake — worse with agents — is to do everything in the request thread: write primary state, call three tools, publish to Kafka, send email, update search, then return 200. Under load or partial failure, that path becomes a latency and consistency trap. Under agent load, it becomes a thundering herd.

A cleaner split:

  • Sync path — authenticate, authorize, risk-gate, validate, check invariants, admit, persist decision + primary state needed for user-visible truth, return a clear status (including challenge).
  • Async path — notifications, projections, analytics, partner webhooks, search indexing, secondary agent follow-ups.

The sync path owns what is true for the product right now. The async path owns who else needs to know. When those blur, on-call spends nights deciding whether a 500 means “nothing happened” or “the agent half-finished a plan.”

For Tier‑1 flows, prefer: admit durably, respond with the decision, process side effects with workers that can retry safely. That is not anti-AI. It is how you make AI features operable.

Fail closed when assurance cannot complete

When step-up is required and the MFA service is down, what should the mutation do? When the risk service times out? When the idempotency store is unreachable?

For read-mostly copilots, degraded answers may be acceptable. For money movement, booking confirmation, or irreversible marketplace state, fail-open is a product decision disguised as resilience — and agents will find it faster than humans will.

Default for sensitive mutations: fail closed. Return a challenge or a retryable 503. Log the reason code. Do not apply the write because “the model was confident” or “auth usually works.” Unavailable assurance means the privileged action does not execute.

Tool schemas are API contracts

At staff level, the action boundary is also how teams scale AI features without inventing a new unsafe write every sprint.

  • Version the command, not only the URL. type: UPDATE_DESTINATION@v2 beats silent field meaning changes — models will invent fields if you let them.
  • Constrain tool JSON schemas the same way you constrain public APIs. Extra properties on sensitive commands should fail closed.
  • Document error classes: validation vs authz vs risk-block vs invariant vs conflict vs challenge.
  • Publish examples of idempotent retries and challenge/resume flows for agent runtimes.
  • Keep OpenAPI honest — if a handler accepts free-form model output that changes money movement, the contract is lying.

Mentorship here is concrete: teach engineers to put new AI capabilities behind an existing admission pattern instead of a one-off POST /agent-do-the-thing.

What this looks like in a design review

When I review a Tier‑1 write path — especially one reachable from an agent — I ask:

  1. Is model output treated as proposal or as decision?
  2. What is the exact command being admitted, and is the schema versioned?
  3. Can agents call this action class at all? Under what scopes?
  4. What risk signals feed allow / monitor / challenge / block?
  5. What is the idempotency key, and where is it stored?
  6. Which invariants are checked before commit?
  7. What is sync vs async after admission?
  8. What happens on duplicate delivery of the request and of the downstream event?
  9. What do we do when a dependency required for assurance or risk is unavailable?
  10. How does an operator reconstruct who/what proposed and who/what admitted, from logs alone?

If those answers are vague, the system will eventually answer them in an incident channel — after an agent has already moved faster than your runbook.

Back to the wire fraud lesson

Deepfakes made one informal habit untenable: treating a conversation as authorization. Agents make another untenable: treating a fluent plan as admission to mutate.

The fix is the same shape. Put the control in the architecture. Separate who is calling from what may happen next. Use ML as a ranked input to the gate, not as a substitute for invariants. Make the decision durable, auditable, and safe under retry. Fail closed when assurance cannot complete.

That is how you move from “we added a copilot” to “we can operate an AI-touched, customer-facing transaction system.”

The core argument

  • Agents scale unsafe writes — retries, tool chains, and speed make weak admission fail faster.
  • Proposal ≠ admission — model output is an input to the gate, not a commit.
  • Four layers — authn, authz, risk judgment, and action admission; AI lives in risk, backend owns admission.
  • Idempotency is mandatory — agent retries make duplicate side effects a design issue, not an edge case.
  • Sync decides, async fans out — keep user-visible truth on the durable admission path.
  • Fail closed on sensitive writes — unavailable assurance or risk should block the mutation.
  • Tool schemas are contracts — versioned commands beat free-form model writes.

What comes next

Once a mutation is admitted — whether proposed by a human or an agent — the rest of the platform usually learns about it through events. At-least-once delivery is the default. The next post in this series will cover how to design consumers so duplicate events do not create duplicate bookings, duplicate wires, or duplicate marketplace state — especially when agents amplify retries.

Further reading

#AIEngineering #SystemDesign #AgenticAI #BackendEngineering #APIDesign #DistributedSystems #Idempotency #StaffEngineer #IAM #MLOps