Guides

Portable Agents

Author a versioned agent definition once, then run it on Claude Code, Cursor, or your own runtime, governed by airlock wherever it runs.

A portable agent is an authored, versioned worker definition that lives in airlock and runs wherever you choose: Claude Code, Cursor, an SDK you are writing, a container in your own cloud. The definition is the system prompt and the model preference. Nothing else: not the tools it may call, and not the skills it may load, both of which are decided by the identity it runs as.

Agents are no longer in alpha. What that means precisely, because it is worth being exact rather than reassuring: the registry, versioning, access, and the governance of tool calls made through airlock are built and supported. Running the agent is yours to do, and always was. airlock can also push an agent into a platform you already run, so that platform runs it for you. ElevenLabs and Anthropic Managed Agents are available now; the other platforms are still coming, and each is marked on its own tile on the agent's Deploy tab.

Read the next section before anything else. It is the boundary the whole page depends on, and it does not move now that the alpha label has gone.

airlock does not run your agent

This is the first thing to get straight, because everything else follows from it.

your host  ->  describe_agent  ->  airlock         (fetch the definition)
your host  ->  execute_tool    ->  airlock  ->  your tools   (governed, per call)

airlock is the registry and the governance plane. It stores the definition, versions it, decides who may read it, and governs the tool calls your agent makes through it. It does not host the loop and it does not call the model.

That boundary is why the definition carries no permissions and no caps. What an agent can actually reach is decided per call, against the identity it connects as. Two people running the same agent with different credentials get different reach, and that is the intended behaviour, not a gap.

What a definition contains

A definition is one document, the envelope. It has two halves that behave differently.

Registry metadata (agent)

Mutable. Edits apply in place and do not create a version.

FieldRequiredWhat it is
nameYesThe agent's registry name, and the name a host resolves it by. The registry only requires it to be non-blank, but keep it slug-safe: it is rendered into generated instructions, pasted shell commands and the describe_agent arguments, and a name that cannot be rendered safely is refused
descriptionYesWhat it does, when to use it, and its trigger phrases. Hosts that pick an agent by description, Claude Code among them, route on this text, so write it for a reader deciding whether to call this agent
tagsNoStrings, for filtering the library
licenseNoFree text

The spec (spec)

Immutable. Any change here writes a new numbered version.

FieldRequiredWhat it is
schemaVersionYesThe literal string "1.0"
systemPromptYesThe agent's instructions, as markdown. Must not be blank
modelPreferenceYesAn ordered list of { family, model } entries, at least one. See Models
vendorHintsNoA map of host name to an object of host-specific knobs. Opaque to airlock and always advisory

What a definition deliberately does not contain

No tool allowlist, no token cap, no call cap, no approval mode. airlock could store fields like those, but it is not on the inference path and could not enforce them, and a field that looks like a permission while granting nothing tells you that you have scoped an agent when you have not. So they are not offered.

Scope an agent by scoping the identity it runs as. That is a real boundary, evaluated on every call. See Access Control and Service Accounts.

Authoring: form or YAML

Open Agents in the sidebar and click Create agent. The editor has two interchangeable modes, Form and YAML, over the same draft: a field you change in the form re-serializes into the YAML, and YAML you edit re-derives the form. A YAML syntax error parks the form at its last valid state and tells you so, rather than silently discarding your edit.

The editor validates the envelope's shape continuously, against the same schema the API uses, so structural mistakes surface inline before you submit.

One check it cannot run in the browser: whether a { family, model } pair is one your organization may actually declare. That is answered against the live model catalog when you save, so a well-formed spec naming a model that does not exist is accepted by the editor and refused by the API with a 400 telling you which models the family offers.

The YAML mode is the canonical shape. It carries no organization-specific IDs at all, so there is nothing to fix up when you reuse one: what an agent can reach is resolved from the identity running it, not from anything written in the definition.

agent:
  name: triage
  description: >-
    Triage incoming GitHub issues. Routes to the right team, applies labels,
    drafts a first-response comment for human review.
    Trigger phrases: "triage this issue", "look at issue #N".
  tags:
    - support
