Authentication v1

Overview

SparkVault supports two authentication methods:

Method Header Best For
API Key X-API-Key Server-to-server integrations, automation, CI/CD pipelines
JWT Token Authorization: Bearer User-facing applications, browser sessions
Which should I use?

For most integrations, API keys are the simplest choice. Use JWT tokens only if you're building a user-facing application that needs to manage individual user sessions.

API Key Authentication

API keys provide simple, persistent authentication for server-side integrations. Create an API key from the API Keys page and include it in the X-API-Key header.

bash Using an API key
curl https://api.sparkvault.com/v1/sparks \
  -H "X-API-Key: sv_live_abc123xyz789..."

API Key Format

API keys follow a consistent format:

  • sv_live_...: Production keys (live data)
Keep API keys secret

API keys grant full access to your account. Never expose them in client-side code, public repositories, or logs.

If a key is compromised, revoke it immediately from the API Keys page.

Example: Creating a Spark with an API key

bash
curl -X POST https://api.sparkvault.com/v1/sparks \
  -H "X-API-Key: sv_live_abc123xyz789..." \
  -H "Content-Type: application/json" \
  -d '{
    "payload": "super_secret_password_123"
  }'

Managing API Keys Programmatically

API keys can be managed in the app at app.sparkvault.com/api/keys, or programmatically with the endpoints below. All three endpoints require authentication (JWT or API key). Creating and revoking keys additionally require an admin or owner role. Non-admin users receive 403 FORBIDDEN.

POST /v1/api-keys

Create a new API key. Requires an admin or owner role. The full key string is returned once and is never retrievable again.

Request Body

ParameterTypeRequiredDescription
name string Required Display name identifying the key's purpose (max 100 characters).
expires_in_days integer Optional Days until the key expires (1-3650). Omit for a non-expiring key.

Response Fields

FieldTypeDescription
api_key_id string Unique key identifier (key_...). Use this id to revoke the key.
api_key string The full API key (sv_live_...). Shown only in this response. Store it securely.
key_preview string Masked preview for display (e.g. sv_live_ab...wxyz).
name string The key's display name.
created_at integer Creation time (Unix epoch seconds).
expires_at integer Expiration time (Unix epoch seconds), or null if the key does not expire.
warning string Reminder that the key will not be shown again.

Example

Request
bash
curl -X POST https://api.sparkvault.com/v1/api-keys \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CI/CD pipeline",
    "expires_in_days": 90
  }'
Response
json
{
  "data": {
    "api_key_id": "key_abc123...",
    "api_key": "sv_live_abc123xyz789...",
    "key_preview": "sv_live_ab...z789",
    "name": "CI/CD pipeline",
    "created_at": 1783036800,
    "expires_at": 1790812800,
    "warning": "Store this API key securely. It will not be shown again."
  }
}
GET /v1/api-keys

List the account's API keys with cursor-based pagination. Key strings are never returned, only masked previews.

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum keys per page (1-100).Default: 50
cursor string Optional Pagination cursor from a previous response's next_cursor.

Response Fields

FieldTypeDescription
api_keys array Key objects: api_key_id, name, key_preview, status (active | expired | revoked), created_at, last_used_at, expires_at, revoked_at.
count integer Number of keys in this page.
has_more boolean Whether more pages are available.
next_cursor string Cursor for the next page. Present only when has_more is true.
active integer Number of active keys in this page.
DELETE /v1/api-keys/:id

Revoke an API key by its api_key_id. Requires an admin or owner role. Idempotent: revoking an already-revoked key succeeds and returns its original revocation time.

Response Fields

FieldTypeDescription
api_key_id string The revoked key's identifier.
name string The key's display name. Returned only on first revocation. When the key was already revoked, the response omits name and includes a message field instead.
status string Always revoked.
revoked_at integer Revocation time (Unix epoch seconds).

JWT Token Authentication

JWT tokens are used for user session management in browser-based applications. SparkVault uses the Identity Product as an OIDC provider for user authentication. After successful authentication via Identity, you'll receive SparkVault session tokens.

