AGENTS.md — Marea Alcalina API
Following the agents.md spec. This file is for AI agents (Claude, GPT, Cursor, Continue.dev, generic LLM-driven tools) consuming the Marea public API. For the full doc index in agent-friendly markdown form, fetch llms.txt.
What this API is for
Operations + retention API for small-business commerce. Bootstrap users, manage storefronts + products (food menus or retail / service catalogs), publish to public hosted URLs. End-customer order channels are the operator’s choice — WhatsApp, web checkout, or in-person pickup — and configurable per storefront. The API does not lock the channel.Auth model
The default credential ismk_user_* — one key per user, covers all storefronts that user owns (1 on Free, up to 5,000 on Agency Growth). The key is owner-scoped, not storefront-scoped. You do NOT issue a separate key per storefront.
A second credential, mk_dev_*, exists only for partner-style callers (agents, vertical SaaS, agencies) that bootstrap Marea accounts for other people. Most integrations never hold one.
End-user AI clients do not paste a key: they connect over OAuth. When a merchant adds Marea to Claude, ChatGPT, Cursor or any MCP client, the client discovers OAuth 2.1 + PKCE from the WWW-Authenticate challenge on a 401 from POST https://mcp.mareaalcalina.com/mcp (RFC 9728), the merchant signs in to Marea and authorizes. Consent mints a per-user mk_user_* behind the token, so everything below about user-key scope still applies. OAuth grants carry marea.read, marea.write and marea.publish, and can never call marea.bootstrap_user. Static keys stay the path for servers, CI and agents with no browser. See the MCP connect guide.
Full model: API keys.
mk_user_*— per-user key, owner-scoped (covers all the user’s storefronts). Either minted from the user’s own dashboard, OR returned byPOST /v1/usersin the partner bootstrap response. Bootstrap-issued keys start restricted (onlyme:verify+me:resendVerification) until the 6-digit code is verified, then upgraded in-place tocatalog:read,catalog:write,storefront:publish. Cannot bootstrap, cannot see other users’ data. Defaultrpd: 10000.mk_dev_*— partner / developer key, held by the integrator. Holdsdeveloper:bootstrap,developer:read,developer:issueUserKey,developer:webhooks. Used to bootstrap users and manage webhook endpoints — never to touch a catalog. Defaultrpd: 50(configurable at issue time; the devrpdIS the bootstrap cap).
Authorization: Bearer <key> (or X-API-Key: <key> as a fallback).
Common patterns
- Bootstrap → verify → use.
POST /v1/usersreturns a restrictedmk_user_*key + emails the user a 6-digit code. The user reads the code aloud (or the agent fetches it via Gmail-MCP), agent callsPOST /v1/users/:userId/verify, the same key is upgraded to full per-user scope. See Bootstrap quickstart. - Idempotency on every mutation. Pass
Idempotency-Key: <uuid-v4>on everyPOSTandPATCH. Same key + same body returns the original response (replay-safe). Same key + different body returns409 idempotency_conflict. See Safe mutations. - Branch on
error.type, noterror.message. Thetypefield is a stable 10-value enum;messageis English today and its wording may change. See Errors. - Surface
nextActions[]verbatim. Errors carry{ label, method, url }[]with the concrete actions the user can take (empty when there is nothing to do, for example on 429 or a validation error). Don’t paraphrase; render verbatim. - Read rate-limit headers proactively.
X-RateLimit-Remainingis on every response. When you hit429, readRetry-After(seconds) and back off. Defaults:rpm: 60. Dev keys:rpd: 50. User keys:rpd: 10000. - Markdown for ingestion. Every doc page also serves at
<page>.md(e.g.,/concepts/errors.md); use that variant when ingesting docs into your context — cleaner than the HTML.
When to call which tool
REST (always available) and MCP tool (when the user has connected Marea in their AI client, over OAuth or with a static key: see the MCP connect guide). Tenmarea.* tools.
There is no delete tool for products, and no delete endpoint either. Hiding is the way:
hide: true takes the item off the customer-facing menu and keeps its history. Treat “delete that dish” as a request to hide it, and say so.
Prefer the MCP tool when the user has Marea connected: ids, scopes and plan gates are already resolved for that account. Fall back to REST when MCP isn’t available. On an OAuth connection the tool list is already filtered by the granted scopes, so a tool that is absent means the scope was not granted, not that the call would fail.
What NOT to do
- Don’t swallow
nextActions[]. The most common agent failure mode is silently retrying past a 402 (paywall) or 451 (ToS not accepted) without ever surfacing the action to the user. Render the array verbatim. - Don’t auto-accept the ToS on the user’s behalf. A 451 → modal flow requires user input by design. The agent cannot bypass.
- Don’t bootstrap past your dev key’s
rpdcap. Default is 50/day (configurable at issue time). Hitting it returns 429rate_limitedwithcode: rate_limit_exceededandrpd_exceededinerror.message. ReadRetry-After(seconds-until-the-day-rolls-over) and back off. - Don’t call publish without explicit user confirmation. Publish is destructive and user-visible. Ask the user in plain language and wait for a yes; never chain it automatically after a create or update. The MCP tool is annotated
destructiveHint: true, so most clients also show their own confirmation, but that is the client’s gate, not yours. - Don’t cache
mk_user_*keys across users or sessions. Per-user keys belong in the user’s own context. - Don’t poll verification status faster than the API allows. The user-facing rate limit is
rpm: 60per user-key. Poll at most once every 30s during the verify window; better, create a webhook endpoint subscribed touser.verified— see Webhook endpoints. - Don’t reuse the same
Idempotency-Keywith a different body. That returns409 idempotency_conflictand you must generate a fresh key. If you’re retrying with edits, that’s a new request — new key. - Don’t branch on
error.message. Branch onerror.type(stable enum) orerror.code(stable string).
Errors
Every error follows the same envelope:{ type, code, message, doc, requestId, recoverable, retryAfterMs, nextActions, upgrade, requiredScopes? }. Branch on error.type. The 10 stable types:
Full error matrix at /concepts/errors.
Where to find docs
- llms.txt index: https://docs.mareaalcalina.com/llms.txt — agent-curated overview of the entire surface
- llms-full.txt: https://docs.mareaalcalina.com/llms-full.txt — concatenation of all pages (Mintlify auto-emits in production)
- Per-page markdown:
https://docs.mareaalcalina.com/<page>.md(e.g.,/concepts/errors.md) - OpenAPI 3.1 spec: https://api.mareaalcalina.com/v1/openapi.json — Zod-derived from the cloud-functions code; 5-min cached
- Postman collection: https://docs.mareaalcalina.com/marea.postman.json — auto-generated from the OpenAPI spec
- MCP server: https://mcp.mareaalcalina.com/mcp (streamable HTTP, POST only); 10
marea.*tools. Merchants connect it in Claude, ChatGPT, Cursor or Claude Code over OAuth; developers and headless agents pass a staticmk_user_*/mk_dev_*bearer. Verify and resend are deliberately REST-only - API catalog (RFC 9727): https://mareaalcalina.com/.well-known/api-catalog — discoverability for agent crawlers
Conventions
- Locale:
Accept-Languagesets the applied defaults onPOST /v1/users(language, currency, country). Error messages are English regardless of the header today. - Currency: configurable per storefront. Pass
currency: "MXN" | "USD" | "CAD" | "BRL" | ...on storefront create. - Phone format: E.164 (
+52...). - Timestamps: ISO 8601 strings.
- IDs: prefixed (
usr_,stf_,prd_,dev_).