spec:
  schemaVersion: '1.0'
  systemPrompt: |
    You triage incoming issues. Read the issue, decide which team owns it,
    apply the right labels, and draft a first response for a human to send.
    Never post a comment without approval.
  modelPreference:
    - family: claude
      model: sonnet-4-6
    - family: gpt
      model: gpt-5.4

Versioning

The split between the two halves of the envelope is the whole versioning model:

  • Registry edits apply in place. Renaming an agent, rewriting its description, adding a tag: no new version.
  • Any spec change creates a new version. Versions are numbered from 1 and are immutable. The newest is the current one.

The Versions tab lists the history and expands any entry into a line-by-line diff against the version before it.

Rolling back is a Restore button beside any version that is not current, shown to the agent's author and to admins. The same operation is reachable from a script as POST .../agents/{name}/versions over REST, and from an agent as the create_agent_version tool. Every route is append-only, copying the old spec into a new version that becomes current, so nothing is overwritten and the restore can itself be undone. Registry metadata is not versioned and a rollback leaves it alone.

One rollback can be refused: if the old version names a model that has since been retired, and the current version does not name it too, the restore is rejected rather than resurrecting a model the catalog has withdrawn. Roll back to a version whose models are still current, or edit the model preference as part of the restore.

Version numbers matter downstream. The fetch-at-runtime call takes an optional version, and pinning one means edits you make in airlock afterwards do not reach that caller until it drops the pin. Resolving an agent by name alone never pins: that call gets the current definition.

Names

An agent's name is unique within your organization. It is how a host resolves an agent (describe_agent, export_agent, every REST route), so exactly one agent answers to it, and renaming onto a taken name is refused.

Who can see and run an agent

A new agent is visible to you and nobody else. Nothing about that is permanent: it is opened up on the agent's Access tab, in one of three ways.

ChoiceWhat it meansIn the data
Only meNobody else in your organization sees it. Your admins still can, because they can see everything.No grants at all
Chosen people and teamsExactly the people, teams and service accounts pickedOne grant each
Everyone in your organizationEvery memberOne organization-wide grant

An agent nobody has been granted answers not-found rather than forbidden, so nobody can probe for the existence of agents they cannot see. That applies in the library, in list_agents and in describe_agent alike.

Sharing is an administrator's decision. Any member can write an agent for themselves and keep it, edit it and delete it; opening it to anybody else is a change an admin makes. Everyone who can open the page sees the current answer, so you can always tell who reaches your agent. Only the controls are an admin's. If you want yours shared, ask one.

Two rules that follow:

  • A grant lets you use an agent, and nothing more. It never lets you rewrite it, re-share it or delete it for everybody else who holds one. Editing and deleting are the author's, plus any admin.
  • Authorship is not revocable. The person who wrote an agent always reaches it, with no grant row involved, so "Only me" really does mean only them. An admin who wants it gone deletes the agent.

Two things that catch people out:

  • Granting the agent grants nothing else. A definition carries no permissions. Whoever runs the agent reaches exactly what their own identity is granted, and no more: the integrations, and the skills, each granted in the Access section of that resource's own Settings tab.
  • A service account needs the agent granted explicitly. Your own user token sees whatever the Control Room shows you, but a service account starts with access to nothing. If you wire a service-account token into a host and the agent's bootstrap answers not-found, this is almost always why. The exception is an agent that service account created itself: an author reaches their own work.

Running an agent

There are three ways to consume a definition, and the first is the one you want by default.

1. Already connected? Just run it

Any host already connected to your organization's MCP endpoint can run an agent with no install step at all:

describe_agent({"agent": "triage", "id": "a7e1c9d4-5b32-4f8e-9c06-3b1d8f27ac54"})

The result is the full envelope: the system prompt and the model preference, usually with a concrete route already resolved against your organization's catalog. Follow it. Nothing is written anywhere, and an edit you make in airlock is live on the next run. This is how a chat client should consume an agent, and how an agent should consume a sub-agent.

