How to handle API keys for AI tools

A key held by an AI client is a password, not a config value. Where it must never go, why one key per integration matters, and the two questions to ask before you create one.

9 min read Updated

A key pasted into a chat window and a key typed into a connector settings field are the same string and completely different objects. One is a credential the client attaches to its own requests. The other is a line of a transcript — stored, re-read into the model's context on later turns, and carried into every export, screenshot and shared link of that conversation. The string does not know the difference. You have to.

A key is a capability, not a setting

API keys get filed as configuration because they arrive as configuration — a value in a form, next to a URL and a display name. But configuration is inert and a key is not. Possession is the entire authentication. No username, no second factor, no device check — whoever holds the bytes is you, as far as the issuing server is concerned.

That changes ordinary decisions:

  • Keys do not belong in a chat message, an issue, a support ticket, or a commit.
  • Keys do not belong in URLs. Query strings land in server logs, browser history, and the referrer sent to the next site — a credential in a URL is one you have published to several systems you do not control.
  • Keys do not belong in an unredacted screenshot — settings fields often have a reveal toggle somebody left on.

None of that is exotic. It is the list you already apply to a password, which is the point.

The chat box is the worst place for it

Connector settings and the chat box feel adjacent — same app, same window. They are not the same storage.

A credential in a settings field is attached by the client to the requests it makes; the model never has to see it. A credential typed into a chat message is content — read back into context on later turns, retained wherever that client retains history, and included in whatever gets exported, screenshotted or shared later. Sharing an AI conversation is a perfectly normal thing to do, and a bad thing to do with a live key sitting in turn four.

Even in the settings field a key is not sealed. It lives in that client's storage, travels in request headers, and can surface in debug logs at either end. Better than chat, not a vault.

If a key has ever been in a chat message, treat it as exposed and rotate it. There is no way to unsee a string.

One key per integration

Reuse one key across four integrations and you have built a coordination problem into your own incident response. When one leaks, the fix breaks the other three, so it waits — for a maintenance window, for someone's approval, for a quieter week. Keys that are painful to revoke do not get revoked.

Separate keys make revocation surgical — one leaks, one dies, everything else keeps running — and they make logs legible, since the key names the integration that made a call.

The two questions

Before creating a key, ask two things of the issuing service.

What can the holder of this key do? Not what your integration does — what possession authorizes. Read or write. Reversible or not. Whose data, and whose name ends up on the resulting action. A key that can only read is bounded by embarrassment; a key that can act toward other people is bounded by whatever it can reach. Same discipline as reading a consent screen properly, where what you actually grant is a scope rather than a category.

How fast can I revoke it? The good answer is a self-serve control that takes effect immediately. The common answer is a message to an operator and an unknown wait. A key you cannot revoke quickly is one to be stingier about issuing, because the response to every incident is identical and the only variable is how long it takes.

Two follow-ups sharpen that for AI tools. Does the key reach history — everything the tool has handled, not just what happens next? And can its actions be undone? History access is the exfiltration risk; irreversible actions are the other.

A RelayLink key is a per-user string sent in an X-RelayLink-Key request header, checked only on paths under /mcp. Possession is the authentication: there is no username, and no second factor to fall back on if the key leaks. RelayLink stores a SHA-256 of it rather than the key itself, so a database backup, a query log, or a support engineer reading a row does not hand over a working credential — but that protects the copy at rest, not the copy in your config file, which is the one that actually leaks.

The hash is unsalted, which is a deliberate choice rather than an oversight. A salt defends a guessable secret against a precomputed table; these keys are random, so there is no table to build and nothing to guess. Salting would also make the lookup impossible without scanning every row, because finding the account by the presented key is the whole operation. If your own keys are derived from anything a person chose, that reasoning does not transfer — salt those.

