The catalog api#
The Moud client, the website, moud fleet and moud publish all talk to one http api. This page
explains how its routes are named, how a caller signs in, what an error looks like and how the limits
work. The full list of routes, with their fields, is the OpenAPI 3.1 spec the api serves itself:
GET /v1/openapi.jsonLoad it into any OpenAPI viewer or client generator. The api checks the spec against the routes it really serves every time it is built, so a route is never missing from it.
Paths#
Every route lives under /v1:
| Prefix | Who calls it |
|---|---|
/v1/fleet/... |
fleets and the game servers they run |
/v1/creator/..., /v1/games/{slug}/... |
creators, studio and CI |
/v1/blobs, /v1/bundles, /v1/content, /v1/media |
game files, uploads and downloads |
/v1/site/..., /v1/privacy/... |
the website, signed with the site secret |
| everything else | the Moud client acting for a signed in player |
Moud releases before /v1 used /api/catalog/.... Those paths still answer: /api/catalog/x is
the same route as /v1/x, with the same request and answer, so older clients and fleets keep
working. New code should use /v1.
A few routes are served by the website and passed on by the api unchanged. The spec marks them with
x-moud-site. To a caller they look like any other route.
Signing in#
Each route lists what it accepts. All tokens go in an Authorization: Bearer ... header, except the
site signature and the join pass.
- Player session. The client asks
POST /v1/auth/challengefor a server id, joins it through Minecraft's session server, then sends its name and the server id toPOST /v1/auth/verify. The answer holds a token good for 30 days. A player banned from Moud is refused here, with the end of the ban in the message. - Fleet token. A fleet sends the game's fleet key once to
POST /v1/fleet/register, together with a token of its own. Every later fleet call carries that token. See Running a fleet. - Creator key. A key starting with
moud_ck_, made on the website. Its scopes arepublish,analytics:read,store:read,store:write,bansandconsole, and it can be tied to one game. A signed in player session reaches the same routes with whatever their studio role allows. See Studios, keys and webhooks. - Site signature. The website signs
/v1/siteand/v1/privacycalls:x-moud-atis the time in milliseconds andx-moud-signatureis the hex HMAC-SHA256 of<x-moud-at>.<body>with the shared site secret. A signature older than five minutes is refused. - Join pass.
GET /v1/content/{hash}takes the pass a join handed out inx-moud-pass. It only reaches the files of the game version that join was for.
Errors#
Every refusal has a json body with two fields:
{ "error": "slow_down", "message": "Too many requests, wait a moment." }error is a short code that stays the same between releases, so check that one in code. message
is a sentence meant for a person and may change. The status tells the kind of problem:
| Status | Means |
|---|---|
| 400 | the request is malformed, the message says which field |
| 401 | no credentials, or ones the api does not know |
| 403 | signed in but not allowed, or banned (banned) |
| 404 | no such thing, or one the caller may not see |
| 409 | it clashes with what is there, like a ticket already used |
| 413 | the body is too large |
| 429 | a rate limit or quota is used up |
| 502, 503 | something the api needs did not answer |
Paging#
Lists that can grow take a cursor and give back the next one:
GET /v1/notifications
GET /v1/notifications?before=<next from the page before>The answer has a next field. Pass it back as before or after, whichever the route takes, and
stop when it is null. A cursor is opaque, so pass it back as it came.
Key listings in the store and memory calls work by key instead: send the last key of the page before
as after.
Limits#
A 429 answer carries retry-after with the seconds to wait when the api knows it.
- Writes that carry no token or signature: 120 a minute for each address.
- A creator key: 300 calls a minute, wherever they go.
- A player: 60 social, inbox and follow actions a minute, 20 joins a minute.
- Sign in: 30 challenges a minute and 20 sign in attempts an hour for each address.
- Game servers: each server has a quota a minute for each kind of call, a base plus some for every
player on it. The spec names the quota of each fleet route in
x-moud-quota:
| Quota | Base | Per player |
|---|---|---|
| store | 60 | 10 |
| messaging | 150 | 60 |
| matchmaking | 60 | 10 |
| badges | 30 | 10 |
| analytics | 120 | 30 |
| moderation | 30 | 5 |
| text | 120 | 60 |
| memory | 600 | 60 |
Memory calls also share 30,000 a minute across all the servers of one game.
Bodies are at most 1 MB, except blob uploads, which go up to the size the api is set to allow.