Build for the resolution being absent. An entry that names a model your catalog carries no route for comes back undecorated, and where several routes exist the host picks between them on the credentials it actually holds. Treat a resolved route as a good default, not a guarantee, and fall through the preference list when you cannot serve one.

Address the agent by agent (its name), by id (its immutable registry id), or both. Prefer the id. Names are mutable and reusable: a name-keyed reference is orphaned when the agent is renamed, and worse, silently re-bound if a new agent later takes the freed name. When you pass both, the id resolves and a stale name comes back as a warning on the result, which is how you find out a rename happened.

Add version to read a historical version. Omit it for the current one.

2. Building a runtime? Fetch per run

If you are writing the loop yourself, call describe_agent at the start of every run rather than baking the definition into your deployment. Then changing the agent in airlock never needs a redeploy.

The raw HTTP form, which any MCP client library will do with less ceremony:

curl -s https://mcp.air-lock.ai/org/acme \
  -H "Authorization: Bearer $AIRLOCK_TOKEN" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"describe_agent","arguments":{"agent":"triage","id":"a7e1c9d4-5b32-4f8e-9c06-3b1d8f27ac54"}}}'

3. Editors: name it, then delegate

Claude Code and Cursor install nothing. Both reach an agent the way every other host does, over MCP, with the definition fetched per run. Connect the endpoint once and the agent is reachable from then on:

  • Claude Code: add the endpoint with claude mcp add, or under mcpServers in your project .mcp.json, then restart.
  • Cursor: add it under mcpServers in .cursor/mcp.json, as a new key alongside anything already there so two airlock organizations never collide, then reload. Use ~/.cursor/ for a single-user install.

You usually do not configure a token at all. The endpoint answers an unauthenticated call with an OAuth challenge, so an interactive editor starts the flow itself: you authenticate in the browser once, and the client holds and refreshes the tokens from then on. See Authentication.

Set a token yourself only where that flow cannot run, which in practice means an unattended process rather than an editor. Then the connection carries it as a header:

{
  "mcpServers": {
    "airlock-acme": {
      "type": "http",
      "url": "https://mcp.air-lock.ai/org/acme",
      "headers": { "Authorization": "Bearer ${AIRLOCK_TOKEN}" }
    }
  }
}

Which token to use is the security decision, and it is covered below.

Then name the agent in your prompt, so the loop calls describe_agent and reads the spec.

Naming it is the reliable path, and in Cursor it is the only one. Both editors used to pick an agent up from a file, a Claude Code subagent or a Cursor always-on rule, and that file is what let the host volunteer it by description. Neither has one now, and the two clients are left in different places:

  • Claude Code still gets a softer version of it. airlock sends discovery instructions when the connection opens, asking the client to call list_agents and check the catalog against the request before hand-rolling an approach, then to fetch and spawn whatever matches. Claude Desktop and claude.ai honour the same instructions. It is an instruction the loop may miss, not registration the host enforces, so do not rely on it when it matters which agent runs.
  • Cursor drops those instructions entirely, so nothing points it at the catalog on your behalf. list_agents is still on its tool list, so asking for the catalog outright works; what will not happen is Cursor checking it against a request you did not frame that way.

In Claude Code, delegate rather than following the prompt yourself. Once the loop has the spec, have it spawn a general-purpose subagent whose instructions are spec.systemPrompt, with its model set from spec.modelPreference, and append the request and any context the agent needs. A subagent starts with none of the parent conversation, so a prompt carrying only the system prompt launches a worker with a role and no job.

That step is what makes it an agent rather than a suggestion: the agent gets its own context window, and it is the only way the model preference applies at all. A loop already running cannot switch its own model, so a prompt it follows itself runs on whatever model you were already in.

Two limits worth knowing. The definition arrives as the subagent's instructions rather than a host-level system prompt, so it shares that window with the task you hand it. And the subagent inherits the host toolbox it already has, rather than a narrowed one.

