Skip to navigation

Authentication

Bearer API keys identify the caller and select the environment.

Every request carries an API key as a bearer token in the Authorization header:

curl https://api.spreadspace.app/api/borrowers \
-H "Authorization: Bearer ss_live_..."

Requests without a valid key return 401.

Key prefixes

One base URL serves both environments. The key prefix, not the URL, selects where the request lands:

  • ss_live_… operates on live workspace data.
  • ss_test_… routes to the isolated developer sandbox (test mode), where uploads return premade sample extractions at no charge. Start here: sandbox calls never touch live borrower data.

Create, rotate, and revoke keys in the SpreadSpace dashboard: Settings → Live API for live keys, Settings → Sandbox API for test keys.

Scopes

Keys carry an explicit grant list checked on every request. There is no implicit access: an API-key request to an endpoint is denied by default unless the key holds the required scope. Missing scope → 403 with type insufficient_scope (the message names the scope).

Format: resource:action (for example documents:read), resource:* for all actions on a resource, or * for everything (avoid in production).

ResourceActionsNotes
documentsread, write, deletewrite = upload
loansread, write, delete
borrowersread, write, deletedelete cascades to the borrower’s loans, documents and extractions
extractionsread, write, edit, exportexport = single-document and bulk exports; write annotates an extraction (add-backs, tags, marks); edit corrects extracted figures (row edits). write and edit are granted to an embed session only when the mint names them and the key holds them
chart_of_accountsread, writeread = the workspace’s chart of accounts and its export map, which extractions:read also reads; write = save the export map
spreadsread, writeread = a loan’s attribute snapshots and its persisted spread envelope; write is granted to an embed session when the mint names it, and the key must hold it to grant it
memosread, renderread = list a loan’s credit memos and fetch a rendered PDF link; render = request a render
jobsreadprocessing job status
intakewriteserver-mediated document intake; auto-provisions a borrower and loan per submission
webhooksread, writeread = list, get, deliveries; write = create, update, rotate, delete, replay
embedwritemint embed-session tokens; requires the key to carry an allowed-origins list
sandboxresetwipe and reseed the test workspace; test-mode keys only

Two identity reads need no scope: GET /api/me and GET /api/organizations only describe the caller.

Each endpoint’s reference page lists its required scope.

Scopes can be added or removed on an existing key without rotating it (dashboard → key → scopes). The change takes effect on the next request. Remove a scope to withdraw a specific permission while retaining the key’s other permissions.

Key lifecycle

A key can carry an expiry (expires_at, set when the key is created or changed later in the dashboard). Past it the key answers 401 exactly as a revoked key does. Rotating a key issues a replacement and keeps the old key working for a grace period, 24 hours unless the rotation names another value between 0 and 720 hours, so a deploy can switch credentials without a gap.

A key can also carry an allowlist of CIDR ranges. A request from any other address answers 403 with type forbidden and no further detail, and the attempt is recorded in the workspace’s audit log.

Keeping keys secret

API keys are server-side credentials. Never ship one in browser code, a mobile app, or a public repository: anyone holding the key can act as your integration. If a key leaks, revoke it in the dashboard and issue a replacement.

For rendering SpreadSpace inside your own app, use the Embed flow instead: the browser gets a short-lived ss_embed_… session token minted by your server, and your API key stays server-side.