Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Numbers and encoding

This page describes how Open Play carries numbers, money, time, and identifiers over JSON. The most common mistake is converting 64-bit integers to JavaScript number and losing precision; read this page to avoid that mistake.

Short rules

Field typeCore RESTPlugin and realtime
64-bit integer (int64)JSON number ("amount": 250)Decimal text ("amount": "250")
32-bit integerJSON numberJSON number
Floating pointNowhereNowhere
MoneyMinor-unit integer (amount, balance)Minor unit, ...Minor fields, int64
PercentageBasis point (10000 = 100%)Basis point
TimeRFC 3339 text (created_at)Unix milliseconds integer (...Ms)
IdentifierUUID textUUID text
Enumsnake_case textsnake_case (plugin), PascalCase (realtime frame types)

64-bit integers

JavaScript’s JSON.parse converts numbers to double; integers above 2^53 − 1 (9,007,199,254,740,991) are silently corrupted. Open Play therefore uses two profiles:

  • Core REST sends int64 fields as JSON numbers (version, balance, amount). In practice the values stay in the safe range, but the guarantee is a parser that reads with bigint. The TypeScript SDK reads with lossless-json and returns bigint.
  • Plugin and realtime (the plugin-v1-decimal-int64 encoding profile) send and expect int64 fields as decimal text: {"expectedVersion":"3"}, {"sessionEpoch":"7"}. This way every client language carries them losslessly.

Canonical decimal text

In plugins a signed int64 must be written in canonical form; the server rejects any other form (400 invalid_argument):

ValidInvalidWhy
"0", "42", "-7""+1"No plus sign
"9223372036854775807""01", "007"No leading zero
"-9223372036854775808""-0"No negative zero
" 5", "5 "No whitespace
"1.5", "1e3"No fraction or exponent
"9223372036854775808"The int64 bound was exceeded
5 (JSON number)A plugin int64 field wants text

The counters in realtime frames (sessionEpoch, inputSeq, revision, sequence, expectedVersion, streamEpoch, barrierSequence) are in the same text form and cannot be negative. protocolVersion, schemaVersion, and other small counters are JSON numbers.

In the plugin schema document, the bounds of an int64 field are also written as text ("minimum": "0", "maximum": "9223372036854775807"); this is normal.

In client languages

LanguageMapping
TypeScript SDKint64 → bigint. Do not convert to a number; use value.toString() for display. Do not read JSON yourself with JSON.parse
Unity (C#)int64 → long; in plugin outputs, raw JSON JsonValue stays as decimal text
Raw JSON (another language)Read plugin fields as string and convert to int64

If you parse JSON yourself in TypeScript, use a library such as lossless-json; otherwise large values are corrupted.

Money and scaled values

  • Money values are minor-unit integers (for example cents): how many main units 250 is depends on your game’s display decision (the currency’s “minor digits” setting in the panel determines the display); the API carries only the integer. Balance and amount fields come as amount, balance, and in plugins as ...Minor.
  • Floating point (float/double) is used nowhere: not in requests, not in responses, and not in computation. On your side too, do not use float for money; work with integers and convert to decimals only for display.
  • Scaled values in content groups are an integer + scale pair: 4350 and scale 2 → 43.50. In the TypeScript SDK, formatScaled(value: bigint, scale) formats this losslessly (0 to 18 digits). Do not do your own float division.

Percentages: basis points

Percentage and ratio fields are basis point integers: 10000 = 100%, 2500 = 25%, 1 = 0.01%. Example: the group reward share share_bp must be between 0 and 10000; outside that gets 400.

Time

WhereFormatExample
Core REST responses (created_at, as_of)RFC 33392026-11-03T10:15:30+00:00
Plugin and realtime (createdAtMs, sent_at_ms)Unix epoch milliseconds, integer1793700930000
Chat query before_msUnix epoch milliseconds1793700930000
t in the webhook signatureUnix epoch seconds1793700930
Deprecation header@<unix seconds> (RFC 9745)@1793491200
Plugin deprecation (sinceUnixS)Unix seconds1793577600

Interpret server times as UTC. The client clock is not used in server decisions: time and randomness are generated on the server and written to the event.

Identifiers and keys

  • UUID: lowercase, hyphenated text (0192a3b4-0000-7000-8000-000000000001). Most resource IDs are time-ordered UUID v7; you can use them for ordering, but do not infer meaning from the format.
  • key fields (definition keys, leaderboard, quest): readable identifiers given by the game developer; use them exactly as created, do not change the case or whitespace.
  • Plugin IDs: lowercase and hyphens (package-ride). Message types: dot-separated, plugin-prefixed (package-ride.delivery-trip.active.input).
  • Cursors: opaque text; do not parse their contents (pagination).
  • API keys: crx_live_<prefix>_<secret>; the prefix is only for identification, the secret part is shown only once (security).

Enumeration (enum) values

All enumeration values come from a single definition on the server, and an unknown value is rejected (not silently ignored):

  • Core REST fields are snake_case text ("type": "set_flag", "channel": "in_game", "status": "pending").
  • Realtime frame type and kind values are PascalCase (Authenticate, PlayerSelf); the field names are camelCase.
  • Chat socket frame type values are camelCase, and the field names are snake_case (channel_id).
  • Webhook event types are snake_case (webhook events).
  • Values listed as enum in a plugin schema document (for example "image/png") are used exactly.

If an unknown enum value arrives in your client (it may have been added in a new version), fall back to a safe default instead of crashing; the SDKs reject an unknown value in some places and skip it in others, so follow version upgrades from the changelog.

Text

  • All text is UTF-8 JSON text.
  • Fields have length limits (for example Idempotency-Key 200 characters, minLength/maxLength on plugin string fields); a limit violation gets 400.
  • Plain-text error messages (core REST) can be in English or Turkish and change between versions; do not use them for machine decisions (errors).