TL;DR — An agentic API is a single endpoint that exposes an LLM-driven agent instead of a fixed business operation. The agent plans, calls tools, retries on failure, and returns a synthesized answer. The trade-off is predictability, so success depends on tight tool contracts, durable observability, hard cost ceilings, and a deterministic escape hatch when the loop goes wrong.

Why “Agentic” Changes the API Contract

A traditional REST endpoint is a contract: given this input, return this output, in roughly this time. An agentic API trades that contract for something closer to a colleague’s inbox. You send a goal — “reconcile the last 24 hours of Stripe payouts against our ledger and flag anything over $500” — and the endpoint returns a result, an explanation, and possibly a partial failure that the agent has already mitigated.

The shift matters because most production LLM apps today are still synchronous completion wrappers: one prompt in, one completion out. Anthropic’s own agent guidance and the OpenAI function calling docs both emphasize that “agentic” specifically means the model is in a loop, selecting tools, observing results, and deciding what to do next. That’s the capability you expose.

The hard parts aren’t model quality. They’re the same things that have always mattered in backend engineering, just relocated to the endpoint boundary:

  • Bounded latency when the agent wants to call five tools in series.
  • Bounded cost when a reasoning model with tool use can rack up real dollars.
  • Bounded blast radius when the agent has database credentials.
  • Debuggability when the “code path” is a sequence of model decisions stored in traces.

This post walks through the architecture, patterns, and failure modes I’ve seen work in production across customer-support, data-reconciliation, and developer-platform deployments.

Anatomy of an Agentic API

The cleanest mental model is a state machine with the LLM in the loop:

[Client] → POST /agents/{name}/invoke { goal, context, budget }
[Policy Gate] — authn, authz, rate limit, PII redaction
[Agent Loop] ───────────────────────┐
        ↓                            │
[Plan] → [Tool Call] → [Observe]     │
        ↑                            │
        └─────────── [Reflect] ←──────┘
[Finalizer] — structured output, citations, audit trail
[Client] ← { answer, trace, tokens, cost }

The interesting differences from a normal API sit in three places: the loop, the tool surface, and the finalizer.

The Loop

Most production agents use a variant of the ReAct (reason + act) pattern, often wrapped in a framework like LangGraph or the OpenAI Agents SDK. What separates a serious implementation from a demo:

  1. Max iterations as a request parameter. Clients pass max_steps=8; you never let the loop run forever.
  2. Step budget by cost, not just count. A single tool call into a vector DB is cheap; one into a paid search API is not. Track dollars, not just tokens.
  3. A loop detector. If the agent calls the same tool with the same arguments three times, terminate with an explanatory error — you’ve almost certainly hit a stuck reasoning loop.
  4. Persistent checkpointing. Every (plan, tool call, observation) tuple is appended to a trace id so you can replay, score, or hand off to a human.

The Tool Surface

Tools are where the rubber meets the road. Each tool is a typed function — name, JSON schema for inputs, JSON schema for outputs, timeout, cost ceiling. Two patterns have worked well:

Pattern A: in-process typed tools. Tools live in the same repo as the agent, share types, and are called directly. Best for read-heavy operations against your own DB.

@tool
def get_recent_payouts(since: str, min_amount: int = 0) -> list[Payout]:
    """Return Stripe payouts since the given ISO timestamp."""
    return stripe_client.payouts.list(
        created={"gte": parse(since)},
        limit=100,
    )

Pattern B: outbound HTTP tools. Tools are themselves HTTP services, often other internal APIs. Best when the agent needs to reach systems owned by other teams, or when you want versioning and auth to match those teams’ standards. The trade-off is latency — a tool that takes 800 ms over the network will eat your step budget fast.

Both patterns benefit from a small, opinionated registry. Anthropic’s guidance is consistent with what I’ve seen: aim for 5–15 well-scoped tools, not 50 loose ones. The model gets worse at selection as the surface grows.

