neowire Publisher APIv1.0.0
https://api.neowire.aiAuthorization: Bearer <token>Applied per endpoint.
The server-to-server API a publisher's backend calls to register its users.
Error handling#
Every endpoint declares a default response carrying this envelope (7 operations). Endpoint-specific status codes are listed with each endpoint.
errorstringcodestringMachine-readable error code; omitted when the error has none.
{
"error": "string",
"code": "string"
}Endpoints7
/s2s/v1/user/{id}/auth#authUserAuthenticate a user and mint a bearer token
Service-to-service endpoint called by an app backend (e.g. airperks-api) with its app token. Creates the user named by the path id on first call, upserts their demographics and currency-conversion settings, then returns a short-lived JWT the caller passes to the client as a bearer token for the /v1/* endpoints.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_timezone | timezone is not a valid IANA timezone name. |
| 400 | too_many_friend_ids | friend_user_ids resolves to more than 100 friends. |
| 400 | currency_not_configured | currency is not one of the app's configured currencies. |
| 409 | username_taken | Another user of this app already uses this username. |
Path parameters
idstringrequiredExternal user id: the app's own identifier for the user, whose first authentication creates them.
Request bodyrequiredapplication/json
countrystringrequiredlanguagestringrequiredageintegerrequiredgenderstringrequiredmalefemaleother
timezonestringrequiredIANA timezone name (e.g. "America/Chicago").
should_reward_userbooleanrequiredWhether the caller has determined this user is eligible to earn rewards. Stored on the user record and consulted by the feed endpoints.
currencystringrequiredIdentifier of the user's local currency for displayed earnings. Any currency identifier is accepted, including custom, non-ISO codes (e.g. an app's own virtual currency) — it is not required to be an ISO 4217 code. Where the app configures currencies, it must be one of them (currency_not_configured otherwise); the matching configuration decides how the client renders amounts.
currency_factorstringrequiredDecimal multiplier applied to USD earnings to display them in the user's local currency (local = usd * currency_factor).
usernamestringUnique handle within the calling app. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
picture_urlstringProfile picture URL. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
friend_user_idsarray of stringThe full set of the user's friends' external user ids (empty allowed). When present the stored friend list is replaced with this set; when absent it is left untouched. Ids of unknown users, and the user's own id, are dropped.
ad_bidnumber · double | nullOptional client-reported ad bid. Consumed only when the user is first created: it is stored for analytics and resolves the reward factor persisted on the user. Omitted or sent on a later auth for an existing user, it is ignored and the factor stays at its default (1.0).
Responses
200User authenticated; bearer token issuedS2SUserAuthResponseapplication/json
tokenstringrequiredJWT bearer token for the /v1/* endpoints.
expires_atstring · date-timerequired/s2s/v1/user/{id}#patchUserPartially update a user's stored profile
Service-to-service endpoint called by an app backend with its app
token. Updates only the provided fields on the user named by the
path id; omitted fields keep their stored values. When
friend_user_ids is present (even as an empty array) the user's
friend list is replaced with the given set; ids that match no
existing user of the calling app are dropped silently. ad_bid
and the reward factor derived from it are frozen at user creation
and cannot be changed here.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_timezone | timezone is not a valid IANA timezone name. |
| 400 | too_many_friend_ids | friend_user_ids resolves to more than 100 friends. |
| 400 | currency_not_configured | currency is not one of the app's configured currencies. |
| 404 | user_not_found | No user with this id exists for the calling app. |
| 409 | username_taken | Another user of this app already uses this username. |
Path parameters
idstringrequiredExternal user id: the app's own identifier for the user, whose first authentication creates them.
Request bodyrequiredapplication/json
countrystringlanguagestringageintegergenderstringmalefemaleother
timezonestringIANA timezone name (e.g. "America/Chicago").
should_reward_userbooleanWhether the caller has determined this user is eligible to earn rewards. Stored on the user record and consulted by the feed endpoints.
currencystringIdentifier of the user's local currency for displayed earnings. Any currency identifier is accepted, including custom, non-ISO codes (e.g. an app's own virtual currency) — it is not required to be an ISO 4217 code. Where the app configures currencies, it must be one of them (currency_not_configured otherwise); the matching configuration decides how the client renders amounts.
currency_factorstringDecimal multiplier applied to USD earnings to display them in the user's local currency (local = usd * currency_factor).
usernamestringUnique handle within the calling app. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
picture_urlstringProfile picture URL. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
friend_user_idsarray of stringThe full set of the user's friends' external user ids (empty allowed). When present the stored friend list is replaced with this set; when absent it is left untouched. Ids of unknown users, and the user's own id, are dropped.
Responses
200User updated
No response body.
/s2s/v1/user/{id}#deleteUserDelete a user
| Code | Status | Meaning |
|---|---|---|
user_not_found |
404 | No live user of this app matches the path id (unknown or already deleted). |
Path parameters
idstringrequiredExternal user id: the app's own identifier for the user, whose first authentication creates them.
Responses
200User deleted
No response body.
/s2s/v1/user/{id}/games#getUserGameActivityRead a user's game activity
Service-to-service endpoint called by an app backend with its app token. Returns one user's whole minigame history, resolved for that user: display names translated into their language, rewards converted to their currency, and mission goals carrying their progress. A game only visible to test users is included only for a test user, and a deleted game is never included.
Every collection is always present and may be empty, so a user who has never played reports five empty arrays rather than an error.
| Status | Code | Meaning |
|---|---|---|
| 404 | user_not_found | No live user with this id exists for the app. |
Path parameters
idstringrequiredExternal user id: the app's own identifier for the user, whose first authentication creates them.
Responses
200The user's game activityS2SUserGameActivityResponseapplication/json
completed_eventsarray of S2SGameCompletedEventrequired
The events the user has completed, most recent first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredThe game's display name.
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward the user actually received for this event, as a decimal string in their currency.
completed_atstring · date-timerequiredprogressarray of S2SGameProgressrequired
Derived per-game state, one entry per game the user has played, most recently played first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredbest_scoreintegerrequiredThe user's best metric value across all their rounds of this game: the highest level for a level game, the highest score for a highscore game.
play_countintegerrequiredHow many rounds the user has played of this game.
total_playtime_secondsintegerrequiredTotal seconds the user has spent in this game.
last_played_atstring · date-timerequiredEvery event of the version the user is on for this game, in the order shown to the client. Completed events carry a non-null completed_at.
playsarray of S2SGamePlayrequired
The user's most recent rounds, newest first, capped at the last 10. One entry per round rather than per submission, so a round continued after a second life appears once.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredscoreintegerrequiredThe metric value reached in this round, 0 when the user did not score.
playtime_secondsintegerrequiredHow many seconds the round lasted, summed over its submissions.
played_atstring · date-timerequiredWhen the round began.
joinable_missionsarray of S2SGameMissionOfferrequired
The missions the user could join right now by playing the corresponding game. A mission moves to mission_participations as soon as they join it.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
The mission's goals in the order shown to the client, all at zero progress since the user has not joined yet.
reward_user_currencystringrequiredWhat completing every goal would pay if the user joined now, as a decimal string in their currency.
joinable_untilstring · date-time | nullrequiredWhen the mission stops being joinable, or null when open-ended.
mission_participationsarray of S2SGameMissionParticipationrequired
The user's ongoing mission windows plus every past one whose window ended within the last 14 days, most recent first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
The goals of the mission version the user joined, in the order shown to the client, carrying their progress.
reward_user_currencystringrequiredThe reward received on completion, as a decimal string in the user's currency. Zero unless status is completed.
joined_atstring · date-timerequiredWhen the user joined, i.e. their first counting play.
resets_atstring · date-timerequiredWhen the mission window ends, or ended for a past participation.
/s2s/v1/ads/{id}#fillServerAdFill ad slots with server-reported revenue
Server-to-server counterpart of the client PATCH/v1/ads/{id} fill, called by an app backend with its app token. Attaches server-reported revenue (server_data, currently currency + bid) to ad slots owned by the calling app.
When id is a slot uuid the body fills that single slot via server_data. When id is the literal _ the body's items array fills many slots at once; items must be non-empty and server_data must be omitted in that case. The batch is atomic: any validation error fails the whole request and nothing is written. Slots that don't exist for the app (or were already server-filled) are silently skipped.
Path parameters
idstringrequiredA slot uuid, or _ to fill a batch via the request body's items.
Request bodyrequiredapplication/json
server_dataS2SServerAdData
currencystringrequiredCurrency identifier for the bid. Any currency identifier is accepted, including custom, non-ISO codes — it is not required to be an ISO 4217 code.
bidstringrequiredBid amount as a decimal string (e.g. "1.25").
itemsarray of S2SFillServerAdItem
Per-slot fills, used when the path id is _.
idstring · uuidrequiredThe slot id to fill.
Responses
204Slots filled
No response body.
Schemas
S2SUserFieldsobject
The user profile an app backend owns, every field optional: the body of PATCH/s2s/v1/user/{id}, and the base S2SUserAuthRequest extends with the fields it requires.
countrystringlanguagestringageintegergenderstringmalefemaleother
timezonestringIANA timezone name (e.g. "America/Chicago").
should_reward_userbooleanWhether the caller has determined this user is eligible to earn rewards. Stored on the user record and consulted by the feed endpoints.
currencystringIdentifier of the user's local currency for displayed earnings. Any currency identifier is accepted, including custom, non-ISO codes (e.g. an app's own virtual currency) — it is not required to be an ISO 4217 code. Where the app configures currencies, it must be one of them (currency_not_configured otherwise); the matching configuration decides how the client renders amounts.
currency_factorstringDecimal multiplier applied to USD earnings to display them in the user's local currency (local = usd * currency_factor).
usernamestringUnique handle within the calling app. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
picture_urlstringProfile picture URL. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
friend_user_idsarray of stringThe full set of the user's friends' external user ids (empty allowed). When present the stored friend list is replaced with this set; when absent it is left untouched. Ids of unknown users, and the user's own id, are dropped.
{
"country": "US",
"language": "en",
"age": 0,
"gender": "male",
"timezone": "America/Chicago",
"should_reward_user": true,
"currency": "USD",
"currency_factor": "1.0",
"username": "string",
"picture_url": "string",
"friend_user_ids": [
"string"
]
}The user profile an app backend owns, every field optional: the body of PATCH /s2s/v1/user/{id}, and the base S2SUserAuthRequest extends with the fields it requires.
S2SUserAuthRequestallOf
countrystringrequiredlanguagestringrequiredageintegerrequiredgenderstringrequiredmalefemaleother
timezonestringrequiredIANA timezone name (e.g. "America/Chicago").
should_reward_userbooleanrequiredWhether the caller has determined this user is eligible to earn rewards. Stored on the user record and consulted by the feed endpoints.
currencystringrequiredIdentifier of the user's local currency for displayed earnings. Any currency identifier is accepted, including custom, non-ISO codes (e.g. an app's own virtual currency) — it is not required to be an ISO 4217 code. Where the app configures currencies, it must be one of them (currency_not_configured otherwise); the matching configuration decides how the client renders amounts.
currency_factorstringrequiredDecimal multiplier applied to USD earnings to display them in the user's local currency (local = usd * currency_factor).
usernamestringUnique handle within the calling app. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
picture_urlstringProfile picture URL. When provided it is stored; when omitted the existing stored value is kept. There is no way to clear it back to empty.
friend_user_idsarray of stringThe full set of the user's friends' external user ids (empty allowed). When present the stored friend list is replaced with this set; when absent it is left untouched. Ids of unknown users, and the user's own id, are dropped.
ad_bidnumber · double | nullOptional client-reported ad bid. Consumed only when the user is first created: it is stored for analytics and resolves the reward factor persisted on the user. Omitted or sent on a later auth for an existing user, it is ignored and the factor stays at its default (1.0).
{
"country": "US",
"language": "en",
"age": 0,
"gender": "male",
"timezone": "America/Chicago",
"should_reward_user": true,
"currency": "USD",
"currency_factor": "1.0",
"username": "string",
"picture_url": "string",
"friend_user_ids": [
"string"
],
"ad_bid": 0
}S2SUserAuthResponseobject
tokenstringrequiredJWT bearer token for the /v1/* endpoints.
expires_atstring · date-timerequired{
"token": "string",
"expires_at": "2026-08-09T12:00:00Z"
}S2SFillServerAdRequestobject
Body for PATCH/s2s/v1/ads/{id}. Set server_data to fill the single slot named in the path; set items to fill a batch when the path id is _.
server_dataS2SServerAdData
currencystringrequiredCurrency identifier for the bid. Any currency identifier is accepted, including custom, non-ISO codes — it is not required to be an ISO 4217 code.
bidstringrequiredBid amount as a decimal string (e.g. "1.25").
itemsarray of S2SFillServerAdItem
Per-slot fills, used when the path id is _.
idstring · uuidrequiredThe slot id to fill.
server_dataS2SServerAdDatarequired
currencystringrequiredCurrency identifier for the bid. Any currency identifier is accepted, including custom, non-ISO codes — it is not required to be an ISO 4217 code.
bidstringrequiredBid amount as a decimal string (e.g. "1.25").
{
"server_data": {
"currency": "string",
"bid": "string"
},
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"server_data": {
"currency": "string",
"bid": "string"
}
}
]
}Body for PATCH /s2s/v1/ads/{id}. Set server_data to fill the single slot named in the path; set items to fill a batch when the path id is _.
S2SFillServerAdItemobject
idstring · uuidrequiredThe slot id to fill.
server_dataS2SServerAdDatarequired
currencystringrequiredCurrency identifier for the bid. Any currency identifier is accepted, including custom, non-ISO codes — it is not required to be an ISO 4217 code.
bidstringrequiredBid amount as a decimal string (e.g. "1.25").
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"server_data": {
"currency": "string",
"bid": "string"
}
}S2SServerAdDataobject
Server-reported ad revenue for a slot.
currencystringrequiredCurrency identifier for the bid. Any currency identifier is accepted, including custom, non-ISO codes — it is not required to be an ISO 4217 code.
bidstringrequiredBid amount as a decimal string (e.g. "1.25").
{
"currency": "string",
"bid": "string"
}S2SUserGameActivityResponseobject
completed_eventsarray of S2SGameCompletedEventrequired
The events the user has completed, most recent first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredThe game's display name.
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward the user actually received for this event, as a decimal string in their currency.
completed_atstring · date-timerequiredprogressarray of S2SGameProgressrequired
Derived per-game state, one entry per game the user has played, most recently played first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredbest_scoreintegerrequiredThe user's best metric value across all their rounds of this game: the highest level for a level game, the highest score for a highscore game.
play_countintegerrequiredHow many rounds the user has played of this game.
total_playtime_secondsintegerrequiredTotal seconds the user has spent in this game.
last_played_atstring · date-timerequiredeventsarray of S2SGameEventStaterequired
Every event of the version the user is on for this game, in the order shown to the client. Completed events carry a non-null completed_at.
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward completing this event pays, as a decimal string in the user's currency. This is the prospective amount derived from the event's configuration, so it can differ from what a completed event actually paid — read S2SGameCompletedEvent.reward_user_currency for that.
completed_atstring · date-time | nullrequiredWhen the user completed this event, or null if they have not.
playsarray of S2SGamePlayrequired
The user's most recent rounds, newest first, capped at the last 10. One entry per round rather than per submission, so a round continued after a second life appears once.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredscoreintegerrequiredThe metric value reached in this round, 0 when the user did not score.
playtime_secondsintegerrequiredHow many seconds the round lasted, summed over its submissions.
played_atstring · date-timerequiredWhen the round began.
joinable_missionsarray of S2SGameMissionOfferrequired
The missions the user could join right now by playing the corresponding game. A mission moves to mission_participations as soon as they join it.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
goalsarray of S2SGameMissionGoalrequired
The mission's goals in the order shown to the client, all at zero progress since the user has not joined yet.
goal_idstringrequiredThe goal's internal name (e.g. "complete-levels").
goal_namestringrequiredThe goal's display name in the user's language, falling back to English and then to its internal name.
valuenumber · doublerequiredThe user's progress toward threshold, 0 while unjoined.
thresholdnumber · doublerequiredThe value at which the goal completes. Frozen when the user joins, so it does not move with a later config change.
completedbooleanrequiredreward_user_currencystringrequiredWhat completing every goal would pay if the user joined now, as a decimal string in their currency.
joinable_untilstring · date-time | nullrequiredWhen the mission stops being joinable, or null when open-ended.
mission_participationsarray of S2SGameMissionParticipationrequired
The user's ongoing mission windows plus every past one whose window ended within the last 14 days, most recent first.
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
goalsarray of S2SGameMissionGoalrequired
The goals of the mission version the user joined, in the order shown to the client, carrying their progress.
goal_idstringrequiredThe goal's internal name (e.g. "complete-levels").
goal_namestringrequiredThe goal's display name in the user's language, falling back to English and then to its internal name.
valuenumber · doublerequiredThe user's progress toward threshold, 0 while unjoined.
thresholdnumber · doublerequiredThe value at which the goal completes. Frozen when the user joins, so it does not move with a later config change.
completedbooleanrequiredreward_user_currencystringrequiredThe reward received on completion, as a decimal string in the user's currency. Zero unless status is completed.
joined_atstring · date-timerequiredWhen the user joined, i.e. their first counting play.
resets_atstring · date-timerequiredWhen the mission window ends, or ended for a past participation.
{
"completed_events": [
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"event_id": "string",
"event_name": "string",
"reward_user_currency": "string",
"completed_at": "2026-08-09T12:00:00Z"
}
],
"progress": [
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"best_score": 0,
"play_count": 0,
"total_playtime_seconds": 0,
"last_played_at": "2026-08-09T12:00:00Z",
"events": [
{
"event_id": "string",
"event_name": "string",
"reward_user_currency": "string",
"completed_at": "2026-08-09T12:00:00Z"
}
]
}
],
"plays": [
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"score": 0,
"playtime_seconds": 0,
"played_at": "2026-08-09T12:00:00Z"
}
],
"joinable_missions": [
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"mission_id": "string",
"mission_name": "string",
"goals": [
{
"goal_id": "string",
"goal_name": "string",
"value": 0,
"threshold": 0,
"completed": true
}
],
"reward_user_currency": "string",
"joinable_until": "2026-08-09T12:00:00Z"
}
],
"mission_participations": [
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"mission_id": "string",
"mission_name": "string",
"status": "ongoing",
"goals": [
{
"goal_id": "string",
"goal_name": "string",
"value": 0,
"threshold": 0,
"completed": true
}
],
"reward_user_currency": "string",
"joined_at": "2026-08-09T12:00:00Z",
"resets_at": "2026-08-09T12:00:00Z"
}
]
}S2SGameTypeenum
levelhighscore
"level"S2SGameCompletedEventobject
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredThe game's display name.
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward the user actually received for this event, as a decimal string in their currency.
completed_atstring · date-timerequired{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"event_id": "string",
"event_name": "string",
"reward_user_currency": "string",
"completed_at": "2026-08-09T12:00:00Z"
}S2SGameProgressobject
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredbest_scoreintegerrequiredThe user's best metric value across all their rounds of this game: the highest level for a level game, the highest score for a highscore game.
play_countintegerrequiredHow many rounds the user has played of this game.
total_playtime_secondsintegerrequiredTotal seconds the user has spent in this game.
last_played_atstring · date-timerequiredeventsarray of S2SGameEventStaterequired
Every event of the version the user is on for this game, in the order shown to the client. Completed events carry a non-null completed_at.
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward completing this event pays, as a decimal string in the user's currency. This is the prospective amount derived from the event's configuration, so it can differ from what a completed event actually paid — read S2SGameCompletedEvent.reward_user_currency for that.
completed_atstring · date-time | nullrequiredWhen the user completed this event, or null if they have not.
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"best_score": 0,
"play_count": 0,
"total_playtime_seconds": 0,
"last_played_at": "2026-08-09T12:00:00Z",
"events": [
{
"event_id": "string",
"event_name": "string",
"reward_user_currency": "string",
"completed_at": "2026-08-09T12:00:00Z"
}
]
}S2SGameEventStateobject
event_idstringrequiredThe event's internal name (e.g. "level-50").
event_namestringrequiredThe event's display name in the user's language, falling back to English and then to its internal name.
reward_user_currencystringrequiredThe reward completing this event pays, as a decimal string in the user's currency. This is the prospective amount derived from the event's configuration, so it can differ from what a completed event actually paid — read S2SGameCompletedEvent.reward_user_currency for that.
completed_atstring · date-time | nullrequiredWhen the user completed this event, or null if they have not.
{
"event_id": "string",
"event_name": "string",
"reward_user_currency": "string",
"completed_at": "2026-08-09T12:00:00Z"
}S2SGamePlayobject
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredscoreintegerrequiredThe metric value reached in this round, 0 when the user did not score.
playtime_secondsintegerrequiredHow many seconds the round lasted, summed over its submissions.
played_atstring · date-timerequiredWhen the round began.
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"score": 0,
"playtime_seconds": 0,
"played_at": "2026-08-09T12:00:00Z"
}S2SGameMissionOfferobject
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
goalsarray of S2SGameMissionGoalrequired
The mission's goals in the order shown to the client, all at zero progress since the user has not joined yet.
goal_idstringrequiredThe goal's internal name (e.g. "complete-levels").
goal_namestringrequiredThe goal's display name in the user's language, falling back to English and then to its internal name.
valuenumber · doublerequiredThe user's progress toward threshold, 0 while unjoined.
thresholdnumber · doublerequiredThe value at which the goal completes. Frozen when the user joins, so it does not move with a later config change.
completedbooleanrequiredreward_user_currencystringrequiredWhat completing every goal would pay if the user joined now, as a decimal string in their currency.
joinable_untilstring · date-time | nullrequiredWhen the mission stops being joinable, or null when open-ended.
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"mission_id": "string",
"mission_name": "string",
"goals": [
{
"goal_id": "string",
"goal_name": "string",
"value": 0,
"threshold": 0,
"completed": true
}
],
"reward_user_currency": "string",
"joinable_until": "2026-08-09T12:00:00Z"
}S2SGameMissionStatusenum
Where a joined mission window stands. There is no not_started: a mission the user has not joined is reported in joinable_missions instead.
ongoingcompletedfailed
"ongoing"S2SGameMissionParticipationobject
game_idstringrequiredThe game slug (e.g. "apex-layers").
game_namestringrequiredmission_idstringrequiredThe mission's internal name (e.g. "daily-grind").
mission_namestringrequiredThe mission's display name in the user's language, falling back to English and then to its internal name.
goalsarray of S2SGameMissionGoalrequired
The goals of the mission version the user joined, in the order shown to the client, carrying their progress.
goal_idstringrequiredThe goal's internal name (e.g. "complete-levels").
goal_namestringrequiredThe goal's display name in the user's language, falling back to English and then to its internal name.
valuenumber · doublerequiredThe user's progress toward threshold, 0 while unjoined.
thresholdnumber · doublerequiredThe value at which the goal completes. Frozen when the user joins, so it does not move with a later config change.
completedbooleanrequiredreward_user_currencystringrequiredThe reward received on completion, as a decimal string in the user's currency. Zero unless status is completed.
joined_atstring · date-timerequiredWhen the user joined, i.e. their first counting play.
resets_atstring · date-timerequiredWhen the mission window ends, or ended for a past participation.
{
"game_id": "string",
"game_name": "string",
"game_type": "level",
"mission_id": "string",
"mission_name": "string",
"status": "ongoing",
"goals": [
{
"goal_id": "string",
"goal_name": "string",
"value": 0,
"threshold": 0,
"completed": true
}
],
"reward_user_currency": "string",
"joined_at": "2026-08-09T12:00:00Z",
"resets_at": "2026-08-09T12:00:00Z"
}S2SGameMissionGoalobject
goal_idstringrequiredThe goal's internal name (e.g. "complete-levels").
goal_namestringrequiredThe goal's display name in the user's language, falling back to English and then to its internal name.
valuenumber · doublerequiredThe user's progress toward threshold, 0 while unjoined.
thresholdnumber · doublerequiredThe value at which the goal completes. Frozen when the user joins, so it does not move with a later config change.
completedbooleanrequired{
"goal_id": "string",
"goal_name": "string",
"value": 0,
"threshold": 0,
"completed": true
}RewardDisplayIconobject
Render the amount with the icon at icon_url on the given side of it.
{
"type": "icon",
"icon_url": "string",
"position": "left"
}RewardDisplayPlainobject
Render the amount with symbol on the given side of it, e.g. "$1.50".
{
"type": "plain",
"symbol": "string",
"position": "left"
}ComicPartPageobject
page_typestringrequiredindexintegerrequiredThe index of the page in the part.
video_urlstringrequired{
"page_type": "page",
"index": 0,
"video_url": "string"
}ComicPartPageAdallOf
page_typestringrequiredslot_idstring · uuidrequiredThe ad slot's id. Use it to fill the slot via PATCH/v1/ads/{id}.
typestringrequiredofferexternal
lock_secondsintegerrequiredHow long the ad is locked before it can be skipped.
external_sub_typestring | nullrequiredSub-type of an external ad; null for non-external ads.
nativeinterstitialrewarded
{
"page_type": "ad",
"slot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "offer",
"lock_seconds": 0,
"external_sub_type": "native"
}Errorobject
errorstringcodestringMachine-readable error code; omitted when the error has none.
{
"error": "string",
"code": "string"
}VideoFriendLikeallOf
uidstringrequiredThe friend's external user id (app-scoped public identifier).
usernamestring | nullrequiredThe friend's username; null when unset.
picture_urlstring | nullrequiredThe friend's profile picture URL; null when unset.
liked_atstring · date-timerequiredWhen the friend liked the video.
{
"uid": "string",
"username": "string",
"picture_url": "string",
"liked_at": "2026-08-09T12:00:00Z"
}Friendobject
A compact projection of a friend user.
uidstringrequiredThe friend's external user id (app-scoped public identifier).
usernamestring | nullrequiredThe friend's username; null when unset.
picture_urlstring | nullrequiredThe friend's profile picture URL; null when unset.
{
"uid": "string",
"username": "string",
"picture_url": "string"
}CreatorLinkobject
textstringrequiredurlstringrequired{
"text": "string",
"url": "string"
}RewardDisplayPositionenum
Which side of the amount the icon or symbol goes on.
leftright
"left"GameCategoryenum
Game category slug.
arcadeboard-cardcasualentertainmentpuzzlerole-playingstrategyother
"arcade"GameTypeenum
levelhighscore
"level"GameEventobject
idstring · uuidrequiredtranslationsmap<string, string>required
«key»stringsort_orderintegerrequiredrewardRewardrequired
totalstringrequiredtotal = bonus + base
bonusstringrequiredbasestringrequireddisplayoneOf · 2 variantsrequired
How the client should render the amounts, as configured for the user's currency. Falls back to a leading "$" where the app configures no rendering for that currency, and wherever there is no user to read a currency from, such as the public games catalog.
type = "icon" RewardDisplayIcon
type = "plain" RewardDisplayPlain
threshold_typestringrequiredlevelhighscoregames_playedcountergaugeset
blocked_bystring · uuid | nullrequiredpublic_id of the event that must be completed first, or null.
marks_endgamebooleanrequiredWhether completing this event marks the user as having reached the endgame. At most one event per version sets this.
completed_atstring · date-time | nullrequired{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"translations": {
"key": "string"
},
"sort_order": 0,
"reward": {
"total": "string",
"bonus": "string",
"base": "string",
"display": {
"type": "icon",
"icon_url": "string",
"position": "left"
}
},
"threshold_type": "level",
"blocked_by": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"marks_endgame": true,
"completed_at": "2026-08-09T12:00:00Z"
}Showing the icon variant.
GameMissionobject
idstring · uuidrequiredtranslationsmap<string, string>required
«key»stringrewardRewardrequired
totalstringrequiredtotal = bonus + base
bonusstringrequiredbasestringrequireddisplayoneOf · 2 variantsrequired
How the client should render the amounts, as configured for the user's currency. Falls back to a leading "$" where the app configures no rendering for that currency, and wherever there is no user to read a currency from, such as the public games catalog.
type = "icon" RewardDisplayIcon
type = "plain" RewardDisplayPlain
statusstringrequirednot_startedongoingcompleted
joinable_untilstring · date-time | nullrequiredUntil when the mission can be started, or null when unbounded.
minutes_to_completeintegerrequiredMinutes the user has to complete the mission: the full cadence window (1440 for daily) before starting, the remainder until the mission resets once started.
goalsarray of GameMissionGoalrequired
translationsmap<string, string>required
«key»stringvaluenumber · doublerequiredThe user's current progress toward the goal.
thresholdnumber · doublerequiredThe value at which the goal completes.
completedbooleanrequired{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"translations": {
"key": "string"
},
"reward": {
"total": "string",
"bonus": "string",
"base": "string",
"display": {
"type": "icon",
"icon_url": "string",
"position": "left"
}
},
"status": "not_started",
"joinable_until": "2026-08-09T12:00:00Z",
"minutes_to_complete": 0,
"goals": [
{
"translations": {
"key": "string"
},
"value": 0,
"threshold": 0,
"completed": true
}
]
}Showing the icon variant.
GameStatemap
The user's current game state keyed by value name: the latest snapshot, or every configured value at its zero state when the user has not played yet. Which field of each value is populated follows the value's configured type.
«key»GameStateValue
totalinteger · int64A counter's or gauge's current total. Both only ever move by whole increments, so this is a whole number - exact across the full int64 range, of which a client reading it into a double can represent up to 2^53.
numbernumber · doubleA number value's current value.
stringstringA string value's current value.
booleanbooleanA boolean value's current value.
setarray of stringA set value's current elements, sorted. Always present (and empty when nothing is held) for set values.
listarray of anyA list value's current items in order, each shaped by the item schema configured for the value. Always present (and empty when nothing is held) for list values.
{
"key": {
"total": 0,
"number": 0,
"string": "string",
"boolean": true,
"set": [
"string"
],
"list": []
}
}GameFriendHighscoreallOf
uidstringrequiredThe friend's external user id (app-scoped public identifier).
usernamestring | nullrequiredThe friend's username; null when unset.
picture_urlstring | nullrequiredThe friend's profile picture URL; null when unset.
highscoreintegerrequiredThe friend's best result for this game (highest level for level games, highest score for highscore games).
{
"uid": "string",
"username": "string",
"picture_url": "string",
"highscore": 0
}GameSectionenum
Which block of the games tab a game belongs to, for rendering the section headers, in the order the sections appear: test holds the test-user-only games and reaches test users only, continue_playing the games the user is part-way through, discover the rest. Responses that rank nothing for a specific user — a single game, the public catalog, a game injected into the feed — report discover.
testcontinue_playingdiscover
"test"Rewardobject
totalstringrequiredtotal = bonus + base
bonusstringrequiredbasestringrequireddisplayoneOf · 2 variantsrequired
How the client should render the amounts, as configured for the user's currency. Falls back to a leading "$" where the app configures no rendering for that currency, and wherever there is no user to read a currency from, such as the public games catalog.
type = "icon" RewardDisplayIcon
type = "plain" RewardDisplayPlain
{
"total": "string",
"bonus": "string",
"base": "string",
"display": {
"type": "icon",
"icon_url": "string",
"position": "left"
}
}Showing the icon variant.
GameMissionGoalobject
translationsmap<string, string>required
«key»stringvaluenumber · doublerequiredThe user's current progress toward the goal.
thresholdnumber · doublerequiredThe value at which the goal completes.
completedbooleanrequired{
"translations": {
"key": "string"
},
"value": 0,
"threshold": 0,
"completed": true
}GameStateValueobject
One state value. Exactly one field carries the value, chosen by the type it is configured as: total for counter and gauge values, number for number values, string/boolean/set/list for the others. The fields that do not apply are omitted.
totalinteger · int64A counter's or gauge's current total. Both only ever move by whole increments, so this is a whole number - exact across the full int64 range, of which a client reading it into a double can represent up to 2^53.
numbernumber · doubleA number value's current value.
stringstringA string value's current value.
booleanbooleanA boolean value's current value.
setarray of stringA set value's current elements, sorted. Always present (and empty when nothing is held) for set values.
listarray of anyA list value's current items in order, each shaped by the item schema configured for the value. Always present (and empty when nothing is held) for list values.
{
"total": 0,
"number": 0,
"string": "string",
"boolean": true,
"set": [
"string"
],
"list": []
}One state value. Exactly one field carries the value, chosen by the type it is configured as: total for counter and gauge values, number for number values, string/boolean/set/list for the others. The fields that do not apply are omitted.
ComicPartobject
indexintegerrequiredThe index of the part in the comic.
versionstringrequiredThe version of the part.
titlestringrequireddescriptionstringrequiredunlockedbooleanrequiredWhether the part is unlocked (already read, or the single next readable part). When false, music_url and pages are null. Pages are only ever populated by GET /v1/comics/{comic_id}/parts/{part_index}, so an unlocked part still has null pages in the list/detail/feed views.
finished_reading_atstring · date-time | nullrequiredThe timestamp the user finished reading the part.
rewardRewardrequired
totalstringrequiredtotal = bonus + base
bonusstringrequiredbasestringrequireddisplayoneOf · 2 variantsrequired
How the client should render the amounts, as configured for the user's currency. Falls back to a leading "$" where the app configures no rendering for that currency, and wherever there is no user to read a currency from, such as the public games catalog.
type = "icon" RewardDisplayIcon
type = "plain" RewardDisplayPlain
cover_urlstringrequiredmusic_urlstring | nullrequiredThe URL of the music for the part. Null if the part is locked.
pagesarray of ComicPartPagesItem | nullrequired
The pages and ads of the part. Only populated by GET /v1/comics/{comic_id}/parts/{part_index} (the full part view); null in the list/detail/feed views and for locked parts.
page_type = "page" ComicPartPage
page_typestringrequiredindexintegerrequiredThe index of the page in the part.
video_urlstringrequiredpage_type = "ad" ComicPartPageAd
page_typestringrequiredslot_idstring · uuidrequiredThe ad slot's id. Use it to fill the slot via PATCH/v1/ads/{id}.
typestringrequiredofferexternal
lock_secondsintegerrequiredHow long the ad is locked before it can be skipped.
external_sub_typestring | nullrequiredSub-type of an external ad; null for non-external ads.
nativeinterstitialrewarded
rating_countinteger · int64requiredNumber of 1-5 star ratings submitted for this part. Reported as 0 (together with rating_avg) until the part has collected at least 5 ratings, so early, unrepresentative scores are hidden.
rating_avgnumber · doublerequiredAverage star rating (1.0-5.0), truncated to one decimal place. Reported as 0 until the part has collected at least 5 ratings (see rating_count).
read_time_avgnumber · doublerequiredMedian time, in seconds, users spent reading this part (reads over one hour are excluded as outliers). 0 when the part has not been read yet.
{
"index": 0,
"version": "string",
"title": "string",
"description": "string",
"unlocked": true,
"finished_reading_at": "2026-08-09T12:00:00Z",
"reward": {
"total": "string",
"bonus": "string",
"base": "string",
"display": {
"type": "icon",
"icon_url": "string",
"position": "left"
}
},
"cover_url": "string",
"music_url": "string",
"pages": [
{
"page_type": "page",
"index": 0,
"video_url": "string"
},
{
"page_type": "ad",
"slot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "offer",
"lock_seconds": 0,
"external_sub_type": "native"
}
],
"rating_count": 0,
"rating_avg": 0,
"read_time_avg": 0
}Showing all 2 variants of the union.
ComicFriendReadingProgressallOf
uidstringrequiredThe friend's external user id (app-scoped public identifier).
usernamestring | nullrequiredThe friend's username; null when unset.
picture_urlstring | nullrequiredThe friend's profile picture URL; null when unset.
last_read_part_indexintegerrequiredThe highest part index the friend has read in this comic.
{
"uid": "string",
"username": "string",
"picture_url": "string",
"last_read_part_index": 0
}ComicPartPagesItemoneOf · 2
page_type = "page" ComicPartPage
page_typestringrequiredindexintegerrequiredThe index of the page in the part.
video_urlstringrequiredpage_type = "ad" ComicPartPageAd
page_typestringrequiredslot_idstring · uuidrequiredThe ad slot's id. Use it to fill the slot via PATCH/v1/ads/{id}.
typestringrequiredofferexternal
lock_secondsintegerrequiredHow long the ad is locked before it can be skipped.
external_sub_typestring | nullrequiredSub-type of an external ad; null for non-external ads.
nativeinterstitialrewarded
{
"page_type": "page",
"index": 0,
"video_url": "string"
}Showing the page variant.
Adobject
slot_idstring · uuidrequiredThe ad slot's id. Use it to fill the slot via PATCH/v1/ads/{id}.
typestringrequiredofferexternal
lock_secondsintegerrequiredHow long the ad is locked before it can be skipped.
external_sub_typestring | nullrequiredSub-type of an external ad; null for non-external ads.
nativeinterstitialrewarded
{
"slot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "offer",
"lock_seconds": 0,
"external_sub_type": "native"
}