/ Documentation

Metriqal Documentation

Overview

Metriqal runs on your competitor Steam App IDs alone. Optionally, it ingests internal signal streams from your studio — developer velocity (Jira/GitHub), player sentiment (Discord), and player telemetry — to enrich the correlation against live competitor market signals, then emits a single executable recommendation with measured impact (incremental lift measured via holdout / geo-split where available).

This guide walks from zero to your first recommendation. You'll need your studio's credentials (delivered by email at signup). The internal streams below are optional enrichment — connect Jira/GitHub, Discord, or your telemetry only if you want those signals layered in; none are required to start.

Step 1 — Connect Webhooks

Each credential email includes three webhook endpoints. Configure whichever signals you want to use:

Jira

In Jira: Project Settings → Webhooks → Create Webhook. Set the URL to your jira_endpoint and enable events: Sprint started, Sprint closed, Issue resolved. Set the secret header X-Jira-Signature to your jira_webhook_secret.

GitHub

In your repo: Settings → Webhooks → Add Webhook. Set the Payload URL to your github_endpoint. Content type: application/json. Secret: your github_webhook_secret. Events: Pull requests, Issues.

Discord

In your server: any bot or webhook integration that can POST messages to your discord_endpoint with the header Authorization: Bearer <discord_webhook_secret>. Each message is run through sentiment analysis automatically.

Step 2 — Ingest Telemetry

POST player events to your ingest endpoint. Your studio_portal_key (spk_...) is the auth token. Include your game's game_id (from the studio portal) and the current build_tag:

POST /api/ingest
Authorization: Bearer spk_...
Content-Type: application/json

{
  "game_id":    "550e8400-e29b-41d4-a716-446655440000",
  "build_tag":  "v2.4.1",
  "player_id":  "player_abc123",
  "event_type": "session_start",
  "event_ts":   "2026-05-29T09:00:00Z"
}

NoteEvents are written to Neon (Postgres). The recommendation engine aggregates the last 7 days of events per build tag. Send events for every session start and key in-game milestone.

Step 3 — Read Your First Recommendation

Once enough telemetry has accumulated (24–48 hours), call the recommendations endpoint:

GET /api/games/{game_id}/recommendations/latest?build_tag=v2.4.1
X-Metriqal-Portal-Key: spk_...

The legacy confidence_score field is uncalibrated heuristic priority (0 to 1), not an outcome probability.

{
  "recommendation_type": "aggressive_uacq",
  "confidence_score": 0.87,
  "data_tier": "tier_2_telemetry",
  "is_algorithmic_fallback": false,
  "upgrade_prompt": null,
  "confidence_degradation_reason": null,
  "generated_at": "2026-06-06T09:00:00.000Z",
  "rationale": "Rival Arena: Champions CCU down 38% in 48h. Your retention holding at 0.94. Acquisition window open.",
  "execution_payload": {
    "action_type":    "increase_ad_spend",
    "target_metric":  "marketing_budget",
    "suggested_value": 1.5,
    "priority":       "high"
  },
  "log_id": "a1b2c3d4-..."
}

is_algorithmic_fallback — true when the AI inference engine was unavailable and the recommendation came from deterministic rules. Outbound dispatch is suppressed for fallback recommendations — do not auto-execute them. Surface this flag in any automated integration so operators know the AI was offline.

data_tier — One of tier_0_no_data, tier_1_scraped_only, tier_2_telemetry, tier_3_velocity. Heuristic priority scores are hard-capped at 0.40 for Tier 0/1. When upgrade_prompt is non-null the engine recommends integrating the telemetry SDK to provide additional first-party context.

