Phoenix Prediction Docs

Launch Tokens

Mint visitor and player JWTs for Phoenix Prediction iframe launch

Your backend mints Phoenix Prediction launch tokens. The browser receives a ready-to-use token, but it never sees your private signing key.

Phoenix Prediction launch tokens are EdDSA JWTs signed with your Ed25519 private key. Phoenix verifies them with the Ed25519 public key registered on your operator account.

Required Claims

ClaimRequiredDescription
issYesYour Phoenix Prediction operator code
envYesOperator environment: sandbox or prod
typOptional (metadata)visitor or player. Phoenix does not validate this claim; the visitor-vs-player distinction is enforced by the presence of sub (player) or its absence (visitor).
audOptionalAudience claim. Phoenix does not verify it for launch tokens; if present, use the constant pmm_default.
expYesExpiry time, Unix seconds
iatRecommendedIssued-at time, Unix seconds
subPlayer onlyYour player ID for this user
namePlayer onlyDisplay name Phoenix can show in player-facing UI
avatarPlayer onlyAvatar object
currencyYes (player)Session money currency, e.g. USD or ETB. Required on every player token; a token without it (or with a currency outside your supported list) is rejected at connect.
cfgOptionalPer-user UI policy object, e.g. { "deposit": false }. Omitted -> all UI visible.

Use short-lived tokens. A practical starting point is minutes, not hours. Phoenix can confirm the exact TTL target for your environment.

Environment Claim

env is not just metadata. It selects which Phoenix operator environment receives the launch.

envMeaning
sandboxTest/review environment for staging launches, test wallets, discovery configuration, and operator training
prodProduction environment for real players and production operations

Each environment can have its own public key, allowed parent origins, wallet settings, discovery configuration, listings, and settlement mode. Mint sandbox tokens only for sandbox/testing traffic. Mint prod tokens only from production systems that are ready for real player activity.

Token Shapes

Use a visitor token when the user is not authenticated or should only browse.

{
  "iss": "acme",
  "env": "sandbox",
  "typ": "visitor",
  "aud": "pmm_default",
  "iat": 1779100000,
  "exp": 1779100900
}

Visitor tokens must not include sub.

Use a player token after your site has authenticated the user.

{
  "iss": "acme",
  "env": "sandbox",
  "typ": "player",
  "sub": "operator-player-123",
  "aud": "pmm_default",
  "iat": 1779100000,
  "exp": 1779100300,
  "currency": "USD",
  "name": "Player Name",
  "avatar": { "kind": "none" }
}

sub is your stable player identifier. Phoenix stores an internal player UUID, but wallet operations, bridge-facing support, and operator investigations continue to use the ID you supplied in sub.

Player display names are trimmed and capped at 80 characters. Player tokens without a valid name and avatar are rejected.

Avatar Values

Use none when you do not want to show an avatar:

{ "kind": "none" }

If image avatars are enabled for your operator, Phoenix will provide the accepted image shape and validation rules.

Session Currency

Phoenix is multi-currency. The top-level currency claim selects the money currency for the player's whole session: every market they trade resolves under that currency.

  • currency must be one of your operator's supported currencies. A token whose currency is outside that list is rejected at connect (the socket never opens).
  • currency is required on every player launch token, including single-currency operators; omitting it rejects the launch at connect.
  • Set currency per launch to route a player to a specific currency (for example a USD player vs an ETB player).

Node.js Example

import { SignJWT, importPKCS8 } from 'jose';

const privateKeyPem = process.env.PHOENIX_PREDICTION_OPERATOR_PRIVATE_KEY_PEM!;
const key = await importPKCS8(privateKeyPem, 'EdDSA');

const now = Math.floor(Date.now() / 1000);

export async function mintPlayerToken(player: {
  id: string;
  name: string;
  phoenixEnv: 'sandbox' | 'prod';
  currency: string; // one of your operator's supported currencies, e.g. 'USD' or 'ETB'
}) {
  return new SignJWT({
    env: player.phoenixEnv,
    typ: 'player',
    sub: player.id,
    currency: player.currency,
    name: player.name,
    avatar: { kind: 'none' },
  })
    .setProtectedHeader({ alg: 'EdDSA' })
    .setIssuer('acme')
    .setAudience('pmm_default')
    .setIssuedAt(now)
    .setExpirationTime(now + 5 * 60)
    .sign(key);
}

Token Rotation

When a token is close to expiry, the iframe can ask the parent page for a replacement. Your site should mint a fresh token for the same visitor or player session and send it to the iframe with pmm:setToken.