Webhook Event Reference
Overview
When you subscribe to events in the Minigames (formerly Adact) Webhook Integration, our system sends an HTTPS POST request with a JSON payload to your specified endpoint whenever that event occurs. This guide details the available events and the structure of their payloads.
Delivery is asynchronous and does not block the player experience. Every attempt—success or failure—is recorded in the Webhooks history, where you can review the full request body and manually re-send any event.
For step-by-step configuration (endpoint, events, segments, and the embed script), see the How to Set Up Webhook Integration guide.
Available Events
Segment Events
These events fire when you manage segments within the API central settings.
segment.created
Sent when a new segment is created.segment.updated
Sent when a segment is updated (e.g. its title or connected Campaign ID is changed).segment.deleted
Sent when a segment is deleted.
Player & Prize Events
These are the core events that fire based on user interaction with your live campaigns.
player.created
Sent the first time a unique player registers in a campaign. The payload contains the registration form field data.player.updated
Sent when a returning/existing player re-registers, or their data is updated.game.ended
Sent when a player finishes the actual game (e.g. a quiz, an instant-win round).gameplay.finished
Sent when a player reaches the final/end screen of a campaign.prize.assigned
Sent when a prize is awarded to a player and a prize code is issued. The payload contains player and prize details.
Choosing between events
game.endedandgameplay.finishedare not the same. A campaign typically has a game step (game.ended) followed by an end screen (gameplay.finished). For most integrations one is enough;gameplay.finishedis the safer "the experience is complete" signal.player.createdfires only the first time a unique player registers for a campaign. Subsequent plays by the same player produceplayer.updated.prize.assignedfires only when a prize is actually awarded—not when a player loses or an already-won prize is re-shown.- For some real-time integrations,
gameplay.finishedmay be suppressed per-play when an equivalent real-time event has already been delivered. If you also rely on the real-time integration, do not expect agameplay.finishedwebhook for the same play.
The Request Envelope
Every webhook delivery has the same top-level envelope:
{
"accountId": 3,
"campaignId": 5,
"type": "<event type>",
"payload": { }
}accountId—your Minigames account id. Useful if a single endpoint serves multiple accounts.campaignId—the campaign that produced the event.type—one of the event wire strings above. Dispatch on this.payload—event-specific body, described below.
Event Payloads
player.created / player.updated
{
"accountId": 3,
"campaignId": 5,
"type": "player.created",
"payload": {
"playerId": "627cc032cbff6f5f549e23b5",
"email": "[email protected]",
"fullName": "James Smith",
"termsAccepted": true
}
}| Field | Type | Optional? | Notes |
|---|---|---|---|
playerId | string | required | Minigames' stable unique id for the player. Treat as opaque. |
email | string | optional | Only present if the campaign collected it and the player provided it. |
phoneNumber | string | optional | Same. |
fullName | string | optional | |
firstName | string | optional | |
lastName | string | optional | |
code | string | optional | Promo/code value collected by some campaigns. |
termsAccepted | boolean | optional | |
privacyPolicyAccepted | boolean | optional | |
extra | string | optional | JSON-encoded string of any custom fields the campaign collects. |
integrationUniqueId | string | optional | Only present when the campaign landing page was opened with an integrationUniqueId query parameter (e.g. ?integrationUniqueId=abc123). Minigames uses this value as the player's unique identity for recognising returning players, instead of relying on email/phone/cid matching. |
cid | string | optional | Optimove customer id, when present. |
Where do these fields come from? Every field except playerId, integrationUniqueId, and cid is collected through the registration form configured for the campaign. The campaign owner decides which fields the form asks for; whatever the player did not provide is simply absent from the payload. The only field guaranteed to be present is playerId.
game.ended / gameplay.finished
{
"accountId": 3,
"campaignId": 5,
"type": "game.ended",
"payload": {
"gameDurationInSeconds": 10.123,
"score": 100,
"leaderboardName": "James Smith",
"player": "{\"playerId\":\"627cc032cbff6f5f549e23b5\",\"email\":\"[email protected]\",\"phoneNumber\":\"555\",\"highestScore\":1,\"gameplayCount\":1}"
}
}| Field | Type | Optional? | Notes |
|---|---|---|---|
gameDurationInSeconds | number | optional | Game length. Some game types do not report duration. |
score | number | optional | Player's score for this play. Absent for game types without a score. |
leaderboardName | string | optional | The name the player entered on the leaderboard, if any. |
player | string | required (see note) | A JSON-encoded string describing the player. See The Embedded player Object for its contents. |
player is always present, but if the play was anonymous (no registration captured) it may decode to an almost-empty object.
playeris a JSON-encoded string, not an object. You mustJSON.parse(payload.player)to access its fields. This is for backwards-compatibility; new integrations should still parse it.
prize.assigned
Includes everything game.ended includes, plus three prize fields.
{
"accountId": 3,
"campaignId": 5,
"type": "prize.assigned",
"payload": {
"gameDurationInSeconds": 10.123,
"score": 300,
"leaderboardName": "James Smith",
"player": "{\"playerId\":\"627cc032cbff6f5f549e23b5\",\"email\":\"[email protected]\"}",
"prizeId": "627cc032cbff6f5f549e23b5",
"prizeTitle": "Prize 1",
"prizeCode": "d7f53b7e-f1e7-4d16-bf8c-310357ff749c"
}
}| Field | Type | Optional? | Notes |
|---|---|---|---|
prizeId | string | required | Minigames' stable id of the prize definition. |
prizeTitle | string | required | Human-readable prize name. |
prizeCode | string | required | Code/voucher issued to this specific player for this prize. |
The Embedded player Object
Inside game.ended, gameplay.finished, and prize.assigned, the player field is a JSON-encoded string. After JSON.parse(payload.player) you get an object. All of its fields are optional except playerId (present whenever the play was associated with a registered player).
| Field | Type | Notes |
|---|---|---|
playerId | string | Minigames' stable player id. |
email | string | |
phoneNumber | string | |
fullName | string | |
firstName | string | |
lastName | string | |
code | string | |
termsAccepted | boolean | |
privacyPolicyAccepted | boolean | |
highestScore | number | Player's best score across the campaign so far. |
gameplayCount | number | Number of plays by this player in the campaign so far. |
integrationUniqueId | string | Only present when the campaign landing page was opened with an integrationUniqueId query parameter. Used as the player's unique identity for recognising returning players, instead of email/phone/cid matching. |
cid | string | Optimove customer id (when present). |
As with the player.created payload, every field except playerId, highestScore, gameplayCount, integrationUniqueId, and cid is provided through the registration form. If your campaign's form doesn't ask for a field, that field won't appear here.
The form can also include custom ("extra") fields—anything your campaign asks for that isn't one of the standard fields above. Those custom fields appear directly on the same player object, at the same level as email, phoneNumber, etc. (not nested under an extra property). For example, if your campaign's form asks for a country field, the decoded player object will look like:
{
"playerId": "627cc032cbff6f5f549e23b5",
"email": "[email protected]",
"country": "Latvia"
}Empty strings and missing values are stripped—you will never see an
Delivery Semantics
| Property | Value |
|---|---|
| Method | POST |
| Scheme | https:// |
| Content-Type | application/json |
| Timeout | 2 seconds. Endpoints that have not responded with a status code in 2s are treated as failed (timeout of 2000ms exceeded). |
| Retry on failure | None automatic. Failures are recorded; you can manually re-send any event from the Webhooks history. |
| Ordering | Not guaranteed. |
| Duplication | At-least-once is possible. Your endpoint should be idempotent. |
| History retention | 14 days. Older deliveries are removed automatically. |
Authentication
There is no signature scheme (no HMAC, no timestamp challenge). The authentication model is a shared secret in a header you choose. When an authorization token is configured, every outbound webhook includes it in the Authorization header by default:
Authorization: <YOUR_TOKEN>Because there is no signature, the only way to verify a request really came from Minigames is to compare the header against the secret you stored on your side. Treat the token like a password: never log it, and rotate it if you suspect leakage.
For where to set or generate the token (and to request a custom header name), see the How to Set Up Webhook Integration guide.
Designing Your Receiver
- Accept fast, process later. Acknowledge with
2xxas soon as the payload is validated and persisted. Do downstream work asynchronously. - Be idempotent. Re-deliveries can happen. Use
(type, payload.player.playerId),prizeCode, or your own dedupe key to ignore duplicates. - Verify the auth header. Store the token as a secret and reject requests that do not match.
- Tolerate unknown fields. New optional fields may be added over time. Parse leniently.
- Always
JSON.parse(payload.player)when reading game/prize events—it is a string by design. - Don't trust ordering. Two events for the same play can arrive in either order. If you need a total ordering, persist events and reconcile.
- Handle missing optional fields. Most player fields are optional and depend on what the campaign collects.
- Pick the right "finished" signal.
gameplay.finishedis usually the right "experience complete" signal. Usegame.endedonly if you specifically need the game step. Don't subscribe to both without a reason.
Quick Reference
Event Triggers
| Event | Fires when |
|---|---|
segment.created | A new segment is created. |
segment.updated | A segment is updated (title or connected Campaign ID changes). |
segment.deleted | A segment is deleted. |
player.created | A unique player registers for the first time in a campaign. |
player.updated | An existing player re-registers, or their profile data changes. |
game.ended | The game portion of a campaign completes. |
gameplay.finished | The player reaches the final screen of the campaign. |
prize.assigned | A prize is awarded to the player and a code is issued. |
Limits & Guarantees
| Property | Value |
|---|---|
| HTTP method | POST |
| Required scheme | https:// |
| Body content type | application/json |
| Delivery timeout | 2 seconds |
| Automatic retries | None |
| Ordering guarantees | None |
| Duplicate deliveries | Possible (manual or upstream re-send) |
| History retention | 14 days |
| Signature/HMAC | None—shared secret in a custom header |
Gating Checklist (if you're not receiving anything)
- Is your endpoint URL set?
- Is the Enable Webhooks master switch on?
- Is the specific event type activated in the events list?
- Is the campaign in question selected under "Campaigns that are sending webhooks"?
- If you use brand overrides—does the brand override for that campaign's brand have the event activated?
- Does your endpoint actually return
2xxwithin the delivery timeout?
If all six are yes, check the Webhooks history for the most recent attempt's error details.
For an overview of the full API & Webhook feature, see API & Webhooks: An Overview.