Skip to Content

Get your API keys

You need one key per server, and they come from different places. This is not an oversight — the two credentials answer to different authorities.

KeyWhere it comes from
Companion HubHub web UI → Settings → Security, or cihub api-key create on the appliance host
Companion MemoryCompanion Memory web UI → Settings → API Keys

Hub gives you both a UI and a CLI because its key is an operator credential — useful to mint from a script on a headless box. Memory’s key is a per-user credential tied to your signed-in session, so the UI is the only route; there is deliberately no memory-side CLI.

Both keys are shown once, at creation. Neither can be retrieved afterwards — if you lose one, mint a replacement and delete the old one.

Both servers issue a key as 64 lowercase hexadecimal characters with no prefix, so a3f9… and nothing else. Companion Memory refuses any value that is not exactly that shape with a plain 401 before it looks the key up, and Hub answers Invalid API key. A trailing space, a doubled Bearer, or a key cut short in the copy all fail the same way. The short prefix in a key list — 5 characters on Memory, 8 on Hub — only identifies a key; it is never enough to use.

The Companion Hub key

From the web UI

Open the Hub dashboard and go to Settings → Security. The API keys card is the last card on the page. Create key opens the Create an MCP API key dialog, which asks for three things:

  • Name — a label that identifies the client, such as laptop-claude-code, so you can revoke it precisely later.
  • Scope — leave MCP selected. An Inference key is for editors and SDKs that use your Hub as an OpenAI-compatible endpoint, and it reaches no MCP tools, so it cannot connect an agent. (Older Hub releases have no Scope choice and always create an MCP key.)
  • What this key can do with Hub tools — Read-only, Read & write (the default), or Read, write & delete. Only an organization owner or admin can choose Read, write & delete. See Choosing a capability.

The raw key is shown once, with a Copy key button.

Companion Hub Settings → Security. Below the two-factor authentication and People on this Hub cards, the API keys card has a Create key button and three rows: Laptop, a key created by the signed-in account (MCP and Read & write chips, with a Created by line), and the app-managed keys OpenClaw WebCLI and Hermes (MCP, App, Read & write, and Managed chips, with last-used times). Each row shows a redacted key prefix and Change access and Revoke actionsCompanion Hub Settings → Security. Below the two-factor authentication and People on this Hub cards, the API keys card has a Create key button and three rows: Laptop, a key created by the signed-in account (MCP and Read & write chips, with a Created by line), and the app-managed keys OpenClaw WebCLI and Hermes (MCP, App, Read & write, and Managed chips, with last-used times). Each row shows a redacted key prefix and Change access and Revoke actions
Hub → Settings → Security. The API keys card lists every key with its prefix redacted; Create key mints a new one. The key names, prefixes, and times are example data.

Each row shows the key’s prefix, carries its scopes and capability as chips, and says when the key was last used and who created it. Change access adjusts the capability — scopes are fixed at creation and render as read-only badges. Revoke deletes the key.

A key created in Settings → Security acts with the app grants and role of the person who created it, so a household member’s key cannot reach apps that person cannot. A key created with cihub api-key create records no creator and shows Creator unknown; it is bounded only by its capability. To limit a key to one person’s access, create it in the dashboard while signed in as that person.

From the CLI

Run this on the appliance host, not on the machine running your agent:

cihub api-key create --name "my-laptop-openclaw"

The full form, if you want to be explicit:

cihub api-key create \ --name "my-laptop-openclaw" \ --scopes mcp \ --capability read
FlagValuesDefault
--nameany labelrequired
--scope, --scopesmcp, qa:read, inferencemcp
--capabilityread, write, fullwrite

--scope and --scopes are the same flag, so give one. Use mcp for an agent: qa:read is a read-only credential for test harnesses and monitors, and inference is for editors that use your Hub as an OpenAI-compatible endpoint. Each of those two must be the only scope on its key, and a key with inference refuses --capability write and --capability full.

List what exists — this prints IDs, names, scopes, capability and a key prefix, but never the key itself:

cihub api-key list

Hub’s MCP server is enabled by default. If a key connects but the endpoint refuses, check cihub mcp config — someone may have set MCP_ENABLED=false.

The Companion Memory key

Open Settings

Sign in to Companion Memory and go to Settings → API Keys. The create control is the + button in the top right of the panel, not a labeled button in the empty state.

Companion Memory Settings → API Keys. A list of settings sections down the left runs from Appearance to GraphiQL, with API Keys selected. The API Keys panel has a plus button in the top right and an empty state reading No API keys created yet.Companion Memory Settings → API Keys. A list of settings sections down the left runs from Appearance to GraphiQL, with API Keys selected. The API Keys panel has a plus button in the top right and an empty state reading No API keys created yet.
Companion Memory → Settings → API Keys, before any key exists. The + in the top right opens the create dialog.

Name, expiration, access and capability are all fields of the same dialog — you fill them in before the key exists, not after. Expiration is optional; an expired key is refused with a plain 401 that does not say it expired, so set a date only if you will remember to replace the key.

Name it

Give it a name that identifies the client you are connecting, so you can revoke precisely later.

