TANITARDevelopers
GuidesGamification

Event ingestion

Send player activity to Tanitar as events, idempotently, and read what each event advanced.

Your backend reports player activity by sending one event per occurrence. Each event names one player and carries a payload that matches the event's declaration in your event registry.

RouteScopeIdempotency
POST /v1/gamification/eventsgamification-events:ingestIdempotency-Key required

Send an event

With match-won.json holding the event:

{
  "eventKey": "match:won",
  "playerId": "player-2231",
  "payload": { "mode": "ranked", "kills": 14, "teamId": "team-orange" }
}
curl -X POST "$GAMIFICATION_API_URL/v1/gamification/events" --data @match-won.json \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Idempotency-Key: match-8841-player-2231" \
  -H "Content-Type: application/json"
FieldRules
eventKeyA published event in your registry, or a built-in event such as player:game-won. An unknown, draft or archived event is 422 GAMIFICATION_EVENT_NOT_ADMISSIBLE.
playerIdYour identifier for the player, 1 to 200 characters. Tanitar does not look the player up; it advances progress for exactly the player you name, within your tenant.
payloadAn object. Every key must be a field the event declares or a platform field, with the declared type, and every required field must be present. Otherwise 422 GAMIFICATION_EVENT_PAYLOAD_INVALID and nothing is recorded.

Include teamId in the payload when the activity belongs to a team; it is what places the player's mission XP on the team leaderboard.

Idempotency

Use an Idempotency-Key that identifies the real-world occurrence, such as a match id plus a player id, rather than a random value per attempt. The key is 1 to 256 characters from letters, digits, ., _, : and -.

Two layers protect you from double counting:

  • The request layer. A retry with the same key and the same body returns the original response. The same key with a different body is 409 ACCESS_REPLAYED; a retry while the first request is still running is 409 ACCESS_IDEMPOTENCY_IN_PROGRESS. See Idempotency.
  • The event record. Tanitar stores each event durably under (eventKey, playerId, Idempotency-Key). Sending that event again later, whether after the request layer has forgotten the key or from your own queue's redelivery, answers with the outcome already recorded and "duplicate": true, and advances nothing twice.

The response

{
  "admitted": true,
  "eventKey": "match:won",
  "duplicate": false,
  "outcomes": [
    {
      "missionKey": "win-three-ranked",
      "missionRevision": 2,
      "currentCount": 3,
      "requiredCount": 3,
      "status": "completed",
      "completed": true
    }
  ],
  "ineligible": [
    { "missionKey": "gold-weekly-grind", "missionRevision": 1, "reason": "MISSION_TIER_NOT_ELIGIBLE" }
  ]
}
  • outcomes lists every mission the event advanced, with the player's count after this event. completed: true means this event completed the mission.
  • ineligible lists missions whose conditions matched but that the player may not advance because the mission is restricted to membership tiers: MISSION_TIER_NOT_ELIGIBLE when the player does not hold one of them, MISSION_ELIGIBILITY_UNRESOLVED when the tier registry could not be read (unknown is treated as not eligible). A duplicate answers with the stored outcomes and an empty ineligible list.
  • A mission whose conditions do not hold, that is disabled or unpublished, outside its startsAt/endsAt window, or already completed for the current period, does not appear.

What happens after ingestion

Everything below happens in one transaction with the ingestion itself. If any step fails, nothing is recorded, and you can resend the same event with the same key.

  1. The event is stored against the player for 90 days, so it can be replayed later.
  2. Each matching mission's progress for the current period is advanced by one. A gamification.progress.updated event is published per mission.
  3. A mission that reaches targetCount completes exactly once, however many events race for it. Its rewards are paid as described in How mission rewards settle. XP may raise the player's level (gamification.xp.granted), and the first completion of a UTC day advances the streak (gamification.streak.advanced).
  4. If the active battle-pass season awards flat battle-pass XP for this event, it is added once per event.

Periods are UTC. When a daily or weekly period ends, unfinished progress for it becomes expired and the next event starts a fresh count.

Subscribe to these events with a webhook endpoint; the payloads are in the event catalog.

Errors

StatuserrorCodeMeaning
400(validation)The body does not have the shape above
400ACCESS_IDEMPOTENCY_REQUIRED / ACCESS_IDEMPOTENCY_INVALIDIdempotency-Key is missing or malformed
409ACCESS_REPLAYED / ACCESS_IDEMPOTENCY_IN_PROGRESSThe key was used with a different body, or the first request is still running
422GAMIFICATION_EVENT_NOT_ADMISSIBLEThe event is not published and not built in
422GAMIFICATION_EVENT_PAYLOAD_INVALIDA payload field is undeclared, has the wrong type, or a required field is missing

All errors use the shared envelope; see Errors. Ingestion is in the mutation rate class; see Rate limits.

On this page