The API key is a member, never an admin — Open Pulse
The security of an API key was never really about the randomness of the string. It was decided the moment the key was issued, by what the key was allowed to do. Why Open Pulse keys carry member scopes and only member scopes, what deny-by-default actually enforces, and why a key that cannot mint another key is the difference between a bad afternoon and a breach notification.
What this page answers
What does this post cover?
The security of an API key was never really about the randomness of the string. It was decided the moment the key was issued, by what the key was allowed to do. Why Open Pulse keys carry member scopes and only member scopes, what deny-by-default actually enforces, and why a key that cannot mint another key is the difference between a bad afternoon and a breach notification.
Who is it for?
Engineers issuing or integrating against API keys, and anyone writing an API key policy
What scopes should an API key have?
The minimum the integration needs, chosen at issuance rather than inherited from whoever created it. A key that pulls signals into a dashboard needs read access and nothing else. The test is to ask what the key could do if it appeared in a public repository tomorrow, and to keep narrowing until that answer is boring.
Should API keys be stored hashed, like passwords?
Yes. Store a hash, show the full secret exactly once at creation, and log only a prefix. The reasoning is the same as for passwords with one difference in your favour: nobody has to remember an API key, so there is no usability cost to making it unrecoverable. A lost key is replaced, not recovered.
Why should an API key be unable to create another API key?
Because that is the step that turns a leak into a takeover. Most serious incidents are not the original leak but privilege escalation afterwards: using a narrow credential to mint a broader one that outlives the revocation of the first. A key that cannot create keys cannot escalate, so revoking it actually ends the incident.
At a glance
- Published
- 2026-10-03
- Written for
- Engineers issuing or integrating against API keys, and anyone writing an API key policy
- Category
- informative
- Reading time
- 8 minutes
- Keywords
- api key security best practices, least privilege api key, owasp api security top 10, api key scopes, api key rotation
Overview
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.
Frequently asked questions
What scopes should an API key have?
The minimum the integration needs, chosen at issuance rather than inherited from whoever created it. A key that pulls signals into a dashboard needs read access and nothing else. The test is to ask what the key could do if it appeared in a public repository tomorrow, and to keep narrowing until that answer is boring.
Should API keys be stored hashed, like passwords?
Yes. Store a hash, show the full secret exactly once at creation, and log only a prefix. The reasoning is the same as for passwords with one difference in your favour: nobody has to remember an API key, so there is no usability cost to making it unrecoverable. A lost key is replaced, not recovered.
Why should an API key be unable to create another API key?
Because that is the step that turns a leak into a takeover. Most serious incidents are not the original leak but privilege escalation afterwards: using a narrow credential to mint a broader one that outlives the revocation of the first. A key that cannot create keys cannot escalate, so revoking it actually ends the incident.
Does revoking an API key use up my allowance?
It should not, and here it does not: revoked keys are marked rather than deleted and only live keys count against the limit. This matters more than it sounds. If rotation costs a slot, people postpone rotation, and the whole point of cheap rotation is that the safe action has to be the easy one.
Is deny-by-default different from just listing the admin routes?
Materially, yes. An exclusion list fails open: add a route, forget to list it, and it is reachable. Deny-by-default fails closed: add a route, forget to declare a scope, and nobody can reach it until somebody notices. The second failure is an annoyed engineer. The first is an incident.
Next
- Start the free trial — Creates a workspace. A card is taken up front and nothing is charged during the trial.
- Pricing — What it costs after the trial.
- All posts — The comparisons and the guides, grouped.