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.

Plan limits

Every Marea user is on a plan. The plan determines:
  1. How many storefronts the user can have
  2. How many products per storefront
  3. Whether the user can publish at all (pre-paywall accounts cannot)
  4. Per-transaction platform fee, multi-branch availability, and monthly-orders visibility (covered below)
Your agent should read the user’s plan from GET /v1/me (plan.tier + plan.limits) and surface _links.upgradeUrl if a higher tier is required. The numbers below are the canonical caps from plan-limits.ts; custom plans can override the storefront cap via planQuantity, and the /v1/me response always reflects the live cap.
The API surfaces a coarse-grained 4-value tier (free, basic, pro, business) for SDK stability. Internally there are more numeric sub-plans (e.g. business_200, business_500, agency_growth) that collapse to "business" on the wire. The numeric planQuantity field carries the true storefront ceiling for custom plans.

The plan.limits shape

/v1/me returns this exact shape — fields are not pluralized the way you might expect:
{
  "plan": {
    "tier": "free",
    "limits": {
      "storefronts": 1,
      "products": 30,
      "publishable": true
    }
  },
  "planQuantity": null
}
FieldMeaning
limits.storefrontsMaximum storefronts the user can own.
limits.productsMaximum products per storefront (each storefront gets its own bucket).
limits.publishabletrue if the plan permits making a storefront publicly available. Only false for pre-paywall accounts.
planQuantityIf non-null, the per-account override for limits.storefronts (custom/agency plans).

Per-plan caps

Surfaced tierInternal plansstorefrontsproducts per storefrontpublishable
free (pre-paywall)NO_ACTIVO (9)12,000 (drafts only — see note)false
freeFREE_NEW (8)130true
freeFREE_OLD (2, legacy)330true
basicBASIC_MONTHLY (4) / BASIC_YEARLY (5)360true
proPRO_MONTHLY (1) / PRO_YEARLY (3)15200true
businessBUSINESS_MONTHLY (6) / BUSINESS_YEARLY (7)502,000true
businessBUSINESS_200 (11)2002,000true
businessBUSINESS_500 (12)5002,000true
businessBUSINESS_1000 (13)1,0002,000true
businessAGENCY (20, legacy)202,000true
businessAGENCY_MONTHLY (21) / AGENCY_YEARLY (22)5,0002,000true
Pre-paywall accounts. A pre-paywall user (NO_ACTIVO) collapses to "free" on the wire but limits.publishable === false. The product cap shows as 2,000 to permit full authoring preview before checkout, but POST .../publish returns 402 and orders are blocked upstream. Use plan.limits.publishable — never the tier name — to decide whether a publish call will succeed.

Beyond storefronts + products

plan-limits.ts also encodes several plan-derived behaviors. Most are not surfaced verbatim in /v1/me.plan.limits today (the public limits object is the locked 3-field shape above), but they govern what the user can do once they hit the dashboard. Surface these to the user when an upgrade CTA needs context:
CapabilityFree (NO_ACTIVO/FREE_*)BasicProBusinessAgency
Platform fee (Stripe + PayPal)2.5%1.9%1.5%0.9%0%
Multi-branch (locations per storefront)0 (off)0 (off)0 (off)3unlimited
Monthly orders visible in dashboard30 (NO_ACTIVO = 0)2001,000unlimitedunlimited
Monthly-order visibility gates dashboard surface — orders still arrive via WhatsApp regardless. The 0 for NO_ACTIVO means dashboard order intake is fully disabled until the user picks a plan.

Scopes vs. plan

Plan tier does not determine the scopes a user key holds. Scopes are set per-key at issuance (catalog:read, catalog:write, storefront:publish). The publish scope passes the auth layer for any plan, but the storefronts/:id/publish handler then re-checks plan.limits.publishable and returns 402 for pre-paywall accounts. In other words: the scope says “this key is permitted to attempt publishing”, and the plan says “this account is permitted to actually publish”. Both must be true.

What happens at the limit

402 plan_blocks_publish — pre-paywall accounts only

