Agent deployment: one agent, every surface
How to deploy one AI agent to web chat, Slack, a REST API, SMS and MCP: what is per-channel, what is shared once, and what changes when it is multi-tenant.
AI agent deployment is the work of taking an agent definition (a model, a system prompt, a tool set and the loop that runs them) and making it reachable from the places people and systems will use it: a chat widget on a web page, a Slack workspace, a REST endpoint another service calls, a phone number, an MCP server an IDE connects to. The difficulty is that each surface has its own authentication, its own turn shape and its own way of collecting a human decision, while the agent's behaviour has to be identical across all of them. The working answer is one published definition behind many surfaces, with only the channel-specific parts kept per channel.
Why deploying an agent is hard
Most teams deploy twice. The first deployment is a demo behind a single chat box; the second happens when another channel is requested, and it is where the failure modes below appear. The gap between the two is the subject of taking an AI demo to production.
The Slack copy drifts from the web copy
A tempting first move when Slack is requested is to copy the prompt and tool list into a second service. Within a few weeks a prompt fix lands in the web copy and a tool's parameter schema changes in the Slack copy. The two agents now answer the same request differently, and there is no record of which one is correct. The structural fix is to store the definition once, version it once, and have each surface reference the published version, the approach in building a multi-channel AI agent.
Credentials leak across channel boundaries
Every surface authenticates differently, and the credentials are not interchangeable. A browser widget needs a public token restricted by origin, because anything in the page source can be read, while a server-to-server API needs a secret key that never appears in a browser. A Slack app needs a signing secret to verify inbound events, and a Twilio number needs the account's auth token to check webhook signatures. The mistake is mechanical: a developer ships the server API key inside the widget bundle because it was the credential at hand, and anyone who views source can now run the agent, with its tools, under your account.
An approval with nobody to answer it
An agent that can send an email or issue a refund normally has a human gate on that tool. On a surface with a live person the gate works: the agent pauses, the person decides, the agent resumes. On an API call made by a cron job, no one is present, so the run either hangs until the gate times out or an implementation quietly skips the gate because the channel cannot host it. The correct behaviour is to refuse the run before the agent starts and name the policy that blocked it, so the fix is visible.
Turn shape does not match the transport
Web chat streams tokens over a held connection. Slack and SMS deliver a webhook that expects a quick acknowledgement, with the answer posted afterwards, and an API caller may want a finished answer in the response or a handle to poll. A tool-using turn can run for minutes and does not fit inside a webhook provider's response window. A deployment that treats every surface as a synchronous request times out on the ones that are not; the symptom is a Slack message with no reply while the agent keeps running somewhere.
Editing production in place
An agent whose system prompt is edited live has no draft state and no rollback. Every edit is a deployment, and a regression found on Thursday cannot be reverted to Tuesday's wording because Tuesday's wording is gone. Pilots stall on this more often than on model quality; see why AI pilots stall.
How it works
The unit of deployment is the definition, and surfaces are attachments to it. The definition holds the behaviour; a surface holds the channel: how a request is authenticated, how the turn is carried, how a decision is collected and how output is formatted. Deciding which side of that line each setting sits on is most of the design work.
| Belongs to the one definition | Belongs to each surface |
|---|---|
| Model and fallback model | Credential type (public token, secret key, signing key) |
| System prompt and instruction bundles | Transport: streaming, webhook plus reply, poll handle |
| Tool set and each tool's schema | Message formatting limits (blocks, plain text, SMS) |
| Loop limits (max turns, max tool calls) | How an approval decision is collected |
| Approval policy (which tools are gated) | Which identity fields the channel can supply |
| Memory scope and retention | Rate limits on the entry point |
| Eval suite and regression cases | Draft, active or paused status |
In outline, a definition that can be deployed to many surfaces contains the fields on the left and nothing about the channel:
agent:
model: claude-sonnet-5
systemPrompt: |
You are the support assistant for Northwind Freight.
Look up orders before answering questions about them.
tools:
toolIds: [lookup_order, issue_refund]
maxToolCalls: 10
approval:
require: [issue_refund]
timeout: 300
loopConfig:
maxTurns: 6
The surfaces then reference the published version of that agent. What differs is summarised below, with the last column showing how each channel collects a decision on issue_refund.
| Surface | Authenticates with | Turn shape | Approval decision |
|---|---|---|---|
| Web chat | Public client token, restricted to listed origins | Streamed over the open connection | None; a gated call returns an error |
| Slack | App signing secret; bot token held server-side | Webhook, reply posted to the thread | Approve and deny controls in the message |
| REST API | Secret API key from a server | Synchronous answer, or a run handle to poll | Refused before the run starts |
| SMS | Twilio auth token verifies the webhook signature | Webhook, reply sent as a text | Read from the reply text (YES or NO) |
| iMessage | Sendblue signing secret | Webhook, reply sent as a message | Read from the reply text |
| MCP server | OAuth, API key or public | Tool call from the client, synchronous | None; fails closed with an error, never a hang |
| A2A | API key; discovery through an agent card | JSON-RPC request from the calling agent | Refused before the run starts |
Email is a surface type too, described on the surfaces overview without its own setup guide, and it collects no approvals: an approval-gated agent reached by email fails closed.
Two details in the tables deserve a decision up front. Reply-based approval on SMS means the agent must resume after a later webhook, which requires a multi-turn loop; a single-pass agent cannot continue and fails closed. And on Twilio, STOP and CANCEL are carrier opt-out keywords, so a denial should be a plain NO, or the person unsubscribes from the number instead of denying the refund.
Never duplicate per channel: the system prompt, the tool schemas, the loop limits, the approval policy and the eval suite. If a channel needs different behaviour (a shorter answer style for SMS, say), express it as a parameter the one definition reads, so the difference lives in one place and is covered by the same tests. Exposing the definition as an endpoint is covered in exposing an AI agent as an API, and the Slack wiring in running an AI agent in Slack.
What changes when the agent is customer-facing and multi-tenant
Everything above holds for an internal agent your own team uses. An agent your customers use, across many tenants, adds constraints, and each shows up at the surface boundary because that is where identity enters.
Identity arrives in a different form on each surface. The widget knows the request origin and whatever your web app asserts about the logged-in user; Slack knows a workspace and a user id; an API call knows the key, and the key belongs to a customer; SMS knows a phone number. A multi-tenant deployment needs one tenancy contract that each surface maps into, with a stated level of assurance (asserted by a client, or verified), and it must reject a request whose identity is insufficient before the agent runs. The wider treatment is at multi-tenant AI agents.
Per-customer configuration is the second constraint. Customer A wants the refund tool, customer B has no refunds, customer C wants a stricter model. One definition with per-tenant parameters (tool subset, model, a prompt fragment, spend limit) resolved at request time from the identified tenant beats three forked definitions.
Cost and isolation follow from identity. If the tenant is recorded at execution time, cost per conversation can be attributed to a customer and capped there; if it is a metadata field one surface forgot to set, that customer's usage is invisible. Memory, when the agent has any, must be keyed by tenant and by end user.
The last constraint is where the runtime lives. Some customers will not let conversation data or tool results leave their cloud account or region, and the property to insist on is that the same definition, surfaces and eval suite run in the customer's own infrastructure without a rewrite; the trade-offs are in self-hosted AI deployment. If the agent is going into a product that already has users and a login, the sequencing of identity, surface and rollout is covered in adding an AI assistant to an existing product.
Where Runtype fits
Runtype is an AI agent platform built around the model described above: one published definition, attached to as many surfaces as you need. An agent you already run reaches those surfaces in four ways that stack.
- Register the agent. An
externalagent whose endpoint speaks Runtype's unified stream or A2A is called by Runtype, so it can be embedded in the open-source Persona widget, added to a product as a capability, reached through every surface in the table above and scheduled. Each surface has a draft, active or paused status, tool calls and cost are recorded per run, and the agent code is unchanged. - Rebuild when it earns it. Port a capability to a hosted agent or flow once an eval suite harvested from real runs can prove parity. It has draft and published versions, so a Thursday regression rolls back to Tuesday's, and
runtype eval runreturns a non-zero exit code in CI on a regression. - Send traces. An OpenTelemetry-instrumented loop exports OTLP over HTTP to
https://api.runtype.com/v1/otelfor the Runs view, token usage and a display-only cost estimate. Point exactly one instrumentation at it, because two doubles tokens and cost. - Serve it tools. An MCP surface publishes a product's flows, agents and tools as MCP tools, for a loop that should stay where it is.
Per channel, a chat surface authenticates with a public client token scoped to specific capabilities and restricted to its allowedOrigins list; an API surface takes a secret key as a Bearer token or an X-API-Key header and publishes an OpenAPI specification. A tool references {{secret:NAME}}, resolved server-side, so the model never sees the value.
Approval gates are set per tool or for all tools with tools.approval.require, with a five-minute default timeout. Slack collects the decision with native controls, SMS and iMessage read it from the reply, and an unattended surface refuses the run and names the policy, as described in approvals on messaging surfaces. The approver sees the agent's own stated reason, which is never used as a control signal.
The turn-shape mismatch is handled by durable turns: an execution has a run handle, can be polled, and has a thirty-minute wall-clock budget, so a Slack webhook is acknowledged while the agent keeps working. These layers are built for a software company's customers, so each resource declares a tenancy strategy (internal, tenant-isolated or end-user-isolated) with an assurance floor of asserted or verified, evaluated before execution. Cost, traces and memory are filed under the tenant and end user the request ran for.
Hosting is the managed cloud or your own infrastructure, with the same definition in both (self-hosting). Where one chat box is all you will ever need, a framework plus a single deployment is simpler, and a platform adds nothing.
Frequently asked questions
- What does it mean to deploy an AI agent?
- Deploying an agent means taking its definition (model, system prompt, tool set, loop limits and approval policy) and making it reachable from a surface people or systems use: a chat widget, Slack, a REST endpoint, a phone number or an MCP server. The definition is stored and versioned once, and each surface carries only what is specific to its channel, such as credentials and how it collects a human decision.
- Should each channel get its own copy of the agent?
- No. A per-channel copy drifts within weeks: a prompt fix lands in one copy and a tool schema change in another, and the two agents start disagreeing about the same request. Keep one published definition and reference it from every surface. Only authentication, formatting limits, transport and the way an approval is collected belong per channel.
- How do you expose an AI agent as an API?
- Put the agent behind an endpoint that authenticates with a secret key sent from a server, never from a browser, and decide up front whether a call returns the finished answer or a run handle the caller polls. Long tool-using turns need the handle. Publish a machine-readable spec for the endpoint, and refuse calls that would reach a human-approval gate, since nobody is there to answer it.
- How does human approval work on Slack or SMS?
- The agent pauses before a gated tool runs and the surface asks the person. Slack can show approve and deny controls; SMS and iMessage read the decision from the reply text, so the agent has to resume after the reply arrives, which requires a multi-turn loop. Unattended surfaces such as scheduled jobs or API calls have nobody to ask, and the right behaviour is to refuse the run and name the policy that blocked it.