Cursor chooses its own model and has no field to pin one, so set the agent's preferred model yourself, or the closest Cursor offers.

The five SDK and runtime targets are code you are writing, so they fetch per run exactly as in step 2 above. For per-target wiring, ask for it: call export_agent with the matching adapter, or GET /v1/orgs/{orgSlug}/agents/{name}/export?adapter=.... What comes back names which SDK object to build, which field of the envelope goes where, and how to attach the airlock endpoint so the loop reaches the tools. For Bedrock and Gemini, which do not speak MCP, it also describes the bridge you write from their native tool calls to execute_tool. That bridge is what keeps policy, approvals and audit in force, so it is the part to get right.

The Control Room's Deploy tab is not that surface: it shows how to invoke a connected client and how to run the agent as an unattended process, the ElevenLabs and Anthropic Managed Agents pushes described below, and the platforms airlock will push to later. The per-target wiring above comes from the export call.

Nothing is installed, so nothing goes stale

This is the design decision most worth understanding, because it removes a whole class of maintenance.

No host is handed a copy of the definition, and no host is handed a file either, so there is nothing on disk to reconcile. Every host reads the same live envelope from describe_agent: the system prompt, the model preference, and the route airlock resolved. A copied definition is a fork the moment it lands on disk, an edit in airlock never reaches it, and nothing reconciles the two. Fetching per run has neither problem.

What follows from that:

  • Prompt edits are live on the next run. There is no re-export step, because there is no export to redo.
  • Model edits are live only where the host can act on them. A runtime that reads modelPreference per run picks the new model immediately, and so does a Claude Code loop that sets its subagent's model from it. Cursor has no model field at all, so its runs stay on whatever model the user selected until the user changes it.
  • Renaming an agent breaks nothing that fetches by id. Prefer the id in every stored reference, as above. It is a stored name that a rename orphans.
  • A version pin is narrower than it looks. It freezes the agent's own spec: the prompt and the model preference. It does not freeze what the agent can reach, which resolves from the running identity's grants on every call, nor the content of any skill it activates. Pin for a stable agent definition, not for a reproducible run.
  • Naming the agent does not pin a version. A host that resolves an agent by name calls describe_agent without one and gets the current definition. To run a specific version, issue the versioned call yourself rather than relying on the name.

Credentials and the correlation id

Two values connect an agent to airlock, and they look alike without being the same kind of thing.

The AIRLOCK_TOKEN variable is the identity the agent runs as. Write it the way the file you are editing expects: $AIRLOCK_TOKEN in a shell, ${AIRLOCK_TOKEN} in a JSON config like the one above. Nothing airlock renders ever contains a real token, so a config you commit carries no secret. Substitute it from your environment.

Which token you use is the security decision on this page, because what the agent can reach is decided against that identity on every call. For an unattended agent, mint a service-account token, grant that account the integrations, skills and the agent itself, and it is that account the audit trail will name. Granting the agent is the step people miss: a service account holds only what it is explicitly granted, so without it the very first describe_agent answers not-found. The one exception is an agent that service account authored itself over MCP, which it reaches as its own work.

X-Airlock-Agent-Invocation-Id is not a second secret. Your runtime supplies it: a fresh unique value, a UUID is ideal, at the start of every agent run. airlock takes whatever arrives on the header and uses it to group that run's tool calls in the audit trail.

  • Never send one fixed value. A constant quietly merges every run of every agent into one indistinguishable trail, and looks like nothing at all is wrong.
  • An editor cannot supply it. Claude Code and Cursor both resolve MCP headers once, when the connection opens, so a value set in .mcp.json or .cursor/mcp.json is reused by every run. Leave it unset there rather than setting a constant.
  • Leaving it unset breaks nothing. The calls are simply not correlated with each other. Every other guarantee is unaffected.

What airlock governs on your host

airlock governs the calls your agent makes to your connected integrations. Those go through the execute_tool meta-tool, and that is where policy, approvals and audit apply, per call, against the identity the agent connects as.

