Authentication and rate limits

Every request carries two headers: the key that says who you are, and the store token that says which store you are talking about. Both come from the merchant.

Getting a key

The merchant creates it in their dashboard under Settings → Developer. The key is shown once, at creation. It cannot be retrieved later — if it is lost, the merchant rotates it and you get a new one.

Making a request

curl https://api.ematjarak.example/admin-api \
  -H 'content-type: application/json' \
  -H 'ms-api-key: YOUR_KEY' \
  -H 'ms-store-token: STORE_TOKEN' \
  -d '{"query":"{ activeChannel { code } }"}'
  • ms-api-key — the merchant’s key. Send it on every request.
  • ms-store-token — the store’s channel token, which the merchant also gives you.

Scoping is enforced by the server, not by you

A key is bound to one store AND to the scopes the merchant ticked when they issued it. There is no request you can construct that reaches another merchant’s data, or that does something outside your scopes — not with a guessed id, not with a different store token. You do not have to be careful about this; it is not possible.

ScopeWhat it allows
ordersRead and update orders; read the customers and products on them.
catalogCreate, edit and delete products, variants, assets and tags.
marketingManage promotions.
reportsRead-only: orders, customers, catalogue.
No scope grants store settings. A key can never change the store’s domain, branding, theme, plan or team — not even one issued with every scope ticked. Ask the merchant for the narrowest set that makes your integration work; a 403 on something you expected usually means a missing scope, not a bug.

Rotation and revocation

The merchant can rotate a key (issuing a new value and invalidating the old one) or delete it outright, at any time, without telling you. Handle a sudden 401 by failing loudly rather than retrying in a loop.

Rate limits

API-key traffic is limited per key, and again per key and source IP, in a rolling one-minute window. Exceeding either returns HTTP 429 with a Retry-After header. Respect it: retrying immediately just burns the next window too.

The limits exist because this is a shared instance. One integration’s runaway loop must not slow down another merchant’s storefront. If your use case genuinely needs a higher ceiling, ask — do not work around it with more IPs.