Skip to main content
The event ingestion endpoint lets you send multiple gameplay events in a single HTTP call. Batching reduces network overhead and powers automated features like coin rewards — the server processes LevelCompleted events to award coins as soon as they are flushed from the queue. You can include up to 200 events per request.
1

Authenticate your request

POST /ingest/events requires a valid Bearer token. Use the access_token obtained from registration or login.
-H "Authorization: Bearer <access_token>"
If the token is missing or expired, the server returns 401 UNAUTHORIZED. If the token is valid but the user_id in the payload does not match the token’s sub claim, the server returns 403 FORBIDDEN.
2

Build your event batch

Every request body has three top-level fields:
FieldTypeDescription
user_idstringThe player’s ID. Must match the sub claim in the access token.
game_idstringYour game’s bundle ID (e.g., com.example.mygame).
eventsarrayBetween 1 and 200 event objects.
Each event object in the events array has:
FieldTypeRequiredDescription
typestringYesOne of the six supported event type names.
timestampstringYesISO 8601 datetime with timezone offset.
customdataobjectNoAny JSON object with additional context.
customEventNamestringNoRequired when type is Custom.
3

Choose your event types

PlaySmart supports six event types:
TypeWhen to send
AppOpenThe player opens the app and the game session begins.
LevelCompletedThe player successfully completes a level. This event triggers coin awards when coins_enabled is true.
GameEndThe current game session ends, whether through completion, failure, or exit.
AdImpressionAn ad was shown to the player.
IAPPurchaseThe player completed an in-app purchase.
CustomAny other game-specific event. Set customEventName to describe it.
When sending a Custom event, include customEventName in the event object alongside type. For example:
{
  "type": "Custom",
  "customEventName": "PowerUpUsed",
  "timestamp": "2024-01-15T12:05:00+00:00",
  "customdata": { "power_up": "shield", "level": 5 }
}
4

Send the batch

Post your assembled batch to POST /ingest/events:
curl -X POST https://playsmart-gateway-1w8ko864.uc.gateway.dev/ingest/events \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "device-abc-123",
    "game_id": "com.example.stackblaster",
    "events": [
      {
        "type": "AppOpen",
        "timestamp": "2024-01-15T12:00:00+00:00",
        "customdata": {}
      },
      {
        "type": "LevelCompleted",
        "timestamp": "2024-01-15T12:04:30+00:00",
        "customdata": { "level": 5, "score": 1250 }
      },
      {
        "type": "AdImpression",
        "timestamp": "2024-01-15T12:04:35+00:00",
        "customdata": { "ad_unit_id": "ca-app-pub-0000/1234567890" }
      },
      {
        "type": "GameEnd",
        "timestamp": "2024-01-15T12:04:40+00:00",
        "customdata": { "reason": "level_complete" }
      }
    ]
  }'
The server responds with 202 Accepted:
{
  "success": true,
  "message": "accepted:4 buffered:4"
}

Timestamp format

All timestamp values must be ISO 8601 strings with an explicit timezone offset. UTC is recommended:
2024-01-15T12:00:00+00:00
Timestamps without a timezone offset are rejected with a 400 BAD_REQUEST.

customdata

The customdata field accepts any flat or nested JSON object. Use it to attach context that is useful for analytics or debugging — for example, the level number, score, ad unit ID, or purchase SKU. There is no enforced schema, but keep payloads small to avoid unnecessary overhead.
A 202 Accepted response means the events were received and queued in memory. They are flushed to the database within a few seconds. There is no need to retry a 202 response.
The user_id in the request body must exactly match the sub claim in the Bearer token. Sending events on behalf of another user returns 403 FORBIDDEN with error code user_id_mismatch.