A support chat can turn into a money-moving action when a FastAPI backend accepts model output as authority.
In the running failure for this course, a caller app sends a normal-looking message to POST /v1/chat, the FastAPI service forwards that text to a model provider, and the model returns a tool instruction that resembles a refund request.
The backend executes the tool call as soon as the model suggests it, without checking whether the end user is allowed to perform that action, and without treating the model output as untrusted text.
Nothing exotic is required for the failure. The backend does exactly what it was built to do, but it runs the most privileged step, the tool action, based on a string that came from outside the trust boundary.
When the irreversible action has already run, no later filter can undo the side effect, so we have to decide where the first effective stop belongs.
The same request crosses several boundaries where untrusted text turns into spend or side effects, and the course stays anchored to that one path so every control lands in a specific place.
Untrusted text enters from three places even when the caller behaves. The caller message arrives in the HTTP request, retrieved documents arrive from the backend’s own data stores, and tool results come back from other systems that can be wrong or hostile.
Money and data can be spent at two points. The model provider call consumes tokens and cost, and the tool execution can read or change real resources that the user might not own.
The only reliable enforcement points are server-side checks in the FastAPI service around those spend points. The course builds authentication, tenant binding, quotas, input and output filters, tool authorization, sandboxing, and audit logging as explicit gates, and prompt wording enforces none of them because the system prompt is readable and the model only follows text.
The diagram below marks each untrusted input and each enforcement point on the single request path we keep extending. Let’s pin down where text becomes authority and where we can refuse.
This diagram makes one rule visible. Every arrow that carries text is untrusted, and only the boxes in our service can refuse, cap, validate, authorize, and record what happened.
A secure API contract needs consistent refusal codes, because clients and on-call debugging both depend on predictable outcomes when a gate rejects a request.
We state the refusal map once and then reuse it, always tying each refusal to the boundary that enforces it rather than to the model.
401 Unauthorized rejects a missing, invalid, revoked, or compromised caller credential, and the service returns it before spending quota or calling the provider.403 Forbidden rejects an authenticated caller outside its allowed scope, including an approval denied or expired, and it also rejects a tool fetch to a blocked destination that is never contacted.422 Unprocessable Content (RFC 9110) rejects model output that fails schema validation, because unvalidated output is just text until the server proves it matches the tool contract.429 Too Many Requests with Retry-After rejects an exhausted rate limit or quota, and the counter must be shared so replicas do not silently multiply the limit.503 Service Unavailable rejects when the quota counter store is down or an open circuit breaker prevents enforcement, because the service cannot safely decide whether spend is allowed.504 Gateway Timeout rejects when the provider call exceeds the service timeout, because hanging calls are unbounded consumption.To keep the course concrete, we build one running artifact and harden it step by step. The artifact is a tenant-aware POST /v1/chat route that can call exactly one tool, and every later lesson adds one gate on that path.
In the opening failure, the caller can even be legitimate, which means authentication can succeed while the request still drives an unauthorized tool action.
If the service waits to validate the model output until after executing the tool call, schema validation can still reject a malformed payload, but schema validation cannot reverse a correct-looking action that should never have been allowed.
The decision that matters is which stage can refuse before an irreversible tool action executes. Which earliest stop contains the failure first, authentication, tool authorization, or output validation?