Skip to main content
InternalMarkdown

Partner KPI API

Your KPIs, covering only the players linked to you. Authenticate with OAuth client credentials or PP-HMAC-SHA256 (hmac-signing.md) using a credential with scope kpis.read. Your party is taken from the credential; there is no party parameter. Definitions are in docs/kpis.md.

GET /v1/kpis/catalogue​

Returns the KPIs your party type can read.

{ "kpis": [ { "id": "own.rounds", "label": "Rounds", "unit": "count", "markets": true,
"grains": ["day", "week", "month"], "account_floor": true } ] }

GET /v1/kpis/own​

ParamRequiredNotes
kpiyesAn ID from the catalogue
from, toyesYYYY-MM-DD (UTC), inclusive, under 400 days
grainnoday (default), week (ISO weeks starting Monday) or month
marketnoISO 3166-1 alpha-2, e.g. MT. Only for KPIs with markets: true
{
"kpi": "own.rounds", "label": "Rounds", "unit": "count",
"from": "2026-09-01", "to": "2026-09-30", "grain": "week", "market": "MT",
"series": [ { "period": "2026-08-31", "value": 1240 },
{ "period": "2026-09-07", "value": null, "suppressed": true } ],
"total": { "value": 5120 },
"previous": { "from": "2026-08-02", "to": "2026-08-31", "value": 4800 },
"delta_pct": 0.0667,
"generated_at": "2026-10-13T12:00:00.000Z"
}
  • Every period in the range is returned. A period with no activity has value 0; for ratios and latencies it is null.
  • A cell is suppressed when fewer than 50 distinct accounts are behind it (ADR 0026). This applies to KPIs with account_floor: true.
  • previous is the period of the same length immediately before from. delta_pct is null when either total is missing or the previous total is 0.

Errors (400): kpi_unknown, kpi_not_available_for_party, date_range_invalid, date_range_too_long, grain_invalid, market_invalid and market_not_supported. 503 kpi_backend_not_configured and 502 kpi_backend_error are server-side problems.

Every call is recorded as a kpi.api.accessed domain event (party, client, KPI).

Own-tier KPIs (v1)​

IDUnitWhoAccount floorMarket
own.roundscountoperator, provider✓✓
own.bet_eur / own.win_eur / own.ggrEURoperator, provider✓✓
own.active_accountscountoperator, provider✓✓
own.linked_playerscount (distinct in period)operator, provider✓
own.new_links / own.unlinkscountoperator, provider
own.link_intents / own.link_conversioncount / ratiooperator
own.points_issuedpointsoperator, provider✓
own.ingest_acceptanceratiooperator, provider
own.batch_latency_p95secondsprovider

GET /v1/kpis/overview​

This is the partner overview (Spec v0.1 §13), computed from the canonical facts (ingest.md). Pass from and to as YYYY-MM-DD. The previous period is the one of the same length immediately before.

Every KPI in the registry (analytics/overview/kpi-registry.ts) comes back typed:

{ "id": "net_revenue", "name": "Net revenue (NGR)", "description": "…", "unit": "eur",
"value": 841.54, "trend": { "previous": 1003.2, "delta": -0.161, "kind": "relative", "direction": "up" },
"dataAvailability": "available", "source": ["operator_commercial"], "requirement": null,
"lastUpdated": "2026-10-13T12:40:05.000Z" }

dataAvailability is never faked as a zero:

ValueMeaning
availableComputed from delivered facts
requires_feedrequirement names the missing feed, e.g. "Requires operator commercial feed"
not_applicableNot shown to this audience, e.g. commercial and reward KPIs for studios (§13.2)
suppressedFewer than 50 active players in the range

Which players count:

  • Operators: their own players.
  • Studios: players of their games.
  • Affiliates: only their verified-attributed players (§13.3).

Ratio trends are in percentage points; other trends are relative changes.

Retention uses fixed observation windows: D1 = days 1–7, D7 = days 7–13, D30 = days 30–36. Only new players whose whole window has elapsed are counted, so recent cohorts aren't biased low.

