Authentication & API keys
Scoped, revocable bearer keys — mint, rotate, revoke, and the scope model
Every API request authenticates with a bearer key:
curl -s $BASE_URL/v1/me -H "Authorization: Bearer ds_live_..."GET /v1/me answers who you are: your organization and role, the key's scopes, the
Blueprint a call reads by default, your plan and balance. It also carries model_allowance, the organization's model-token
allowance for report chat, onboarding and the parse surfaces (a paid research run never
counts against it):
| Field | Meaning |
|---|---|
input_tokens, output_tokens | each {used, limit}; the two are capped separately, and output includes reasoning tokens |
reset, resets_on | monthly (with the date it resets) or lifetime (resets_on: null) |
near | either cap is at least 80% used |
exhausted | either cap is reached: counted calls are refused 429 model_allowance_exhausted (Retry-After is the reset), or, for a lifetime allowance, 402 plan_upgrade_required with param: "model_allowance" |
model_allowance is null when it could not be read (unknown, never zero), and absent
when it does not apply.
It also carries subscription, so you know when money moves before it moves:
| Field | Meaning |
|---|---|
status | none (no plan), past_due (a charge was declined; ask your human to update the card, unless cancel_at_period_end is true: a cancelled plan's declined charge is not retried), ended (a trial cancelled before its first charge, now over: nothing was charged and access has ended), trialing (in the free trial, or just past it with the first charge not paid yet), or active |
cancel_at_period_end, access_through | a cancellation is scheduled, and the date access runs to (the next renewal, or the end of a cancelled period or trial) |
trial.eligible, trial.offer_days | this organization may start a free trial, and how long it would be. Checkout takes a card, so it needs a person: tell your human and hand them {app_url}/pricing. Eligibility depends on who is asking: the owner of another organization that already had a trial is not offered one. null means it could not be determined, so don't offer one |
trial.ends_on, trial.days_left | the day the trial ends (and the first charge is made), and the days left before it, counted for you |
trial.first_charge_on, trial.first_charge_cents | when the first charge is made and how much, while it is still owed. Both are null once the trial is cancelled, because a cancelled trial is never charged, unless that charge is already in flight |
trial.conversion_pending | the trial has ended and the first charge has not gone out yet. Cancelling now still stops it |
trial.conversion_submitted | the first charge is in flight. It can no longer be stopped; cancelling stops only the charges after it |
trial.conversion_failed | the first charge was declined |
trial.ended_without_charge | the trial was cancelled and ended before its first charge: nothing was charged |
Cancel and resume. POST /v1/subscription/cancel cancels at the end of the period; nothing
new is charged after it (a free trial is never charged, a declined charge is not retried), except a
first charge already in flight (trial.conversion_submitted), which goes ahead.
POST /v1/subscription/resume undoes it while the period runs; in a trial the original
first-charge date stands, and a past-due account gets one immediate charge attempt (retry).
Both answer with the same subscription block, are safe to repeat, and honour
Idempotency-Key. Cancel needs billing:cancel (it cannot start a charge) and resume
billing:write (it can lead to one), both with the admin role. Only do either when your
human asked. 409 subscription_ended means it's over: hand your human {app_url}/pricing.
The trial's dates are fixed when it starts, and are UTC calendar dates. subscription is null
in open mode, or when it could not be read (unknown, never "none").
API keys authenticate the /v1 API only. The legacy /api/* routes are
the web app's private, cookie-authenticated surface — a key used there gets a
typed 403 pointing back here.
Keys are minted at Settings → API keys (or via
POST /v1/keys with an existing admin key). The secret is shown once;
DeepSieve stores only a hash.
Scopes
A key holds resource:action scopes; a request needs the route's scope or it
gets 403 insufficient_scope. Presets:
| Preset | Scopes | Use for |
|---|---|---|
| Agent (default) | runs:*, data:read, data:write (undo a change, merge rows), reports:*, export:read, blueprint:read, blueprint:create (create-only), blueprint:propose (ask-only), knowledge:write (teach runs what you know about the field), webhooks:* | coding assistants, agents — can start runs (prepaid credits; cap with a per-key budget), manage reports, and create NEW Blueprints. Changing the active Blueprint's shape is human-only regardless of scopes; knowledge:write adds domain knowledge, which changes no table or stored value |
| Read-only | all :read scopes | dashboards, sync jobs |
| CLI | runs:read, runs:write, data:read, data:write, export:read, reports:read, blueprint:read, members:read, billing:cancel, knowledge:write | what deepsieve login asks for. It cannot create a Blueprint; deepsieve login --preset agent asks for the Agent preset instead |
| Full | everything except platform admin | trusted backends |
Custom scope lists: POST /v1/keys {"name": "...", "scopes": ["runs:read", ...]}.
A key can never exceed its minter, then or now: it acts with the lower of the
role it was created with and its creator's current role, it stops working if
its creator leaves the organization, and a key can't mint or rotate its way to
scopes it doesn't hold.
data:write has a role floor. It changes stored data (undoing a change,
merging rows), so it only works for callers whose current role is member or
above. A viewer's key or MCP connection is refused with 403 insufficient_role
whatever scopes it carries, and a viewer is never issued data:write in the
first place. Demote a member to viewer and their keys lose it on the next
request.
knowledge:write has the same member floor, and requires data:read
(a conflict refusal returns the entries it conflicts with). It writes a
Blueprint's domain knowledge, which changes only what future runs are told,
never a stored value or the Blueprint's shape, so unlike editing the
Blueprint it is open to agents. The CLI preset and an MCP browser-login
connection carry it too. A deepsieve login from before it was added does not:
sign in again to write knowledge from the CLI.
Some scopes require another, and we add it for you. blueprint:propose
requires data:read: the propose response contains the Blueprint's full spec,
so it asks the same rights as reading that spec directly. Request
blueprint:propose on its own and the key is created with data:read as well,
rather than failing at its first call. The scopes array in the response is
what was actually granted — compare it with what you asked for to see any
addition. Nothing unrelated to your request is ever added.
Rotate and revoke
curl -s -X POST $BASE_URL/v1/keys/{key_id}/rotate -H "Authorization: Bearer ds_live_..."Rotation returns a new secret; the old one keeps working for 24 hours so you
can redeploy without downtime. DELETE /v1/keys/{key_id} revokes immediately.
Revoking a key also switches off any re-research auto-approve it turned on, and
suspending it pauses that auto-approve until it is unsuspended (see
Runs).
Optional per-key limits
At mint time you can set expiry, a Blueprint pin, and budgets — in the
create-key dialog (expiry + budgets) or via POST /v1/keys (expires_at,
blueprint_id, budget_runs, budget_cents). workspace_id is the old name
for blueprint_id and still works until its sunset, with a warning. A key at its budget gets
402 budget_exceeded — the agent should stop and alert a human. Combined with
prepaid billing, the worst an agent can do is exhaust its budget and stop; a
key can never generate an invoice.
Browser login for MCP (OAuth 2.1)
When you add DeepSieve to a coding agent over --transport http, you don't
paste a key — the client runs an OAuth 2.1 browser flow. The consent screen is
DeepSieve's own login (via our identity provider). The session it grants
carries runs:read, runs:write, data:read, data:write, export:read,
reports:read, reports:write, blueprint:read, blueprint:create,
blueprint:propose, knowledge:write, webhooks:read, members:read and
billing:cancel, and your role still applies on top: data:write and
knowledge:write need the member role, and billing:cancel (which only stops
spend) needs admin. It never carries key-management scopes or any scope that
can start a charge — a browser session is a person, not a service
credential. Tokens are short-lived and refresh silently; revoke a client's
access from your account without affecting your API keys. Discovery follows
RFC 9728: the endpoint advertises itself at
/.well-known/oauth-protected-resource. Use an API key instead for CI,
containers, and server-to-server, where no browser exists.