The Finalizer

The finalizer’s job is to convert “the loop ended” into “here’s the answer.” That means:

  • Structured output via JSON schema (the OpenAI structured outputs endpoint or Anthropic’s tool-use-for-output pattern). Don’t parse free-form prose.
  • Citations back to tool calls so a reviewer can verify any claim.
  • A confidence or partial-completion flag when the agent ran out of budget mid-task.
  • A trace id the client can pass to support.

Patterns in Production

Across the agentic systems I’ve watched ship — including customer-support triage at a fintech, code-migration tooling at a SaaS company, and reconciliation agents at a logistics firm — three patterns keep appearing.

Pattern 1: Synchronous With a Long Ceiling

For low-latency, human-in-the-loop flows (developer copilots, support drafting), you expose a synchronous endpoint with a 30–60s ceiling. Internally you stream step updates back over Server-Sent Events or WebSockets so the UI can show the agent working.

POST /agents/copilot/invoke
  → 202 Accepted
  → SSE stream of {step: 1, type: "plan", content: "..."}
  → {step: 2, type: "tool_call", name: "search_docs", args: {...}}
  → {step: 3, type: "observation", content: "..."}
  → {step: 4, type: "final", answer: {...}, trace_id: "..."}

This is the path most teams start with. The downside: you’re now holding a connection open and a container warm for the entire agent run, which limits concurrency per node.

Pattern 2: Async With a Polling/Webhook

For longer flows (multi-hour reconciliations, batch code migrations, deep research), the endpoint kicks off the agent as a durable job and returns immediately. Status is fetched via a separate endpoint or pushed via webhook when the agent hits a terminal state.

This is where Temporal, Inngest, or even a managed queue like Amazon SQS shine: they make a multi-step agent run a first-class durable workflow instead of a fragile in-memory loop. If the container dies after step 6 of 10, the workflow resumes from step 6, not from scratch.

Pattern 3: Streaming for Tool-Heavy Agents

For agents that emit intermediate text the user wants to see — code generation, document drafting — stream the model’s tokens directly to the client, and attach tool calls as out-of-band metadata. OpenAI’s streaming tool use and Anthropic’s streaming make this tractable; the work is in the client SDK that has to interleave the two streams cleanly.

Security: The Part That Bites Later

A traditional API has a small, audited attack surface. An agentic API does not — its attacker is potentially the model itself, through prompt injection, indirect prompt injection, or just confused reasoning. The defenses that have actually mattered:

Tool Allowlists Per Request

Every request carries a list of tool names the caller is authorized to invoke. The agent loop refuses to call anything else, even if the model asks. This is the single biggest mitigation against tool-exfiltration attacks.

Indirect Prompt Injection in Tool Outputs

If your agent reads external content — web pages, emails, PDFs, fetched documents — that content can contain instructions. A 2024 paper, Not What You’ve Signed Up For, demonstrated this concretely against several commercial agents. Defenses:

  • Strip and re-render external content into a clearly delimited block before it enters the model’s context.
  • Run a second, cheaper model as an “instruction detector” over tool outputs.
  • Treat every instruction found inside a tool result as untrusted data, not as a directive.

Egress Controls

A tool that’s supposed to read from your internal wiki should not be able to make arbitrary outbound HTTP requests to evil.example.com. Tools should be wrapped in a proxy that enforces a domain allowlist and a request schema. I’ve seen teams use eBPF-based egress filtering on agent pods for exactly this.

Per-Tool Spend Caps

A model can decide to call an expensive tool in a tight loop. Cap each tool’s per-request spend at the SDK level — not just per-minute global rate limits. If a single agent run can blow your monthly budget on a paid search API in two minutes, you have a reliability problem, not just a cost problem.

Observability for Non-Deterministic Endpoints

