all insights

The Agentforce Agent API: putting a governed agent inside your own app

You built an agent in Studio, and it's trapped in the Salesforce chat widget. Your customers live in your mobile app; your ops runs in a queue. Headless 360 exposes Agentforce as an API — here's how the Agent API drives an agent from your own code, how it differs from MIAW, and the identity and cost boundaries that decide whether it's safe.

The Agentforce Agent API: putting a governed agent inside your own app — article illustration

You spent three weeks building a good Agentforce agent — scoped topics, governed actions, grounded on real data — and then it went live inside the Salesforce chat widget, where roughly none of your customers are. They’re in your iOS app, your React front-end, your call-center console, your nightly batch that triages a queue of inbound requests. The agent is excellent and it’s in the wrong room.

Salesforce’s answer, formalized under the Headless 360 banner announced at TDX 2026, is that the whole platform is an API — CRM, Data 360, Slack, and agents all callable without a browser. For agents specifically, the door is the Agent API: a REST interface that lets your own code start a conversation with an Agentforce agent, send messages, stream responses, and end the session, entirely server-side, with no logged-in human and no Salesforce-hosted UI. This is what turns an agent from a widget into a service you can call from anywhere. This post is how it works, when to use it instead of the messaging API it’s often confused with, and the two boundaries — identity and cost — that decide whether pointing it at production is a good idea or an incident.

Agent API vs. MIAW: two doors, different rooms

The confusion worth clearing first: Salesforce has two ways to talk to an agent programmatically, and they’re built for different jobs.

Messaging for In-App and Web (MIAW) is the human-in-the-loop channel. It’s built for a person typing into a chat surface — your website, a mobile app, Experience Cloud — with all the plumbing that implies: pre-chat forms, message delivery receipts, typing indicators, channel routing, escalation to a human agent. If you’re embedding a customer-facing chat experience, MIAW is the road, and it carries the weight to do that job.

The Agent API is the headless, developer-first door. It exposes just the operations you need to drive an agent from code — start a session, send a message, get a response, end the session — and nothing about rendering a chat UI. It’s for the cases where there is no human staring at a chat window: a backend workflow that asks an agent to triage a case, a custom app with its own front-end that wants agent reasoning behind it, an internal tool, an automated pipeline. Salesforce’s own framing is that Agent API is “just the right operations” for interacting with an agent, where MIAW was built for UI-central, human-facing conversations.

Pick MIAW when a person is typing and you’re rendering the chat. Pick the Agent API when your code is the one talking to the agent. Using MIAW for a headless workflow means fighting its UI assumptions; using the Agent API to build a full customer chat means rebuilding delivery and routing yourself.

The build-in-Studio work doesn’t change either way — the agent, its topics, and its actions are the same ones you’d stand up following our first service agent build. The Agent API only changes who drives it.

Setup: an external client app and the client-credentials flow

The Agent API authenticates with OAuth 2.0, and for a headless, no-human integration the flow is client credentials — the server proves its own identity, no user login step. The pieces:

  1. Create an External Client App (Setup → Apps → External Client Apps; you may need “Allow creation of connected apps” enabled). Enable OAuth, and enable the client credentials flow.
  2. Bind a run-as user. Client credentials runs as a specific Salesforce user — this is the identity every action the agent takes will be attributed to and permission-checked against. Use a dedicated, least-privilege integration user with API Enabled, not a human’s account and not a sysadmin.
  3. Connect the agent to the API. In Agent settings, enable the API connection and associate the app so the agent is reachable headlessly.

That run-as user is not a formality — it’s the entire security model, and we’ll come back to it.

The request flow, end to end

Authentication first. Exchange the app’s credentials for a token against your org’s token endpoint:

curl -X POST https://MyDomain.my.salesforce.com/services/oauth2/token \
  -d "grant_type=client_credentials" \
  -d "client_id=${CONSUMER_KEY}" \
  -d "client_secret=${CONSUMER_SECRET}"
# → { "access_token": "00D...", "instance_url": "https://MyDomain.my.salesforce.com", ... }

Start a session. The Agent API lives at https://api.salesforce.com/einstein/ai-agent/v1. You start a session against a specific agent id, handing it a client-generated key for idempotency and telling it which org instance to route to:

curl -X POST \
  https://api.salesforce.com/einstein/ai-agent/v1/agents/${AGENT_ID}/sessions \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "externalSessionKey": "b6b1f2e0-1c9a-4e6f-9b3a-9d2f1e0a7c44",
    "instanceConfig": { "endpoint": "https://MyDomain.my.salesforce.com" },
    "streamingCapabilities": { "chunkTypes": ["Text"] },
    "bypassUser": true
  }'
# → { "sessionId": "b1d...", "messages": [ ... greeting ... ] }

The externalSessionKey is a UUID you generate — reuse it and you get the same session, which is what makes retries safe. bypassUser tells the agent there’s no human on the other end to prompt for missing info, which is exactly right for a backend caller.

Send a message. Messages are ordered by a sequenceId you increment yourself. Send synchronously and you block until the agent finishes:

curl -X POST \
  https://api.salesforce.com/einstein/ai-agent/v1/sessions/${SESSION_ID}/messages \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "message": { "sequenceId": 1, "type": "Text", "text": "Why was order SO-40021 charged twice?" },
    "variables": []
  }'
# → { "messages": [ { "type": "Inform", "message": "That order shows a single settled charge and one authorization hold that will drop off in 3–5 days..." } ] }

That sequenceId is not decoration. It’s how the API orders turns; send two messages with the same or out-of-order sequence and you get undefined ordering. Treat it as a monotonically increasing counter per session and you’re fine.

