DOCS/API Reference/Scoring API

Scoring API

Real-time ML scoring endpoints — first-user payer conversion (Starter Pack popup). Six features, 30-min post-install scoring, top-N% bucket output for in-session targeting.

10 min read

Overview

The Scoring API exposes real-time machine-learning endpoints for in-session player-targeting decisions. The first available model is the First-User Payer Conversion scorer — designed to drive the Starter Pack popup decision at the 30-minute post-install mark.

Send six behavioural features from a new install's first 30 minutes, get back a score in [0,1] and a bucket label (top_1pct / top_5pct / top_10pct / rest) for operational thresholding. Fire the popup on top_5pct-or-higher for ~8× lift over base rate.

EndpointMethodDescription
/v1/scoring/first-user-conversionPOSTScore D1 payer probability from first-30-min behaviour
Scope
The current artifact was trained on iOS US users only. Calls for Android or non-US users will return a score, but the model has never seen that distribution — block off-distribution traffic at your gateway. Cross-platform retrains are planned as drops arrive.

Authentication

All scoring endpoints require a client-side API key with the scoring:write scope.

HeaderValueNotes
X-API-Keypk_live_v1_...Client (pk_) key. Server (sk_) keys also accepted.
Content-Typeapplication/json
Acceptapplication/json
Scope required
Requests without scoring:write return 403. Contact your account team to grant the scope on existing keys.

First-User Conversion

Predicts the probability that a new install will pay within their first day (D1). Use the bucket label to drive in-session offers like the Starter Pack popup.

Request

POST /v1/scoring/first-user-conversion
bash
curl -X POST class="code-string">"https:class="code-commentclass="code-string">">//api.ilara.ai/v1/scoring/first-user-conversion" \
-H class="code-string">"X-API-Key: pk_live_v1_xxx" \
-H class="code-string">"Content-Type: application/json" \
-d '{
class="code-string">"devtodev_id": class="code-string">"DCD5D755-E12C-4E3C-9010-CDAE3232B059",
class="code-string">"loot_distinct_types": 3,
class="code-string">"sess_duration_total": 500.0,
class="code-string">"match_avg_fight_time": 45.0,
class="code-string">"match_wins": 4,
class="code-string">"install_hour": 14,
class="code-string">"vpay_count": 2
}'

Request body

One identity field plus six behavioural features observed in the first 30 minutes post-install. Every feature defaults to 0 — omit fields you don't have, never send null.

FieldTypeRequiredDescription
devtodev_idstring (1-64)YesThe DevToDev anonymous user ID. Echoed in the response; stored with the score.
loot_distinct_typesintNo (0)Count of distinct _LootcaseType values opened in the first 30 minutes. Top predictor — ~35% of model gain.
sess_duration_totalfloat (sec)No (0)Sum of activityduration across session-END events. Excludes idle/background time. NOT wall-clock elapsed time.
match_avg_fight_timefloat (sec)No (0)Mean of _FightTime across all matches. NOT total match duration including loading.
match_winsintNo (0)Count of matches where _Result == "WIN" (exact uppercase).
install_hourint 0-23No (0)Hour-of-day of install. UTC. Not IST, not device-local.
vpay_countintNo (0)Count of ALL virtual-currency events (earns + spends of in-game currency). NOT real-money in-app purchases.

Response

200 — Score returned
json
{
class="code-string">"success": true,
class="code-string">"data": {
class="code-string">"devtodev_id": class="code-string">"DCD5D755-E12C-4E3C-9010-CDAE3232B059",
class="code-string">"score": 0.0823,
class="code-string">"bucket": class="code-string">"top_5pct",
class="code-string">"model_version": class="code-string">"19423a2fc71c",
class="code-string">"thresholds_used": {
class="code-string">"top_1pct": 0.0992,
class="code-string">"top_5pct": 0.0795,
class="code-string">"top_10pct": 0.0673
},
class="code-string">"scored_at": class="code-string">"2026-05-20T14:30:00.913Z"
},
class="code-string">"error": null,
class="code-string">"meta": null
}
FieldTypeDescription
devtodev_idstringEchoed from the request.
scorefloat [0,1]Model probability the user pays within D1.
bucketenumOne of top_1pct, top_5pct, top_10pct, rest.
model_versionstringsha256[:12] of the artifact. Rotates with each retrain.
thresholds_usedobjectThe 99/95/90th-percentile cutoffs used to assign the bucket. Read these from the response; do NOT hardcode.
scored_atRFC 3339UTC timestamp when the score was computed.

