Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mareaalcalina.com/llms.txt

Use this file to discover all available pages before exploring further.

Storefronts

A storefront is the unit of catalog ownership in Marea. One user owns one or more storefronts; each has a name, a category list, products, branding, contact info, business hours, delivery settings, and a publishable hosted URL. Under the hood a storefront is the users/{ownerUid}/menus/{menuId} document; the API surface presents it with the typed id stf_<menuId>. Internal ids never leak across the API — every handler strips the stf_ prefix at the top before any Firestore access.
ConceptAPI surfaceNotes
Storefront idstf_<token>Opaque, agent-stable
Product idprd_<token>Opaque, agent-stable
Preview link_links.previewUrl24h token, anyone with the link can open
Public link_links.publicUrlnull until publish; stable after publish
Edit link_links.editUrlDashboard deep link for the owner

Lifecycle

created (draft)        ←─ POST /v1/storefronts

   ├─ previewUrl available immediately (24h-token preview)

   └─ POST /v1/storefronts/:storefrontId/publish ──▶ public live URL

                                                         ├─ 402 if pre-paywall account
                                                         ├─ 422 if 0 products
                                                         └─ 451 if ToS not accepted
A storefront is created in draft state. Anyone with the previewUrl can open it; the URL token expires after 24 hours. To make it public, call POST /v1/storefronts/:storefrontId/publish (see Publishing).

Creating a storefront

POST /v1/storefronts accepts a StorefrontManifest. Required scope: catalog:write (user keys only).
POST /v1/storefronts HTTP/1.1
Authorization: Bearer mk_user_...
Idempotency-Key: 2f1a8c4b-2e3a-4b9d-9f1a-8c4b2e3a4b9d
Content-Type: application/json

{
  "name": "Tacos La Marea",
  "businessType": "restaurante",
  "language": "es",
  "currency": "MXN",
  "categories": [
    { "title": "Tacos", "description": "Estilo tradicional" },
    { "title": "Bebidas", "description": null }
  ],
  "products": [
    { "title": "Taco al pastor", "price": 25, "imageUrl": "https://...", "category": "Tacos" },
    { "title": "Coca Cola",       "price": 30, "imageUrl": null,         "category": "Bebidas" }
  ],
  "schedule": [{ "day": "mon", "open": "08:00", "close": "22:00" }]
}
  • products is optional in the manifest. To batch-add more after creation, use the product endpoints.
  • If the manifest contains more products than the plan allows, the response is 207 Multi-Status with the storefront created up to the plan cap plus an errors array describing the over-cap items. See Plan limits.
  • Idempotency: send Idempotency-Key to make retries safe. See Safe mutations.
Response (StorefrontDto):
{
  "storefront": {
    "id": "stf_abc123",
    "name": "Tacos La Marea",
    "language": "es",
    "currency": "MXN",
    "published": false,
    "categories": [{ "title": "Tacos", "description": "Estilo tradicional" }],
    "products": [{ "id": "prd_xyz", "title": "Taco al pastor", "price": 25 }],
    "_links": {
      "previewUrl": "https://marea.pro/preview/<token>",
      "publicUrl": null,
      "editUrl": "https://mareaalcalina.com/dashboard/menu/stf_abc123"
    }
  }
}
_links.publicUrl is null until publish — never expose the preview URL as the “public” URL.

Updating a storefront

PATCH /v1/storefronts/:storefrontId is a partial update. Required scope: catalog:write. Three merge rules:
Field shapeBehavior
Nested objectDeep-merge. { "delivery": { "fee": 50 } } only changes delivery.fee.
ArrayFull-replace. { "categories": [...] } overwrites the whole array.
Explicit nullClears the field. { "delivery": null } removes the delivery config.
Returns the updated StorefrontDto (200 OK). Republishing is a separate call — PATCH only mutates the draft.

Products

Products live on the storefront. Use POST /v1/storefronts/:storefrontId/products to create and PATCH /v1/storefronts/:storefrontId/products/:productId to update. Both require catalog:write and accept Idempotency-Key. The per-plan product ceiling is enforced on every create; over-cap creates return 402 plan_limit with upgrade.upgradeUrl populated.

Cross-tenant access is silently denied

If your mk_user_* key tries to access a storefront owned by a different user, the API returns 404 storefront_not_found — not 403. The 404 also fires before the empty-storefront check on publish to avoid leaking existence by status-code timing. Common cause when you get a 404 on a stable stf_ id you used moments ago: you’re sending the wrong user key (e.g. dev environment vs. prod). Confirm with GET /v1/me which user the key acts as.

What a storefront does NOT include

  • A storefront is not a channel. Where customers reach you (WhatsApp, web, in-person) is per-storefront configuration; the channel mix is not surfaced as a top-level field today.
  • A storefront is not an order ledger. Orders are a separate resource (see Page webhooks for order events).
  • Storefronts do not own subscriptions. Billing/plan lives on the owning user (see /v1/me).

Limits

See Plan limits for per-plan storefront-count and product-count ceilings. The product cap is per-storefront, not per-account.

Verification in code

  • src/api/v1/storefronts.create.ts
  • src/api/v1/storefronts.update.ts
  • src/api/v1/storefronts.publish.ts
  • src/api/services/translation/storefront-id.ts (stf_ ↔ menuId translation)
  • src/api/services/shape/storefront.shape.ts (toStorefrontDto)