Stream, when latency matters. For anything a person will read as it arrives, use the streaming endpoint, which returns server-sent events instead of one blocking response. You get a sequence of typed events: Progress Indicator events carry the “working on it” messages tied to the actions the agent is running, Text Chunk events carry fragments of the reply as it’s generated (when chunking is on), a single Inform event carries the complete response regardless, and an End of Turn event marks the turn done. Render the chunks for perceived speed; treat the Inform event as the source of truth.

End the session when you’re done, so you’re not holding server state:

curl -X DELETE \
  https://api.salesforce.com/einstein/ai-agent/v1/sessions/${SESSION_ID} \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "x-session-end-reason: UserRequest"

Passing context is the last piece: the variables array on a message lets you inject typed context — a record id, a locale, a channel — that the agent’s topics and actions can reference, the same way a UI session would carry it. Put identifiers there, never secrets: variables are conversation input, not a credential vault.

The run-as user is the whole security model

Here’s the sentence to tape to the wall: the agent can do exactly what its run-as user can do, no more and no less. Every action fires under that identity, every record it reads or writes is permission-checked against it, and every row in the audit trail names it. That’s a feature — it means an Agentforce agent isn’t a magic bypass around your sharing model — and it’s a trap, because the fastest way to ship is to bind the integration to a wide-open account and never look again.

Don’t. Scope the run-as user to precisely the objects and fields the agent’s actions need, and nothing else. A headless agent is a fleet member like any other, and everything in our agent-fleet governance piece applies with extra force here, because there’s no human in the loop to notice when it does something odd. The Einstein Trust Layer still sits in the path — masking, logging, zero-retention against the model — but the Trust Layer governs what reaches the LLM; it does not scope what the agent’s actions can touch. That’s the run-as user’s job, and it’s yours to set.

The prompt-injection surface is also live and arguably worse headless. If your backend feeds the agent text from an untrusted source — an inbound email, a form field, a scraped page — that text can carry instructions aimed at the agent. Least privilege is the defense that holds, exactly as in the prompt-injection guide: the agent physically cannot do what its user can’t, so an injected “now refund everything” fails at the permission layer whether or not any filter caught it.

Cost and operations: every turn is still a meter

The Agent API is a new front door onto the same billed engine. Each turn still consumes against your Agentforce entitlement — conversations or Flex Credits depending on your plan — and a headless caller is unnervingly easy to run in a loop. A batch job that fires the agent across 50,000 queued records is 50,000 turns, and if the design is chatty (three turns per record), it’s 150,000. Model that against your plan before you point a pipeline at it; the real cost math is in our Agentforce pricing breakdown, and it does not get cheaper because the caller is code instead of a person.

Two more operational notes. Sessions have lifecycles and time out — don’t assume a session id is valid forever; start fresh and handle expiry. And headless does not mean invisible: Agent API sessions still produce the same telemetry as any other, so Command Center and Agent Analytics show you what your backend-driven agent actually did. Wire that up before launch, because a quietly failing headless agent has no human on the channel to report it.

What to actually do

If your agent is good and stuck in the wrong surface, the Agent API is the way out — but treat it as production integration, not a demo. Use the Agent API for headless, code-driven conversations and MIAW when you’re rendering a human chat; don’t force one into the other’s job. Authenticate with client credentials through a dedicated External Client App, and bind it to a least-privilege integration user, because that user is your security boundary. Generate an externalSessionKey per logical conversation and drive sequenceId as a strict counter so retries and ordering are safe. Stream when a human reads the output, block when a workflow consumes it, and always end sessions. Put identifiers in variables, never secrets. Model the credit cost of any loop before you run it, and turn on observability so the headless agent isn’t operating in the dark.

Headless 360’s promise is that everything Salesforce can do, your code can call. The Agent API is the sharpest edge of that promise, because an autonomous agent driven by a backend with a real user’s permissions is powerful in both directions. Wire the identity and the cost boundaries first, and it becomes the thing that finally lets a well-built agent do its work wherever your customers and workflows actually are.

Understanding the basics

What is the Agentforce Agent API?

The Agent API is a REST interface that lets external code interact with an Agentforce agent headlessly — starting a session, sending messages, receiving responses (synchronously or streamed as server-sent events), and ending the session — with no Salesforce-hosted UI and no logged-in human. It’s part of the Headless 360 direction of exposing the whole platform as APIs, and it’s how you put a governed agent behind your own app, a backend workflow, or an automated pipeline instead of leaving it in the built-in chat widget.

How is the Agent API different from the MIAW API?

They target different jobs. The Messaging for In-App and Web (MIAW) API is built for human-in-the-loop chat experiences — a person typing into a UI, with routing, delivery, and escalation plumbing. The Agent API is headless and developer-first: it offers just the operations needed to drive an agent from code, with no assumptions about rendering a chat interface. Use MIAW when you’re building a customer-facing chat surface and the Agent API when your own code is the participant talking to the agent.

Is a headless Agentforce agent still governed and billed?

Yes to both. It runs as a specific integration user and can do only what that user’s permissions allow — that run-as identity is the security boundary, so scope it tightly. The Einstein Trust Layer still processes what reaches the model, and sessions still produce the same observability telemetry. On cost, every turn consumes against your Agentforce plan (conversations or Flex Credits) exactly as a UI conversation would, which makes headless loops over large record sets easy to under-budget — model them first.


Trying to put a governed agent behind your own product or a backend workflow without handing it more access than it should have? Talk to us — wiring Agentforce into real systems with the identity and cost boundaries right is exactly the work we do.

Keep reading

All insights