Roanuz Cricket API — Inventory & Audit (2026-05-14)
Companion to the local-only roanuz-api-inspector skill (.claude/skills/roanuz-api-inspector/). This file is the committed, reviewable source of truth; the skill is the operational tool that ties it to live SSH commands. Keep them in sync.
Why this exists
On 2026-05-14, the Strykr UI showed PBKS as 0/0 (17.3) STRUCK against MI 40/0 (4.2) LIVE during an IPL 2026 fixture, with caption "Mumbai Indians needs 161 off 94 balls" — an internally contradictory state. A target of 161 implies PBKS scored 160; the UI showed 0. This document captures the audit that traced the bug to adapter path mismatches, not provider data loss.
TL;DR
- Roanuz is sending correct data. PBKS at 200 confirmed via
/match/{id}/and/match/{id}/worm/against the same fixture. - The adapter reads the wrong paths. Five distinct bugs in
RoanuzDataAdapter.ts+RoanuzWebSocketManager.tsproduce the symptoms. - Sample responses (live captures) are in
./sample-responses/. Eight endpoints total: 5 return 200, 2 return 403, 1 returns 404.
Service surface
- REST base URL:
https://api.sports.roanuz.com - Socket.IO URL:
https://socket.sports.roanuz.com/cricket - Auth: JWT-style token, ~5 min TTL, obtained by exchanging
ROANUZ_API_KEYat/v5/core/{key}/auth/. Header on subsequent calls:rs-token. - Server env file:
backend/.envon strykrdev / strykrprod. Required vars:ROANUZ_API_KEY,ROANUZ_PROJECT_KEY, optionalROANUZ_BASE_URL. - Adapter code:
backend/src/exchanges/adapters/roanuz/RoanuzDataAdapter.ts(REST),RoanuzWebSocketManager.ts(Socket.IO).
REST endpoint table
| # | Endpoint | Status | Sample | Adapter |
|---|---|---|---|---|
| 1 | GET /v5/cricket/{key}/featured-matches-2/ | 200 ✓ | featured-matches-2.json | getMatches line 263 |
| 2 | GET /v5/cricket/{key}/match/{id}/ | 200 ✓ | match-detail.json | getMatchState line 354 |
| 3 | GET /v5/cricket/{key}/match/{id}/ball-by-ball/{over}/ | 200 ✓ | ball-by-ball.json | getBallByBall line 398 |
| 4 | GET /v5/cricket/{key}/match/{id}/worm/ | 200 ✓ | worm.json | getWormData line 875 |
| 5 | GET /v5/cricket/{key}/match/{id}/run-rate/ | 200 ✓ | run-rate.json | getRunRateData line 922 |
| 6 | GET /v5/cricket/{key}/tournament/{id}/fixtures/ | 403 | fixtures-403.json | getSchedule line 660 |
| 7 | GET /v5/cricket/{key}/match/{id}/commentary/ | 403 | commentary-403.json | getCommentary line 595 |
| 8 | GET /v5/cricket/{key}/match/{id}/scorecard/ | 404 | scorecard-404.json | getScorecard line 618 |
403 = our project key lacks that entitlement. 404 = the path is stale or moved. All three error responses are silently swallowed by the adapter — no log lines, no metrics.
Response shapes (the parts that matter)
The full sample JSONs are in ./sample-responses/. The fields the adapter must read correctly:
Endpoint 2 — /match/{id}/ (the bug epicenter)
{
"data": {
"play": { // ← state lives under `play`, NOT at root
"innings": { // dict, NOT array
"a_1": {
"score": { "runs": 200, "wickets": 7, "overs": 20.0, "balls": 120 }
},
"b_1": { "score": { "runs": 40, "wickets": 0, "overs": 4.2 } }
},
"live": {
"innings_number": 2,
"score": { "runs": 40, "wickets": 0, "overs": 4.2 },
"required_run_rate": 8.50,
"current_run_rate": 9.40
},
"target": { // ← under `play.target`, NOT `data.target`
"runs": 201, "overs": 20.0, "required_run_rate": 8.50
}
}
}
}
Endpoint 4 — /worm/ (independent verification)
{ "data": { "x": [1..20], "y": [
[7,16,21,31,46,55,58,67,76,89,100,111,122,128,135,140,147,166,188,200],
[4,12,21,31,40]
] } }
data.y[0] ending at 200 confirms PBKS scored 200 — independently of endpoint 2.
Adapter bugs found
Full snippets and fix sketches in the skill at .claude/skills/roanuz-api-inspector/references/adapter_bugs.md.
| Bug | File:Line | Class | UI effect |
|---|---|---|---|
| A | RoanuzDataAdapter.ts:1167 | Reads data.innings not data.play.innings | innings list always [] |
| B | RoanuzDataAdapter.ts:1156-1159 | score.runs || 0 etc. | score reads 0/0/0 — also violates financial-security.md §1.2 |
| C | RoanuzWebSocketManager.ts:759 | innings?.[N-1] on a dict | live updates silently no-op |
| D | RoanuzDataAdapter.ts:1244 | Reads data.target not data.play.target | target/RRR always undefined |
| E | adapter-wide | Toss has raw-response log (line 1182); score does not | forensic blind spot |
Plus secondary issues: 403/404 endpoints fail silently; no per-endpoint metrics; token cache doesn't invalidate on 401.
Socket.IO surface
| Direction | Event | Notes |
|---|---|---|
| outbound | join_match | Subscribe to a match |
| outbound | leave_match | Unsubscribe |
| inbound | on_match_joined | Full snapshot, same shape as /match/{id}/ |
| inbound | on_match_update | Deltas: ball_by_ball, wicket, over_complete, innings_change, match_end |
| inbound | on_error | Provider error |
Subscribed events (adapter line 134): ['ball_by_ball', 'wicket', 'over_complete', 'innings_change', 'match_end'].
Cache keys (Redis)
| Key | Type | TTL | Purpose |
|---|---|---|---|
roanuz:state:<matchId> | string (JSON) | 30s | Normalized match state — currently stores wrong values per Bug A/B |
roanuz:fixtures:live | set | event-driven | Live match IDs |
roanuz:mg100:* | string | 60s | Aggregated chart data |
roanuz:token | string | ~5 min | Bearer token |
Cache is downstream of the adapter — flushing won't fix the bugs. The bugs are in the read/normalize path before the cache write.
Recommended next actions (NOT in this PR)
- Fix Bug A and B together. They're causally linked and B masks A's diagnostic value.
- Fix Bug D alongside A/B (independent but small).
- Fix Bug C in a separate PR — WS path has higher blast radius.
- Add Bug E logging as a forensic precondition; can ship before fixes.
- Decide whether to upgrade Roanuz plan to unlock endpoints 6 & 7 (fixtures, commentary), or accept the 403s as permanent and remove the dead calls.
How to verify these claims yourself
Use the local-only skill: /roanuz-api-inspector prod verify <matchId>. Or run the inline commands from .claude/skills/roanuz-api-inspector/SKILL.md against any in-play IPL fixture.
What this PR contains
docs/roanuz/api-inventory.md— this filedocs/roanuz/sample-responses/*.json— 8 raw responses captured from prod on 2026-05-14
The matching skill (.claude/skills/roanuz-api-inspector/) is gitignored (per .gitignore .claude/* rule) and lives locally on each engineer's machine.