Be precise about the edge of that: the meta-tools themselves are not policy-evaluated. Discovery (list_services, search_tools, describe_tools), the agent tools (list_agents, describe_agent, export_agent) and activate_skill are answered directly by the endpoint. They are gated by who you authenticate as and by your access grants, not by your policy rules, so do not write a policy expecting it to stop an agent from reading a skill or listing your integrations.

Host-local tools are not governed. The file system, the shell, in-editor edits, other MCP servers you have configured: all of it stays visible to the agent, and nothing airlock renders restricts any of it. This is true of every host, not just one.

So routing the tools you want governed through airlock is your decision to make, and only calls that transit airlock's endpoint appear in its audit trail. Size your trust to that.

Models

modelPreference is an ordered list of logical { family, model } pairs, never a vendor model ID. family is something like claude or gpt; model is a logical name like sonnet-4-6 or opus-4-8. A host walks the list in order and takes the first entry it can serve, so put your preferred model first and a fallback after it.

Which pairs you may declare is data, not a fixed list in the product, and it comes from two places at once: airlock's model catalog, which names what a runtime host can execute and how (the provider, the model ID, where inference physically runs, and which credential a route needs), and the export targets' own model tables, which name what an export can render.

The catalog is global and curated by airlock, not per-customer: every organization reads the same one. It describes which models exist and where they run, so what differs between two organizations running the same agent is not the catalog but which routes their hosts hold credentials for. A pair either one carries is declarable, with one veto: the catalog wins where the two disagree, so a family or a model the catalog has archived is refused even when an export target still maps it. A pair nothing offers is refused when you save, rather than accepted and quietly downgraded at run time.

Editing an existing agent never strands it: an update always accepts the exact pairs its current version already declares, even ones the catalog has since retired. That is per pair, not per family, so a retired family keeps the model your agent already names without becoming a menu you can pick a different model from.

When airlock serves a definition it decorates each entry with the route it resolved to, so a host does not have to maintain its own mapping from logical name to vendor ID. An agent that is writing its own runtime can read the whole catalog with the get_model_catalog tool and cache it, re-fetching when the catalog version changes.

An entry comes back undecorated when airlock could not resolve it. Treat that as "use your own mapping", which is exactly what a host did before catalog resolution existed.

Skills

A definition does not list skills. It used to, and the list granted nothing: which skills an agent could actually activate was always decided at run time against the identity it authenticated as, so the authored list and the real answer could disagree with nothing to reconcile them.

So the agent discovers them instead. Its loop calls list_skills or search_skills on the same MCP endpoint, then activate_skill by name. What comes back is exactly what the running identity is granted, which is the same answer the platform would have enforced anyway.

Choose what an agent can reach by granting it to the identity the agent runs as: a service account for an unattended agent, the author's own token for a personal one. See Skills.

The identity running the agent needs access to a skill in its own right. Nothing on the agent grants it.

Stopping an agent that is already running

There is exactly one honest answer, and it is worth stating plainly because the intuitive one is wrong: airlock cannot stop your agent's loop. It is not on the inference path. What it can do is take away what the agent reaches through it, which is a different thing and usually enough.

What you doWhat actually happens
Revoke the service token the run authenticates withThe next call to airlock is refused. The loop keeps running and keeps calling whatever else it can reach
Disable the service accountSame, for every token bound to it
Revoke a grantThe agent stops reaching that integration, skill or agent on its next call
Stop the processYours to do, on your infrastructure

There is no kill switch, and no surface in the Control Room claims one. An agent mid-run holds a model connection you opened; airlock never saw it start and cannot end it.

Pushing an agent to ElevenLabs

If your organization runs voice agents on ElevenLabs, airlock can push an agent into your ElevenLabs workspace from the agent's Deploy tab. ElevenLabs runs the voice agent, in your workspace and on your bill. airlock performs the push, registers itself as the agent's MCP server, and checks every tool call the agent makes.

