# What a scoped API key is actually protecting

> Every tool on netweft's MCP surface reads stored rows, so narrowing a key removes no capability. It removes a subject, and the one that matters is billing.

Source: https://netweft.com/blog/what-a-scoped-key-protects
Updated: 2026-10-15

**Because every netweft MCP tool is read-only over rows already stored — none of them writes, crawls or spends a credit — a scope cannot withhold a capability, only a subject. The useful one is billing: it is how a key handed to an outside integration is stopped from reading the workspace's balance and plan.**

When we added scopes to API keys, the first draft had scopes that mapped to
verbs: read, write, admin. That draft was deleted, because it described a
product netweft does not have.

## Every tool reads

The [MCP surface](/mcp) exposes twenty-five tools across six scopes. Not one of
them writes a row, starts a crawl, or spends a credit. They read what netweft
has already stored: audits and their issues, saved keywords, rank trackers and
their results, project context, the credit balance.

That is a deliberate constraint rather than an accident of scheduling. An agent
holding a key should not be able to move the workspace's balance, because the
whole reason to hand an agent a key is that you are not watching what it does
with it. A surface where the worst case is an expensive afternoon is a surface
nobody sensible connects.

So `write` had nothing to gate, and `admin` had nothing to describe.

## A scope withholds a subject

What is left is the question of *what* a key may read, which turns out to be
the question people actually have. The scopes are named after tool families
rather than HTTP route segments — `rank_tracking`, not `rank-tracking` —
because the tools are what a key holder sees.

The one that earns its place is `billing`. A key given to a reporting script,
a client's own dashboard, or somebody else's agent has no business reading what
the workspace pays and what it has left. Withholding `billing` and granting the
other five produces a key that can answer every question about the *work* and
none about the *account*.

## Two details that took longer than the feature

**A key with no scopes named gets all of them.** The alternative — defaulting
to none — produces a key that silently does nothing and a support conversation
about why. Defaulting to everything is the behaviour people expect from a key
they did not narrow.

**An unrecognised scope is dropped, not honoured.** A row written by a newer
deploy can carry a scope this build has never heard of. Dropping it is the safe
direction: the tool it would unlock does not exist here, so keeping the string
could only widen a set that nothing can act on.

The rest of what a key is — shown once, stored as a SHA-256 hash, compared in
constant time, revoked rather than deleted — is on the
[security page](/security).
