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
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.
- GET
- 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`).
- GET
- 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.
- POST
- 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`.
- POST
Ready to build ordering website or app?
Create an app to get your API key, then follow the steps above.