Skip to main content
When a user opens the webview (or hits any /me/* endpoint directly), they present a JWT signed by your app. We verify it using the public key you publish via jwksUrl or supply as a static jwtPublicKey.

Required claims

ClaimTypeDescription
issstringMust match jwtIssuer on your partner record. This is how we route the request to your verifier.
substringYour external user ID. Identical to what you sent to POST /integrations/users. This links the JWT to the right internal user.
audstringRequired only if you set jwtAudience on your partner record.
expnumberUnix seconds. Recommend ≤ 1 hour.
iatnumberUnix seconds.

Optional claims (JIT user creation)

If you skip the POST /integrations/users step entirely, we’ll create the user the first time a JWT for an unknown sub arrives (“just-in-time” provisioning). These optional claims populate the new row:
ClaimMaps toNotes
emailemail
displayName or namedisplayNamedisplayName wins if both present.
phonephone
countryCode or countrycountryCodeFirst two chars, uppercased.
localelocale
JIT works, but the recommended pattern is to upsert the user from your backend before opening the webview. That way you can correct stale data (e.g. user changed their email) and you don’t depend on JWT claims for PII integrity.

Algorithm

We require RS256 with a 2048+ bit key. We do not currently accept HS256, ES256, or unsigned tokens.

Example

import { SignJWT, importPKCS8 } from 'jose';

const privateKey = await importPKCS8(process.env.JWT_PRIVATE_KEY!, 'RS256');

const jwt = await new SignJWT({
  email: user.email,
  displayName: user.name,
  countryCode: user.country,
})
  .setProtectedHeader({ alg: 'RS256' })
  .setIssuer('https://acme.example.com')   // == partner.jwtIssuer
  .setAudience('voucher-platform')          // optional
  .setSubject(user.id)                      // == externalUserId
  .setIssuedAt()
  .setExpirationTime('1h')
  .sign(privateKey);

Key rotation

If you set jwksUrl, we fetch the JWK set on each unique kid we haven’t seen, with internal caching. To rotate:
  1. Publish the new key alongside the old one at your JWKS URL.
  2. Start signing new JWTs with the new kid.
  3. Wait for any in-flight tokens signed with the old key to expire.
  4. Remove the old key from your JWKS.
We don’t pin keys, so no admin action is needed on our side. If you supply a static jwtPublicKey instead, rotation requires an admin update to your partner record. JWKS is preferred for production.