Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.calmtreasury.xyz/llms.txt

Use this file to discover all available pages before exploring further.

curl -X POST https://api.calmtreasury.xyz/v1/session \
  -H "Authorization: Bearer <privy_identity_token>"
{
  "wallet":    "0xaabbccddeeff00112233445566778899aabbccdd",
  "expiresAt": "2026-06-02T21:30:00.000Z"
}

Headers

Authorization
string
required
Bearer <privy_identity_token>. The Privy app id is read from the JWT’s aud claim — no separate header.

Body

Empty.

Response

wallet
string
The Ethereum address bound to this session (lowercased, 0x-prefixed).
expiresAt
string
ISO timestamp when the cookie expires (currently 1 hour after issue).

What the server does

1

Reads the JWT

From Authorization: Bearer …. 401 token_required if missing.
2

Decodes (unverified) to read `aud`

401 invalid_token if malformed or aud is missing.
3

Verifies the signature

Against https://auth.privy.io/api/v1/apps/<aud>/jwks.json. Issuer must be privy.io, audience must equal the aud. 401 invalid_token or token_expired on failure.
4

Extracts the wallet

The first entry in linked_accounts with type: "wallet". 403 wallet_not_linked if absent.
5

Mints the session cookie

HS256 JWT, claims { sub, wallet, app_id }, signed with CALM_SESSION_SECRET. Returns the cookie + body.

Errors

CodeStatus
token_required401
invalid_token401
token_expired401
wallet_not_linked403
See Errors for the full table.

Notes

This is the only endpoint that needs the Privy JWT. Every subsequent call uses the cookie via credentials: "include".