Skip to main content
The Sumvin platform exposes two API surfaces with different authentication mechanisms.

Platform API — JWT Authentication

All Platform API requests require a valid JWT passed in a custom header. The API supports multiple authentication providers — your platform administrator configures which provider to use.

Making Authenticated Requests

Pass the JWT from your auth provider in the x-juno-jwt header:
curl https://api.sumvin.com/v0/user/me \
  -H "x-juno-jwt: <your-jwt-token>"
If your Platform API credentials belong to more than one Sumvin organisation, scope the request with the x-juno-orgid header:
curl https://api.sumvin.com/v0/user/me \
  -H "x-juno-jwt: <your-jwt-token>" \
  -H "x-juno-orgid: org-3b8b85b2a0fc4564b813fa93c8ef89cf:env-fd617c5800134505a1c205e5a3123589"
The Platform API uses x-juno-jwt, not the standard Authorization: Bearer header.

The x-juno-orgid Header

Multi-tenant integrations identify both the organisation and the environment a request belongs to using a single header value formatted as <org-id>:<env-id>.
HalfSource
<org-id>The Organisation ID under Organisation → Settings
<env-id>The Environment ID under Environment → Settings
Both halves are required, even if your organisation only has a single environment — Sumvin always resolves auth configuration per environment, so the env half pins the request to the correct provider credentials. A complete header looks like this:
x-juno-orgid: org-3b8b85b2a0fc4564b813fa93c8ef89cf:env-fd617c5800134505a1c205e5a3123589
If the value is present but not formatted as two non-empty halves separated by a single colon, the API responds with 401 Unauthorized and error code USR-401-002:
{
  "type": "https://api.sumvin.com/errors/usr-401-002",
  "title": "Invalid Org ID Format",
  "status": 401,
  "detail": "x-juno-orgid must be formatted as '<org-id>:<env-id>'.",
  "instance": "/v0/user/me",
  "error_code": "USR-401-002"
}
Single-tenant integrations can omit the header entirely; the API resolves the JWT against the default provider in that case.

Supported Auth Providers

ProviderDescription
Dynamic LabsWeb3-native auth with wallet and email login
PrivyEmbedded wallets and email/SMS/social login, with MPC key management
SIWESign-In With Ethereum (EIP-4361) — direct wallet-based auth
Which providers are active is configured per environment in the SIS Dashboard. Your platform is configured with one or more providers — refer to your provider’s SDK documentation for how to obtain JWTs.

Token Requirements

Sumvin verifies the JWT signature against the configured provider’s JWKS endpoint (or pinned verification key) and reads the following standard claims from the payload:
ClaimRequiredDescription
subYesUser’s unique identifier from the auth provider
expYesToken expiration time (Unix timestamp, seconds)
issYesIssuer URL — used to detect which provider signed the token (e.g. privy.io, dynamicauth.com)
audRecommendedAudience — for Privy, this should be your App ID
A Privy identity token decoded payload looks roughly like this:
{
  "sub": "did:privy:cm5n6f0h300ab12abcdef1234",
  "iss": "privy.io",
  "aud": "cm5h7abcdefghijklmnopqrst",
  "exp": 1735689600,
  "iat": 1735603200,
  "linked_accounts": [
    { "type": "email", "address": "user@example.com" }
  ]
}
Expired or tampered tokens are rejected with 401 Unauthorized and error code USR-401-001.

Privy: Identity Tokens vs Access Tokens

When your environment is configured in App ID only mode, Sumvin verifies Privy identity tokens. Identity tokens carry user claims (linked accounts, wallets, email) directly in the JWT — Privy access tokens do not, and will fail verification.
Privy identity token ≠ Sigil. The Privy identity token authenticates the end-user’s linked accounts (wallets, email, OAuth) to Privy’s system and is the credential Sumvin uses to establish a session. It tells the API who the user is, but carries no KYC or personhood verification. Sigil is Sumvin’s portable Proof of Personhood — a separate product layer provisioned after KYC passes and onboarding completes. A valid Privy identity token is a prerequisite for reaching the Sumvin API, but it does not constitute a Sigil. See the Juno Privy authentication guide for token mechanics.
Privy exposes identity tokens through the useIdentityToken React hook:
import { useIdentityToken } from "@privy-io/react-auth";