bash Using a JWT token
curl https://api.sparkvault.com/v1/sparks \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Token Types

Type Lifetime Purpose
Access Token 1 hour Short-lived token for API requests
Refresh Token 30 days Single-use token to obtain new access tokens (rotated on every refresh)

User Authentication Flow

SparkVault uses an OIDC (OpenID Connect) flow with the Identity Product for user authentication. This provides passwordless login via passkeys, magic links, social login, and more.

Flow Overview

  1. Initiate Login: Redirect user to Identity Product with PKCE challenge
  2. User Authenticates: Via passkey, magic link, or social login
  3. Callback: Identity Product redirects back with authorization code
  4. Token Exchange: Exchange code for SparkVault session tokens
  5. Registration (New Users): Complete profile setup if needed
Identity Product

For detailed OIDC integration instructions, see the Identity Product documentation. The SparkVault web app uses Identity Product for all user authentication.

Session Endpoints

Browser and native transports

Browser dashboard requests send X-SV-Client: web with credentials enabled. SparkVault stores the refresh credential only in the host-locked __Host-sv_refresh HttpOnly cookie and omits it from JSON. Native and server clients omit the browser marker, receive refresh_token in JSON, and send it in the refresh/logout body. Browser access tokens are memory-only.

POST /v1/auth/identity/token

Exchange an OIDC authorization code from Identity Product for SparkVault session tokens. This is called after the user completes authentication via Identity Product.

Request Body

ParameterTypeRequiredDescription
code string Required Authorization code from Identity Product callback (max 512 characters).
code_verifier string Required PKCE code verifier that must match the code_challenge sent to Identity Product. 43-128 characters, per RFC 7636.

Response (Existing User)

FieldTypeDescription
access_token string JWT access token (1 hour lifetime)
refresh_token string Native/server only: JWT refresh token (30 day lifetime). Browser responses set the HttpOnly cookie instead.
user object User profile information
account object Account information

Response (New User)

FieldTypeDescription
access_token string Identity-only JWT (limited access)
refresh_token string Native/server only: identity-only refresh token. Browser responses set the HttpOnly cookie instead.
user null null indicates registration required
account null null indicates registration required

Example

Native/server request
bash
curl -X POST https://api.sparkvault.com/v1/auth/identity/token \
  -H "Content-Type: application/json" \
  -d '{
    "code": "auth_code_from_identity_product",
    "code_verifier": "your_pkce_code_verifier"
  }'
Native/server response
json
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
    "user": {
      "user_id": "usr_abc123...",
      "email": "user@example.com",
      "name": "John Doe",
      "role": "admin"
    },
    "account": {
      "account_id": "acc_xyz789...",
      "organization_name": "Acme Corp",
      "status": "active"
    }
  }
}
POST /v1/auth/identity/verify

Directly verify an Identity Product JWT token without using the OIDC redirect flow. Useful for XHR-based authentication where redirect flows are not practical.

Request Body

ParameterTypeRequiredDescription
token string Required JWT token received from Identity Product verification

Response

FieldTypeDescription
access_token string JWT access token (1 hour lifetime)
refresh_token string Native/server only: JWT refresh token (30 day lifetime). Browser responses set the HttpOnly cookie instead.
user object User profile (null if registration required)
account object Account information (null if registration required)

Example

Native/server request
bash
curl -X POST https://api.sparkvault.com/v1/auth/identity/verify \
  -H "Content-Type: application/json" \
  -d '{"token": "eyJhbGciOiJFZDI1NTE5..."}'
Native/server response
json
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
    "user": {
      "user_id": "usr_abc123...",
      "email": "user@example.com",
      "name": "John Doe",
      "role": "admin"
    },
    "account": {
      "account_id": "acc_xyz789...",
      "organization_name": "Acme Corp",
      "status": "active"
    }
  }
}
When to use this endpoint

Use this endpoint when you're using the Identity Product SDK in XHR mode (without redirects). The SDK will return a JWT token directly after verification, which you can exchange for SparkVault session tokens using this endpoint.

POST /v1/auth/complete-signup