Connect the workspace once. An admin stores an ElevenLabs API key for the whole organization. Use a Service Account key with write access to Agents: it belongs to the workspace rather than to a person, so it keeps working after whoever created it leaves. airlock checks the key with ElevenLabs before saving it, refuses one ElevenLabs rejects, and never shows it again. Only admins can store, replace or remove it.

Push. Pick the service account the voice agent should run as, then push. airlock creates three things in your workspace:

  • a secret holding an airlock token bound to this deployment and that service account,
  • an MCP server pointing at your organization's airlock endpoint, authenticated with that secret,
  • the agent itself, with this agent's instructions and the model chosen on the agent. The model always comes from the agent: to run a different one on ElevenLabs, change it on the agent and push. If ElevenLabs does not offer the agent's model, the push is refused before anything changes and the Deploy tab lists the models it does offer.

Every caller runs as the same service account. ElevenLabs gives an MCP server one fixed token, so everyone who talks to the voice agent reaches airlock as that one service account. Grant the account only what every caller may do.

Naming the caller. Tick Name the caller when you push, or on the deployment afterwards (it applies on the next push), and airlock sets up the MCP server to send the conversation's user_email dynamic variable on every tool call. Whatever starts the conversation then passes the caller's email there: the dynamic-variables attribute on the ElevenLabs widget, or dynamic_variables when you start the conversation through their SDK or API. airlock records the call, and any approval request it raises, on behalf of that member of your organization, and History and the approval page show them. It is off by default, and three things are worth knowing before you switch it on:

  • Every conversation must then pass user_email. ElevenLabs refuses to start a conversation that does not supply a variable its MCP server uses, and a default value set on the agent does not change that. Switch it on only once everything that starts this agent's conversations sends the email.
  • It names the caller, it does not authenticate them or give them anything. What the agent may call is still decided by the service account's access alone, and who may approve a request is still decided by the approval rules: a caller who is not an approver cannot approve a request made on their behalf. An email that matches nobody in your organization is still shown next to the call, marked as asserted by the caller like every caller name, but it is not linked to any member. A mistyped or made-up address is therefore visible, not silently dropped.
  • A phone call carries only a phone number. To name a phone caller, look their email up from the number in ElevenLabs' conversation initiation webhook and return it as user_email. Without that, leave the setting off for an agent that takes phone calls.

Switching it off takes the header back out on the next push. A caller header you bound to a variable of your own in the ElevenLabs console is left alone either way.

Edits reach the voice agent when you push them. ElevenLabs keeps its own copy of the instructions, so changing the agent in airlock does not change the voice agent. The Deploy tab shows which version ElevenLabs is running against the current one, the agent page tells you when a new version has left it behind, and pushing again updates it. What the agent may call is different: airlock decides that on every call from the service account's access, so access and policy changes apply straight away, with no push.

Stopping it. Suspending the deployment cuts the voice agent's calls to airlock off on the next call. Deleting the deployment in airlock does not remove the agent from your ElevenLabs workspace; remove it there yourself.

If a push stops partway, for example because ElevenLabs did not answer, the Deploy tab says where it stopped. Pushing again picks up from there and does not create duplicates.

Switch on MCP in the workspace first. ElevenLabs turns MCP servers off in every new workspace. Before the first push, open ElevenLabs Agents, go to Integrations, MCP servers, start adding a server and accept the MCP Server Terms: that switches MCP on for the whole workspace, and you can cancel the rest of the form. Until then a push stops at the MCP server step and the Deploy tab says why. Workspaces on Zero Retention Mode or HIPAA cannot switch MCP on at all.

Pushing an agent to Anthropic Managed Agents

If your organization builds on Anthropic's Managed Agents, airlock can push an agent into your Anthropic workspace from the agent's Deploy tab. Anthropic runs the agent's sessions, in your workspace and on your bill. airlock performs the push, is the agent's only tool, and checks every tool call it makes.