You can’t log “the request was a GET” and call it a day. The unit of observability for an agentic API is the trace, not the request. Every trace contains:

  • The full prompt and every model completion.
  • Every tool call: name, arguments, latency, result summary, and error.
  • The step counter, the cost accumulator, and the reason for termination.
  • A span tree compatible with OpenTelemetry, so traces plug into the same backend as your other services.

The teams that do it well treat traces as a first-class data product:

  • Every trace lands in a columnar store (BigQuery, ClickHouse, Snowflake).
  • A weekly job samples 1% of traces for human scoring.
  • A eval suite runs nightly against a fixed set of test goals and asserts that pass rate didn’t regress.
  • Latency and cost dashboards are per-agent, per-tool, and per-caller.

This last point matters more than it sounds: without per-caller dashboards, you can’t see that one customer’s integration is calling your agentic endpoint in a way that’s 10x more expensive than everyone else.

When Not to Build an Agentic API

Not everything should be an agent. A useful decision rule: if the task has a single correct algorithm, expose a normal API and use the LLM only at the edges (parsing user input, formatting output). Agents earn their complexity when:

  • The decision tree is too big to hand-code but small enough that a few hundred examples teach the model the right shape.
  • You need to compose multiple existing APIs and the composition logic changes often.
  • Failure modes are graceful — if the agent gets 80% of cases right and surfaces a clear “I need help” signal for the rest, the system is useful.

If a wrong answer has severe consequences (legal filings, medical decisions, financial trades), don’t ship it as an autonomous agent. Ship it as a draft a human reviews.

Architecture: A Reference Layout

Putting it all together, here’s a layout that’s worked across a few production deployments:

┌─────────────────────────────────────────────────────────────┐
│  Edge: API Gateway + WAF                                     │
│  - TLS termination                                           │
│  - Per-tenant rate limits                                    │
│  - PII scrubbing on incoming goals                           │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Control Plane (stateless)                                   │
│  - Authn/authz (JWT + per-tenant tool allowlists)            │
│  - Budget check: cost ceiling for this tenant/run            │
│  - Dispatch to agent runtime                                 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Agent Runtime (stateful, durably checkpointed)              │
│  - Loop: plan → tool → observe → reflect                    │
│  - Tools: in-process + outbound via tool proxy               │
│  - Streaming output → SSE/WebSocket                          │
│  - Every step → OpenTelemetry trace + event log              │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Tool Proxy                                                  │
│  - Domain allowlist                                          │
│  - Per-tool timeout + spend cap                              │
│  - Schema validation                                         │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Durable Storage                                             │
│  - Postgres: agent definitions, audit log                    │
│  - Object store: full trace blobs                            │
│  - Columnar store: trace analytics + eval sampling            │
└─────────────────────────────────────────────────────────────┘

Three details worth highlighting:

  1. The budget check happens before dispatch, not inside the loop. A customer who has blown their monthly quota should never start a run, even if the loop would have terminated it at step 2.
  2. The tool proxy is a separate service, not a library inside the agent runtime. When a CVE hits your outbound HTTP library, you patch one place.
  3. The agent runtime is the only stateful tier, and it uses a durable workflow engine so a crashed container doesn’t restart a 9-step run from scratch.

Key Takeaways

  • An agentic API exposes a planner-in-the-loop endpoint, not a fixed business operation — the contract is “goal in, answer + trace out.”
  • The agent loop must have hard ceilings: max iterations, max cost, max latency, and a loop detector that terminates stuck reasoning.
  • Tools are the real attack surface. Per-request tool allowlists, indirect-prompt-injection defenses, and egress controls matter more than prompt-level guardrails.
  • Observability is per-trace, not per-request. Land every trace in a columnar store, sample for human eval, and run nightly regression suites.
  • A durable workflow engine (Temporal, Inngest, or a managed equivalent) is the difference between an agent that survives a container restart and one that loses 90% of its step budget on retry.
  • Don’t build an agentic API where a deterministic one will do. Agents earn their complexity on tasks that are too varied to hand-code but tolerant of partial success.

Further Reading