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.
What a RelayLink key authorizes
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 nine-tool surface and nothing else: read inbox envelopes, pull a full briefing (which records a read receipt the sender can see), create a draft, confirm it, cancel it, and list threads, drafts and contacts. It grants nothing in any mailbox, because RelayLink never connects to one.
What it cannot do is more interesting. It cannot create a contact — no tool does; an accepted pair is made by a person accepting a request on their own account page — so it reaches only people the account could already reach, plus email addresses with no RelayLink account, where a rolling 24-hour cap on new first contacts applies. It cannot unsend anything and it cannot search; packages carry no file field, so there is nothing to attach.
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. 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. There is an account page now: sign in with your email address, and Replace the key issues a new one and kills the old one immediately, with no support request and no wait.
The honest caveat is that there is one key per account, so replacing it breaks every agent using the old one at once. If you have wired the same key into four things, that is four things to update in the same minute — which is the coordination problem two sections up, arriving from the other direction.
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.