export function useSumvinAuthHeaders() {
  const { identityToken } = useIdentityToken();

  if (!identityToken) {
    return null;
  }

  return {
    "x-juno-jwt": identityToken,
    "x-juno-orgid": "org-...:env-...",
  };
}
getIdentityToken() from the headless SDK returns the same value if you’re not using React. See the Privy documentation for setup details — in the Privy console, identity tokens must be enabled under User management → Authentication → Advanced → Return user data in an identity token.

Token Freshness

Privy identity tokens follow the JWT lifetime configured in the Privy console (typically one hour for identity tokens; access tokens are shorter-lived). The token Sumvin verifies must be fresh on every request.
  • useIdentityToken re-fetches when the underlying session refreshes — read identityToken per request rather than caching the value.
  • For long-lived sessions (dashboards, background tasks), refresh proactively before exp to avoid silent 401s. Privy’s React SDK refreshes in the background as long as usePrivy is mounted.
  • Once exp has passed, Sumvin returns 401 with USR-401-001. Treat this as a signal to refresh and retry once.

User Identity

The API resolves user identity from the JWT — you never pass user_id in URLs or request bodies. Endpoints scoped to “the current user” derive identity from the token rather than a path parameter. For example: GET /v0/user/me — Fetch the current user. POST /v0/user/me/onboarding/steps — Submit onboarding for the current user. On first request, if no user exists for the JWT’s sub claim, you must create one:
curl -X POST https://api.sumvin.com/v0/user/ \
  -H "x-juno-jwt: <your-jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "primary_eoa_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
    "chain_id": 1329
  }'
If the JWT is missing or invalid, this call returns 401:
{
  "type": "https://api.sumvin.com/errors/usr-401-001",
  "title": "User Authentication Failed",
  "status": 401,
  "detail": "Missing or invalid authentication token",
  "instance": "/v0/user/",
  "error_code": "USR-401-001"
}
Subsequent requests with the same JWT will resolve to this user.

SIS API — API Key Authentication

This surface is how you query — Sumvin’s portable, KYC-verified Proof of Personhood. On the wire, Sigil is the SRI identity system: the SRI identifiers, identity tokens, scopes, and KYC verification status you read through the endpoints below. The Sumvin Identity Service (SIS) uses API key authentication via the Authorization: Bearer header.
curl https://sis.sumvin.com/v0/users/sr:us:person:safe:0x... \
  -H "Authorization: Bearer <your-api-key>"
API keys are scoped to your registered external party identity and determine which data fields you can access when looking up users by .

Scopes & partitions

SIS keys are scoped at mint time. Each key carries an explicit set of scopes drawn from a dotted-namespace catalogue. A key’s partition — public-safe vs server-only — is derived from the namespace prefix of its scopes.

Scope catalogue

ScopeAllowsNamespace
sis.lookupGET /v0/users/{sri} (basic lookup)sis (server)
sis.get_detailsFull identity record fieldssis (server)
sis.get_kycKYC verification statussis (server)
sis.token_exchangeToken exchange flowssis (server)
sis.get_pintsPINTs balancessis (server)
rpc.invokeCalling the public RPC endpointrpc (public)

Partitions

The sis and rpc namespaces sit in two distinct partitions. A key cannot span both.
PartitionNamespacesWhere to usePublic-safe?
Serversis.*Server-side only — your backend calling sis.sumvin.comNo — never embed in browsers, mobile bundles, or anything user-controlled
Publicrpc.*Client-side — browsers, mobile apps, server-side proxiesYes — designed to be exposed in client bundles
Partition is fixed post-mint. A key’s partition is determined by the namespace of its scopes at mint time and cannot be changed. To move a key between partitions, revoke and mint a new one. Within a partition, scopes are mutable (a future PATCH endpoint will let you grant additional same-partition scopes — not yet shipped). The mint response surfaces partition ("public" or "server") and the verbatim scopes array, so dashboards and ops tooling can render badges without a second round-trip.

Minting a key