Tick the areas it may reach

The scope checkboxes are grouped under Access. For MCP you must select both MCP and Intents. See the scope trap below — this is the single most common setup mistake.

Companion Memory Create API Key dialog — a Name field reading openclaw, an optional expiration date, an Access group with Memory unticked and Intents and MCP ticked, and a What this key can do group with Read-only, Read & write (selected), and Read, write & deleteCompanion Memory Create API Key dialog — a Name field reading openclaw, an optional expiration date, an Access group with Memory unticked and Intents and MCP ticked, and a What this key can do group with Read-only, Read & write (selected), and Read, write & delete
The create dialog with Intents and MCP ticked under Access — the minimum for an MCP client — and Read & write selected, the default.

The dialog warns about both traps itself: the MCP checkbox reads “pair with Intents to see any tools”, and the note under the group confirms the key “is limited to the selected areas and is refused everywhere else, including bulk ingest and GraphQL.”

Add Memory if you use the key for anything beyond MCP — including first-class memory: the passive-capture provider talks REST, so a key without Memory is refused on every call it makes, silently. Ticking those three (Memory, Intents, MCP) yields one key that covers both the MCP setup and the provider.

The fourth checkbox, Capture, is for a different kind of client entirely — a capture agent that streams observations in and reads nothing back. Leave it unticked for an agent setup; see Scopes below for what it opens.

Get this right the first time. Scopes cannot be changed after creation — the edit dialog offers only the name and the capability. A key with the wrong access has to be replaced.

Choose a capability

Under What this key can do, the dialog labels these Read-only, Read & write, and Read, write & delete — the same three levels this page calls read, write, and full. See Choosing a capability.

The dialog starts on Read & write, as in the screenshot above. For a client whose job is recall, change it to Read-only — that is a deliberate step, not the default. The exception is a key for first-class memory: passive capture writes, so that key must stay on Read & write — on a Read-only key, recall works and every capture is silently refused, which looks exactly like a healthy setup that never remembers.

Create it, and copy the key

The key is shown once, on submit, under “Your new API key has been created” — and the panel tells you plainly that “the key value won’t be shown again.” Store it in your password manager or export it as an environment variable now.

Companion Memory API Keys panel after creating a key. A green confirmation reads Your new API key has been created, with the key value hidden behind dots and a copy button, and the note The key value won't be shown again. Below it the new openclaw key is listed with intents, mcp and Read & write chips and a short key prefix.Companion Memory API Keys panel after creating a key. A green confirmation reads Your new API key has been created, with the key value hidden behind dots and a copy button, and the note The key value won't be shown again. Below it the new openclaw key is listed with intents, mcp and Read & write chips and a short key prefix.
The one moment the key is visible. The key value is hidden in this screenshot. Below the banner, the new row keeps only a short prefix and the chips.

Once you dismiss that banner the key row keeps only a short prefix alongside its scope chips and its capability. Enough to identify the key, never enough to use it.

Scopes: which surface the key opens

Scopes say where a key may go. They are not the same thing as capability, which says what it may do once it gets there.

