Skip to main content
This page is the practical “what does my game server need to send” reference. It overlaps with the Events API page, but is framed around concrete payloads you can copy and adapt.

The envelope

Every event you send is wrapped in the same envelope. Required fields apply to all event types.

Validation rules at a glance

  • event_id must be a valid ULID (the 26-char part after any prefix).
  • event_type must match one of the allowed values exactly.
  • game_id and player_id are required and length-bounded.
  • event_time rejected if more than 7 days in the past or future.
  • device.type rejected if not in the allowed set.
  • geo.country rejected if not 2 uppercase letters.
  • revenue_usd rejected if negative or non-numeric.
  • custom events: properties.custom_properties map is capped at 20 keys.
  • Per-type required properties listed below — missing any of them rejects the event.
If any rule fails, that event is rejected. Other events in the same batch are still accepted (you’ll get a 207 response — see REST API).

Examples by event type

session_start

No required properties. Send when the player opens the game.

level_complete

No strictly-required properties, but include level identity and outcome details.

currency_earned

Required: currency_type, amount, balance_after, source. The balance_after is critical — it’s how Qyren tracks economy inflation.
source is a free-form string — but be consistent. Always dungeon_reward, never sometimes dungeon_complete.

currency_spent

Required: currency_type, amount, balance_after, sink.

purchase

Required: offer_id, transaction_id, store, revenue_usd, is_first_purchase.
store is free-form — typical values are app_store, play_store, steam, direct. revenue_usd must always be in USD regardless of the player’s local currency, non-negative.

offer_impression

Required: offer_id, placement, position. Send one of these every time an offer is rendered on screen. Skip these and conversion rate becomes uncomputable.
placement is whatever name your game uses internally — shop_main, level_complete_popup, boss_defeat. position is a 0-indexed integer for where the offer appeared in the list. Including the request_id from the recommendation response lets Qyren tie the impression back to the call that produced it.

Best practices

  • Generate ULIDs server-side. Sortable, unique, no coordination needed across instances.
  • Be consistent with free-form strings. Pick one spelling for each source, sink, placement, and stick to it. Inconsistency makes dashboards messy and recommendations weaker.
  • Never send PII. No emails, no real names. player_id should be opaque.
  • Buffer locally. If Qyren is slow or unreachable, queue events on disk and retry. Events up to 7 days old are still accepted.
  • Retry on 429 and 5xx with exponential backoff starting at 1 second.