Skip to main content

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.
HoldsDefault 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:
  1. AI agents (Claude, ChatGPT, Cursor) that bootstrap Marea accounts as part of helping their own end-users.
  2. Vertical SaaS (e.g., a LATAM restaurant-management product) embedding Marea so their customers get a storefront without the SaaS building one.
  3. Agencies running marketing for many small clients — one operator console, many client storefronts.
If none of those describe you, skip this section.
HoldsDefault scopes
The partner integration itselfdeveloper: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.
ScopeHeld byUsed by
catalog:readuser keysGET /v1/storefronts/..., GET .../products
catalog:writeuser keysPOST / PATCH on storefronts and products
storefront:publishuser keysPOST /v1/storefronts/:storefrontId/publish
me:verifyrestricted user keys (pre-verify)POST /v1/users/:userId/verify
me:resendVerificationrestricted user keysPOST /v1/users/:userId/resendVerification
developer:bootstrapdev keysPOST /v1/users
developer:readdev keysGET /v1/users, GET /v1/users/:userId
developer:issueUserKeydev keysPOST /v1/users/:userId/keys
developer:webhooksdev keysPOST /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:
  1. Revoke the old key.
  2. 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).
  3. 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.codeWhenRecovery
missing_authorizationNeither Authorization nor X-API-Key presentAdd the header
invalid_authorization_formatHeader present but not `Bearer mk_(devuser)_…`Fix the format
key_not_foundKey not recognized (typo or never issued)Issue a new key
key_revokedKey was issued but later revokedIssue a new key
All four are 401 with recoverable: false. Don’t loop — fix the credential.