# Authentication All Papaya Open API endpoints require a **Bearer token** for authentication. Create a separate **API key** for each use case (e.g. _Digital Ordering_, _Reservations_), and include the corresponding token in the Authorization header of each request. ### **Why token-based auth?** * **Stateless**: No cookies or sessions—each request stands alone. * **Secure**: Tokens are long, opaque strings (JWTs) that you store server-side. * **Granular**: You can label and rotate individual tokens without disrupting others. ### **Setting the header** Include your token exactly as shown below. Any deviation (missing space, wrong case) will result in `401 Unauthorized`. ```plaintext GET /api/v1/menus?channelType=partner Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9… ``` plaintext ### **Creating your first token** API keys are scoped to individual outlets and can be created and managed per outlet here: Once Open API is enabled for an outlet: 1. Click **“New API Key”** 2. Fill in the required fields: 1. **Use Case** – Select the relevant use case (e.g. _Digital Ordering_) 2. **Label** – Name your integration for easy reference 3. **Webhook URL** – Specify where Papaya should send event notifications 3. Click **Save** to generate the key 4. **Copy and securely store the token** – it will only be shown once [![](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/jkVJpot0rYW61CqWmgh8B.png)]() Copy the token immediately, you won’t be able to retrieve it again. Store it in a secure vault or environment variable. ### **Revoking tokens** To revoke a token, open the corresponding API key entry and toggle the **“Revoked”** field. This will immediately disable the token, preventing it from being used for any further API requests. ### **Common pitfalls** * **Missing header**: If you forget Authorization, you’ll see: ```plaintext { "code": 401, "message": "Unauthorized" } ``` plaintext * **Malformed token**: Extra characters or truncated tokens also yield 401. * **Wrong environment**: Don’t mix production tokens with sandbox APIs. * **Expiration**: While Papaya tokens don’t expire by default, rotating them every 90 days is a good security practice. # Request & Response Formats ### Request Formats * **JSON only**: * Content-Type: application/json * `Authorization: Bearer {token}` * `Idempotency-Key: {uuid4}` (`POST` requests only) * **Params**: * **Path**: `/api/v1/{resource}/:id` * **Query**: `?status=active` * **Body**: * camelCase fields matching components/schemas * ISO 8601 for dates (`YYYY-MM-DDTHH:mm:ss.sssZ`) ### **Response Formats** * **JSON** with appropriate HTTP status: * `200 OK (GET/PUT/DELETE)` * `201 Created (POST)` * **Payloads**: * Single resource → object * Collections → array or named wrapper per spec # Webhooks Papaya uses webhooks to deliver real-time event data—such as order status updates—to your server. Each API key is configured with a single **Webhook URL**, and Papaya will `POST` relevant event payloads to that URL. ### **Enabling Webhooks** When creating an API key via the Papaya merchant dashboard, enter a valid URL in the Webhook URL field. This URL will receive event updates related to the key's assigned use case only: * **Digital Ordering** — Receives order status updates (`order.inKitchen`, `order.readyForPickup`, `order.complete`, `order.cancelled`) only for orders created via that integration. * **Reservations** — Receives order updates (`order.totalsUpdated`, `order.statusUpdated`, `order.channelUpdated`, `order.guestCountUpdated`) only for reservation-linked orders. * **Order Data** — Receives `order.complete` and `order.cancelled` events for all orders placed through the POS, regardless of source. * **Inventory Management** — Receives `menus.published`, `order.complete` and `order.cancelled` events for menu syncing and inventory tracking. General POS orders not associated with any API key will not trigger webhooks. ### Published Updates For potentially noisy updates—such as **Menu** or **Channel** changes—merchants must manually click **“Publish”** in the dashboard after making changes. These types of updates are typically less frequent, but involve larger resources (e.g. full menus) that may undergo many small edits. Without publishing, each minor change could trigger excessive webhook traffic. The manual publish step ensures that only intentional, consolidated updates are sent via webhook. In these cases, Papaya sends a lightweight webhook notifying you that the resource has changed — **not** the full updated data. It’s your responsibility to call the relevant `GET` endpoint to retrieve the latest version. [![](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/r9rVXYkxsbxGJ_8T17RNk.png)]() ### **Supported webhooks** | **Event** | **Trigger** | **Typical Use-Case** | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Channels Published | A merchant clicks **Publish** after editing channels.`event: channels.published` | Refresh your local list of channels and restaurant table definitions. | | Menu Published | A merchant clicks **Publish** after editing a menu.`event: menus.published` | Update menu in digital ordering apps so customers always see the latest menu. | | Order Status | Order status changes triggering the `event`:- `order.inKitchen` - `order.readyForPickup` - `order.complete` - `order.cancelled` | Update customer UI or send notifications. | | Outlet Updated | Outlet details changed in merchant dashboard.`event: outlet.updated` | Sync branding, opening hours, or address details in external systems.  | | Reservation Order Update | One of four reservation-linked order `event`:- `order.totalsUpdated` - `order.statusUpdated` - `order.channelUpdated` - `order.guestCountUpdated` | Pull fresh order data and apply business-specific logic (e.g. update a reservation platform’s bill, move table assignments, adjust guest count analytics). | ### **Handling responses** | **HTTP Status** | **Meaning** | | --------------- | --------------------------------------- | | **200** | Acknowledged – no retry will occur | | ≠ 200 | Failure – Papaya retries with backoff\* | **\*** Papaya will retry non-2xx responses several times using exponential backoff until it succeeds or times out. Always return 200 OK immediately once you’ve enqueued or processed the event. ### **Republishing Order Webhooks** If your system missed webhook events or needs to reprocess orders, merchants can republish order webhooks from the Papaya dashboard: 1. Navigate to **Orders** and filter by the **Complete** or **Cancelled** tab. 2. Select the orders you want to republish, or use the **⋮** menu to bulk update all visible orders. 3. Click **Republish orders** (when selecting individual orders) or **Bulk update orders** (from the menu). [![Papaya dashboard Orders page filtered to Complete and Cancelled tabs, with the three-dot menu open in the top-right showing the ](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/VykJcQesXCJ0pttbbQJlp.png)]() Bulk update orders [![Papaya dashboard Orders page showing two completed orders selected via checkboxes, with the Actions dropdown menu open displaying the ](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/GGNfqTnFHsyNaeeHJAR1Q.png)]() Republish orders This will re-send the `order.complete` or `order.cancelled` webhook events for the selected orders, allowing your system to re-fetch the latest order details via `GET /api/v1/orders/:id`. For recovering from downtime or backfilling larger periods, you can either republish the order webhooks as above or use the `GET /api/v1/orders` date range endpoint to retrieve orders in bulk. See the Order Data and Inventory Management use cases for details. ### **Best practices** * **Idempotency**: design your handler so repeated deliveries don’t cause duplicates * **Low latency**: respond within < 5 seconds to avoid timeouts or retries * **Logging**: record incoming payloads and responses for debugging * **Security**: * Validate requests originate from Papaya’s IP range or via a shared secret in the URL path/query * Use HTTPS with a valid TLS certificate * **Monitoring**: track failure rates and alert when retry thresholds are exceeded With webhooks configured, you’ll receive realtime updates without polling—enabling faster, more efficient workflows. # Introduction Papaya's Open API lets you integrate directly with our POS system — whether you're a merchant connecting your own tools or a software partner building for multiple locations. ### Use Cases The API currently supports four primary workflows: 1. **Digital Ordering**\ Integrate online ordering and delivery platforms with Papaya’s POS to enable real-time order placement and processing. 2. **Reservations**\ Connect a restaurant booking system to initiate linked orders on the POS from reservations and walk-ins, and receive real-time order updates to enrich customer profiles and unlock new insights. 3. **Order Data** Capture complete sales and order history from Papaya’s POS for accounting, inventory, or analytics needs. Ideal for syncing reports, reconciling transactions, and gaining deeper visibility into business performance. 4. **Inventory Management** Sync POS menus and real-time orders with inventory management systems like MarketMan. Automatically update stock levels and ingredient usage as orders are finalised, enabling accurate inventory tracking and automated reordering. ### Getting Started **Merchants** — You can get started right away. Enable Open API in your [merchant dashboard](https://merchant.papaya.co.th/settings/openapi), generate an API key for your use case, and start making requests. **Integration partners** — If you're building a software integration and need a dedicated sandbox environment, you can [create a sandbox account here](https://merchant-sandbox.papaya.co.th/register). ### What’s Next In the sections that follow, you’ll find: * [**Base URL**](https://docs.papaya.co.th/guide/base-url) * [**Use Cases**](https://docs.papaya.co.th/guide/use-cases) * [**Authentication**](https://docs.papaya.co.th/guide/authentication) * [**Request & Response Formats**](https://docs.papaya.co.th/guide/request-response-formats)   * [**Errors**](https://docs.papaya.co.th/guide/errors) * [**Webhooks**](https://docs.papaya.co.th/guide/webhooks) * [**Changelog**](https://docs.papaya.co.th/guide/changelog) # Use Cases Papaya’s API will continue to evolve with innovative capabilities that unlock new possibilities for partners and merchants. The initial focus is on digital ordering, reservation integrations, access to order data for analytics and reporting, and inventory management systems. ### Setup The API is scoped to a Papaya **outlet**, which represents an individual restaurant, shop, or branch at a specific physical location. In Papaya’s structure, an outlet sits under a **merchant** — the top-level entity that groups together multiple outlets. To begin using the API, you must first **Enable Open API** for a specific outlet via the merchant dashboard: [![](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/Ynu5ol8r5iD0seifnKc2I.png)]() Once enabled, you can generate one or more **API keys**, each linked to a specific use case along with a corresponding webhook URL. [![](https://api.scalar.com/cdn/images/b0saerQavdpA5vlYlp4Em/iEBUIzeQRe2Lh5zKI9h8O.png)]() Access tokens are shown only once. Please copy and store them securely. If forgotten, you’ll need to delete and recreate the API key for that use case. ### Digital Ordering **Integrate food-delivery aggregators or other online ordering platforms with Papaya’s POS.** #### **Flow** 1. **Credential & Channel Setup** 1. In the Papaya merchant dashboard, create an API key with the use case set to “Digital Ordering”, and securely store the generated Bearer `token` for authentication. 2. Under **Channels** in the merchant dashboard, register your ordering platform as a channel of type “Partner” (e.g. Grab, LINEMAN). This allows it to be retrieved via the `GET` `/api/v1/channels` endpoint. 2. **Sync Menu** 1. Fetch the current menu for that channelType: ```plaintext GET /api/v1/menus?channelType={channelType} ``` plaintext 2. Noting that for `channelType = partner`, you should also include the `partnerChannel `query parameter with the appropriate string value (e.g. `grab`) to ensure partner-specific menu overrides are applied. 3. **Create Order** 1. When the ordering platform receives a new order, forward it into Papaya: ```plaintext POST /api/v1/orders { "channelId": "{channel.id}", "partnerInfo": { "partnerChannel": "grab", "orderId": "GF-123" }, "items": [ { "customerName": "Pim", "menuItemId": "{menuItem.id}", "quantity": 2, "options": [ { "optionId": "{option.id}", "quantity": 1 } ] } ], "payments": [ { "offlineMethod": "card", "amount": 100, "customerName": "Pim", "externalId": "stripe-123", "currency": "THB", "created": "2025-06-22T08:55:26.169Z" } ], } ``` plaintext 4. **Track & Fulfill** 1. Receive live updates via your configured webhook **(recommended)** 2. or, poll the order using `GET` `/api/v1/orders/:id` 5. **To Cancel an Order** Use the update order endpoint to change the order `status` to `cancelled` when an order is no longer proceeding. ```plaintext PUT /api/v1/orders/:id { "status":"cancelled" } ``` plaintext #### **Key Endpoints** | | | | | ------------- | ---------- | ------------------------------------- | | **Endpoint** | **Method** | **Description** | | `/channels` | `GET` | List available channels | | `/menus` | `GET` | Retrieve menu for a given channelType | | `/orders` | `POST` | Create a new order | | `/orders/:id` | `GET` | Fetch order details & status | | `/orders/:id` | `PUT` | Cancel an order | | Webhook URL | `POST` | Receive order status notifications | ### **Reservations** **Connect a restaurant booking system to initiate orders on the POS from reservations and walk-ins, and receive real-time order updates.** #### **Flow** 1. **Credential & Channel Setup** 1. In the Papaya merchant dashboard, create an API key with the use case set to “Reservations”, and securely store the generated Bearer `token` for authentication. 2. In the merchant dashboard under **Channels**, create a channel for each table, ensuring each is set to type “Dine-in”. All channels can then be retrieved via the `GET` `/api/v1/channels` endpoint. 2. **Open an Order (Table)** 1. When you mark a customer as arrived on your reservation platform, your system calls: ```plaintext POST /api/v1/orders/reservations { "channelId": "{channel.id}", "guestCount": 4, "partnerReservationInfo": { "partnerName": "Reservation App Name", "reservationId": "res-123", "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "phoneNumber": "+66922801111" }, "payments": [ { "offlineMethod": "card", "amount": 100, "currency": "THB", "externalId": "stripe-123", "created": "2025-06-22T08:55:26.169Z" } ] } ``` plaintext 2. This creates an order in Papaya POS linked to the reservation. A `payments` array is optional and should be included if a deposit has already been paid. Only channels with type `dine-in` can be opened using the `POST` `/orders/reservations` endpoint. 3. **Update an Order** 1. When the reservation changes on your platform — host moves the table, marks the booking cancelled, edits the party size, or updates guest contact info — call: ```plaintext PUT /api/v1/orders/reservations/{id} { "channelId": "{channel.id}", // move table "status": "cancelled", // close the order "guestCount": 6, "partnerReservationInfo": { "firstName": "Jane", "email": "jane@example.com", "note": "VIP — window seat" } } ``` plaintext 2. All fields are optional; send only what's changing. Notes: * `status: "cancelled"` cannot be combined with other fields. * `partnerName` and `reservationId` are immutable. * Only the API key that created the order can update it. 4. **Listen for Order Updates** 1. After initiating an order, your reservation platform will receive order update notifications via the Webhook URL configured when creating the API key in the merchant dashboard. 2. Four events can trigger a [reservation order update](https://docs.papaya.co.th/api-reference#webhook/post/reservation-order-update-postback): 1. `order.totalsUpdated` — order totals have changed 2. `order.statusUpdated` — order status has changed (e.g. from `open` to `cancelled`) 3. `order.channelUpdated` — the order was moved between tables 4. `order.guestCountUpdated` — guest count has changed 3. Use the `GET` `/api/v1/orders/:id` endpoint to fetch the latest order details and respond as needed based on your platform’s business logic. Webhook notifications will only be sent for orders initiated from the reservation platform. 5. **Listen for Channel Updates** 1. When a merchant updates their channel (table) configuration in the merchant dashboard, those changes are published to reservation partners. 2. You will receive a `channels.published` notification via the Webhook URL. 3. Upon receiving this event, call `GET` `/api/v1/channels` to retrieve the latest channel (table) configuration data. #### **Key Endpoints** | **Endpoint** | **Method** | **Description** | | ---------------------- | ---------- | ------------------------------------------------------------------------------------------ | | `/channels` | `GET` | List available channels | | `/orders/reservations` | `POST` | Open a reservation-linked order in Papaya | | `/orders/reservations` | `PUT` | Update a reservation-linked order | | `/orders/:id` | `GET` | Fetch latest order details | | Webhook URL | `POST` | Receive postbacks for:- Reservation order updates - Channel / table configuration changes | ### **Order Data** **Get complete sales and order data from Papaya’s POS to simplify reporting, reconciliation, and performance tracking.** #### **Flow** 1. **Credential Setup** 1. In the Papaya merchant dashboard, create an API key with the use case set to “Order Data”, and set a webhook URL for receiving order updates. 2. Securely store the generated Bearer `token` for authentication. 2. **Listen for Order Completion (Recommended)** 1. When orders are placed through Papaya's POS, regardless of source, you'll receive real-time updates via your configured webhook URL. These updates are sent as lightweight "thin" events containing only the event name and Order ID.. 2. For the Order Data use case, only two event types are sent from the [Order status webhook](https://docs.papaya.co.th/api-reference#webhook/post/order-status-postback): 1. `order.complete` — the order was successfully completed and paid 2. `order.cancelled` — the order was cancelled, either before or after payment These two events represent the final, permanent state of an order. ```plaintext POST {webhook_url} { "id": "01K7KAT54KQ6544MKY9G5TTEWG", "event":"order.complete", "updatedAt": "2025-10-10T08:55:26.169Z" } ``` plaintext 3. Use the `GET` `/api/v1/orders/:id` endpoint to retrieve the latest order details for your integration or reporting needs. **We recommend this webhook-driven approach as your primary integration pattern.** It ensures you always have the latest data without unnecessary polling. You may receive multiple order updates, as orders can be cancelled after completion or reopened and modified before being completed again. However, merchants should always transition orders back to either a _complete_ or _cancelled_ state. 3. **Retrieve Orders by Date Range (Alternative)** If you need to reconcile, backfill, or retrieve orders in bulk, use the `GET /api/v1/orders` endpoint to query orders within a specified date range. * Filter by status (`complete`, `cancelled`) via the `status` query parameter. Defaults to both. * Maximum date range is 31 days. * Results are paginated with up to 25 orders per status per page using cursor-based pagination. * Each order includes the full payload (items and payments), matching the `GET /api/v1/orders/:id` response shape. ```plaintext GET /api/v1/orders?from=2026-01-01T00:00:00Z&to=2026-01-31T23:59:59Z ``` plaintext This endpoint is best suited for reconciliation and backfilling — not as a replacement for webhooks. Excessive polling may result in rate limiting (5 requests/second). #### **Key Endpoints** | **Endpoint** | **Method** | **Description** | | ------------- | ---------- | ------------------------------------------------------------ | | `/orders/:id` | `GET` | Fetch latest order details | | `/orders` | `GET` | Fetch orders by date range | | Webhook URL | `POST` | Receive `order.complete` and `order.cancelled` notifications | ### Inventory Management **Sync Papaya POS menus and real-time orders to inventory management systems. Automatically update stock levels and ingredient usage as orders are finalised.** #### **Flow** 1. **Credential Setup** 1. In the Papaya merchant dashboard, create an API key with the use case set to “Inventory Management”, and set a webhook URL for receiving menu and order updates. 2. Securely store the generated Bearer `token` for authentication. 2. **Listen for Menu Change**s 1. When the merchant publishes menu updates (new items, pricing changes, availability toggles), you'll receive a webhook notification: 1. `menus.published` — the menu has been updated ```plaintext POST {webhook_url} { "event":"menus.published", "updatedAt": "2025-10-10T08:55:26.169Z" } ``` plaintext 2. Use the `GET` `/api/v1/menus` endpoint to retrieve the latest POS menu data and sync with inventory system. 3. **Listen for Order Completion (Recommended)** 1. When orders are placed through Papaya’s POS, you’ll receive real-time updates via your configured webhook URL. These updates are sent as lightweight “thin” events containing only the event name and Order ID. 2. For the Inventory Management use case, only two event types are sent from the [Order status webhook](https://docs.papaya.co.th/api-reference#webhook/post/order-status-postback): 1. `order.complete` — the order was successfully completed and paid 2. `order.cancelled` — the order was cancelled, either before or after payment These two events represent the final, permanent state of an order. ```plaintext POST {webhook_url} { "id": "01K7KAT54KQ6544MKY9G5TTEWG", "event":"order.complete", "updatedAt": "2025-10-10T08:55:26.169Z" } ``` plaintext 3. Use the `GET` `/api/v1/orders/:id` endpoint to retrieve the latest order details, ensuring the inventory system has all the necessary sales context for ingredient usage, etc. **We recommend this webhook-driven approach as your primary integration pattern.** It ensures your inventory system always reflects the latest order state without unnecessary polling. You may receive multiple order updates, as orders can be cancelled after completion or reopened and modified before being completed again. However, merchants should always transition orders back to either a _complete_ or _cancelled_ state. 4. **Retrieve Orders by Date Range (Alternative)** If you need to reconcile inventory adjustments or backfill order data for a specific period, use the `GET /api/v1/orders` endpoint to query orders within a date range. * Filter by status (`complete`, `cancelled`) via the `status` query parameter. Defaults to both. * Maximum date range is 31 days. * Results are paginated with up to 25 orders per status per page using cursor-based pagination. * Each order includes the full payload (items and payments), matching the `GET /api/v1/orders/:id` response shape. ```plaintext GET /api/v1/orders?from=2026-01-01T00:00:00Z&to=2026-01-31T23:59:59Z ``` plaintext This endpoint is best suited for reconciliation and backfilling — not as a replacement for webhooks. Excessive polling may result in rate limiting (5 requests/second) #### **Key Endpoints** | **Endpoint** | **Method** | **Description** | | ------------- | ---------- | ------------------------------------------------------------------------------- | | `/menus` | `GET` | Retrieve menus for a given `channelType` | | `/orders/:id` | `GET` | Fetch individual order details | | `/orders` | `GET` | Fetch orders by date range | | Webhook URL | `POST` | Receive `menus.published`, `order.complete` and `order.cancelled` notifications | # Changelog All notable changes to the Papaya Open API will be documented in this file. ### \[**2.324.0**] – 2026-07-15 #### Added * **Inventory Good Receipts Endpoint** * Added `GET /api/v1/inventory/goods-receipts` — lists the outlet's goods receipts (purchase records) within a date range, oldest first, cursor-paginated. Requires RFC 3339 `from`/`to` bounds (maximum range 31 days) filtering on `created`, and supports a comma-separated `status` filter (`draft`, `completed`, `cancelled`, `partiallyReceived`; defaults to `completed`) and an optional `supplierId`. Each receipt includes header amounts (`totalPrice`, `tax`, `shippingFee`, `total`, `isTaxInclusive`), the supplier snapshot, and full line items with quantity, purchase unit, unit cost, line total, and the tax rate snapshot applied at receipt time. Use `deliveryDate` as the supplier bill date in accounting systems, and treat the line `totalPrice` as authoritative. Pages return up to 25 receipts; a page may return fewer but still carry a `next` cursor — always follow the cursor until it is `null`. * Authenticate with the same outlet API key used for the `/orders` endpoints (merchant/outlet scope comes from the key); MCP access tokens are also accepted. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[2.315.0] – 2026-07-01 #### Added * **Order Action Log Endpoints** * Added `GET /api/v1/orders/{id}/action-log` — returns the full action log for a single order, newest first. Each entry has a structured `action` type, a projected `actor` (`type`, `id`, `name`, `role`), action-specific `metadata`, and `created`. Mirrors the access rules of `GET /api/v1/orders/{id}`: returns `404` for missing or partner-aggregated orders. * Added `GET /api/v1/orders/action-logs` — lists the outlet's action log entries within a date range, newest first, cursor-paginated. Requires RFC 3339 `from`/`to` bounds (maximum range 31 days) and supports filtering by `action`, `actorId`, `actorType`, `channelId`, and free-text `search`. A page may return fewer entries than requested but still carry a `next` cursor — always follow the cursor until it is `null`. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.227.0**] – 2026-03-17 #### Added * **List Orders by Date Range Endpoint** — Added `GET /api/v1/orders` endpoint for retrieving orders within a specified date range. Supports filtering by status (`complete`, `cancelled`), cursor-based pagination (25 orders per status per page), and returns the full order payload including items and payments, matching the `GET /api/v1/orders/:id` response shape. Maximum date range is 31 days. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.206.0**] – 2026-02-06 #### Added * **Historical Order Import Endpoint** * Added `POST /orders/import` endpoint for importing historical orders from external POS systems (e.g., Revel, Square). * Accepts caller-provided `orderDate`, `paidAt`, `number`, and `receiptNumber` to preserve original order data. * Orders are created directly as `complete` with timestamps reflecting the original order date, skipping real-time validations and side effects (printing, webhooks, inventory checks). * Requires `externalOrderSource` to identify the originating POS system. * Supports idempotency keys for safe retries during bulk migration. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.196.0**] – 2026-01-19 #### Added * **Discount Support for Create Order** * Added `appliedDiscounts` field to create order endpoint for order-level fixed discounts. * Supports optional custom discount `name` (defaults to "Open Discount"). * Validates discount total does not exceed bill total before creating order. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.189.0**] – 2026-01-05 #### Added * **Inventory Management Use Case** * Added new Inventory Management use case to send webhooks for menu updates and all `complete` or `cancelled` orders. * Uses thin event payload (`orderId`, `event`, `updatedAt`) for partners to fetch full details via `/orders/:id`. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.177.0**] – 2025-12-02 #### Added * **Rate Limiting** * Implemented global rate limiting for all OpenAPI endpoints (5 requests per second per API key) * Added HTTP 429 response with `retryAfter` field when rate limit is exceeded * Rate limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`) included in all responses * Rate limiting is scoped per API key to prevent abuse while allowing reasonable burst traffic #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.160.0**] – 2025-10-22 #### Added * **Order Data Use Case** * Added new Order Data use case to send webhooks for all `complete` or `cancelled` orders (POS, API, partner-created). * Uses thin event payload (`orderId`, `event`, `updatedAt`) for partners to fetch full details via `/orders/:id`. #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[**2.124.1**] – 2025-07-17 #### Added * **Idempotent Request Support** * All `POST` endpoints now support idempotency via the `idempotency-key` header * Prevents duplicate operations by caching request hashes for 24 hours * Safe retries return the original response * Mismatched payloads with the same key return `409 Conflict` * Header `x-idempotent-replay: true` included in replayed responses #### Changed _None_ #### Fixed _None_ #### Removed _None_ ### \[2.91.0] – 2025-06-13 #### Added * **Authentication** * Token-based (Bearer JWT) auth * **Core Endpoints** * Channel, Menu, Order and Outlet * **Request & Response Formats** * JSON only, camelCase fields, ISO 8601 timestamps * Standard HTTP status codes (`200`, `201`, `400`, `401`, `404`, `500`) * **Error Handling** * Uniform error body: `{ code, message[, errors] }` * **Webhooks** * Order status callbacks to your configured `webhookUrl` #### Changed _None_ #### Fixed _None_ #### Removed _None_ # Errors Here’s a quick reference for all the error responses defined in the OpenAPI spec. All errors return application/json bodies with at least: * `code` – the HTTP status * `message` – a human-readable description * optional `errors` – field-level details (on 400/500) | **Status** | **Description** | **When it can happen (examples)** | | ---------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `400` | Bad Request, validation failed | - `POST /api/v1/orders` with missing/invalid payload | | `401` | Unauthorized | * `GET /api/v1/menus` without a valid token | | `404` | Not Found | - `GET /api/v1/orders/:id` with nonexistent order | | `409` | Conflict | * `POST /api/v1/orders` with the same `idempotency-key` but a different payload | | `429` | Too Many Requests, rate limit exceeded | - `GET /api/v1/orders/:id` (or any endpoint) with more than 5 requests per second using the same API key | | `500` | Internal Server Error | * Something went wrong on our end | Keep an eye on the exact message text in the spec for each endpoint, but this table covers all of the HTTP error codes you’ll see. ## API Rate Limits * **5 requests per second per API key** * Uses a fixed window algorithm that resets every second * Rate limiting is scoped per API key, so each key has its own independent limit #### **429 Response Details:** When the rate limit is exceeded, you'll receive an HTTP 429 response with the following: ```plaintext { "code": 429, "message": "Rate limit exceeded. Please retry after the specified time.", "retryAfter": 1 } ``` plaintext The `retryAfter` field indicates the number of seconds (rounded up) until the rate limit window resets. This value will typically be 1 second or less, since the window resets every second. #### **Rate Limit Headers:** All API responses include the following headers (even when not rate-limited) to help you implement proactive backoff: * `X-RateLimit-Limit`: The maximum number of requests allowed (5) * `X-RateLimit-Remaining`: Number of requests remaining in the current window * `X-RateLimit-Reset`: ISO 8601 timestamp indicating when the current window resets #### **Recommended Backoff Strategy:** 1. **Exponential backoff with jitter**: When you receive a 429, wait for the `retryAfter` duration (or slightly longer) before retrying. For subsequent retries, you can implement exponential backoff with jitter to avoid thundering herd problems. 2. **Monitor headers proactively**: Use the `X-RateLimit-Remaining` header to throttle your request rate before hitting the limit. For example, if `remaining` is 1 or 2, you might want to slow down your request rate. 3. **Batch operations**: For order sync operations that might require many requests, consider batching or spacing out requests to stay within the 5 req/sec limit. # Base URL All API requests are routed through the base URLs defined in our OpenAPI specification: **API Endpoints** * **Production** → * **Sandbox (Testing)** → **Merchant Dashboard** The setup and management of the POS merchant account is handled via: * **Production** → * **Sandbox (Testing)** → # Idempotency To prevent duplicate operations (e.g. placing the same order multiple times), all `POST` requests support idempotency using the `idempotency-key` header. ### How It Works * Include an `idempotency-key` header with your `POST` request. * The request body is hashed and cached for 24 hours. * If the same key is reused: * If the request body is the same, the original response is returned. * The response will include the header: `x-idempotent-replay: true` * If the request body is different, a `409 Conflict` error is returned. This ensures safe retries without performing the same operation multiple times. ### Requirements * The value of `idempotency-key` **must be a UUID v4** string. * Keys must be unique per logical operation. * Cached responses expire after 24 hours.