Complete registration for new users. Requires an identity-only JWT (from token exchange with user: null). Creates the account and upgrades to full session tokens.

Request Body

ParameterTypeRequiredDescription
organization_name string Required Name of the organization/company (1-255 characters)
full_name string Optional User's display name (up to 255 characters)

Response

FieldTypeDescription
access_token string Full JWT access token
refresh_token string Native/server only: full JWT refresh token. Browser responses set the HttpOnly cookie instead.
user object Created user profile
account object Created account object
Authentication Required

This endpoint requires an identity-only JWT in the Authorization header. The JWT proves email ownership via the Identity Product verification.

Claimed Domains

Signup returns 403 Forbidden if the email's domain is already claimed by another organization. The user must be invited by that organization's admin instead.

POST /v1/auth/refresh

Rotate a refresh session and obtain a new access token. Browser callers send an empty body and authenticate with the HttpOnly cookie; native/server callers send the body token.

Request Body

ParameterTypeRequiredDescription
refresh_token string Optional Required for native/server callers. Browser dashboard callers use the HttpOnly cookie and must omit this field.

Response

FieldTypeDescription
access_token string New JWT access token
refresh_token string Native/server only: new rotated refresh token. Replace the prior value atomically.
token_type string Always "Bearer"
expires_in integer Access-token lifetime in seconds
session_type string Authoritative session shape: full or identity_only
user object|null Current user for a full session; null for identity-only registration
account object|null Current account for a full session; null for identity-only registration

Example

Native/server request
bash
curl -X POST https://api.sparkvault.com/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "eyJhbGciOiJSUzI1NiIs..."}'
Native/server response
json
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "session_type": "full",
    "user": { "user_id": "usr_abc123..." },
    "account": { "account_id": "acc_xyz789..." }
  }
}
Refresh Tokens Rotate on Every Use

Refresh tokens are one-time-use. Every successful call advances the session family's generation. Native/server clients must atomically replace the body token; the browser receives a rotated cookie automatically. Reusing a superseded generation returns 401 TOKEN_REPLAY and revokes that session family, so the client must sign in again.

One narrow exception makes a lost response recoverable: the token a rotation supersedes stays exchangeable for 30 seconds from that rotation, so a call that timed out or dropped its response can be retried with the same token and will be issued the next generation. Retrying repeatedly inside those 30 seconds is safe; the window is measured from the rotation that superseded the token and is never extended by these retries. After it closes, the same token is treated as replay.

A session family is also bound, when it is issued, to the delivery path it was issued on. A credential issued in the JSON body must always be presented in the body, and a browser cookie session must always refresh from the cookie. Presenting one on the other path returns 401 without revoking anything. Never copy a credential out of a browser session into a script or server job: give that job its own session, or the two holders will rotate independently and revoke each other.

If a refresh credential is ever copied or leaked, destroy it by POSTing it to /v1/auth/logout. Revocation accepts the credential from any client holding it, on either delivery path, and kills the whole session family immediately.

POST /v1/auth/logout

Revoke the session authorized by the supplied refresh credential. No access-token bearer is required. Returns 204 No Content on success.

Request Body

ParameterTypeRequiredDescription
refresh_token string Optional Required for native/server callers. Browser dashboard callers use the HttpOnly cookie and send an empty body.

Example

Request
bash
curl -X POST https://api.sparkvault.com/v1/auth/logout \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "eyJhbGciOiJSUzI1NiIs..."}'
Response
text
(204 No Content)
POST /v1/auth/viewer-token

Generate a short-lived viewer token for cross-domain direct-access viewing on x.sv. Requires authentication. Takes no request body.

Response

FieldTypeDescription
viewer_token string Short-lived JWT for the x.sv viewer (5 minute lifetime)
expires_in integer Token lifetime in seconds, always 300

Vault Access Tokens (VAT)

Vault Access Tokens are special, short-lived tokens required for reading and writing data in encrypted vaults. They are obtained by "unsealing" a vault with the key you hold: its Vault Master Key (VMK), or a DVAK token issued for it.