Connect the workspace once. An admin stores an Anthropic API key for the whole organization, from the workspace the agent should live in, one with access to Managed Agents. airlock checks the key with Anthropic before saving it, refuses one Anthropic rejects, and never shows it again. Only admins can store, replace or remove it.

Push. Pick the service account the agent should run as, then push. airlock creates four things in your workspace, each tagged with the airlock deployment it belongs to:

  • an environment for the agent's sessions, with outbound traffic limited to its MCP servers,
  • a vault, which your sessions attach to reach airlock,
  • a credential in that vault: an airlock token bound to this deployment and that service account, for your organization's airlock endpoint,
  • the agent, with this agent's instructions and the model chosen on the agent. Its only tool is airlock: no sandbox shell, file or web tools, so everything it does goes through airlock's policy and approvals. The model always comes from the agent, and it has to be a Claude model: an agent with no Claude model in its preferences is refused before anything changes, and the Deploy tab lists the models Anthropic can run.

Start sessions with the vault. A Managed Agent takes its credentials per session, not per agent, so your own code has to attach the vault every time it starts a session. After a push the Deploy tab shows the agent, environment and vault ids and a snippet to copy, for cURL and TypeScript:

{
  "agent": "agent_...",
  "environment_id": "env_...",
  "vault_ids": ["vlt_..."]
}

A session started without vault_ids still starts, but it reaches airlock with no credential: the session reports mcp_authentication_failed_error for the airlock server and none of its tool calls go through. If you see that error, check that the session was created with the vault.

Anyone in the workspace can use the vault. Anthropic scopes vaults to a workspace, so any API key in that workspace can attach this vault to a session, and that session then acts as the service account you picked. That is the same trust you give the key itself. Use a workspace for the agent that only the people who should run it can create keys in.

Every caller runs as the same service account. Managed Agents does not send airlock anything that names who started a session, so every call reaches airlock as the deployment's service account, and History shows that account. Grant it only what every caller may do. The Name the caller option ElevenLabs offers does not exist here.

Approvals work as they do everywhere else. A tool call that needs approval becomes a pending request in airlock, and the agent is told it is waiting. The session does not ask anyone on the Anthropic side, because airlock decides.

Edits reach the agent when you push them. Anthropic keeps its own copy of the instructions, so changing the agent in airlock does not change the Managed Agent. Each push is a new version of the agent in Anthropic, and the Deploy tab shows both the airlock version that is live and Anthropic's version number. What the agent may call is different: airlock decides that on every call, so access and policy changes apply straight away, with no push. A push that has to renew the airlock token updates the credential in the vault in place, and sessions that are already running pick it up without a restart.

Stopping it. Suspending the deployment cuts the agent's calls to airlock off on its next tool call, in sessions that are already running too. Deleting the deployment revokes its token, so the vault's credential stops working. It does not remove the agent, the environment or the vault from your Anthropic workspace; remove them there yourself.

If a push stops partway, for example because Anthropic did not answer, the Deploy tab says where it stopped. Pushing again picks up from there and does not create duplicates.

Channels

An agent can be reached from a chat tool by running a listener that authenticates as a service account. airlock does not observe the message, the listener, or the connection: it governs the calls that listener makes through it, exactly as it governs any other caller. The setup, including what each credential does and does not permit, is in Agent Channels.

Managing agents from an agent

The registry operations on this page are available over your organization's MCP endpoint, so an agent connected with the right credentials can search the library, author a new agent, roll one back, and render an install for a host, without anyone opening the Control Room.

Channels are not among them: running a listener is something you set up once, on your own infrastructure, following the guide.

One rule from the REST surface is worth knowing here: an agent that has a deployment record cannot be deleted, and the refusal names them. Pushing an agent to ElevenLabs or Anthropic Managed Agents from the Deploy tab creates one of those records, and so can the API. Remove them first, from the Deploy tab or over REST.

The full tool list, and the REST equivalents, are in the Agents API reference.

Reference implementations

Worked examples of running an airlock-authored agent on a real runtime live at github.com/Air-Lock-AI/airlock-reference-implementations.