What the holder can do is the seventy-two-tool surface and nothing else: read inbox envelopes, pull a full briefing or a whole thread (either records a read receipt, shown to the sender only if the account has not turned that off — for every sender or for one of them), search the account's past correspondence, save and resume a hand-off to the account's own inbox, draft a package, confirm it, cancel it, list threads, drafts and contacts, make and answer contact requests, block somebody, set the account's display name, list or revoke the account's other keys, open or answer a support request with the people who run the deployment, send them feedback about the product, read and write the account's own notebook — the private workspace of notes, tasks, journal entries, working preferences and routines — and read and act on every shared project the account owns or has joined: the decisions, tasks and notes everybody on one can see, and whatever is assigned to the account itself. That reach stops exactly where the account's own does — a member's role on a project, whether Viewer, Contributor or Manager, is what a key on that account can act with, and inviting, removing or handing off a project stays a portal action no key can take. The notebook point is worth being clear about too: a leaked key reaches somebody's own saved material as well as their correspondence, and notebook_share can turn a note into a draft — but not into a sent message, because confirm_send is still what delivers anything. The same is true of documents: a leaked key can save a new one from text, mint a single-use upload link and put one file into it with nothing more than the link — though a link dies with the key that minted it, so revoking the key also ends any link it left behind — list and read anything already attached to a briefing, and archive, trash or hand back its own access — but the two consequential document actions, retiring one everywhere or ending one recipient's share, are attachment_manage requests that still need the account's own explicit yes through confirm_intent, the same gate revoke_api_key answers to. It grants nothing in any mailbox, because RelayLink never connects to one — and a revocation emails the account owner, because a key going off is the shape of both ordinary housekeeping and a lockout, and the mailbox is the one channel a compromised assistant session does not control.

One honest limit on the newer confirm_intent gate, worth stating plainly here: it stops an assistant acting on a sentence it merely read somewhere, because the two calls have to be answered by the user's own words, not conjured from a package. It is not a defence against a fully leaked key used directly — the same credential that opens a gated call can answer its own confirmation, so a script holding a stolen key loses nothing but a second HTTP request. What the gate buys here is a paper trail: revoke_api_key and the other gated tools now leave a stated intent behind before anything happens, which is one more thing an assistant reading the transcript afterward can notice went wrong.

What it cannot do is more interesting, and one line of it has changed since this was written. Contact decisions used to be browser-only: no tool could make or accept a pair. invite_contact and accept_contact exist now, so a stolen key can ask somebody to become a contact and can answer a request waiting on the account — a real widening of what a leak costs, and a reason to revoke rather than watch. The gate underneath is unchanged: it still reaches only people who have accepted a pair, plus email addresses with no RelayLink account, where a rolling 24-hour cap on new first contacts applies. It cannot issue itself a successor, because issuing a key is deliberately not a tool — a key exists in the clear exactly once, and a conversation transcript held by a third party is the wrong place to write it down — so revoking a leaked key ends it rather than starting a race. It cannot close or export the account, which stay in the browser. And it cannot unsend anything and cannot search; a package still carries only a plain-text or Markdown document, never a PDF, a deck or a spreadsheet.

Two honest limits, since a security article that only lists strengths is an advertisement. The draft-then-confirm split exists so a steered model cannot compose and transmit in one shot; it does not stop somebody holding the key, who can make both calls. And /mcp carries a generous per-key ceiling (300 calls a minute) meant to catch a runaway loop or a stolen key driving unlimited database work, not to police ordinary use — so a stolen key is still bounded mainly by the consent gate and the cold-recipient cap, with that ceiling as a backstop rather than the real limit.

Revocation deserves the blunt answer, and it changed twice. Keys used to be provisioned by whoever ran the deployment, so rotating one meant asking them rather than pressing a button — the weaker half of the two questions above, on our own test. Then there was an account page with one key on it, and replacing that key broke every agent using the old one at once, which is the coordination problem two sections up arriving from the other direction. Now the account page holds a key per agent: each one named by you, shown once when it is made, listed with when it was last used, and revoked on its own. One leaks, one dies, the others keep running — the practice this article recommends, on the product it is about. Listing and revoking are also tools, so "revoke the laptop key" is a sentence rather than a browser trip; issuing deliberately is not, because a key exists in the clear exactly once and a chat transcript is a poor place to leave it.

When you connect an assistant, the key goes in the connector settings field, never the conversation — and for RelayLink, the connect section spells out the surface a key opens.

Frequently asked questions

Is it safe to paste an API key into a chat with an AI assistant?
No. Treat any key typed into a chat message as exposed and rotate it. A chat message is content, so it is retained in the conversation history, read back into the model's context on later turns, and carried into any export, screenshot or shared link of that conversation. A connector settings field is different — the client attaches the credential to its own requests instead of storing it in the transcript.
Why use a separate API key for each integration?
So revocation is surgical. If four tools share one key, revoking it after a leak breaks all four, which means the revocation gets postponed until a convenient moment that may never arrive. A key per integration means one leak kills one key, everything else keeps running, and the logs tell you which integration made a given call.
What should I ask a vendor before creating an API key?
Two things. What the holder of the key can do, meaning what possession alone authorizes rather than what your integration happens to use, including whether those actions are reversible and whether the key can read past data as well as create new. And how fast you can revoke it, meaning whether there is a self-serve control that takes effect immediately or a support request with an unknown wait.