ScopeOpens
MCPThe Model Context Protocol endpoint at /api/mcp
IntentsThe agent intent catalog — and the tools an MCP client can see — and the pending-action routes (/api/pending-actions)
MemoryThe memory REST API (/api/memory/*) — recall, context, write, turn, compact — plus /api/v1/intelligence/* and the agent job routes (/api/v1/agent/jobs)
CaptureThe write-only observation plane: the /api/v1 bulk-ingest routes (not /api/v1/files, /api/v1/ingest/* or /api/v1/intelligence/*) and /api/files upload/update

Capture exists for capture agents — Companion Capture, the browser extension — that stream what they saw in and read nothing back. It deliberately does not open /api/memory/*, GraphQL, or the maintenance routes.

The grant is narrower than the /api/v1 prefix suggests. It is declared on the v1 controller itself, so a Capture key opens exactly its ingest routes — /api/v1/events, /api/v1/communications, /api/v1/contents, /api/v1/things, /api/v1/browser-activity, /api/v1/notes, /api/v1/positions, /api/v1/receipts, /api/v1/product-models, /api/v1/observations, and /api/v1/inventory, a deprecated alias of /api/v1/things — plus POST /api/files/upload and PATCH /api/files/:id. The sibling mounts under /api/v1 sit outside that grant and 403 a Capture key: POST /api/v1/files (the batch upload lane), POST /api/v1/ingest/finish and GET /api/v1/ingest/status declare no scope at all, and /api/v1/intelligence/* requires Memory.

The ingest-lifecycle pair is the one that bites. A bulk load is three steps — push the batches, then POST /api/v1/ingest/finish to run the enrichment and search-index backfill the push deliberately deferred, then poll GET /api/v1/ingest/status until the queue drains. A Capture-only key can do step 1 and neither of the others, so the rows land and nothing ever indexes them. Steps 2 and 3 need a key with no scopes ticked, or a signed-in session.

The scope trap: MCP without Intents

A key with MCP but not Intents authenticates successfully, opens a session, and then advertises zero tools. Your client reports a connected server with nothing on it, which looks exactly like a broken server and is not one.

Measured against a live instance: MCP alone advertises no tools at all — tools/list answers -32601 Method not found, because nothing was ever registered. Adding Intents fixes it. See the full table, which also shows how capability changes the count.

Different jobs need different scopes. If you are wiring MCP and want your agent to reach the memory REST API, you need MCP + Intents + Memory. A key minted for MCP alone connects over MCP and returns 403 on every /api/memory/* request.

Start from the job and read off the boxes to tick:

Scopes are not additive permissions — enforcement is deny-by-default. Only those four surfaces declare a scope at all, and a key with any box ticked is refused everywhere no scope is declared: GraphQL, the graph routes, the batch-upload lane, and the /api/v1/ingest/* lifecycle routes. So MCP + Intents + Memory is not “the maximal key” — it is narrower than leaving every box unticked. An unscoped key is the only one that reaches those remaining surfaces, and it is full-access by construction.

Choosing a capability

Capability says what a key may do on the surfaces its scopes opened. It is a separate axis, and every key has one.

CapabilityReachesUse it when
readRead-only tools. Nothing it calls changes state.The agent’s job is recall, search, and inspection
writePlus mutating tools — install, start, stop, restart, record. Default.The agent should manage apps or record memories
fullPlus destructive tools — uninstall, reset, update an app, restore or delete a backup, change an app store, bulk actions.You specifically want the agent to be able to remove things

Prefer read unless you want the agent changing things. A key that only needs to recall memories does not need permission to uninstall your apps, and a key pasted into a third-party client is a key you no longer fully control.

Capability also filters what your agent sees. A read key’s tools/list returns only the tools it could actually call, so the agent never wastes a turn on a tool that was always going to be refused.

A full key still waits for Approvals

Capability decides which tools your agent can call. It does not decide whether Companion Memory runs a risky one without asking you. A tool that deletes data or sends something outside the appliance — memory_forget, messaging_send, planning_completeTask, import_import_inbox — is held for your approval when Settings → Autonomy is Balanced (the default) or Cautious. Memory queues a card under Approvals and answers the client with the error code TOOL_QUEUED and a pendingActionId. The call runs once you approve the card, and a card nobody answers expires after 24 hours. Tell your agent to report that the call is waiting for approval rather than retry it. Hands-off runs these calls without asking, except for a key that Hub minted for an installed agent app, which is always asked first.

This covers the curated intent tools. The GraphQL-derived tools that a key with no scopes also sees are checked against capability only, so a full key can change or delete through them without a card. Give an unscoped key the lowest capability that works.

The Approvals page in Companion Memory with one card. The card names the tool memory.forget and says it is irreversible and an installed app asked for it. It shows the call's arguments, an expiry time, and Decline and Approve buttons.The Approvals page in Companion Memory with one card. The card names the tool memory.forget and says it is irreversible and an installed app asked for it. It shows the call's arguments, an expiry time, and Decline and Approve buttons.
A held call under Approvals. An unscoped key tried memory.forget, and the card shows the tool, its arguments and when it expires. The call runs only if you select Approve.

read with no scopes selected

On Companion Memory v2026.9.28.1 and later, capability is enforced on every route a key can reach. A route that declares a level requires it, and a route that declares nothing requires what its shape implies: a GraphQL mutation or any request that is not GET needs write, and a query or GET needs read. So Read-only holds for a key with no scopes selected too. Scopes add something else — they confine the key to particular areas.

On earlier releases the rule was opt-in per route. GraphQL, the POST /api/v1/files batch upload, /api/v1/ingest/*, the maintenance routes, and the graph and import routes declared neither a scope nor a capability, so a read key with no scopes reached them with nothing checking what it did. If you cannot upgrade, select the areas the key needs rather than leaving them all unchecked.

The two app-proxy tools are gated differently

hub_call_app_api proxies an HTTP call to an app, so what it does depends on the arguments. It appears in every capability’s tool list and the decision is made per call from the method.

The dividing line is narrower than it looks: only GET, HEAD and OPTIONS count as reading. Everything else — POST, PUT, PATCH, DELETE — is treated as destructive and needs full. So a write key can see this tool and use it to read, but is refused for every mutating method, the same as a read key. An unrecognised or missing method counts as mutating.

If your agent needs to drive an app’s HTTP API beyond reads, full is the only capability that works.

hub_call_app_tool is gated the other way. A bridged tool’s effect is opaque to the Hub, so it is flagged destructive outright rather than judged per call — only a full key sees it. If your agent needs to call tools on an app’s bridged MCP server, read and write do not work.

Changing a key later

You do not need to replace a key to change what it can do — but where you do it differs by server.

In Settings → API Keys, edit the key and change its capability. Raising a key’s authority asks for confirmation; lowering it does not, so tightening a key is never harder than loosening one.

Revoking

Revoke a memory key from Settings → API Keys with the delete icon on its row, and a Hub key from Settings → Security with Revoke. Because keys are per-client if you minted them that way, revoking one leaves your other clients working.

Last updated on