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.
Page webhooks
Marea POSTs JSON events to a URL each merchant configures for their store / digital menu / page. Use them to keep a POS, accounting integration, delivery tool, or automation platform (Zapier, Make.com, n8n) in sync with the lifecycle of every order — without polling.Two webhook surfaces, do not confuse them:
- Page webhooks (this page) — fire on merchant order events (
order.created,order.status_updated,order.paid) for a single store / digital menu / page. Configured per-store by the merchant in the profile page at/menus/profile?section=integrations. - Agent webhooks — fire on user lifecycle events (
user.verified,user.cancelled) for users a developer key bootstrapped. See Agent webhooks.
When do page webhooks fire?
Three events ship in v1:| Event type | When it fires |
|---|---|
order.created | A customer completes checkout. Fires when the order transitions to orderStatus: 'pending' and a publicOrderId has been assigned. |
order.status_updated | The merchant changes the order’s status (e.g. pending → inProgress → completed, or any custom-pipeline transition). One event per transition. |
order.paid | Payment is captured (Stripe Checkout success or PayPal capture success). Cash / custom payments do not fire this event in v1. |
Granularity vs. coarse status: Marea’s internal
orderStatus enum is coarse (pending | inProgress | completed | cancelled | refunded | abandonedCart | erased). Most merchants run a more granular per-store pipeline (e.g. received → preparing → ready → delivered). The order.status_updated payload carries BOTH the coarse orderStatus and the resolved customStatus { id, label }. Use whichever your integration cares about.Configure your webhook
A merchant configures their webhook in their Marea profile at/menus/profile?section=integrations:
- Paste an HTTPS URL — your POS-vendor’s hook URL, your Zapier catch URL, your accounting integration endpoint, etc.
- Pick events — tick
order.created/order.status_updated/order.paid. Marea only dispatches subscribed events. - Save — Marea generates a 32-byte HMAC signing secret server-side and reveals it once in a modal. Copy it; you’ll need it to verify signatures on your receiver.
- [Probar webhook] — fire a synthetic test event to your endpoint. Receivers can identify test events via
data.__test === true.
localhost, 127.0.0.1), private IP ranges (RFC1918), the cloud-metadata IP (169.254.169.254), .internal and .local DNS suffixes, and IPv6 ULA / link-local / IPv4-mapped private addresses are rejected.
Event payload shape
Every webhook is aPOST with Content-Type: application/json and a payload wrapped in a common envelope:
order.created
The full order payload — items, pricing, customer, delivery, payment method:
order.status_updated
A slim payload — previousState → newState only. Receivers cache the order.created payload (which has full order context) and merge by orderId. PII is omitted from this event because status updates can fire many times per order.
order.paid
A finance-focused payload — pricing + provider-specific reconciliation IDs. Customer PII is omitted. Use this event for accounting integrations that don’t need order context.
Verify signatures
Every webhook is signed with HMAC-SHA256. The signature is in theX-Marea-Signature request header:
"<timestamp>.<rawBody>" using your raw signing secret (the 32-byte hex string Marea revealed when you first saved the webhook).
Key difference from Agent webhooks
Agent webhooks derive the signing key from the developer-key hash via HKDF-SHA256. Page webhooks use the raw signing secret directly — there is no HKDF step. Use theX-Marea-Source header to disambiguate:
| Header | Source | Secret derivation |
|---|---|---|
X-Marea-Source: developer | Agent webhook | HKDF-SHA256 from apiKeys/{keyId}.keyHash |
X-Marea-Source: merchant | Page webhook | Raw 32-byte hex from users/{uid}.webhookSigningSecret |
X-Marea-Source to pick the right verification path.
Verification flow
- Parse the
t=...andv1=...from the header - Reject if
|now - t| > 300(5-minute replay window) - Compute
HMAC-SHA256(secret, "<t>.<rawBody>")and hex-encode - Use a constant-time comparison against
v1 - If match: trust the payload. If not: respond
401.
Headers reference
| Header | Value |
|---|---|
Content-Type | application/json |
X-Marea-Signature | t=<unix-ts>,v1=<hex-hmac-sha256> |
X-Marea-Event-Type | order.created / order.status_updated / order.paid |
X-Marea-Event-Id | UUID v4 (same value as data.eventId) — use for idempotency |
X-Marea-Source | merchant for Page webhooks |
User-Agent | marea-webhook/1.0 |
Retry behavior
Marea expects a2xx response from your endpoint within 5 seconds. Anything else (non-2xx, timeout, connection error) triggers the retry path:
| Attempt | Delay from first dispatch |
|---|---|
| 1 | Immediate |
| 2 | +30 seconds |
| 3 | +5 minutes |
max_attempts_reached with the final HTTP status. There is no automatic replay in v1; if you need bulletproof delivery, queue events on your end (e.g. via Zapier / Make.com / n8n).
Recent deliveries log
The merchant’s profile page surfaces the last 50 deliveries (rolling 30-day window) with:- Outcome — success / failed / max_attempts_reached / pending
- Response status — color-coded (green
2xx, yellow4xx, red5xxor max-attempts) - Latency — time to receive a response from your endpoint
- Attempt count —
1/3,2/3,3/3 - Error message — for failed deliveries (truncated to 500 chars)
data.__test === true) are flagged in the log so they don’t pollute success-rate metrics.
Receiver implementation tips
- Respond 200 immediately, then process asynchronously. Marea’s 5-second timeout is tight; if your business logic takes longer, ack quickly and process in a queue.
- Idempotency: design your handler to dedupe on
eventId— Marea retries on transient failures, so duplicate deliveries are possible. - Constant-time signature comparison: don’t use
===or==on the HMAC — usecrypto.timingSafeEqual(Node) orhmac.compare_digest(Python). - Test with
webhook.sitefirst: configure your webhook URL to point at a uniquewebhook.siteURL while you wire up your real handler — you’ll see the exact payloads Marea sends. - Distinguish surfaces by
X-Marea-Sourceif your endpoint also receives Agent webhooks.
Rotate the signing secret
If you suspect your secret has leaked, click Rotar secreto in the integrations tab. Marea generates a new 32-byte hex secret, reveals it once, and immediately invalidates the old one. There is no overlap window in v1 — update your receiver’s secret immediately. In-flight deliveries that were enqueued before the rotation are signed with the new secret at delivery time, so receivers configured with the new secret will accept them.Limitations and roadmap
- Three events only today (
order.created,order.status_updated,order.paid) —menu.*/product.*/customer.*/subscription.*events are TIER 2. - No missed-event replay endpoint — events that hit
max_attempts_reachedare gone. Use a buffered receiver if you need stronger guarantees. - Single webhook URL per merchant — fan-out to multiple endpoints via Zapier/Make.com.
- Cash / custom payments don’t fire
order.paidin v1. Subscribe toorder.createdand look atpaymentMethodif you need cash-order coverage. - Refund event (
order.refunded) is TIER 2. - 24h secret-rotation overlap window is TIER 2.