Authentication & API keys

Scoped, revocable bearer keys — mint, rotate, revoke, and the scope model

Every API request authenticates with a bearer key:

bash
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):

FieldMeaning
input_tokens, output_tokenseach {used, limit}; the two are capped separately, and output includes reasoning tokens
reset, resets_onmonthly (with the date it resets) or lifetime (resets_on: null)
neareither cap is at least 80% used
exhaustedeither 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:

FieldMeaning
statusnone (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_througha 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_daysthis 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_leftthe 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_centswhen 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_pendingthe trial has ended and the first charge has not gone out yet. Cancelling now still stops it
trial.conversion_submittedthe first charge is in flight. It can no longer be stopped; cancelling stops only the charges after it
trial.conversion_failedthe first charge was declined
trial.ended_without_chargethe 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:

PresetScopesUse 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-onlyall :read scopesdashboards, sync jobs
CLIruns:read, runs:write, data:read, data:write, export:read, reports:read, blueprint:read, members:read, billing:cancel, knowledge:writewhat deepsieve login asks for. It cannot create a Blueprint; deepsieve login --preset agent asks for the Agent preset instead
Fulleverything except platform admintrusted 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

bash
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.

Authentication & API keys — DeepSieve API