Skip to content

Conventions

View .mdllms.txt
Environment URL Keys
Test https://api.staging.mongrl.com sk_test_…, pk_test_…
Live https://api.mongrl.com sk_live_…, pk_live_…

All requests and responses are JSON (application/json), except photo uploads (the image’s bytes). See Authentication for keys and sessions.

Errors are RFC 9457 problems, sent as application/problem+json:

{
"title": "Unprocessable Entity",
"status": 422,
"detail": "the listing isn't ready to go on sale",
"errors": [
{ "location": "body.price", "message": "a price is needed", "value": null },
{ "location": "body.images", "message": "add at least 3 photos" }
]
}
  • Show detail to people. It’s written to be read.
  • errors[].location points at the field: body.<field>, query.<name> or path.<name>. Map it onto your form.
  • Validation errors list every problem at once, not just the first.
Status Meaning
400 The request is malformed (bad JSON, a bad Idempotency-Key).
401 No key, a key that doesn’t work, or a user session that has ended.
403 The key or user may not do this (e.g. a blocked user).
404 Not found, or not yours. Other marketplaces’ and users’ things are always 404.
409 Not possible in the current state: sold, already in use, a transition that isn’t available.
422 The input is wrong. See errors.
429 Too many attempts. Wait for Retry-After seconds.
503 A dependency (such as payments) isn’t available. Try again later.

Money is always an object with an integer amount in minor units and an ISO 4217 currency:

{ "amount": 125000, "currency": "SEK" }

That’s 1 250,00 kr. Never use floats for money. Totals and line items on orders are plain integers in the order’s currency.

  • Ids are UUIDs ("8f3c2a1e-…"). Treat them as opaque strings.
  • Times are RFC 3339 in UTC ("2026-10-19T14:05:00Z"). Dates without a time are YYYY-MM-DD.

Two styles, depending on the list:

Lists Style
Listings, favourites, a seller’s listings, catalogue terms Cursor: pass next_cursor back as cursor. No next_cursor means the last page.
Orders, conversations Offset: limit and offset. A page shorter than limit is the last.
Messages in a conversation After: after=<message id> returns only newer messages, oldest first.

limit is at most 100 (200 for catalogue terms and messages). Search also returns total, the number of matches.

Send Idempotency-Key on any POST, PUT, PATCH or DELETE you might retry, above all placing an order. Use a fresh UUID per user action.

  • A retry with the same key and the same request doesn’t run again: it replays the first response, with Idempotent-Replayed: true.
  • The same key with a different request is 422; while the first request still runs it’s 409.
  • Server errors (5xx) aren’t stored, so retrying them runs the request again.
  • Keys last 24 hours and belong to your key (and user), so they never collide with anyone else’s.

Limits protect people, not the API’s capacity:

  • Sign-in: 10 wrong passwords in 15 minutes locks the account for the rest of that window.
  • Emailed links (sign-in, password reset, confirmation): 5 an hour per address.

Over a limit you get 429 with Retry-After.

The marketplace’s languages are in GET /v1/marketplace → locales (BCP 47, the first is the default). Pass locale where an endpoint takes one (categories, catalogue terms); a user’s locale decides the language of their emails.

Everything is under /v1. Within v1, changes only add things: new endpoints, new optional request fields, new response fields, new enum values. Write clients that ignore fields they don’t know. Anything that would break a client gets a new version.

The OpenAPI 3.1 spec (YAML) is generated from the API’s code and published with it, so it always matches the API. It covers everything a storefront can call; the console’s own endpoints aren’t in it.