API keys
Almost every integration with Marea uses one kind of key: a mk_user_* issued to a single Marea user, used to manage all of that user’s storefronts and products.
A second kind — mk_dev_* — exists for a separate class of caller: agents, vertical-SaaS, and agencies that create Marea accounts on behalf of other people. Most readers will never need one.
GET /v1/me HTTP/1.1
Host: api.mareaalcalina.com
Authorization: Bearer mk_user_xxxxxxxxxxxxxxxx
Format is locked: ^mk_(dev|user)_[A-Za-z0-9]+$. The random suffix is 24 base62 chars (~143 bits of entropy).
The default: mk_user_* (user-key)
A user-key represents one specific Marea user and is the credential you use for everything that happens inside that user’s account.
One key covers ALL of that user’s storefronts. A user-key is owner-scoped, not storefront-scoped. The same mk_user_* can GET / POST / PATCH / publish against every storefront the user owns — whether that’s 1 (Free plan) or 5,000 (Agency Growth plan). You do not issue a separate key per storefront. You do not pass a Storefront-Id header — the storefront is in the URL path; the key proves you own it.
| Holds | Default scopes |
|---|
| One specific end-user (and all their storefronts) | catalog:read, catalog:write, storefront:publish (after verify) |
How to get one:
- If you signed up at mareaalcalina.com and own a storefront, mint a
mk_user_* in one click from your dashboard at /developers/keys.
- If you were bootstrapped through a partner integration, you received your
mk_user_* in the POST /v1/users response — store it then.
What you can do with it:
GET /v1/me — confirm identity, read your plan + limits
GET /v1/storefronts, POST /v1/storefronts, PATCH /v1/storefronts/:id — manage your storefronts
POST .../products, PATCH .../products/:id, GET .../products — manage your catalog
POST .../publish — take a storefront live (gated by ToS + plan)
- (after Phase B)
POST /v1/webhook_endpoints — receive order.* events from your store
What you can’t do:
- Bootstrap other users (that requires
developer:bootstrap, held only by mk_dev_*)
- See or edit anyone else’s data — tenant boundary is encoded in the key itself
Pre-verify vs post-verify scopes
If your mk_user_* came from a fresh POST /v1/users (partner bootstrap), it starts with a restricted scope set — me:verify, me:resendVerification, catalog:read. The only mutating calls it can make are verify and resend.
When the user submits the 6-digit code (POST /v1/users/:userId/verify), the same key is upgraded in place to catalog:read, catalog:write, storefront:publish. No rotation — store the value once at bootstrap time.
User-keys minted from the dashboard (self-issued by an already-verified user) start in the full post-verify state.
The advanced case: mk_dev_* (partner / developer key)
A mk_dev_* is for callers that act on behalf of many users — three real archetypes:
- AI agents (Claude, ChatGPT, Cursor) that bootstrap Marea accounts as part of helping their own end-users.
- Vertical SaaS (e.g., a LATAM restaurant-management product) embedding Marea so their customers get a storefront without the SaaS building one.
- Agencies running marketing for many small clients — one operator console, many client storefronts.
If none of those describe you, skip this section.
| Holds | Default scopes |
|---|
| The partner integration itself | developer:bootstrap, developer:read, developer:issueUserKey, developer:webhooks |
How to get one: Issue from the developer dashboard at mareaalcalina.com/developers/keys. The raw value is shown once; server-side only the SHA-256 hash is stored.
What you can do with it:
POST /v1/users — bootstrap a new Marea user (returns their mk_user_* in the response)
GET /v1/users, GET /v1/users/:userId — list / inspect users you’ve bootstrapped
POST /v1/users/:userId/keys — mint an additional mk_user_* for a user you’ve bootstrapped (e.g., a second integration)
- All
/v1/webhook_endpoints/* operations — manage your webhook endpoints (user.* lifecycle on accounts you bootstrapped + order.* from those accounts’ storefronts)
What you can’t do:
- Touch any user’s catalog directly. To edit a user’s storefront, use the
mk_user_* you got back from their bootstrap. Even a partner who owns a Marea storefront themselves uses their own mk_user_* for that storefront — the dev-key never grants catalog access.
The split is a security boundary: a leaked mk_dev_* can bootstrap new fake accounts and read user metadata, but can’t rewrite a single user’s prices.
Authenticating
Pass the key in Authorization: Bearer <key> (preferred). X-API-Key: <key> is accepted as a fallback. Any other header — or a non-Bearer Authorization scheme — fails with invalid_authorization_format.
Scopes table
Scope checks run after key lookup. Endpoints declare a required scope set; the auth layer supports both any (one-of) and all (require-all) semantics.
| Scope | Held by | Used by |
|---|
catalog:read | user keys | GET /v1/storefronts/..., GET .../products |
catalog:write | user keys | POST / PATCH on storefronts and products |
storefront:publish | user keys | POST /v1/storefronts/:storefrontId/publish |
me:verify | restricted user keys (pre-verify) | POST /v1/users/:userId/verify |
me:resendVerification | restricted user keys | POST /v1/users/:userId/resendVerification |
developer:bootstrap | dev keys | POST /v1/users |
developer:read | dev keys | GET /v1/users, GET /v1/users/:userId |
developer:issueUserKey | dev keys | POST /v1/users/:userId/keys |
developer:webhooks | dev keys | POST /v1/webhook_endpoints + the rest of /v1/webhook_endpoints/... |
GET /v1/me accepts any of the user scopes; developer keys bypass the scope check entirely (a developer key always sees its own identity).
Scope mismatch (403)
{
"error": {
"type": "auth",
"code": "insufficient_scope",
"message": "Missing required scopes: catalog:write.",
"requiredScopes": ["catalog:write"],
"heldScopes": ["developer:bootstrap"],
"recoverable": false
}
}
Surface requiredScopes and heldScopes verbatim — the diff between them tells you exactly which kind of key you should have used (mk_user_* for catalog scopes; mk_dev_* for developer scopes).
Rotation
There is no rotate call. To rotate:
- Revoke the old key.
- Issue a new one (developer dashboard for
mk_dev_*; user dashboard for mk_user_*; or POST /v1/users/:userId/keys with a developer key for an additional user-key on a bootstrapped user).
- Update your client.
Revocation
Revoked keys stop authenticating within ~60s end-to-end (the negative-lookup cache TTL). After revocation:
- The key returns
401 key_revoked on every request.
- Webhook endpoints are NOT tied to a single key — they belong to your account and survive key revocation. Revoking a single key does not delete or disable any endpoint. See Webhook endpoints for the lifecycle.
- Revocation is non-cascading: revoking a developer key does NOT revoke the user keys it minted. To wipe everything for an owner, the owner-scoped revoke-all path is used (operator path; not in the public API).
Auth errors (401)
error.code | When | Recovery | |
|---|
missing_authorization | Neither Authorization nor X-API-Key present | Add the header | |
invalid_authorization_format | Header present but not `Bearer mk_(dev | user)_…` | Fix the format |
key_not_found | Key not recognized (typo or never issued) | Issue a new key | |
key_revoked | Key was issued but later revoked | Issue a new key | |
All four are 401 with recoverable: false. Don’t loop — fix the credential.