Mint an anonymous Portal JWT

​

Mints an anonymous user JWT (claims.anon = true). Two ways to call it, both returning the same token shape:

(a) Secret key — server-side. Send Authorization: Bearer sk_.... You may set channels grants and a custom ttl. Browser requests are rejected (a secret key must never reach a browser).

(b) Publishable key — browser-side, zero backend. Send Authorization: Bearer pk_... or the x-portal-key: pk_... header. The body may contain only anonId — channels and ttl are rejected with validation_failed; the platform default TTL (one hour) and default grants apply. This path is the only route on the control plane that is browser-CORS-enabled, scoped to the environment's registered allowed origins (loopback is always allowed). It is coarsely rate-limited per key. An Origin that resolves to a real environment but isn't registered for it gets 403 origin_not_allowed instead of a token (see the Forbidden response).

anonId (optional, both paths) must match the anon_... id format exactly. When present it is reused as the token subject, so an SDK can re-mint on expiry for the same anonymous identity. When absent, a fresh id is generated and returned as the token subject.

A browser preflight (OPTIONS) never carries the credential's value (only the header NAME it intends to send), so the environment can't be resolved there — preflight is permissive instead (204 + ACAO for any Origin). Enforcement is entirely on the actual POST, where the credential is present in a header (?key=pk_... also still works there, kept for compatibility, though it was never an auth path — only a CORS-resolution one).

Query Parameters

  • Publishable key (pk_...) — a CORS-resolution fallback on the actual POST, checked alongside the header. Never used on the preflight (which is unconditionally permissive) and never an auth credential on its own: key with no header still fails auth, even though CORS gating may still resolve the environment from it.

Body·

application/json
  • Optional, both paths. Must match the anon_... id format exactly. When present, it is reused as the token subject so an SDK can re-mint on expiry for the same anonymous identity. When absent, a fresh id is generated.

  • Secret-key path only. Rejected on the publishable-key path.

    Properties: 1
  • Secret-key path only. Rejected on the publishable-key path.

Responses

  • application/json
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for post/v1/tokens/anonymous
curl https://api.useportal.co/v1/tokens/anonymous \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "channels": {
    "chat-general": [
      "connect",
      "publish"
    ]
  },
  "ttl": "30m",
  "anonId": "anon_Vk8v2pBpHx7mJ9sXkFq3Qw"
}'
A signed anonymous JWT and its expiry
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImVudl8xMjMifQ.eyJzdWIiOiJhbm9uX1ZrOHYycEJwSHg3bUo5c1hrRnEzUXciLCJhbm9uIjp0cnVlLCJpYXQiOjE3NTI5MTIwMDAsImV4cCI6MTc1MjkxNTYwMH0.signature",
  "expiresAt": "2026-07-19T03:00:00.000Z"
}