confidence_degradation_reason — Non-null when the studio crisis gate fired (your game's retention declining). The gate prevents aggressive_uacq during a retention crash and caps heuristic priority. Surface this in your UI so operators understand why the score is lower than expected.

generated_at — ISO 8601 timestamp of when the recommendation was computed. Recommendations reflect the correlation snapshot at that instant. Re-call the endpoint every 15–30 minutes during active market windows; market conditions can shift faster than that during major competitor incidents.

Save the log_id. You'll use it to submit your outcome 7 days later so the engine can compute the measured impact (an internal before/after effect-size estimate; directional unless you ran a holdout).


Authentication

All studio-facing API endpoints require your studio portal key (spk_... prefix). Pass it in the X-Metriqal-Portal-Key header.

X-Metriqal-Portal-Key: spk_abc123...

Keys are tenant-scoped. Requests using a valid key for a different tenant return 403 Forbidden. Missing or invalid keys return 401 Unauthorized.

WarningTreat your portal key as a secret. It authorises data export and competitor watchlist mutations. Rotate it from your studio portal if compromised.

Studio Portal API

GET /api/client/portal

Returns the full dashboard payload: games list, latest recommendations per game, competitor vulnerability scores, and signal summaries.

POST /api/client/portal/games

Register a new game under your studio.

{ "title": "My Game", "genre": "Action", "platforms": ["Steam"] }
DELETE /api/client/portal/games/:game_id

Remove a game from monitoring.

GET /api/client/portal/competitors

List your studio's competitor watchlist.

POST /api/client/portal/competitors

Add a rival to your watchlist. The poller will pick it up within ~30 minutes.

{ "steam_app_id": "730", "platform": "steam" }
DELETE /api/client/portal/competitors/:steam_app_id

Remove a rival from your watchlist.

Telemetry Ingestion

POST /api/ingest

Ingest a player event. Events are written to Neon (Postgres) and aggregated into velocity, retention, and CCU signals for the recommendation engine.

FieldTypeRequiredDescription
game_idUUIDYesYour game's ID from the studio portal
build_tagstringYesSemver of current build — e.g. v2.4.1
player_idstringYesStable anonymous player identifier
event_typestringYessession_start | session_end | custom event name
event_tsISO 8601YesClient-side event timestamp
metadataobjectNoArbitrary key-value pairs (max 10 keys)
event_idUUIDNoIdempotency key — send the same event_id when re-sending an event (e.g. after a crash or reload) and the server returns the original ingestion_id instead of storing a duplicate row

Noteplayer_id is used to compute distinct player counts (units) and CCU. It is hashed on receipt and never stored in plaintext. Do not send PII.

Recommendations

GET /api/games/:game_id/recommendations/latest

Returns the current recommendation for a game. Combines live telemetry, competitor signals, and market estimates. Responds in under 2 seconds; falls back to a deterministic matrix if inference times out.

Query parameters

ParamRequiredDescription
build_tagYesCurrent build semver — e.g. v2.4.1
tenant_idNoInferred from portal key if omitted

Recommendation types

TypeMeaning
aggressive_uacqCompetitor distress + your retention holding → ramp user acquisition now
review_bomb_containmentSentiment crisis + stable retention → pause feature rollout, monitor
change_freezeExternal market chaos → lock releases, do not ship
no_actionNo significant signal → continue monitoring

Response headers

HeaderDescription
X-Metriqal-Log-IDUUID identifying this recommendation. Pass to outcome submission.
X-Metriqal-Recommendation-Staletrue if market context timed out — recommendation based on cached signals
X-Data-Statusready | insufficient | stale

Market Context

GET /api/games/:game_id/market-context

Returns estimated unit sales for your competitor watchlist alongside your own game's first-party actuals. Data on your own games is sourced from your telemetry events — it is never an estimate.

{
  "own_game": {
    "game_id":        "550e8400-...",
    "title":          "My Game",
    "units":          84200,
    "ccu_peak":       3100,
    "is_estimate":    false,
    "data_source":    "first_party_telemetry",
    "confidence":     "measured"
  },
  "competitor_estimates": [
    {
      "steam_app_id":           "730",
      "competitor_name":        "Rival Arena: Champions",
      "total_estimated_units":  420000,
      "is_estimate":           true,
      "confidence":            "medium",
      "coverage_score":        0.74
    }
  ]
}

NoteCompetitor estimates use the Boxleiter model: review count × a genre-dependent multiplier. The multipliers are heuristic public-domain defaults, not locally calibrated against a private actuals corpus. Steam-only. Console scrapers are running; console estimation is not yet available.

Outcomes

Submit metric deltas 7 days after executing a recommendation. Metriqal runs a before/after significance test automatically and returns a measured-impact estimate (lift evidence — directional unless you ran a holdout / geo-split).

POST /api/recommendations/:log_id/outcome
{
  "measured_retention_delta": 4.2,   // percentage points
  "measured_velocity_delta":  null,   // null if not measurable
  "pre_retention":            0.88,
  "cohort_size":              1200
}

The response includes p_value, confidence_level, and observed_effect ("lift" | "regress" | "neutral" | "insufficient_signal"). Legacy aggregate inputs do not support inference: p-value and confidence level are null; effects remain descriptive.

NoteYou don't have to submit outcomes manually. Metriqal's auto-outcome computer runs nightly and computes results automatically for completed recommendations with sufficient post-action data. Manual submission just gets you the result sooner.

Webhooks

All webhook endpoints are provisioned at signup. Secrets are per-tenant and rotatable from the studio portal.

EndpointAuth headerEvents captured
/api/jira/:tenant_idX-Jira-Signature: sha256=...Sprint open/close, issue resolved
/api/v1/webhooks/github/:tenant_idX-Hub-Signature-256PR merged, issue closed
/api/discord/:tenant_idAuthorization: Bearer ...Messages → sentiment analysis
/api/webhooks/crm/:tenant_idX-Webhook-Secret or provider signatureStripe, Shopify, HubSpot sales events

NoteAll webhook endpoints are idempotent. Duplicate deliveries (same X-GitHub-Delivery or equivalent) are deduplicated and ignored.

Data Export

GET /api/pov/export/:tenant_id

Exports your complete recommendation history, outcomes, playbook definitions, and raw signals in GS-CompInt-2026 format. Requires your spk_ portal key — cross-tenant requests return 403.

Responses carry the header X-GS-CompInt-Version: GS-CompInt-2026. The export is yours to keep — no Metriqal dependency required to read or validate it.


The Open Competitive Intelligence Standard

GS-CompInt-2026 is an open data standard for game studio competitive intelligence. It specifies a portable, vendor-neutral format for competitor incidents, studio outcomes, correlation signals, and playbook definitions.

Any tool that can read JSON can read a GS-CompInt-2026 export. You're not locked in to Metriqal's platform to use your own data.

Schema

A GS-CompInt-2026 document is a JSON object with four top-level fields:

{
  "version": "GS-CompInt-2026",
  "incidents": [ /* MarketIncident[] */ ],
  "outcomes": [ /* RecommendationOutcome[] */ ],
  "correlation": [ /* CorrelationSnapshot[] */ ],
  "playbooks": [ /* PlaybookDefinition[] */ ]
}

MarketIncident

{
  "competitor_game_id": "string (UUID)",
  "competitor_build_tag": "string (semver)",
  "incident_type": "patch_day | server_outage | content_drop | live_event | emergency_fix | api_breaking",
  "source_ts": "ISO 8601"
}

RecommendationOutcome

{
  "log_id": "string (UUID)",
  "recommendation_type": "string",
  "measured_retention_delta": "number | null",
  "p_value": "number | null",
  "confidence_level": "number | null",
  "observed_effect": "lift | regress | neutral | insufficient_signal | null"
}

CorrelationSnapshot

{
  "game_id": "string (UUID)",
  "build_tag": "string (semver)",
  "velocity_dau_pct": "number",
  "retention_inverse": "number",
  "sentiment_score": "number",
  "queried_at": "ISO 8601"
}

Discovery Endpoint

Third-party tools can discover the current schema version without cloning this repo:

GET https://metriqal.se/.well-known/gs-compint-2026.json

// Response header:
X-GS-CompInt-Version: GS-CompInt-2026

Validator CLI

Validate any GS-CompInt-2026 document without a Metriqal account:

# Via npm
npx gs-compint-validate path/to/export.json

# Via Metriqal's built-in script
npm run validate-outcome -- path/to/export.json

Exit codes

CodeMeaning
0Valid GS-CompInt-2026 document
1Invalid — schema violations printed to stderr
2Usage error — no file provided or file not found

Open-Source Outcome Validator

The outcome validator runs a two-proportion z-test on pre/post recommendation retention deltas to estimate whether an observed change is a measurable effect — or could plausibly be explained by random variance. This is an internal effect-size indicator, not proof of causation; a single-cohort before/after test is directional unless you ran a holdout / geo-split control.

The math is open. You can audit it, run it locally, or integrate it into your own data warehouse. No Metriqal account required.

Usage

npm run validate-outcome -- export.json

# Or pipe a JSON document directly:
cat export.json | npx gs-compint-validate

The validator reads outcomes[] from a GS-CompInt-2026 export and prints a verdict per outcome:

✓ log_id: a1b2c3... LIFT                p=0.031  retention_delta=+4.2pts
~ log_id: d4e5f6... INSUFFICIENT_SIGNAL  p=0.112  cohort below minimum
✗ log_id: g7h8i9... REGRESS              p=0.008  delta in wrong direction

Statistical Model

Retention is a proportion (players retained ÷ cohort size), so its variance is structurally p(1-p)/n - a two-proportion z-test uses this binomial variance directly. Welch's t-test would instead make the test statistic grow with the square root of cohort size regardless of effect size, manufacturing significance for trivial retention changes at large cohorts. Continuous metrics (revenue per player, playtime) are a different measurement type and do use Welch's t-test - see the GS-CompInt-2026 spec, section 5.1 for why the two are not interchangeable.

Inputs

  • pre_retention — baseline retention rate before the recommendation was executed
  • measured_retention_delta — observed change in retention after 7 days
  • cohort_size — number of players in the measurement window

Thresholds

observed_effectp-valueMinimum cohort
lift< 0.05 (positive direction)30+ players
insufficient_signal≥ 0.05Any
regress< 0.05 (wrong direction)30+ players

Limitations — When Not to Use This Test

Warning — Correlation ≠ CausationThe test tells you whether the delta is statistically significant. It does not prove that the recommendation caused the change. Confounding variables (seasonal events, competitor news, platform updates) can produce significant p-values for reasons unrelated to your action.

  • Legacy aggregate retention inputs cannot establish separate independent comparison arms. The current legacy API returns descriptive effects with p_value: null and confidence_level: null, regardless of a single cohort count.
  • Measurement window < 7 days — behavioural changes take time to manifest in retention metrics. Submit outcomes after the full window.
  • Multiple simultaneous changes — if you shipped a patch and ran a UA campaign simultaneously, you cannot attribute the delta to either independently.
  • Velocity delta — DAU velocity is not computed by the retrospective validator because the pre-action baseline cannot be reliably reconstructed from retention-only data. This field is always stored as null.

NoteThe validator follows the honesty gate principle: when data is insufficient, it skips the computation entirely rather than returning a zero or a confident-looking number. An empty result is always preferable to a fabricated one.