All recipes
πŸ›’

Ordering website or app

Let customers browse a live menu and send orders straight to the counter.

This is the flagship Ewity integration: a storefront that reads the merchant's real catalogue and inventory in real time and drops confirmed orders directly onto their POS. You host a small Connect endpoint, pull the catalogue, price the cart server-side, and place the order β€” Ewity handles the merchant, the bill, and the kitchen.

Hand it to an agent

This prompt briefs an AI coding agent to implement the entire recipe against the live docs β€” authentication, every endpoint, and webhooks.

Build it with AI

Copy this prompt into your coding agent.

You are building an online ordering storefront on top of the **Ewity Platform Orders API** (base URL https://api.ewitypos.com/platform-v1). Implement it end-to-end.

IMPORTANT β€” the docs are authoritative. Before implementing each call, open the exact reference page linked below and follow its request/response schema precisely; the summaries here are a guide, not the source of truth. Start at https://ewity-api.readme.io/docs/getting-started and refer back to the docs whenever you're unsure β€” do not guess field names, enums, or shapes.

AUTH
- Every request sends header `Authorization: Bearer <EWITY_PLATFORM_TOKEN>`. It is ONE token per platform (not per merchant); keep it server-side only. The merchant/store is selected by the `online_location_id` you pass in each call. Auth overview: https://ewity-api.readme.io/docs/getting-started

STEP 1 β€” CONNECT (onboarding) β€” docs: https://ewity-api.readme.io/docs/implementing-connect
- In the developer portal, set your Connect URL in the app's Configuration β†’ Connection section. Configuring it IS the account-link opt-in (there is no separate toggle) β€” merchants link an account when they install.
- Expose ONE HTTPS endpoint (your "Connect URL"). Ewity calls it two ways — and like every Ewity→app request the calls are signed with X-Ewity-Signature (verify as in STEP 4); reject unverified callers with 401:
  - GET  -> respond 200 { "instructions": "<=4 lines>", "code_label"?: "Connection code" }
  - POST -> body { "connect_code": string }. Verify the code is single-use, account-bound, unexpired; consume it; respond 200 { "external_account_id": string, "display_name"?: string }. On failure respond 4xx { "error": "invalid_code|expired_code|code_already_consumed", "error_description": "<shown to merchant>" }.
- Also build a small authenticated screen in YOUR app where a signed-in merchant mints that single-use ~10-min code.

STEP 2 β€” CATALOGUE
- GET /stores (ref: https://ewity-api.readme.io/reference/liststores) -> persist online_locations[].id (this routes everything).
- Initial sync per location: GET /locations/{locationId}/catalogue (ref: https://ewity-api.readme.io/reference/getfullcatalogue) OR BFS via GET /locations/{locationId}/categories (ref: https://ewity-api.readme.io/reference/listrootcategories) then GET /locations/{locationId}/categories/{categoryId} (ref: https://ewity-api.readme.io/reference/getcategorydetails).
- Prices are TAX-INCLUSIVE. A product with no variants[] IS the variant. Buyable = available && !out_of_stock, evaluated per location. Currency: read it from the merchant (typically MVR) β€” never hard-code. Confirm every field name against https://ewity-api.readme.io/reference/getfullcatalogue.

STEP 3 β€” CART + ORDER
- POST /validate (ref: https://ewity-api.readme.io/reference/validatecart) { online_location_id, fulfilment_type: "delivery"|"pickup", lines:[{ variant_id, unit_quantity, modifiers:[modifier_id] }], fees?:[{name, amount}] } -> returns 200 ALWAYS; render data.total/total_tax/fees/total_components and BLOCK checkout if data.errors[] or any data.lines[].error is set.
- POST /orders (ref: https://ewity-api.readme.io/reference/createorder) { online_location_id, fulfilment_type, customer:{ mobile(required), name?, email?, address?:{ type, text } (text required when delivery) }, lines:[...], customer_notes? } -> returns Order (status "pending", payment_method null, no bill yet). Show "awaiting confirmation".

STEP 4 β€” STATUS, PAYMENTS, WEBHOOKS β€” docs: https://ewity-api.readme.io/docs/webhooks
- In the developer portal, on your app's Webhooks tab, add a webhook endpoint (your webhook_url) subscribed to the modern events: order.status_changed, order.notification, order.review_reminder, store.turned_on (and catalogue.updated for catalogue sync). Keep the default "Signing secret" verification; copy the whsec_… signing secret from the Webhooks tab and keep it server-side.
- Every delivery wraps the payload in the signed envelope { id, event, created_at, company, install, data } and carries headers X-Ewity-Event (event name), X-Ewity-Delivery (delivery id) and X-Ewity-Signature: t=<unix ts>,v1=<hex hmac_sha256(secret, "{t}.{raw_body}")>. Verify by recomputing the HMAC over the exact raw request bytes, comparing in constant time, and rejecting |now - t| > 300s. Dedupe on the envelope id (deliveries are at-least-once), respond 2xx within ~5s, process async. Event list + payloads: https://ewity-api.readme.io/docs/webhooks
- On event order.status_changed with data.status "accepted", the bill now exists -> POST /orders/{id}/payment (ref: https://ewity-api.readme.io/reference/addorderpayment) { amount, ref? } to record what you collected (partial payments allowed; not idempotent). For bank transfer, POST /orders/{id}/payment/bank-transfer (ref: https://ewity-api.readme.io/reference/paybanktransfer) (multipart file + payment_key from GET /orders/{id} β€” ref: https://ewity-api.readme.io/reference/getorder).
- Forward order.notification (data.title, data.message) to the customer. Let the customer cancel/confirm via POST /orders/{id}/status-change/{cancelled|pending} (ref: https://ewity-api.readme.io/reference/changeorderstatus) only when the order's extra.customer_next_states allows it.

DELIVERABLES
- A storefront (menu browse -> cart -> checkout -> order tracking), a server that holds the token + implements Connect + the webhook receiver, and a periodic catalogue sync. Handle: out-of-stock lines, validate-before-order, and idempotent webhook processing. Keep the token out of the browser. When any detail is ambiguous, consult the linked ReadMe reference page rather than assuming.

…or follow the build, step by step

  1. 1

    Implement Connect (onboarding)

    Host one Connect URL so a merchant can pair your app to their Ewity account with a short code β€” no OAuth to run.

    • GET{your_connect_url}
    • POST{your_connect_url}
    • Set your Connect URL in your app's Configuration β†’ Connection section. Configuring it IS the account-link opt-in β€” merchants link an account at install; there is no separate toggle.
    • Behind your own login, mint a single-use, account-bound, ~10-minute code and show it to the merchant.
    • The merchant opens Ewity POS β†’ Ecommerce β†’ Platforms β†’ your card β†’ Enable. Ewity calls GET {your_connect_url} (return `instructions` + optional `code_label`), then POST {your_connect_url} with `{ connect_code }`.
    • Verify + consume the code and return `{ external_account_id, display_name? }`. On errors return `4xx` with `{ error, error_description }` β€” `error_description` is shown to the merchant verbatim.
    Read the docs for this step
  2. 2

    Discover stores & fetch the catalogue

    List the merchant's online locations, then pull the full catalogue (or walk categories) and keep it in sync.

    • GET/stores
    • GET/locations/{locationId}/catalogue
    • GET/locations/{locationId}/categories
    • POST/locations/{locationId}/catalogue/lookup
    • `GET /stores` returns `online_locations[].id` β€” that id routes every other call.
    • Prices are tax-inclusive; `taxes[]` gives the breakdown. Real availability = `available` AND NOT `out_of_stock` (stock is per online location β€” never cache it across locations).
    • Stay in sync: re-sync ~every 15 min, on the `store.turned_on` webhook, and on `catalogue.updated` (call `catalogue/lookup` with the changed `variant_ids`).
    Read the docs for this step
  3. 3

    Validate the cart & place the order

    Price the cart server-side for accurate totals/taxes/fees and per-line stock errors, then create the order.

    • POST/validate
    • POST/orders
    • `POST /validate` always returns 200 β€” inspect `data.errors[]` and per-line `data.lines[].error` before you let the customer check out.
    • `POST /orders` needs `online_location_id`, `fulfilment_type` (delivery|pickup), `customer` (mobile required; address required for delivery) and `lines[]` of `{ variant_id, unit_quantity, modifiers[] }`. The order starts `pending` with no bill yet.
    Read the docs for this step
  4. 4

    Handle status, payments & webhooks

    React to acceptance, record payment once a bill exists, and forward merchant notifications to your customer.

    • POST{your_webhook_url}
    • GET/orders/{id}
    • POST/orders/{id}/payment
    • POST/orders/{id}/status-change/{status}
    • On your app's Webhooks tab, add a webhook endpoint subscribed to `order.status_changed`, `order.notification`, `order.review_reminder` and `store.turned_on` (plus `catalogue.updated` if the same endpoint handles catalogue sync). Keep the default signing-secret verification; copy the `whsec_…` secret from the same tab.
    • Every delivery arrives wrapped in the signed envelope `{ id, event, created_at, company, install, data }` with `X-Ewity-Event` / `X-Ewity-Delivery` headers. Verify `X-Ewity-Signature` (`t=<ts>,v1=<hex hmac_sha256(secret, "{t}.{raw_body}")>`, reject |now βˆ’ t| > 300s), dedupe on the envelope `id` (at-least-once delivery), ack with 2xx fast.
    • Watch `order.status_changed` for `data.status: "accepted"` β€” the bill exists from then on, so `POST /orders/{id}/payment` (partial payments allowed) becomes valid.
    • Forward `order.notification` (`data.title` / `data.message`) to your own SMS/push, and nudge on `order.review_reminder`.
    Read the docs for this step

Ready to build ordering website or app?

Create an app to get your API key, then follow the steps above.