Contro1 Action Gateway
Choose the applications your organization approves, decide who may do what, and connect one MCP. Every person and agent gets exactly their access, and every action is recorded on their name.
Start here
Connect your own agent (LangGraph, Mastra, OpenAI Agents, CrewAI or plain HTTP) with an Agent Credential, give it Actions in Access, and call them.
Updated Oct 21, 2026
An agent in your own code gets its own identity and an Agent Credential, a key bound to that one agent. You give it Actions in Access, and it calls them with the SDK or plain HTTP; every call is attributed to it and checked by Contro1.
Your own code, running unattended: a backend, a worker or a scheduled job. For: LangGraph, Mastra, OpenAI Agents SDK, CrewAI, Pydantic AI, Strands, n8n. Acts as: The agent itself. Credential: An Agent Credential in your secret manager.
An agent in your own code gets its own identity and an Agent Credential, a key bound to that one agent. You give it Actions in Access, and it calls them with the SDK or plain HTTP; every call is attributed to it and checked by Contro1.
Three things have to exist before the first call: the agent, the application access it was given, and its credential. Then one call proves it works.
| Step | What to do | What you should see |
|---|---|---|
| Create the agent | contro1 auth login contro1 init --name "Invoice agent" --framework langgraph | The agent appears in Agents with you as its owner. contro1 init prints "Connected and set agt_... as the default agent for profile default" and a table with its agent_id. |
| Give it the Actions it needs | If the application is not on yet, an administrator turns it on under Apps and chooses which of its Actions the organization allows. Then open Access, choose the agent, connect the account it should use (for example the team mailbox), tick the Actions it may run, for example gmail.message.list, and press Save. | On the agent's page, the "Get it working" card shows "Can do something" as done. |
| Create its Agent Credential | Open Settings, Agent keys, press Add agent key, choose the agent, name the place it will run (for example "Production server") and press Create Agent Credential. The New agent guide offers the same button right after Create identity. | The key is shown once. Copy it straight into your secret manager. Agent keys then lists it under the name you gave. |
| Call an Action from your code | # pip install "centcom>=1.5.0" import os, uuid from centcom import CentcomClient, needs_human_resolution client = CentcomClient(api_key=os.environ["CONTRO1_API_KEY"]) out = client.actions.invoke( "gmail.message.list", {"max_results": 5}, authority_mode="agent_principal", # the agent acts as itself account_mode="shared", # the team mailbox you granted connection_id="acn_...", # optional when only one account fits idempotency_key=str(uuid.uuid4()), # required for anything with side effects ) invocation = out["invocation"] if invocation["state"] == "executed": messages = out["result"] elif invocation["state"] == "awaiting_approval": settled = client.actions.wait_for_invocation(invocation["invocation_id"]) if needs_human_resolution(settled): raise RuntimeError("Outcome unknown: a person must check. Do not retry.") messages = client.actions.get_result(settled["invocation_id"]) else: raise RuntimeError(f"Not run: {invocation['state']} {out.get('not_run')}") | HTTP 201 with "state": "executed" and a "result". Or 202 with "state": "awaiting_approval" while a person decides: poll GET /api/centcom/v1/actions/{invocation_id} until the state is terminal, then read "result". |
| Confirm it in Contro1 | Open the agent in Contro1. | The call is in its activity under the agent's own name, and the "Get it working" card is gone, because all three of its steps are done. |
In Contro1, open Agents, press New agent and choose "An agent you are building for production". Give it a name and press Create identity. You are its accountable owner, and it starts with no access and no credential.
Or from a terminal, with the CLI installed: sign in, then register it. A coding agent connected to Contro1 over MCP can also do this with the register_agent tool.
What you should see: The agent appears in Agents with you as its owner. contro1 init prints "Connected and set agt_... as the default agent for profile default" and a table with its agent_id.
If the application is not on yet, an administrator turns it on under Apps and chooses which of its Actions the organization allows. Then open Access, choose the agent, connect the account it should use (for example the team mailbox), tick the Actions it may run, for example gmail.message.list, and press Save.
What you should see: On the agent's page, the "Get it working" card shows "Can do something" as done.
Open Settings, Agent keys, press Add agent key, choose the agent, name the place it will run (for example "Production server") and press Create Agent Credential. The New agent guide offers the same button right after Create identity.
Store it as CONTRO1_API_KEY in the secret manager of the place it runs. Never in code, a prompt or a tool argument.
What you should see: The key is shown once. Copy it straight into your secret manager. Agent keys then lists it under the name you gave.
authority_mode "agent_principal" means the agent acts as itself. account_mode "shared" names a shared account you gave it. Send an Idempotency-Key on anything with side effects, and never re-submit while a person decides.
What you should see: HTTP 201 with "state": "executed" and a "result". Or 202 with "state": "awaiting_approval" while a person decides: poll GET /api/centcom/v1/actions/{invocation_id} until the state is terminal, then read "result".
Open the agent in Contro1.
What you should see: The call is in its activity under the agent's own name, and the "Get it working" card is gone, because all three of its steps are done.
Each guide shows this call as a tool in that framework's own idiom, plus its trace hook, so every tool call is reported from the runtime with one trace_id per run.
LangGraph ·
Mastra ·
OpenAI Agents SDK ·
CrewAI ·
Pydantic AI · Strands Agents ·
n8n · Claude Managed Agents · Python SDK · JavaScript and TypeScript SDK
A personal account, such as somebody's own Gmail, is always used on behalf of the person it belongs to. The agent names them: authority_mode "user_delegated" and acting_user_id set to that person. Contro1 accepts it only if that person let this agent act for them with that Action.
When the account's owner is also the agent's owner and gives it the Action in Access, that counts as letting it act for them. In every other case the account holder opens the agent in Access and turns it on themselves. The agent's whoami lists who it may act for under acts_for.
Actions are for work Contro1 performs with an account it holds. For work your own code performs, such as a database migration or a payment through your own client, ask for a decision first and continue only when it is approved. It uses the same Agent Credential.
If you are an AI coding agent wiring an application to Contro1, these are the rules the server enforces. Follow them exactly.
| What you see | What it means | What to do |
|---|---|---|
| APPLICATION_ACTIONS_NOT_ENABLED | This computer connection is Approvals only (set by its owner or a setup policy). | Open the agent in Access and press "Allow applications", or open the page contro1 apps enable prints. |
| An Action is missing from list_actions | Nobody gave it to this agent, or the app is not turned on in Apps. | Give it under Access, or call request_capability to ask the account owner. |
| DELEGATION_NOT_FOUND | The agent tried to act for a person who has not let it use this Action on their account. | That person opens the agent in Access and turns it on. The refusal names them and links the page. |
| "A personal account is used on behalf of the person it belongs to" | A personal account was called without naming the person. | Send authority_mode "user_delegated" with acting_user_id. |
| EXECUTE_SCOPE_REQUIRED (MCP) | This MCP connection was approved to read and preview only. | Sign in again with "Run Actions as you" ticked (contro1 mcp login --execute for a local server). |
| SCOPE_UPGRADE_REQUIRED | An old organization key without scopes. | Create an Agent Credential for the agent. |
| awaiting_approval | A person has to decide. | Poll the invocation. Do not submit it again. |
| execution_indeterminate | The provider may or may not have done it. | Stop. A person checks the provider and decides. |
Yes, if the framework has an MCP client that can complete the device sign-in (Contro1 refuses sign-ins that return to 127.0.0.1). It then acts as the person who approved it, not as its own agent. For an agent that runs unattended in production, an Agent Credential gives it its own identity and permissions.
No. Connect the system once, as one agent. Each part says who it is with the Contro1-Sub-Agent header (client.as_sub_agent in the SDKs) and appears under it on the org map, marked ! until an administrator approves it, with its own trail. A part runs on the agent's grants and can be narrowed or blocked on its own. Connect a part separately only when it runs as its own program somewhere else, and link it with contro1 init --parent.
In your secret manager, read from the environment as CONTRO1_API_KEY. Never in code, a skill, a prompt or a tool argument.
Choose the applications your organization approves, decide who may do what, and connect one MCP. Every person and agent gets exactly their access, and every action is recorded on their name.
Install and use the framework-agnostic Contro1 Python SDK to request human approvals or input, verify signed webhooks, log autonomous actions, and fetch audit evidence.
Install and use @contro1/sdk in Node, TypeScript, Express, Fastify, or Next.js backends to create approval requests, verify webhooks, and write audit records.
Pause LangGraph execution at deterministic checkpoints, route review through CENTCOM, and resume with signed decisions.