Authentication

Cloven supports two distinct authentication paths. They cover every deployment shape we have seen: humans clicking through a console, server-side agents pulling fresh context, and fully autonomous agents that no human ever logs into.

Mode 1 — Bearer API keys (credit-balance path)

A user-issued API key tied to a Supabase account and a prepaid credit balance. Use this when there is a human owner, when you want a dashboard with usage charts and key rotation, and when you want predictable cost from bulk credit packs.

Issuance

Open /console, claim your anonymous session with magic link or wallet, and click "New key". The key is shown exactly once, prefixed with cv_, then hashed with SHA-256 server-side. Cloven stores only the hash. If you lose the key, you must revoke and re-issue.

cv_<32 bytes of base62>

The first 16 characters form the public keyId — safe to log. The rest is the secret. Cloven never displays the full key again after issuance.

Sending the key

All REST and HTTP MCP endpoints accept a standard Bearer header:

GET /v1/fresh?pack=crypto HTTP/1.1
Host: api.cloven.cloud
Authorization: Bearer cv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

For stdio MCP (Claude Desktop, Cursor), put the key in the env block of your claude_desktop_config.json:

{
  "mcpServers": {
    "cloven": {
      "command": "npx",
      "args": ["-y", "@cloven/mcp"],
      "env": { "CLOVEN_API_KEY": "cv_xxx" }
    }
  }
}

The SDK reads the same env var by default, or accepts apiKey as a constructor argument.

Validation flow

On every authenticated request the middleware:

  1. Extracts the Bearer token, hashes it with SHA-256.
  2. Looks up the hash in the api_keys table (RLS-bypassing service-role read).
  3. Checks revoked_at is null.
  4. Returns an ApiKeyContext with keyId, userId, tier, creditBalance, and the list of pack ids the key is subscribed to.
  5. Hands off to the rate-limiter (free-tier: 100 calls/day sliding window in Redis) then to the credit debit gate (paid calls: decrements credit_balance via the debit_credits Postgres fn).

Invalid bearer → 401 invalid_token. Revoked key → 401 key_revoked. Quota exhausted → 429 quota_exhausted with Retry-After header. Insufficient credits → 402 insufficient_credits.

Mode 2 — x402 anonymous (agent-to-agent path)

Use this when an autonomous agent needs context and no human is in the loop to provision a key. The agent signs a USDC transfer on Base for the per-call price; Cloven verifies the transaction and serves the response. No account, no dashboard, no monthly bill.

Flow at a glance

  1. Agent calls GET /v1/fresh?pack=crypto with no auth header.
  2. Cloven responds 402 Payment Required with PaymentRequirements (recipient, amount, validUntil, nonce).
  3. Agent signs a USDC transfer on Base for the quoted amount.
  4. Agent retries the call with x-payment: <base64-encoded payment proof>.
  5. Cloven verifies the tx on-chain, caches the tx hash for 24h idempotency, and returns the MindResponse.

Full wire format and verification rules: x402 protocol guide.

The SDK handles this dance automatically when constructed with a viem WalletClient instead of an API key — see the SDK x402 mode page.

Mode 3 — SSE token (subscription-only)

The EventSource browser API cannot set custom request headers. SSE subscriptions therefore accept the API key as a token query parameter:

GET /v1/subscribe?pack=crypto&token=cv_xxx

The token is validated identically to a Bearer header (same hash, same RLS, same rate-limit consume). Use the same value you would pass as Authorization: Bearer …. Treat the URL as secret; SSE tokens leak through server logs and HTTP proxies if not handled carefully.

x402 is not currently supported on SSE because we cannot prompt mid-stream for a fresh payment.

Credit deposits via USDC on Base

To fund a key's credit balance, open /console/credits in the console and select a pack (Starter / Hobby / Pro / Team). The UI creates a deposit intent and presents:

  • The exact USDC amount to send and the Cloven treasury address on Base.
  • A QR code encoding the EIP-681 payment URI for wallet-app scanning.
  • An expires-in countdown (30-minute window).

Send the USDC transfer from any Base-compatible wallet. Once you have a tx hash, paste it into the "Submit tx hash" field for an immediate fast-path verify — or wait up to 60 seconds for the background cron to pick it up automatically.

On verification the deposits row flips to status = verified and your api_keys.credit_balance is incremented atomically. Credits are shared across all your non-revoked keys.

Deposits that receive no matching on-chain transfer within 30 minutes flip to status = expired. No charge applies. Re-open /console/credits to start a new deposit.

Tier-gated pack access

A key's packIds array determines which packs it can call. Free tier is restricted to ["crypto"]. Keys with a credit balance unlock every registered pack. Calling ?pack=ai from a free-tier key with no credit balance returns:

{
  "error": {
    "code": "pack_access_denied",
    "message": "your key does not have access to pack 'ai'; buy a credit pack to unlock all packs",
    "requestId": "req_abc123"
  }
}

Pack access is gated on credit balance, not a separate subscription toggle.

Credit balance and revocation

Revoking a key does not destroy its remaining credits — the credits stay on the row. If you re-issue a replacement key, transfer the balance manually via ops@cloven.cloud or buy a new pack on the replacement key.

Rotation + revocation

Revoke a key from /console. Revoked keys are rejected instantly (Redis cache invalidates within 60 seconds; Postgres lookup catches the rest). Issue a replacement before revoking if you care about uptime. Keys cannot be edited or "rotated" — the only safe rotation pattern is issue-new, deploy, revoke-old.