Authentication
Wallet lane (the iframe)
Every request under /v1/wallets/... carries two pieces of auth:
| Header | Value |
|---|---|
X-Sigil-Publishable-Key | The org’s pk_live_… |
Authorization | Bearer <jwt> — the end-user token |
The publishable key identifies the org. The JWT is either:
- Sigil mode: minted by
POST /v1/wallets/auth/email-otp/verifyafter the user completes the email OTP. HS256 with our internal secret. - OIDC mode: minted by your own provider; we validate against your JWKS via the autodiscovery URL configured per-org.
Two endpoints are gated by publishable key only because they exist to mint the JWT in the first place:
POST /v1/wallets/auth/email-otp/startPOST /v1/wallets/auth/email-otp/verify
Everything else under /v1/wallets/... requires both headers.
Server-to-server lane
GET /v1/s2s/wallets HTTP/1.1Host: api.sigilkeys.comAuthorization: Bearer sk_live_xxxSecret keys are revocable from the portal API keys tab. Rotate them the moment you suspect leakage.
Portal lane
The Kratos session cookie (ory_kratos_session) is set by the
auth.sigilkeys.com server-side flows. The Go backend validates it on
every request via Kratos whoami. There’s no app-level auth header.
The portal SPA never sees the cookie value — it’s HttpOnly.
CORS
The API is open to the configured allowed origins per org plus the
fixed Sigil-owned origins (https://wallet.sigilkeys.com,
https://platform.sigilkeys.com). Browsers from other origins will get
the request blocked client-side; the server still returns the data
correctly when CORS isn’t an issue (server-to-server).