Moud

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.json

Load 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/challenge for a server id, joins it through Minecraft's session server, then sends its name and the server id to POST /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 are publish, analytics:read, store:read, store:write, bans and console, 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/site and /v1/privacy calls: x-moud-at is the time in milliseconds and x-moud-signature is 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 in x-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:

json
{ "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.