What is credential custody for AI agents?

Credential custody means the agent asks for an action and the platform performs it with the right account. The agent never holds the token, so a leak exposes no app credential.

Illustration: A small friendly robot with a round camera-eye head asks at a counter.

Credential custody means the agent asks for an action and a platform performs it with the right account. The OAuth token or API key stays with the platform: stored, refreshed, scoped and revoked there. The agent receives tools, never the token. A leak from the agent's process, logs or transcript then exposes no app credential at all.

The agent asks, the platform acts
The Slack token stays with CoreSpeed. The agent holds tools, never the token.
Claude Code
calls slack__post_message
CoreSpeed
acts with the stored token
Slack
posted as your account
In the agent's context
tools/list lists slack__post_message
tools/call sends a channel and a message
The result comes back
No app token to leak
In CoreSpeed's custody
Slack
OAuth token, connected once in the dashboard
Refreshed when the provider allows
Scoped: private to a member, or shared with the org
Disconnected in one place: Dashboard → Connectors
The same custody holds for OAuth, a pasted API key, and your own OAuth app.

Why does pasting tokens into an agent fail?

The quick way to give an agent Slack is to put a bot token in its environment. It works on day one. The problems arrive later.

  • Leaks: the token sits in the process, in shell history, in a config file, in a transcript, and in any log line that prints the environment. A prompt injection that asks the agent to print its secrets gets them.
  • Rotation: when the token changes, every agent process that holds it needs a new copy, and you have to find them all.
  • Revocation: cutting off one agent means cutting off every process that shares the token, or never learning which copy was used.
  • Scope: a token belongs to whoever created it, so the nightly job acts with a person's full reach and the trail says that person did it.

Each problem grows with each client and each app. Three clients and ten apps is thirty copies of ten secrets.

How does custody work?

In CoreSpeed, a member connects an app once, in the dashboard. The provider's consent screen names CoreSpeed. The resulting credential goes into the platform's custody and never leaves it. CoreSpeed refreshes it when the provider allows, and exposes the app as namespaced tools: slack__post_message, notion__create_page, linear__search_issues.

The agent's side is only MCP. It calls tools/list, sees the tools its sign-in may use, and calls tools/call. The platform performs the action with the right account and returns the result. The token was never in the request, the response or the agent's context.

Three ways to connect exist, and the custody model is the same for each.

MethodHow it startsWhen the credential changes
OAuth (default)Consent screen names CoreSpeed; tokens refreshed automatically; a few vendors such as Trello and Zotero use OAuth 1.0aThe member reauthorizes in Dashboard → Connectors when the account reaches needs_reauth
Pasted API keyauth.type: "api_key" on the index; Stripe is one; stored encryptedRotation is a reconnect; disconnect deletes CoreSpeed's copy only
Bring your own OAuth appAn org admin pastes client_id and client_secretReplacing the client_id or deleting the app moves every connection to needs_reauth

Bring your own app is the route for vendors whose review gates a shared app, such as Google, Microsoft and Meta. Calls through your own app are not metered by CoreSpeed; holds and the activity trail still apply.

How are connections scoped?

Every connection is private or shared. A private connection is reachable only by the member who connected it and their clients. A shared connection is reachable by the whole organization. Any member can connect either kind.

Principals differ in reach. A browser sign-in or an sk-cs- member key acts as the member and reaches both kinds. An sk-csa- agent key belongs to an agent principal and reaches shared connections only, never a member's private ones. Where the vendor can install CoreSpeed's app as its own actor, a Slack workspace bot or Linear's app under the alias corespeed-bot, org-wide access is an organization integration that only an org admin connects, and actions through it are attributed to the app. The authentication docs describe each principal.

Several accounts of the same app can coexist, two Slack workspaces for instance, each with an alias. Tools take an optional account argument. With several accounts in scope and none named, the call answers ambiguous_account with the aliases in structuredContent.error.aliases. The connectors docs cover aliases and manage__accounts_rename.

What does a stale credential look like?

A connection that stops working enters needs_reauth. Its tools stay visible in tools/list; calls fail until the member reauthorizes in Dashboard → Connectors. Do not rotate the CoreSpeed key and do not rewrite client config: neither is broken. Account state is on the authenticated index, which is the only inventory of what this caller can connect:

curl https://api.corespeed.io/connectors -H "Authorization: Bearer $CORESPEED_API_KEY"

What does custody not cover?

Custody protects the credential. It does not decide whether an action should happen. That is the job of approvals and of the client's own confirmation. Content the agent fetches, pages, posts and documents, is untrusted and can carry instructions aimed at the agent, so keep client confirmation on for writes when other MCP servers are connected alongside. Give unattended agents an agent key, cap the spend on every key, and read the trail in Dashboard → Activity. The MCP server docs list these practices.

FAQ

Does the agent ever see the OAuth token?

No. It sees tools. The platform performs the call with the stored credential.

What happens when I disconnect an app?

Its tools leave the agent's tools/list. For a pasted API key, CoreSpeed deletes its copy only; the key still works wherever else you use it.

Can an unattended job use my private connections?

Only if you give it your member key. Give it an agent key instead; it reaches shared connections only.

Why is a visible tool failing with needs_reauth?

The connection needs a fresh authorization from the member who owns it, in Dashboard → Connectors. Nothing else needs changing.