Bucket Semantics

Buckets are computed from the model's out-of-fold score distribution and rotate with each retrain. Read thresholds_used from every response — don't hardcode the cutoffs.

BucketApprox score ≥Payer probability (D1)Recommended action
top_1pct~0.099~47%Fire popup; highest confidence
top_5pct~0.080~23%Fire popup; standard cohort
top_10pct~0.067~9%Optional cheaper offer
rest< 0.067<3%No popup; base rate cohort
Lift over base rate
Base D1 payer rate is 2.59%. Top-1% bucket converts at 18.26× base; top-5% at 8.94×. Firing the popup on bucket in {top_1pct, top_5pct} captures most paying users with minimal popup fatigue.

Call Timing

The model is trained on features observed in the user's first 30 minutes post-install. Score at 30 minutes (or at end-of-first-session, whichever fires first) for the calibration the model expects.

Field≤15m≤30m≤60m≤24h
loot_distinct_types82.5%83.3%83.9%86.3%
sess_duration_total90.6%97.2%99.1%99.4%
match_avg_fight_time88.5%89.0%89.4%90.9%
match_wins88.4%89.0%89.3%90.8%
install_hour100%100%100%100%
vpay_count83.2%83.9%84.6%86.9%

At 30 minutes, 81% of users (and 92% of payers) have all 6 fields populated. Scoring earlier than 30 minutes is supported but drops ROC-AUC below 0.75 — only do so if the popup must fire in-FTUE.

Model Card

Headline metrics for the current production artifact:

MetricValue
Training cohort20,048 iOS-US users (Mar 1 – Apr 30 2026)
Positives (D1 payers)519 (2.59% base rate)
Features6 numeric
ROC-AUC (out-of-fold)0.8044
PR-AUC (out-of-fold)0.2667
Lift @ top 1%18.26×
Lift @ top 5%8.94×
Cross-validation5-fold StratifiedKFold
HPO objectivePR-AUC (50 Optuna TPE trials)
p95 latency< 50 ms
Artifact size82 KB (model.joblib)

Field-Definition Gotchas

Six places where a backend-side bug will silently send values that don't match what the model was trained on. Confirm each with your engineering team before integration:

  • install_hour must be UTC, not IST or device-local. Off-by-5.5h on every score otherwise.
  • sess_duration_total uses the activityduration field on session-END events, not wall-clock elapsed time and not the (start_ts, end_ts) difference. activityduration excludes idle.
  • match_wins counts the exact uppercase string "WIN". "Win" and "win" are not matched.
  • vpay_count covers ALL virtual-currency events (earns + spends of in-game currency), NOT real-money IAPs. Sending IAP-count instead will yield values ~10× smaller than training.
  • loot_distinct_types counts distinct values of _LootcaseType. Use the same enumeration as devtodev exports.
  • Omit fields you don't have — send 0 if you must, never null. Pydantic strict-types numerics and 422s on null.

Error Responses

StatusCodeDescription
401UNAUTHORIZEDMissing or invalid X-API-Key
403FORBIDDENAPI key lacks the scoring:write scope
422VALIDATION_ERRORBody validation failed (e.g. install_hour=24, null in numeric field)
503MODEL_UNAVAILABLEScoring artifact failed to load — retry after 30s

Operational Notes

Idempotency

The endpoint is not idempotent in v1 — client-side retries produce additional ml_scores rows for the same devtodev_id. Don't retry on 422; retry once after 30s on 503.

Persistence

Every score is persisted asynchronously to our ml_scores table (within ~1 s of the HTTP response) with devtodev_id, score, bucket, model_fingerprint, the raw feature payload, and the game/tenant context. Available for A/B attribution joins.

Rate limits

Rate limit follows the standard tier scheme (1000 rpm for growth, 10000 for pro, unmetered for enterprise). For high-volume integrations, contact your account team — we can right-size dedicated capacity.

Next Steps