panels carries the chart data, each with its own availability:

  • activity
  • cohorts (weekly, or monthly when weekly cohorts would fall under the floor)
  • mix
  • geo (markets under the floor fold into Other)
  • segments (now and previous)
  • campaigns (affiliates)
  • rewards
  • missions
  • reward_impressions (requires_feed: impressions are not a v1 feed)

GET /v1/kpis/breakdown​

Same parameters as /own, plus dimension set to market, game or partner. A partner is one of your own integration counterparts: a studio if you are an operator, an operator if you are a studio. Activity KPIs only.

The response contains the top 10 rows by value, each with its share of the visible total (additive KPIs only), then Other (n). Rows under the account floor are never listed; they are counted in suppressed_rows.

GET /v1/kpis/cohorts/retention​

Takes from and to as YYYY-MM, defaulting to the last 12 closed months. Each cohort is the month of a player's first round with you. retention[o] is the share of that cohort active again o months later. Cohorts under the account floor are suppressed.

network is the pooled retention of other parties of your type. It is only published when at least 5 parties contribute, there are at least 50 accounts, and no party dominates; otherwise network_reason says why.

Takes from and to as YYYY-MM-DD.

  • Operators: link started → linked → played within 30 days.
  • Studios: linked through your integrations → played your games within 30 days.

Each step gives its rate against the first step and its step_rate against the previous step. The whole funnel is suppressed when the first step is under the account floor.

/own also returns previous_series: the period before, aligned by position with series, so charts can overlay it.

Comparison and benchmark periods​

Comparisons and benchmarks use fixed, closed periods only. grain=month with period=YYYY-MM, or grain=week with period=YYYY-Www (ISO week). An open or future period is rejected with period_not_closed. Results are recomputed every 6 hours for recent periods.

GET /v1/kpis/compare​

Compares your players with the rest of the PlayerPal network: players of other parties of your type. See kpis.md, Network comparison KPIs.

ParamRequiredNotes
kpiyesA cmp.* ID (table below)
period, grainyesSee above
marketnoIf that market is too thin to publish, you get the global figure with fallback: "global"
{
"kpi": "cmp.bet_per_player", "period": "2026-09", "grain": "month",
"from": "2026-09-01", "to": "2026-09-30", "market": null, "fallback": null,
"you": 45,
"network": { "baseline": "rest_of_network", "p25": 20, "p50": 36, "p75": 52, "mean": 38.4,
"size_band": "100-999", "parties_band": "5-9" },
"index": 125, "band": "p50-p75",
"suppressed": false, "reason": null, "computed_at": "…"
}
  • you is your players' median for per-player KPIs, or your ratio for party KPIs.
  • index is you ÷ network median × 100.
  • band is the quartile of the network distribution your value falls in.
  • Network size and party count are given only as bands.

reason (when anything is withheld):

ReasonMeaning
peer_floorThe rest of the network spans fewer than 5 parties of your type
account_floorThe rest of the network has fewer than 50 accounts
dominanceOne party has more than 50% of the measure, or two have more than 75%
y_floorYour own population is under 50 accounts; the network is still shown
elsewhere_floorCross-party KPI where your players' activity elsewhere spans fewer than 5 parties, or is dominated by one
not_computedNo data for that period yet
cmp.* KPIWhoKind
cmp.active_days, cmp.rounds_per_player, cmp.bet_per_player, cmp.ggr_per_playeroperator, providerYour players' median vs the network's player distribution
cmp.points_per_playeroperatorAs above (global only)
cmp.avg_stake, cmp.payout_ratio, cmp.churn_rateoperator, providerYour ratio vs other parties' ratios
cmp.link_conversionoperatorAs above (global only)
cmp.multi_homing, cmp.overlap_rate, cmp.share_of_walletoperator, providerCross-party (elsewhere_floor applies)

GET /v1/kpis/compare/summary​

Takes period, grain and optionally market. Returns every cmp.* KPI for your party type in one response, as { …period fields, kpis: [ { kpi, fallback, you, network, index, band, suppressed, reason } ] }.

GET /v1/kpis/benchmarks​

Compares you with anonymous peers of your party type. Takes the same period, grain and market parameters.