POST /v1/storefronts/:id/publish returns 402 when the user is in the pre-paywall state (NO_ACTIVO, plan 9). Once any paid plan or legacy free plan is active, this endpoint stops rejecting.
{
  "error": {
    "type": "plan_limit",
    "code": "plan_blocks_publish",
    "message": "Your plan does not permit publishing. Upgrade to publish this storefront.",
    "recoverable": true,
    "upgrade": {
      "currentPlan": "free",
      "requiredPlan": "basic",
      "upgradeUrl": "https://mareaalcalina.com/upgrade?planSource=api"
    }
  }
}
Surface upgrade.upgradeUrl to the user as a CTA. Do not retry without a plan change.
currentPlan: "free" here is the coarse-grained tier surface — pre-paywall and FREE_* both collapse to "free" so upgrade copy stays consistent. The authoritative signal is plan.limits.publishable from /v1/me, not the tier string.

207 products_over_limit — bulk manifest exceeds product cap

POST /v1/storefronts accepts a manifest with up to 100 products in a single call. If the user’s plan caps lower than the manifest count, the storefront is still created up to the cap and the response status is 207 Multi-Status:
{
  "storefront": { "id": "stf_xxx", "...": "..." },
  "errors": [
    {
      "type": "plan_limit",
      "code": "products_over_limit",
      "message": "Plan allows 30 products; 12 were not created.",
      "param": "products",
      "doc": "https://docs.mareaalcalina.com/errors/products_over_limit",
      "recoverable": true,
      "recovery": {
        "skippedCount": 12,
        "skippedProducts": [
          { "index": 30, "title": "Tacos al pastor" },
          { "index": 31, "title": "Tacos de chorizo" }
        ],
        "upgrade": {
          "currentPlan": "free",
          "requiredPlan": "basic",
          "upgradeUrl": "https://mareaalcalina.com/upgrade?planSource=api",
          "previewUrl": "https://marea.pro/preview/<token>"
        }
      }
    }
  ]
}
The storefront is real and usable. errors[].recovery.skippedProducts tells your agent exactly which items were dropped, with the original manifest index. Surface the upgrade CTA — once the user upgrades, retry the dropped products via POST /v1/storefronts/:storefrontId/products.

402 plan_max_products_reached — individual product POST hit the cap

Once the storefront exists, individual POST /v1/storefronts/:storefrontId/products calls that would push past the per-storefront product cap return a plain 402 with the same upgrade shape:
{
  "error": {
    "type": "plan_limit",
    "code": "plan_max_products_reached",
    "message": "Plan limit of 30 products reached. Upgrade to add more.",
    "param": "products",
    "recoverable": true,
    "upgrade": {
      "currentPlan": "free",
      "requiredPlan": "basic",
      "upgradeUrl": "https://mareaalcalina.com/upgrade?planSource=api"
    }
  }
}
Note the different code from the bulk path: single-product POSTs do not return 207 — there’s nothing partial to report.

Reading the user’s current plan

GET /v1/me HTTP/1.1
Authorization: Bearer mk_user_xxxxxxxxxxxxxxxx
{
  "id": "usr_xxx",
  "type": "user",
  "plan": {
    "tier": "free",
    "limits": { "storefronts": 1, "products": 30, "publishable": true }
  },
  "planQuantity": null,
  "_links": {
    "upgradeUrl": "https://mareaalcalina.com/upgrade?planSource=api",
    "dashboardUrl": "https://mareaalcalina.com/dashboard"
  }
}
Check plan.limits.publishable before calling publish, and compare current storefront / product counts against plan.limits.{storefronts, products} before bulk-creating to avoid the 207 round-trip.

What this page does NOT cover

  • Pricing. Pricing, billing cycles, and promotional terms live at mareaalcalina.com. The numbers above are the technical caps; what each plan costs is on the public pricing page.
  • Plan sub-tiers on the wire. Business and Agency have multiple internal sub-tiers (200 / 500 / 1,000 storefronts, monthly / yearly cadences, legacy variants). Agents see the public 4-value tier surface; the underlying numeric plan ID is not exposed. The true storefront ceiling for custom plans is in planQuantity.
  • Cache TTLs. /v1/me reads the user doc through a 30-second in-instance cache (BL-4). A plan change made through the dashboard becomes visible to the API within 30 seconds without any explicit invalidation.