An API key is a credential you hand to a machine, and machines are careless. They write keys into logs, commit them into repositories, paste them into chat threads, leave them in screenshots.
The security of an API key was never really about the randomness of the string. It is about what the key was allowed to do, and that question was answered the moment the key was issued, not the moment it leaked.
This is the part most API key design gets backwards. Teams generate a 256-bit secret, feel satisfied, and then give that secret the same powers as the account that created it. The randomness is theatre. A key with admin privileges does not care how it was leaked. It only cares that someone picked it up.
The blast radius is decided at issuance, not at breach time
When a key leaks, everything that matters has already happened. What the key can read, what it can change, whether it can create more keys, whether it can reach admin routes: all fixed when somebody minted it. Incident response is then arithmetic. If the key was narrow, the incident is small. If the key was godlike, the incident is a crisis.
This is why API key guidance starts with privileges rather than entropy. The practical upshot is simple: if your threat model is "the key will eventually leak", because with enough keys in enough logs it will, then every design decision should be made under that assumption.
Authentication is a top-two API risk, by OWASP's own ranking
This is not a niche concern. In the OWASP API Security Top 10 2023, Broken Authentication sits at API2, second only to Broken Object Level Authorization. The description is blunt: authentication mechanisms are often implemented incorrectly, "allowing attackers to compromise authentication tokens or to exploit implementation flaws to assume other user's identities temporarily or permanently".
Broken Function Level Authorization sits at API5, and names the failure directly: complex access control policies and "an unclear separation between administrative and regular functions, tend to lead to authorization flaws. By exploiting these issues, attackers can gain access to other users' resources and/or administrative functions."
The interesting part is the overlap. An overprivileged key is both risks at once: a broken authentication boundary, because the key was never meant to hold those powers, and a broken function-level authorization, because the key can reach admin functions it should never touch. Fix the privilege model and both shrink together.
Store the hash, never the secret
The first rule of storing a credential you did not ask a human to remember is the same as the rule for passwords: store only what you need to verify it. Open Pulse stores only a hash of the key, never the plaintext. A lost secret is replaced rather than recovered, because recovering something you never stored is impossible by construction.
It is worth saying plainly why. A database of plaintext API keys is a treasure chest. A database of hashes is a pile of locks without their keys. If a backup leaks, a snapshot is misconfigured, or somebody runs the wrong query, the attacker gets values that verify nothing anywhere else.
The surrounding practices are the consensus position and worth stating even though they are unglamorous: show the full key exactly once at creation, hash it before storing, log only a prefix, and never let the full secret reach an audit log.
Scopes are deny by default
The second rule is least privilege, enforced mechanically rather than advised.
In Open Pulse a key reaches a route only if that route declares a scope the key holds. A route that declares no scope is unreachable by any key. That is the posture that stops the API becoming a public contract the day somebody adds a route file. The alternative, allow-by-default with an exclusion list, fails open in exactly the case nobody is thinking about.
There are eight scopes, and the interesting thing about them is what is missing:
| Scope | What it opens |
|---|---|
signals:read | List and read signals, their scores and their reasoning. |
accounts:read | List companies and what has been found about them. |
listeners:read | List listeners and read their configuration. Cannot change one. |
listeners:analyse | Turn a URL into a listener plan. Creates nothing. |
pipeline:read | Read the pipeline board. |
pipeline:write | Move a deal between stages. |
collections:write | Organise signals into lists. |
export:read | Pull data out. |
There is no listeners:write, no competitors:write and no billing:*, because those are admin actions and a key is never an admin. The scope vocabulary has no way to express "admin", which means the property is not a rule somebody has to remember to apply. Adding such a scope would be the thing that quietly undoes it.
So a key cannot create or run a listener, delete a competitor, change billing, or mint another key. Read that list again, because each item is a specific failure mode closed off. It cannot start recurring spend by scheduling runs. It cannot destroy your competitor watchlist. It cannot touch your subscription. And it cannot mint another key, which means it cannot clone itself into something stronger.
That last one is where leaked-key incidents usually turn into takeovers. The exploit that matters is rarely the leak. It is privilege escalation: using a narrow credential to obtain a broader one. A key that cannot create keys cannot escalate, and the incident stays bounded at whatever the integration needed.
The one scope that spends money
Honesty requires naming the exception. listeners:analyse is the single scope whose call costs real money at the moment it is made, because it crawls a site and runs generation to produce a plan. It ships with a burst limit and a monthly quota, and neither is optional.
It does not breach the member rule: the endpoint returns a document, and creating a listener from that document remains an admin action no key can reach. But a reader auditing this design deserves to know that one scope has a cost attached rather than discovering it on an invoice.
Enforcement is server-side, not screen-side
The interface hides what a member cannot do. The API refuses it regardless. The rule is enforced where the request context is built, not by convention in each route handler, which is the difference between a property and a habit.
A leaked key presented to an admin endpoint gets the same answer as a revoked key: no.
Revoke, do not delete. Rotate, do not punish
Revocation policy is part of the blast radius too. Revoked keys are marked rather than deleted, so the audit trail keeps a record of what existed and when it stopped being trusted. And a revoked key does not occupy a slot, so a workspace that has rotated twice can still mint another.
That second half sounds like a billing detail. It is a security decision wearing a billing costume. If rotating a key costs you one of your limited slots, or requires a support ticket, you will postpone the rotation. You will tell yourself you will do it next week, and next week the leaked key is still live. Security design that makes the safe action expensive gets the safe action skipped.
For reference, the allowance is two keys on Go, five on Growth, ten on Pro and unlimited on Enterprise, counting live keys only.
The same philosophy reaches your AI assistant
There is one more place this shows up, aimed at the newest kind of credential holder. Open Pulse is an MCP server, so an assistant can read your workspace once connected, and the same member-never-admin logic applies at the tool level. There is deliberately no create, update or delete for listeners over MCP.
An agent that can create listeners can commit you to recurring spend on every schedule tick. So the tools expose reading, searching, rating and triggering runs, and nothing more. It is the same sentence restated for a different client: the credential is allowed to look, not to act destructively. The read-only MCP server post has the longer version.
The checklist, for anyone minting keys this week
Whether or not you use Open Pulse, the shape of a safe key is the same. Mint it with the minimum scopes the integration needs, not the scopes your account has. Store only the hash. Show it once. Log the prefix, never the secret. Default to deny on every route. Make sure the key cannot create another key. Make revocation instant and rotation free.
These are not exciting decisions. Nobody demos a permission scope. But they are the decisions that decide, months later and without warning, whether a leaked key is a bad afternoon or a breach notification.
References
- OWASP Foundation, API Security Top 10 2023. Source for Broken Authentication at API2 and Broken Function Level Authorization at API5, and for both quoted descriptions.
- Open Pulse, "Your AI agent can read the workspace, but it can never create a listener", on the same rule applied to MCP tools.
- Open Pulse, "Wire buying signals into your own stack", for the working integration and the scope table as the server enforces it.
See it on your own market
Paste your website, review the plan it proposes, and read what comes back tomorrow morning.
Questions about anything here? Email support@openpulse.cloud.