{ "kpi": "own.bet_eur", "period": "2026-09", "grain": "month", "market": null, "fallback": null,
"peers": { "party_type": "OPERATOR", "count_band": "5-9", "p25": 1200, "p50": 1800, "p75": 2600, "mean": 1950 },
"band": "p0-p25", "suppressed": false, "reason": null, "computed_at": "…" }

Benchmarkable KPIs: own.rounds, own.bet_eur, own.ggr and own.active_accounts. These are global and per market. own.linked_players, own.ingest_acceptance and own.link_conversion are global only. A segment is published only when at least 6 parties are in it (you plus at least 5 others) and no peer dominates it.

Staff​

GET /v1/staff/kpis/own?party=<public party id>&kpi=… takes the same parameters. It is not floored and is audited as kpi.api.accessed with actor staff.

Analytics workstation (/v1/kpis/explore/*)​

These endpoints back the partner portal's Analytics pages. Every one is scoped to the authenticated party. Each takes from and to (YYYY-MM-DD) and these optional filters:

FilterFormatExample
gamegame reforbit-breaker
marketISO 3166-1 alpha-2MT
partnercounterpart public idpp_opr_…

A filter value the party has no data for returns 400 filter_invalid.

EndpointReturns
explore/optionsThe games, markets and counterparts this party can filter by. Only values with at least 50 players are offered.
explore/performanceEngagement KPIs (MetricValue: value, previous value, change, formula, sample size, availability class, source, freshness) and a series per period against the previous period.
explore/dimension?dim=game|market|partnerOne row per entity: active players and change, new players, sessions, minutes, repeat rate, D7/D30 (players returning to that entity), game GGR (bet − win) per player, and an 8-week series. Also deterministic highlights. Rows under 50 players are left out and counted in suppressed.
explore/playersLifecycle KPIs, a 7-state mix by week, and the flows between the previous and current period. Flows under 50 players are left out and counted.
explore/retention?months=Monthly first-play cohorts × D1…D90 (DN = played again in days N to N+6). A cell is shown only once the whole cohort has had its window. Also your curve against the pooled network curve.
explore/benchmark-historyYour stored monthly comparisons against the network quartiles. Unpublished segments carry no network values.

Network curve. It pools every other party of your type, excluding you. It is returned only with at least 5 peers and no peer above 50% of the pooled cohort. Otherwise the response carries available: false and a reason. No peer is ever returned on its own.

Lifecycle states. Each player is classified by their own activity with you. The first matching state wins, in this order:

StateRule
NewFirst play in the last 14 days
ReactivatedPlaying in the last 14 days after 30+ quiet days
Highly engaged8+ active days in the last 28, not slowing down
DecliningPlaying, on fewer than half the days of the 14 before
ActivePlayed in the last 14 days
At riskLast played 15–30 days ago
DormantLast played 31–119 days ago

compare/summary also returns position: { favourable, assessed }: the metrics in the better half of the network, out of those where a direction is defined.

Insights (/v1/kpis/intelligence/insights?from&to)​

These are ranked, explainable insights plus an executive summary. They are computed by fixed rules and statistical tests over the KPI engine's outputs, so every privacy floor already applies. No language model computes or writes them.

Detectors

DetectorWhat it reports
ChangeA material move against the previous period: at least 8% relative, or 2pp for rates.
DriverThe market, game or counterpart carrying at least 30% of the net change in active players.
Retention gap / strengthA segment whose D7 retention is at most 0.75×, or at least 1.25×, that of your other segments.
Portfolio opportunityA game with above-median retention but below-median reach.
BenchmarkA top- or bottom-quartile position against anonymous peers.
AnomalyOne of the last 14 days far outside the 14 days before it (robust z of at least 3.5, and a change of at least 25%).

Confidence

Confidence is 1 − p from a two-sided test:

  • Counts: a Poisson test.
  • Rates: a two-proportion z-test against the rate's real base.
  • Rates with an unknown base: capped at 0.85.
  • Rates whose base is under the 50-player floor: never reported.

Ranking. Insights are ranked by magnitude × population × business weight × confidence × freshness. primary holds three of them: a positive, a risk and an opportunity.

Wording. The text describes association and concentration only. Tests reject causal wording.