Skip to content

Authentication

Two identities, and they are not interchangeable.

The API key

This is the one for integrations. It lives in the database, carries no default expiry, and travels in the Authorization: Bearer header.

sh
curl "$SPAVIK_BASE_URL/v1/models" -H "Authorization: Bearer sk-spv-api-..."

The API also accepts it in X-API-Key. Both forms are equivalent; the libraries use the first.

PrefixWhat it isCan predict
sk-spv-api-API keyyes
sk-spv-admin-Admin keyno

Keys issued before September 2026 started with ssk-. They are no longer recognised: create a new key from your workspace.

An admin key cannot predict: it answers ADMIN_KEY_CANNOT_PREDICT. And an API key cannot mint others.

The web session

This is the one for interfaces. A magic link goes out by email, is exchanged for a token pair, and the access token travels in Authorization: Bearer.

sh
curl -X POST $SPAVIK_BASE_URL/v1/auth/magic-link/request \
  -H 'Content-Type: application/json' -d '{"email":"you@example.com"}'

The link and the code go out by email only, never in the response. That is what stops a third party who can trigger the send from opening the session.

The access token is short-lived. A long-running application must renew it along the way, with POST /v1/auth/refresh, without asking for a new link.

The refresh token is single use

Replaying it signals that a copy is circulating: the API then revokes the whole token family for the account. Serialise your renewals, do not fire them in parallel from several requests.

The separation is by route, not by header

An API key opens the engine and usage routes. It opens neither the account routes, which require a web session, nor the administration routes, which require an admin key. The API says so plainly rather than leaving you to guess.

What you presentWhereAnswer
A valid API key/v1/models, /v1/forecast, /v1/usage200, it works
A valid API key/v1/auth/me and account routes401 AUTH_REQUIRED, a web session is required
A valid API key/v1/api-keys, /v1/webhooks403 ADMIN_KEY_REQUIRED
An unknown keyanywhere401 API_KEY_INVALID
An expired access tokenanywhere401 TOKEN_INVALID

Rotation without downtime

POST /v1/api-keys/{keyId}/rotate creates a twin key and gives the old one an end date, seven days by default. Both work during the migration. grace_days: 0 revokes the old one immediately, for a compromised secret.

Part of this documentation is generated from the OpenAPI contract.