# API reference

The full Mercemur REST API: **574 operations across 406 endpoints**, grouped by resource
in the sidebar.

```text
https://api.mercemur.com/api/v1
```

<Info>
  Every page in this reference has a playground. Paste your secret key into it and the
  request runs against **your own store**, so the response you see is your real data.
  Requests from the playground count against your [rate limit](/essentials/rate-limits),
  and writes made from it are real writes.
</Info>

## Before you start

Four conventions apply to every endpoint here, and none of them are visible from a single
example request. Each one has cost an integration real time.

<Columns cols={2}>
  <Card title="Money is integer minor units" icon="money-bill" href="/requests/money">
    `1999` with `usd` is 19.99. Sending `19.99` is refused, sending `19` charges 19 cents.
  </Card>
  <Card title="Writes need an Idempotency-Key" icon="repeat" href="/requests/idempotency">
    Required on every write, not just retries. It is what makes a retry after a timeout safe.
  </Card>
  <Card title="Read, write and delete are separate" icon="lock" href="/authentication/scopes">
    `write_products` does not grant reads, and deleting needs `write_products:delete`.
  </Card>
  <Card title="Paging is by cursor, forward only" icon="list" href="/requests/pagination">
    No `page` or `offset`. Pass `page.next_cursor` back as `after`.
  </Card>
</Columns>

## Authentication

Every request carries a secret key as a bearer token:

```bash
curl https://api.mercemur.com/api/v1/products \
  -H "Authorization: Bearer $MERCEMUR_SECRET_KEY"
```

Create one in **Settings, API keys** in your dashboard. It is shown once, at creation.
See [API keys](/authentication/api-keys).

## What every operation shares

| | |
|---|---|
| Success, single record | The object itself |
| Success, collection | `{ "data": [...], "page": { "has_more", "next_cursor" } }` |
| Any refusal | `{ "error": { "code", "message" } }`, at every status |
| Every response | `x-request-id` header, and `RateLimit-*` budget headers |
| Max body | 1 MB |

Branch on `error.code`, never on `error.message`. See [Error envelope](/errors/envelope).

## Scopes on each endpoint

Every operation lists the scope it requires. A key missing that scope gets `403` with a
message naming the exact scope to add, so you can develop against a minimal key and grant
scopes as you hit them rather than guessing up front.

## Unknown fields are refused

A body field or query parameter this API does not define is refused with `400`, at every
nesting depth. It is not silently dropped.

A typo that is ignored looks like a working integration until someone notices the field
never took effect. Being refused on the first call is the cheaper failure. See
[Query parameters](/requests/query-parameters).

## Finding an endpoint

Use the sidebar, grouped by resource, or search. The largest families are products,
orders, customers, discounts, marketing campaigns, digital products and returns.