The request body takes a scopes array. The partition is derived from the namespaces of the scopes you request — mixing sis.* and rpc.* in the same request is rejected with 422 SIS-422-006. Minting keys is a dashboard-admin operation on the SIS host (sis.sumvin.com), authenticated with your dashboard session token via the Authorization: Bearer header. The organisation is taken from the <org_id> path segment.
curl -X POST https://sis.sumvin.com/v0/organisation/<org_id>/keys \
  -H "Authorization: Bearer <dashboard-session-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-public-rpc-key",
    "env_id": "<env-id>",
    "scopes": ["rpc.invoke"]
  }'
Response (server-partition key, full SIS scopes): 201 Created
{
  "id": "key_abc123",
  "name": "my-server-key",
  "key": "sk_live_xxx...",
  "partition": "server",
  "scopes": [
    "sis.lookup",
    "sis.get_details",
    "sis.get_kyc",
    "sis.token_exchange",
    "sis.get_pints"
  ],
  "meta": {
    "org_id": "org_abc",
    "env_id": "env_def"
  },
  "_links": {
    "self":         { "href": "/v0/organisation/<org_id>/keys/key_abc123" },
    "organisation": { "href": "/v0/organisation/<org_id>" },
    "environment":  { "href": "/v0/organisation/<org_id>/environments/<env_id>" },
    "revoke":       { "href": "/v0/organisation/<org_id>/keys/key_abc123", "method": "DELETE" },
    "list":         { "href": "/v0/organisation/<org_id>/keys" }
  }
}
Response (public-partition RPC key): 201 Created — same shape plus an rpc_docs HAL link:
{
  "id": "key_abc123",
  "name": "my-public-rpc-key",
  "key": "sk_live_xxx...",
  "partition": "public",
  "scopes": ["rpc.invoke"],
  "meta": {
    "org_id": "org_abc",
    "env_id": "env_def"
  },
  "_links": {
    "self":         { "href": "/v0/organisation/<org_id>/keys/key_abc123" },
    "organisation": { "href": "/v0/organisation/<org_id>" },
    "environment":  { "href": "/v0/organisation/<org_id>/environments/<env_id>" },
    "revoke":       { "href": "/v0/organisation/<org_id>/keys/key_abc123", "method": "DELETE" },
    "list":         { "href": "/v0/organisation/<org_id>/keys" },
    "rpc_endpoint": { "href": "https://rpc.sumvin.com" },
    "rpc_docs":     { "href": "https://docs.sumvin.com/rpc" }
  }
}
The plaintext key is returned once — store it immediately. Subsequent reads do not return the plaintext. Public-partition keys carry both rpc_endpoint (the RPC base URL the key authenticates against) and rpc_docs (the RPC method reference) HAL links. Server-partition keys carry neither.

Validation errors

Statuserror_codeWhen
422SIS-422-006 (Invalid Scopes)The scopes array is empty, contains an unknown scope, or mixes sis.* and rpc.* in one request
422(FastAPI default)scopes field missing entirely from the body, or env_id missing
Pre-existing partner keys are migrated to the dotted scope catalogue automatically. Their partition is preserved (a key with the legacy lookup scope ends up in the server partition with sis.lookup); they cannot be elevated across partitions via mutation — the only path between partitions is re-mint.
For more on the SIS API and identity concepts, see Identity & PINTs.

Error Responses

Authentication failures return RFC 7807 Problem Details:
{
  "type": "https://api.sumvin.com/errors/usr-401-001",
  "title": "User Authentication Failed",
  "status": 401,
  "detail": "Missing or invalid authentication token",
  "instance": "/v0/user/me",
  "error_code": "USR-401-001"
}
StatusError CodeMeaning
401 UnauthorizedUSR-401-001Missing, expired, or invalid JWT / API key
401 UnauthorizedUSR-401-002x-juno-orgid header is malformed (not <org-id>:<env-id>)
403 ForbiddenToken is valid but the user lacks required permissions
404 Not FoundUSR-404-001Token is valid but no user account exists (call POST /v0/user/ first)

Request Headers Summary

HeaderAPI SurfaceRequiredDescription
x-juno-jwtPlatform APIYesJWT from your configured auth provider
x-juno-orgidPlatform APIMulti-tenant onlyComposite <org-id>:<env-id> scoping the request to a specific organisation and environment.
Authorization: BearerSIS APIYesAPI key issued during partner onboarding
Content-TypeBothFor POST/PUT/PATCHapplication/json
X-Timestamp-FormatPlatform APINoSet to iso8601 for ISO 8601 timestamps