{"openapi":"3.0.3","info":{"title":"Breshna API Docs","description":"These are the basic API routes used to communicate with Breshna Games and Blitz SDK\n\n\nFollow these steps to setup an API Key and start creating your games with Blitz SDK\n\n1. You need to have a Breshna account to generate a new API Key. If you don't have an account, signup using `stg.breshna.io`\n2. Login to your account to get you JWT Bearer token\n3. Generate a new API Key and API secret with the Bearer Token and User ID from your login response\n4. Use the API Key to get suggestions for the type of assets and then using those asset name, create a game","contact":{"name":"Yasir Hassan","email":"yasir.hassan@stax3.com"},"version":"0.2.0"},"servers":[{"url":"https://stg.breshna.io/api","description":"Staging Server"}],"tags":[{"name":"Blitz","description":"Everything about Blitz SDK"},{"name":"Stats_Public_APIs","description":"Everything about Stats Public API"},{"name":"Developer_API","description":"Third-party access to statistics and leaderboard data for games you created on Breshna.\n\nFree at launch. The service is metered from day one, and paid plans or quotas may be introduced later with notice.\n\n## Eligibility\n\nA game is available through this API only if **all** of the following are true:\n\n| Rule | Detail |\n|---|---|\n| You own it | The game's owner is the account the API key belongs to |\n| It is published | The game's status is `publish` — drafts are not available |\n| It is not deleted | Soft-deleted games disappear from the API immediately |\n\nCollaborators, agencies, and team accounts are **not** supported in this release — access follows the game creator only. Unpublishing or deleting a game removes it from the API straight away; there is no grace period.\n\nIf a game fails any rule, every endpoint returns the same `404 game_not_found`. A 404 does **not** tell you whether the game exists — that is deliberate, so game identifiers cannot be probed.\n\nCall `GET /developer/v1/games` to see exactly which games your key can read.\n\n## Getting an API key\n\nCreate and manage your keys from your Breshna account portal. Key management is not part of this API — you never need to call an endpoint to create, rename, replace, or deactivate a key.\n\n**The full key is shown only once, at the moment it is created.** Breshna stores a hash and never the key itself, so it cannot be displayed again. Copy it there and then, and store it somewhere you can update without redeploying your application.\n\nEverywhere else — the portal, support requests, this documentation — a key is identified by its non-secret `key_prefix`, for example `brsh_a1b2c3d4`. That prefix is safe to display and to share.\n\nReplacing a key takes effect **immediately**, with no overlap window, so do it at a moment when you can update your application promptly. Replacing or deactivating a key never affects ownership of your games or any of your data.\n\n## Authenticating\n\nSend your key in the `x-api-key` header on every data request.\n\n```bash\ncurl https://stg.breshna.io/api/developer/v1/games \\\n  -H \"x-api-key: brsh_a1b2c3d4_XZ8pM2vQ...\"\n```\n\nNever send your Breshna login token or password to these endpoints, and never embed your Breshna credentials in a third-party application — the API key exists precisely so you don't have to.\n\n> **This is not the Blitz SDK key.** The key issued by `POST /public_apps` is a different credential for a different API. Both are sent in an `x-api-key` header, but a Developer API key always begins `brsh_`.\n\n### Scopes\n\n| Scope | Grants |\n|---|---|\n| `stats:read` | `GET /games/{game_id}/statistics` |\n| `leaderboard:read` | `GET /games/{game_id}/leaderboard` |\n\nBoth are granted by default. Choose a narrower set when you create the key in the portal. Listing your games with `GET /games` requires only a valid key, not a particular scope.\n\n## Dates and time\n\n**Every date and time in this API is UTC.** No other timezone is accepted or returned.\n\nFilter results two ways — they are mutually exclusive, and sending both is a `400 invalid_date_range`:\n\n- `?days=7` — the last 7 days, ending today\n- `?from_date=2026-07-18&to_date=2026-08-17` — an explicit range\n\n| Rule | Behaviour |\n|---|---|\n| Default | The last 30 days, if you send neither |\n| `from_date` | Inclusive, from `00:00:00.000Z` on that date |\n| `to_date` | **Inclusive of the whole day**, through `23:59:59.999Z` |\n| `to_date` alone | Rejected — it requires `from_date` |\n| `from_date` alone | From that date through the end of today |\n| Maximum span | 365 days |\n| `days` range | 1 to 365 |\n\nEvery response echoes the window it actually used in a `period` block, where `to` is the **last instant included**. A session recorded at `2026-08-17T23:59:59.999Z` is in the result; one at `2026-08-18T00:00:00.000Z` is not.\n\n`?days=7` and the equivalent explicit range return identical data — the `period` block will match exactly.\n\n## Player privacy\n\nYou are receiving data about real people. Breshna deliberately limits what this API exposes, and you are responsible for protecting what you retrieve.\n\n**What you never receive:** player email addresses, Breshna player IDs, or free-text player feedback.\n\n`player_ref` is a stable, opaque reference. The same player produces the same `player_ref` across pages, requests, and days, so you can deduplicate and track repeat play — but it cannot be reversed into a Breshna identity or correlated with any other system.\n\n`display_name` is the name the player entered in the game. Players who gave no name appear as exactly `Anonymous`.\n\nYour obligations:\n\n- Protect data you retrieve, and don't attempt to re-identify players.\n- Don't use the API to work around Breshna's game privacy, publishing, moderation, or account-access rules.\n- Keep your API key private.\n\nBreshna monitors for unusual usage and for attempts to access other creators' games, and may deactivate a key that is compromised or misused.\n\n## Errors\n\nEvery error uses the same envelope. Branch on `error.code`, not on the message text — messages may be reworded.\n\n```json\n{\n  \"status\": 401,\n  \"error\": {\n    \"code\": \"api_key_invalid\",\n    \"message\": \"The provided API key is not recognised.\"\n  }\n}\n```\n\n| Status | `error.code` | Meaning | What to do |\n|---|---|---|---|\n| 400 | `invalid_date_range` | Bad or contradictory date parameters | See `message`; check the date rules above |\n| 401 | `api_key_missing` | No `x-api-key` header | Send the header |\n| 401 | `api_key_invalid` | Key not recognised | Check for truncation or a stale copy |\n| 401 | `api_key_inactive` | Key deactivated, replaced, or suspended | Create or obtain a new key — do not retry |\n| 403 | `scope_denied` | Key lacks the scope for this endpoint | Create a key with the required scope |\n| 404 | `game_not_found` | Not yours, not published, deleted, or nonexistent | Reconcile against `GET /games` |\n| 429 | `rate_limit_exceeded` | Too many requests | Back off — see Limits |\n| 500 | `internal_error` | Breshna-side failure | Retry with backoff; contact support if it persists |\n\nA `429` also carries `error.details.retry_after_seconds`.\n\nMalformed query parameters — `limit` above 100, a non-ISO date, `days` outside 1–365 — are rejected as `400` by request validation before reaching the handler, and use the framework's validation error format rather than the envelope above.\n\n## Limits\n\nPer API key:\n\n| Window | Default |\n|---|---|\n| Per minute | 60 requests |\n| Per day | 10,000 requests |\n\nLimits exist during the free period and may be tuned once real usage is observed. Successful responses carry:\n\n```\nX-RateLimit-Limit: 60\nX-RateLimit-Remaining: 59\nX-RateLimit-Window: minute\n```\n\nA `429` adds `Retry-After` in seconds. Honour it rather than retrying immediately.\n\nOther constraints: 365-day maximum span per request; 100 rows maximum per leaderboard page; 5 active keys per account.\n\nRepeated identical requests may be served from cache for a short period. Windows ending today are cached briefly; fully historical windows are cached longer. Cached responses are byte-identical to fresh ones.\n\nBreshna records API usage — which key, which endpoint, which game, response status, and result size. This supports the service, informs future plans, and identifies abuse.\n\n**Free access is not guaranteed indefinitely.** Breshna may introduce plans, quotas, or paid tiers later. Affected developers will be notified with a reasonable migration period, and existing keys and integrations will be preserved wherever possible.\n\n## Handling an inactive key\n\nA `401 api_key_inactive` means the key was recognised but is no longer usable — deactivated, replaced, or suspended by Breshna. Build for it:\n\n1. **Do not retry.** This is a configuration problem, not a transient error, and retrying will never succeed.\n2. Alert whoever operates the integration, and read the key from configuration you can update without a redeploy.\n3. Distinguish it from `api_key_invalid`, which usually means a truncated or mistyped key rather than a retired one.\n\nIssue a replacement from your Breshna account portal.\n\n## Support\n\nInclude your **`key_prefix`** (for example `brsh_a1b2c3d4`) in any support request. It identifies the key without exposing it.\n\n**Never send your full API key to anyone, including Breshna support.** Support cannot read it — only a hash is stored — and will never ask for it.\n\nAlso helpful: the endpoint, the exact query string, the UTC timestamp, and the `error.code` received."}],"paths":{"/users/login":{"post":{"tags":["Blitz"],"summary":"Login user based using email and password","description":"Login user based using email and password","operationId":"loginUser","requestBody":{"description":"Data for the user to login","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":404},"error":{"type":"string"},"message":{"type":"string"}}}}}}}}},"/public_apps":{"post":{"tags":["Blitz"],"summary":"Get new API Key and Secret","description":"Generate a new API Key and Secret for accessing Blitz SDK","parameters":[{"name":"userid","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Data to generate a new API Key and Secret","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"message":{"type":"string"},"data":{"type":"object","properties":{"name":{"type":"string"},"api_key":{"type":"string"},"api_secret":{"type":"string"},"user_id":{"type":"string"},"is_active":{"type":"boolean"},"_id":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"__v":{"type":"integer"}}}}}}}},"500":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":500},"message":{"type":"string"},"error":{"type":"string"}}}}}}},"security":[{"Bearer":[]}]}},"/assets/game-gen-search-public":{"get":{"tags":["Blitz"],"summary":"Find assets for a game corresponding to asset types","description":"Get an array of asset names for a given game and asset type. It requires Blitz API key to get all the asset names","parameters":[{"name":"game_type","in":"query","description":"Game names","required":true,"schema":{"type":"string","enum":["Rev and Ride","Runner Rave","Seek and Smash"]}},{"name":"asset_type","in":"query","description":"Asset Types","required":true,"schema":{"type":"string","enum":["avatar","background","collectibles","nonCollectibles","obstacles","laser","interface","platform"]}},{"name":"query","in":"query","description":"Query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"data":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":404},"message":{"type":"string"}}}}}}},"security":[{"api_key":[]}]}},"/assets/game-gen-public":{"post":{"tags":["Blitz"],"summary":"Creates a new Game with predefined game template, title and type assets assigned","description":"Generate a new game with the Blitz SDK using predefined Assets and Game template","requestBody":{"description":"Data to generate a new Game using Blitz SDK","content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","example":"Blitz Public"},"game_type":{"type":"string","description":"Type of game template","example":"Seek and Smash","enum":["Rev and Ride","Runner Rave","Seek and Smash"]},"data":{"type":"object","properties":{"avatar":{"type":"object","description":"Avatar will show up as the main player","properties":{"count":{"type":"string","example":""},"keyword":{"type":"string","example":"space"}}},"background":{"type":"object","description":"The background for the generating game","properties":{"count":{"type":"string","example":""},"keyword":{"type":"string","example":"space"}}},"collectibles":{"type":"object","description":"These will be collected by the player, resulting in increasing the score","properties":{"count":{"type":"string","example":"5"},"keyword":{"type":"string","example":"moon"}}},"nonCollectibles":{"type":"object","description":"You need to avoid these","properties":{"count":{"type":"string","example":"5"},"keyword":{"type":"string","example":"sun"}}},"music":{"type":"object","description":"Music in the game","properties":{"count":{"type":"string","example":""},"keyword":{"type":"string","example":""}}},"laser":{"type":"object","description":"Laser will be available in Seek and Smash game","properties":{"count":{"type":"string","example":""},"keyword":{"type":"string","example":""}}},"interface":{"type":"object","properties":{"count":{"type":"string","example":""},"keyword":{"type":"string","example":""}}}}}}}}},"required":true},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","description":"HTTP status code of the response","example":200},"message":{"type":"string","description":"A message providing additional information about the response"},"url":{"type":"string","description":"URL of the created game"}}}}}}},"security":[{"api_key":[]}]}},"/games_stats/public/stats":{"get":{"tags":["Stats_Public_API"],"summary":"Get the summary of stats of the user","description":"Get the object containing the summarized stats of the user.","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"data":{"type":"object","properties":{"total_play_time":{"description":"Total play time of all your games","type":"integer"},"max_play_time":{"description":"Highest play time among all your games","type":"integer"},"players":{"description":"Total number of players of all your games","type":"integer"},"plays":{"description":"Total number of times your games have been played","type":"integer"},"total_games":{"description":"Total number of games","type":"integer"}}}}}}}},"500":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":500},"message":{"type":"string"},"error":{"type":"string"}}}}}}},"security":[{"Bearer":[]}]}},"/games_stat/public/list":{"get":{"tags":["Stats_Public_API"],"summary":"Get the list of all the games your created","description":"Get the top level information about all the games you have created.","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"totalCount":{"type":"integer","example":20},"data":{"type":"array","items":{"type":"object","properties":{"game_name":{"type":"string"},"poster":{"type":"string"},"created_at":{"type":"string"},"id":{"type":"string"},"category":{"type":"string"},"game_type":{"type":"string"}}}}}}}}},"400":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":500},"message":{"type":"string"},"error":{"type":"string"}}}}}}},"security":[{"Bearer":[]}]}},"/games_stat/public/stats/{game_id}":{"get":{"tags":["Stats_Public_API"],"summary":"Find detailed stats for a specific game","description":"","parameters":[{"name":"game_id","in":"query","description":"Provide it as url parameter to get stats for a specific game","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"data":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"object","properties":{"play_id":{"type":"string"},"game_id":{"type":"string"},"user":{"type":"string"}}},"levels":{"type":"array","items":{"type":"object","properties":{"level_id":{"type":"integer"},"low_score":{"type":"integer"},"avg_score":{"type":"integer"},"high_score":{"type":"integer"},"total_time":{"type":"integer"},"max_time":{"type":"integer"},"avg_time":{"type":"integer"},"attempts":{"type":"integer"}}}},"updatedAt":{"type":"string"},"user":{"type":"object","properties":{"_id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"__v":{"type":"string"}}},"highest_score":{"type":"integer"},"lowest_score":{"type":"integer"},"total_play_time":{"type":"integer"},"max_play_time":{"type":"integer"},"total_avg_score":{"type":"integer"}}}},"totalCount":{"type":"integer","example":10},"high_score":{"type":"integer","example":5000},"avg_score":{"type":"integer","example":2500},"low_score":{"type":"integer","example":1000},"total_play_time":{"type":"integer","example":3600},"game":{"type":"object","properties":{"game_name":{"type":"string"},"poster":{"type":"string"},"createdAt":{"type":"string"},"id":{"type":"string"},"category":{"type":"string"},"game_type":{"type":"string"}}},"totalLevels":{"type":"integer","example":5},"createdAt":{"type":"string"}}}}}},"400":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":404},"message":{"type":"string"}}}}}}},"security":[{"Bearer":[]}]}},"/developer/v1/games":{"get":{"tags":["Developer_API"],"summary":"List games available to this API key","description":"Games owned by the account behind the key, with status `publish`, excluding deleted games.\n\nUse this to discover which `game_id` values the statistics and leaderboard endpoints will serve. If a game you expect is missing, it is unpublished, deleted, or owned by another account.\n\nRequires only a valid API key — no particular scope.","operationId":"developerListGames","parameters":[{"$ref":"#/components/parameters/DeveloperSkip"},{"$ref":"#/components/parameters/DeveloperLimit"}],"responses":{"200":{"description":"The games this key can read.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/DeveloperRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/DeveloperRateLimitRemaining"},"X-RateLimit-Window":{"$ref":"#/components/headers/DeveloperRateLimitWindow"}},"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"total_count":{"type":"integer","description":"Total eligible games, not the number returned on this page.","example":8},"eligibility":{"type":"object","description":"The rule applied, echoed so it cannot drift from the implementation.","properties":{"rule":{"type":"string"}}},"data":{"type":"array","items":{"$ref":"#/components/schemas/DeveloperGame"}}}},"example":{"status":200,"total_count":8,"eligibility":{"rule":"Games owned by this account with status 'publish' that have not been deleted."},"data":[{"id":"6712a4f0c1d2e3b4a5f60789","title":"Desert Dash","game_type":"endless-runner","category":"Arcade","poster":"https://cdn.breshna.io/posters/desert-dash.png","created_at":"2026-03-04T09:12:44.881Z"}]}}}},"401":{"$ref":"#/components/responses/DeveloperError401"},"429":{"$ref":"#/components/responses/DeveloperError429"},"500":{"$ref":"#/components/responses/DeveloperError500"}},"security":[{"DeveloperApiKey":[]}]}},"/developer/v1/games/{game_id}/statistics":{"get":{"tags":["Developer_API"],"summary":"Statistics for one game over a date range","description":"Aggregated play statistics for a single game, over a UTC date range.\n\nReturns three views of the same window:\n\n- **`totals`** — one summary object for the whole window\n- **`levels`** — one entry per `level_id`, ascending\n- **`daily`** — one entry per UTC day that had at least one play\n\nA game with no plays in the window returns `200` with zeroed totals and empty `levels` / `daily`. That is a valid answer, not an error.\n\nScores and times are whatever the game reports — Breshna does not normalise them across game types. All time values are in seconds. Free-text player feedback is never returned.\n\nRequires the `stats:read` scope.","operationId":"developerGameStatistics","parameters":[{"$ref":"#/components/parameters/DeveloperGameId"},{"$ref":"#/components/parameters/DeveloperDays"},{"$ref":"#/components/parameters/DeveloperFromDate"},{"$ref":"#/components/parameters/DeveloperToDate"}],"responses":{"200":{"description":"Statistics for the requested window.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/DeveloperRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/DeveloperRateLimitRemaining"},"X-RateLimit-Window":{"$ref":"#/components/headers/DeveloperRateLimitWindow"}},"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"game":{"$ref":"#/components/schemas/DeveloperGame"},"period":{"$ref":"#/components/schemas/DeveloperPeriod"},"totals":{"$ref":"#/components/schemas/DeveloperStatsTotals"},"levels":{"type":"array","items":{"$ref":"#/components/schemas/DeveloperLevelStat"}},"daily":{"type":"array","items":{"$ref":"#/components/schemas/DeveloperDailyStat"}}}},"example":{"status":200,"game":{"id":"6712a4f0c1d2e3b4a5f60789","title":"Desert Dash","game_type":"endless-runner","category":"Arcade","poster":"https://cdn.breshna.io/posters/desert-dash.png","created_at":"2026-03-04T09:12:44.881Z"},"period":{"from":"2026-07-19T00:00:00.000Z","to":"2026-08-17T23:59:59.999Z","days":30,"timezone":"UTC","end_date_inclusive":true},"totals":{"plays":173,"players":96,"completed_plays":121,"completion_rate":0.6994,"high_score":1240,"low_score":0,"avg_score":318.4,"total_play_time":58412,"max_play_time":742,"avg_play_time":337.64,"avg_rating":4.2,"ratings_count":38},"levels":[{"level_id":1,"attempts":412,"high_score":240,"low_score":10,"avg_score":122.4,"total_time":19840,"max_time":52,"avg_time":48.16}],"daily":[{"date":"2026-08-16","plays":12,"players":9,"completed_plays":8,"total_play_time":3120,"high_score":980}]}}}},"400":{"$ref":"#/components/responses/DeveloperError400"},"401":{"$ref":"#/components/responses/DeveloperError401"},"403":{"$ref":"#/components/responses/DeveloperError403"},"404":{"$ref":"#/components/responses/DeveloperError404Game"},"429":{"$ref":"#/components/responses/DeveloperError429"},"500":{"$ref":"#/components/responses/DeveloperError500"}},"security":[{"DeveloperApiKey":[]}]}},"/developer/v1/games/{game_id}/leaderboard":{"get":{"tags":["Developer_API"],"summary":"Leaderboard for one game over a date range","description":"Ranked play sessions for a single game, over a UTC date range.\n\n### The ranking rule\n\nReturned inline as `ranking` on every response, so it can never drift from what the code does. In words:\n\n- **The ranking unit is a play session, not a player.** A player who plays five times can occupy five rows.\n- A session's `score` is the sum of that session's **best score per level**. Replaying level 3 within one session contributes only the best level-3 result.\n- Sorted by score descending; ties broken by the **earlier** session ranking higher.\n- **Incomplete sessions are included.** Use the `completed` flag to filter them out yourself.\n- `rank` is the position in the ordering, accounting for `skip`. Tied scores receive different ranks, resolved by the tie-break above.\n\nTo build a per-player leaderboard, group by `player_ref` client-side and keep each player's highest score.\n\n### Pagination\n\n`skip` (default 0) and `limit` (default 25, **maximum 100**). `total_count` is the number of ranked sessions in the window, not the number returned.\n\nCross-game leaderboards are not offered in this release.\n\nRequires the `leaderboard:read` scope.","operationId":"developerGameLeaderboard","parameters":[{"$ref":"#/components/parameters/DeveloperGameId"},{"$ref":"#/components/parameters/DeveloperDays"},{"$ref":"#/components/parameters/DeveloperFromDate"},{"$ref":"#/components/parameters/DeveloperToDate"},{"$ref":"#/components/parameters/DeveloperSkip"},{"$ref":"#/components/parameters/DeveloperLimit"}],"responses":{"200":{"description":"Ranked sessions for the requested window.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/DeveloperRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/DeveloperRateLimitRemaining"},"X-RateLimit-Window":{"$ref":"#/components/headers/DeveloperRateLimitWindow"}},"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"integer","example":200},"game":{"$ref":"#/components/schemas/DeveloperGame"},"period":{"$ref":"#/components/schemas/DeveloperPeriod"},"ranking":{"$ref":"#/components/schemas/DeveloperRankingRule"},"total_count":{"type":"integer","description":"Ranked sessions in the window, not the number returned on this page.","example":173},"data":{"type":"array","items":{"$ref":"#/components/schemas/DeveloperLeaderboardEntry"}}}},"example":{"status":200,"game":{"id":"6712a4f0c1d2e3b4a5f60789","title":"Desert Dash","game_type":"endless-runner","category":"Arcade","poster":"https://cdn.breshna.io/posters/desert-dash.png","created_at":"2026-03-04T09:12:44.881Z"},"period":{"from":"2026-07-19T00:00:00.000Z","to":"2026-08-17T23:59:59.999Z","days":30,"timezone":"UTC","end_date_inclusive":true},"ranking":{"unit":"session","score":"sum of best score per level within a session","sort":"score descending, then earliest session","ties":"earliest session ranks higher","same_player_may_repeat":true,"includes_incomplete_sessions":true},"total_count":173,"data":[{"rank":1,"player_ref":"p_9f2c4e77a1b03d55","display_name":"Ali Khan","score":1240,"completed":true,"achieved_at":"2026-08-14T11:02:31.004Z"},{"rank":2,"player_ref":"p_3b81de0247fc9a16","display_name":"Anonymous","score":1185,"completed":false,"achieved_at":"2026-08-12T19:47:02.771Z"}]}}}},"400":{"$ref":"#/components/responses/DeveloperError400"},"401":{"$ref":"#/components/responses/DeveloperError401"},"403":{"$ref":"#/components/responses/DeveloperError403"},"404":{"$ref":"#/components/responses/DeveloperError404Game"},"429":{"$ref":"#/components/responses/DeveloperError429"},"500":{"$ref":"#/components/responses/DeveloperError500"}},"security":[{"DeveloperApiKey":[]}]}}},"components":{"schemas":{"User":{"type":"object","properties":{"email":{"type":"string","example":"john@email.com"},"password":{"type":"string","example":"12345"},"type":{"type":"string","example":"user"}}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}}},"LoginResponse":{"type":"object","properties":{"status":{"type":"integer","example":200},"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"password":{"type":"boolean"},"type":{"type":"string"},"name":{"type":"string"},"avatar":{"type":"string"},"status":{"type":"string"},"coins":{"type":"integer"},"user_plots":{"type":"array","items":{"type":"string"}},"is_premium":{"type":"boolean"},"user_assets":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"token":{"type":"string"}}}}},"DeveloperError":{"type":"object","description":"Standard error envelope for the Developer API.","properties":{"status":{"type":"integer","example":401},"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable identifier. Branch on this rather than on `message`.","example":"api_key_invalid"},"message":{"type":"string","description":"Human-readable explanation. May be reworded without notice.","example":"The provided API key is not recognised."},"details":{"type":"object","description":"Present on some errors. A `429` carries `retry_after_seconds` here."}}}}},"DeveloperPeriod":{"type":"object","description":"The UTC window actually served. `to` is the last instant included, so a session at `23:59:59.999Z` on the end date is inside the result.","properties":{"from":{"type":"string","format":"date-time","example":"2026-07-19T00:00:00.000Z"},"to":{"type":"string","format":"date-time","example":"2026-08-17T23:59:59.999Z"},"days":{"type":"integer","example":30},"timezone":{"type":"string","example":"UTC"},"end_date_inclusive":{"type":"boolean","example":true}}},"DeveloperGame":{"type":"object","description":"A game this key is allowed to read.","properties":{"id":{"type":"string","example":"6712a4f0c1d2e3b4a5f60789"},"title":{"type":"string","example":"Desert Dash"},"game_type":{"type":"string","example":"endless-runner"},"category":{"type":"string","example":"Arcade"},"poster":{"type":"string","example":"https://cdn.breshna.io/posters/desert-dash.png"},"created_at":{"type":"string","format":"date-time","example":"2026-03-04T09:12:44.881Z"}}},"DeveloperStatsTotals":{"type":"object","description":"Totals across the whole window. A game with no plays in the window returns zeroes and nulls with a `200` — that is a valid answer, not an error.","properties":{"plays":{"type":"integer","description":"Play sessions started in the window.","example":173},"players":{"type":"integer","description":"Distinct players, counted once each across the whole window. Summing `daily[].players` gives a **larger** number, because a player active on three days counts once per day there.","example":96},"completed_plays":{"type":"integer","description":"Sessions the game reported as completed.","example":121},"completion_rate":{"type":"number","description":"`completed_plays / plays`, between 0 and 1, to 4 decimal places.","example":0.6994},"high_score":{"type":"integer","nullable":true,"description":"Best single level score recorded.","example":1240},"low_score":{"type":"integer","nullable":true,"description":"Worst single level score recorded.","example":0},"avg_score":{"type":"number","nullable":true,"description":"Mean of every level score in the window.","example":318.4},"total_play_time":{"type":"integer","description":"Seconds, summed across every level result.","example":58412},"max_play_time":{"type":"integer","nullable":true,"description":"Seconds. The longest single level result recorded.","example":742},"avg_play_time":{"type":"number","nullable":true,"description":"Seconds per play session.","example":337.64},"avg_rating":{"type":"number","nullable":true,"description":"Counts only sessions where the player left a rating.","example":4.2},"ratings_count":{"type":"integer","example":38}}},"DeveloperLevelStat":{"type":"object","description":"One entry per `level_id`, ascending.","properties":{"level_id":{"type":"integer","example":1},"attempts":{"type":"integer","description":"Level results recorded. A player replaying a level increments this.","example":412},"high_score":{"type":"integer","example":240},"low_score":{"type":"integer","example":10},"avg_score":{"type":"number","example":122.4},"total_time":{"type":"integer","description":"Seconds.","example":19840},"max_time":{"type":"integer","description":"Seconds.","example":52},"avg_time":{"type":"number","description":"Seconds.","example":48.16}}},"DeveloperDailyStat":{"type":"object","description":"One entry per UTC day **that had at least one play**. Days with no activity are omitted rather than returned as zeroes — pad client-side if your chart needs a continuous axis.","properties":{"date":{"type":"string","description":"UTC calendar day, `YYYY-MM-DD`.","example":"2026-08-16"},"plays":{"type":"integer","example":12},"players":{"type":"integer","description":"Distinct players on this day.","example":9},"completed_plays":{"type":"integer","example":8},"total_play_time":{"type":"integer","description":"Seconds.","example":3120},"high_score":{"type":"integer","example":980}}},"DeveloperRankingRule":{"type":"object","description":"The ranking rule actually applied, returned on every leaderboard response so the documented rule and the implemented rule cannot drift apart.","properties":{"unit":{"type":"string","example":"session"},"score":{"type":"string","example":"sum of best score per level within a session"},"sort":{"type":"string","example":"score descending, then earliest session"},"ties":{"type":"string","example":"earliest session ranks higher"},"same_player_may_repeat":{"type":"boolean","example":true},"includes_incomplete_sessions":{"type":"boolean","example":true}}},"DeveloperLeaderboardEntry":{"type":"object","properties":{"rank":{"type":"integer","description":"Position in the ordering, accounting for `skip`. Tied scores receive different ranks, resolved by the tie-break.","example":1},"player_ref":{"type":"string","description":"Stable, opaque player reference. The same player produces the same value across pages, requests, and days — never a Breshna player id, and not reversible.","example":"p_9f2c4e77a1b03d55"},"display_name":{"type":"string","description":"The name the player entered in the game. Players who gave no name appear as exactly `Anonymous`.","example":"Ali Khan"},"score":{"type":"integer","description":"Sum of this session's best score per level.","example":1240},"completed":{"type":"boolean","description":"Incomplete sessions are included in the ranking. Filter on this yourself if you need only completed runs.","example":true},"achieved_at":{"type":"string","format":"date-time","example":"2026-08-14T11:02:31.004Z"}}}},"requestBodies":{},"parameters":{"DeveloperGameId":{"name":"game_id","in":"path","required":true,"description":"The game's 24-character Breshna id. Obtain it from `GET /developer/v1/games`.","schema":{"type":"string","minLength":24,"maxLength":24},"example":"6712a4f0c1d2e3b4a5f60789"},"DeveloperFromDate":{"name":"from_date","in":"query","required":false,"description":"Start of the range (UTC, inclusive from `00:00:00.000Z`). Mutually exclusive with `days`. Sent alone, the range runs through the end of today.","schema":{"type":"string","format":"date"},"example":"2026-07-18"},"DeveloperToDate":{"name":"to_date","in":"query","required":false,"description":"End of the range, **inclusive of the whole UTC day** through `23:59:59.999Z`. Requires `from_date`. Mutually exclusive with `days`.","schema":{"type":"string","format":"date"},"example":"2026-08-17"},"DeveloperDays":{"name":"days","in":"query","required":false,"description":"The last N days ending today (UTC). Mutually exclusive with `from_date` and `to_date`. When no date filter is sent at all, the API defaults to the last 30 days.","schema":{"type":"integer","minimum":1,"maximum":365},"example":30},"DeveloperSkip":{"name":"skip","in":"query","required":false,"description":"Rows to skip, for pagination.","schema":{"type":"integer","minimum":0,"default":0}},"DeveloperLimit":{"name":"limit","in":"query","required":false,"description":"Rows per page. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}},"headers":{"DeveloperRateLimitLimit":{"description":"Requests permitted in the tightest currently-active window.","schema":{"type":"integer","example":60}},"DeveloperRateLimitRemaining":{"description":"Requests remaining in that window.","schema":{"type":"integer","example":59}},"DeveloperRateLimitWindow":{"description":"Which window the two values above describe.","schema":{"type":"string","enum":["minute","day"],"example":"minute"}},"DeveloperRetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","example":42}}},"responses":{"DeveloperError400":{"description":"Invalid or contradictory date parameters (`invalid_date_range`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":400,"error":{"code":"invalid_date_range","message":"Use either 'days' or an explicit 'from_date'/'to_date' range, not both."}}}}},"DeveloperError401":{"description":"Missing, unrecognised, or deactivated API key — `api_key_missing`, `api_key_invalid`, or `api_key_inactive`. Treat `api_key_inactive` as a configuration problem and do not retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":401,"error":{"code":"api_key_invalid","message":"The provided API key is not recognised."}}}}},"DeveloperError403":{"description":"The key does not grant the scope this endpoint requires (`scope_denied`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":403,"error":{"code":"scope_denied","message":"This API key does not grant the 'stats:read' scope."}}}}},"DeveloperError404Game":{"description":"No published, non-deleted game with that id exists for this account (`game_not_found`). Returned identically whether the game is someone else's, a draft, deleted, or nonexistent — so a 404 does not confirm that the id is real.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":404,"error":{"code":"game_not_found","message":"No published game with that id exists for this account."}}}}},"DeveloperError429":{"description":"Rate limit exceeded (`rate_limit_exceeded`). Honour `Retry-After` rather than retrying immediately.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/DeveloperRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/DeveloperRateLimitRemaining"},"X-RateLimit-Window":{"$ref":"#/components/headers/DeveloperRateLimitWindow"},"Retry-After":{"$ref":"#/components/headers/DeveloperRetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":429,"error":{"code":"rate_limit_exceeded","message":"Request limit reached. Retry after the period resets.","details":{"retry_after_seconds":42}}}}}},"DeveloperError500":{"description":"Breshna-side failure (`internal_error`). Retry with backoff; contact support with your `key_prefix` if it persists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeveloperError"},"example":{"status":500,"error":{"code":"internal_error","message":"Unable to complete the request."}}}}}},"securitySchemes":{"api_key":{"type":"apiKey","name":"x-api-key","in":"header"},"Bearer":{"type":"apiKey","name":"Authorization","in":"header","description":"Enter the token with the `Bearer `"},"DeveloperApiKey":{"type":"apiKey","name":"x-api-key","in":"header","description":"Breshna Developer API key, for example `brsh_a1b2c3d4_XZ8pM2vQ...`.\n\nCreate one from your Breshna account portal — key management is not part of this API.\n\nThis is **not** the Blitz SDK key issued by `POST /public_apps`. Both are sent in an `x-api-key` header, but they are separate credentials for separate APIs — a Developer API key always begins `brsh_`."}}}}