Your game client sends analytics events to PlaySmart in batches. On the server side, events are placed into an in-memory buffer and flushed to the database asynchronously — either on a timed interval or when the buffer reaches its configured maximum size. This design keeps the ingest endpoint fast under load: you receive an HTTP 202 Accepted immediately, before the events have been written to persistent storage.
Available event types
PlaySmart recognises the following named event types:
| Event type | Description |
|---|
AppOpen | The player opened the parent app. Updates last-seen timestamps and session tracking. |
LevelCompleted | The player finished a level. Triggers a coin award when coins_enabled is true for the game. |
GameEnd | A game round ended (win or loss). Updates total_games_played and score fields. |
AdImpression | An ad was shown to the player. Contributes to ad revenue tracking used in elastic conversion mode. |
IAPPurchase | The player made an in-app purchase. Recorded for analytics purposes. |
Custom | A custom game-defined event. Supply a customEventName string to distinguish between custom event types. |
Event object structure
Each event in a batch must conform to the following shape:
| Field | Required | Description |
|---|
type | Yes | One of the event type strings listed above. |
customEventName | No | A string identifier for your custom event. Only used when type is Custom. |
timestamp | Yes | ISO 8601 datetime string with a timezone offset (e.g. 2024-03-15T14:30:00+00:00). Represents when the event occurred on the client. |
customdata | No | A free-form object containing any key-value pairs you want to associate with this event. Values can be any JSON type. |
{
"type": "LevelCompleted",
"timestamp": "2024-03-15T14:30:00+00:00",
"customdata": {
"level_number": 12,
"duration_seconds": 47,
"perfect_run": true
}
}
For a Custom event, include customEventName:
{
"type": "Custom",
"customEventName": "boss_defeated",
"timestamp": "2024-03-15T14:31:22+00:00",
"customdata": {
"boss_id": "fire_dragon",
"attempts": 3
}
}
Batch structure
You send events to POST /ingest/events as a single batch object. Every batch must include:
| Field | Required | Constraints | Description |
|---|
user_id | Yes | Non-empty string | The player’s ID. Must match the sub claim in the bearer token. |
game_id | Yes | Non-empty string | The ID of the game the events came from. |
events | Yes | 1–200 items | Array of event objects as described above. |
{
"user_id": "device-abc-123",
"game_id": "64f1a2b3c4d5e6f7a8b9c0d1",
"events": [
{
"type": "LevelCompleted",
"timestamp": "2024-03-15T14:30:00+00:00",
"customdata": { "level_number": 12 }
},
{
"type": "AdImpression",
"timestamp": "2024-03-15T14:30:05+00:00",
"customdata": { "ad_unit": "interstitial_post_level" }
}
]
}
A single batch accepts a maximum of 200 events. If you have more than 200 events to send, split them into multiple requests.
Buffering and persistence
The ingest endpoint does not write directly to the database. Instead, events are pushed into an in-memory buffer. The buffer is flushed to the database in two situations:
- Timed flush — the buffer flushes on a regular interval (typically every few seconds).
- Size flush — if the buffer accumulates a large number of events, it flushes immediately regardless of the timer.
An HTTP 202 Accepted response means the batch was accepted into the buffer — not that it has been persisted to the database yet.
{
"success": true,
"message": "accepted:2 buffered:47"
}
The message field tells you how many events from your batch were accepted and how many events are currently waiting in the buffer across all recent requests.
If the server restarts before the buffer is flushed, buffered events that have not yet been written to the database will be lost. For critical game events, ensure your client retries on network failure but be aware that duplicate events sent within the same session may appear more than once in raw storage.
How events drive coin rewards
LevelCompleted events are the primary driver of coin awards in PlaySmart. When the event processor picks up a LevelCompleted event, it checks:
- Is
coins_enabled set to true on the game?
- Is
level_completion_enabled set to true in the game’s config?
If both are true, a coin transaction is written with trigger: "level_complete" and the player’s coins_balance increases.
Custom events can also drive coin awards when event_trigger_enabled is true in the game’s config. The processor counts custom events and fires a coin award once the count reaches event_trigger_threshold. Each award uses trigger: "event_trigger".
See the coin economy page for details on how coin amounts are calculated and how coins eventually convert to USD.
Stored event fields
Once flushed, each event is stored in the raw_events collection with additional server-set fields alongside the client-provided data:
| Field | Description |
|---|
server_time | When the server received the event. Immutable. |
ingested_at | When the event was written to the database. Immutable. |
processed_at | When the event processor handled this event. null until processed. |
attempt_count | Number of processing attempts made. |
last_error | The last processing error, if any. |
Raw events are retained for 180 days before automatic expiry.