Property Value
Header X-Vault-Access-Token
Lifetime 1 hour (default), up to 24 hours
Scope Single vault only
Revocation Automatic on expiry, or manual via seal operation
POST /v1/vaults/:id/unseal

Unseal a vault with the Vault Master Key or a DVAK token to obtain a Vault Access Token. Send exactly one of them.

Request Body

ParameterTypeRequiredDescription
vmk string Optional The Vault Master Key. A DVAK token is also accepted here, so integrations written before the dvak_token field keep working unchanged.
dvak_token string Optional A DVAK token (Delegated Vault Access Key, prefix dvak_) issued for this vault, exactly as the create response returned it. The resulting VAT inherits the DVAK's access level.
ttl_seconds integer Optional VAT lifetime in seconds (1-86400).Default: 3600
Send exactly one key

Supply vmk or dvak_token — never both, and never neither. A request carrying two keys, or none, is rejected with 400 VALIDATION_ERROR.

Response Fields

FieldTypeDescription
vat string The Vault Access Token (vat_...). Send it in the X-Vault-Access-Token header.
vault_id string The unsealed vault's id.
issued_at integer Issue time (Unix epoch seconds).
expires_at integer Expiration time (Unix epoch seconds).
ttl_seconds integer Effective VAT lifetime in seconds.
warning string Reminder to store the VAT securely.
bash Using a Vault Access Token
# First, unseal the vault to get a VAT
curl -X POST https://api.sparkvault.com/v1/vaults/vlt_abc123/unseal \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"vmk": "YOUR_VAULT_MASTER_KEY"}'

# Holding a delegated key instead? Send it as dvak_token
curl -X POST https://api.sparkvault.com/v1/vaults/vlt_abc123/unseal \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"dvak_token": "dvak_kQ8n...w2Xz"}'

# Then use the VAT for ingot operations
curl https://api.sparkvault.com/v1/vaults/vlt_abc123/ingots \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "X-Vault-Access-Token: vat_xyz789..."
VAT Best Practices
  • Obtain a VAT only when you need to access vault contents
  • Store VATs securely in memory, never persist to disk
  • VATs are vault-specific. Each vault requires its own VAT.
  • Consider sealing vaults when done to immediately invalidate VATs

Authentication Errors

Error Responses

StatusCodeDescription
400 VALIDATION_ERROR Missing or invalid request parameters
401 AUTHENTICATION_ERROR Invalid credentials, expired access token, invalid or revoked refresh token, or suspended account
401 TOKEN_REPLAY A superseded refresh token was presented outside the 30-second grace window, or one more than a generation old. The session family is revoked; sign in again
403 FORBIDDEN Insufficient permissions: e.g. identity-only token on a protected endpoint (complete registration first), or admin role required
429 RATE_LIMIT_EXCEEDED Too many requests
503 TOKEN_GENERATION_FAILED Signing infrastructure is temporarily unavailable; retry without discarding a valid refresh credential

Security Best Practices

API Keys

  • Generate separate API keys for each integration or service
  • Use descriptive names to identify key purpose
  • Rotate keys periodically (every 90 days recommended)
  • Revoke keys immediately if compromised
  • Never commit keys to version control. Use environment variables.

JWT Tokens

  • Dashboard browsers keep access tokens in memory and refresh only through the host-locked HttpOnly cookie
  • Native and server clients keep refresh credentials in their platform secure store, never localStorage
  • Implement automatic token refresh before expiry
  • Native and server clients atomically persist each rotated response token before using it again
  • Clear the browser access token immediately when logout starts; the API revokes and clears the refresh cookie
  • Retry transient 503 responses; redirect to login only when refresh returns an authentication failure

PKCE Security

  • Always use S256 code challenge method (never plain)
  • Generate cryptographically random code verifiers (32+ bytes)
  • Store PKCE parameters in sessionStorage (not localStorage)
  • Validate the state parameter to prevent CSRF attacks

General

  • Always use HTTPS. Never send credentials over plain HTTP.
  • Implement proper error handling for auth failures
  • Log authentication events for security monitoring
  • Use passkeys where possible for phishing resistance