# Introduction

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://apidocs.cata.sg/pos-integration-service-api/llms.txt.
> For full documentation content, see https://apidocs.cata.sg/pos-integration-service-api/llms-full.txt.

Standardized API layer for Point-of-Sale (POS) integrations supporting multiple POS systems
including Deliverect, Lightspeed, and Atlas Kitchen.

This service provides a unified abstraction layer that standardizes interactions with different
POS systems using the adapter pattern.

## Partner Integration (H2H)

External POS partners (e.g. Jamezz, Uber, QR vendors) integrate with pos-integration-service
host-to-host. Operations tagged **Partner H2H** below are the supported partner surface.

### Authentication

Every partner request carries a partner API key:

<Tabs>
  <Tab title="JavaScript" id="cb-0-javascript">
    ```javascript
    X-Api-Key: <partner-api-key>
    ```
  </Tab>

  <Tab title="Python" id="cb-0-python">
    ```python
    X-Api-Key: <partner-api-key>
    ```
  </Tab>

  <Tab title="Java" id="cb-0-java">
    ```java
    X-Api-Key: <partner-api-key>
    ```
  </Tab>

  <Tab title="cURL" id="cb-0-curl">
    ```curl
    X-Api-Key: <partner-api-key>
    ```
  </Tab>

  <Tab title="Shell" id="cb-0-bash">
    ```bash
    X-Api-Key: <partner-api-key>
    ```
  </Tab>
</Tabs>

The key is tenant-scoped. Validation is silent on the key's purpose — an invalid or expired key
always returns `401 Unauthorized`. Rotate keys by issuing a new one and revoking the old one in
the admin console.

> **Coming soon — OAuth 2.0 with scoped permissions.**
> A future release will offer OAuth 2.0 alongside the API-key flow, with per-operation scopes
> (e.g. `products:read`, `orders:write`, `loyalty:*`) for partners that need granular access
> control. API-key auth will remain supported as the simple path. No migration required for
> today's partners.

### Tenant resolution

The tenant is resolved from the request hostname. Each tenant has a subdomain, e.g.:

<Tabs>
  <Tab title="JavaScript" id="cb-1-javascript">
    ```javascript
    https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/...
    ```
  </Tab>

  <Tab title="Python" id="cb-1-python">
    ```python
    https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/...
    ```
  </Tab>

  <Tab title="Java" id="cb-1-java">
    ```java
    https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/...
    ```
  </Tab>

  <Tab title="cURL" id="cb-1-curl">
    ```curl
    https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/...
    ```
  </Tab>

  <Tab title="Shell" id="cb-1-bash">
    ```bash
    https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/...
    ```
  </Tab>
</Tabs>

Partners do **not** pass a tenant header themselves. Pointing the request at the right
subdomain is what selects the tenant's database and loyalty credentials.

### Auth smoke test

Before integrating any business endpoint, verify the key works:

<Tabs>
  <Tab title="Shell" id="cb-2-bash">
    ```bash
    curl -i "https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/auth/whoami" \
      -H "X-Api-Key: <partner-api-key>"
    ```
  </Tab>

  <Tab title="JavaScript" id="cb-2-javascript">
    ```javascript
    curl -i "https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/auth/whoami" \
      -H "X-Api-Key: <partner-api-key>"
    ```
  </Tab>

  <Tab title="Python" id="cb-2-python">
    ```python
    curl -i "https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/auth/whoami" \
      -H "X-Api-Key: <partner-api-key>"
    ```
  </Tab>

  <Tab title="Java" id="cb-2-java">
    ```java
    curl -i "https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/auth/whoami" \
      -H "X-Api-Key: <partner-api-key>"
    ```
  </Tab>

  <Tab title="cURL" id="cb-2-curl">
    ```curl
    curl -i "https://{tenant}.sgp.samba-technologies.xyz/service/pos-integration/api/v1/auth/whoami" \
      -H "X-Api-Key: <partner-api-key>"
    ```
  </Tab>
</Tabs>

`200` with `{ "authenticated": true, "tenantId": "...", "apiKeyId": N, "expiresAt": "..." }`
confirms auth + subdomain routing are correct. `401` means the key is missing/invalid/expired.

### Idempotency

Mutating operations accept a caller-supplied idempotency key where noted (commonly `refCode`).
Replays with the same key return a success response without performing the action twice.

### Error envelope

All error responses follow the same shape:

<Tabs>
  <Tab title="json" id="cb-3-json">
    ```json
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>

  <Tab title="JavaScript" id="cb-3-javascript">
    ```javascript
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>

  <Tab title="Python" id="cb-3-python">
    ```python
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>

  <Tab title="Java" id="cb-3-java">
    ```java
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>

  <Tab title="cURL" id="cb-3-curl">
    ```curl
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>

  <Tab title="Shell" id="cb-3-bash">
    ```bash
    {
      "error": {
        "code": 401,
        "message": "invalid api key",
        "details": "the provided X-Api-Key is invalid or expired"
      }
    }
    ```
  </Tab>
</Tabs>

`code` mirrors the HTTP status. Partner-relevant categories:

| HTTP  | Meaning                                                                    |
| ----- | -------------------------------------------------------------------------- |
| `400` | Client-side: body/query validation failed. Fix and retry.                  |
| `401` | API key missing, invalid, or expired. Do not retry; re-authenticate.       |
| `404` | Referenced resource (outlet, product, customer) not found.                 |
| `409` | Conflict on a unique constraint (e.g. duplicate outlet external ID).       |
| `429` | Rate-limited (reserved — no limit enforced today).                         |
| `500` | Server-side error. Safe to retry with backoff.                             |
| `502` | Upstream dependency (e.g. loyalty-service) returned an error. Retry later. |
| `503` | Tenant not fully configured (e.g. missing loyalty credential).             |
| `504` | Upstream timeout. Retry with backoff.                                      |

### Loyalty proxy

The `/api/v1/loyalty/*` endpoints are thin proxies to a separate loyalty-service. The partner
still uses their own `X-Api-Key`; pos-integration translates to the tenant's loyalty credential
internally. A `503` on these endpoints means the tenant hasn't finished loyalty onboarding.

## Date & Time Convention

All `date-time` fields in requests and responses use **RFC 3339** format (a strict profile of ISO 8601):

* **DateTime**: `2026-03-16T15:05:33Z` or `2026-03-16T15:05:33+08:00`
* **Date-only** (where noted): `2026-03-16`

JavaScript's `new Date().toISOString()` produces valid values. The API accepts both `Z` (UTC) and `±HH:MM` timezone offsets.

## Menu APIs

|                    | Menu V1                                                                     | Menu V2                                                                                                             |
| ------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Approach**       | POS-driven menu sync                                                        | Cata-owned menu composition                                                                                         |
| **How it works**   | POS pushes a full menu tree (Menu → Categories → Items → Modifiers) to Cata | Cata composes menus from flat products with sections, per-menu pricing, operating hours, and multi-store assignment |
| **Data flow**      | POS → Cata (one-way push)                                                   | Cata UI → Cata Backend (full CRUD)                                                                                  |
| **Menu structure** | Defined by POS system                                                       | Defined by Cata user via Menu Manager                                                                               |
| **Pricing**        | Inherited from POS                                                          | Per-menu overrides (base\_price, special\_price)                                                                    |
| **Sections**       | Auto-created from POS categories (2-level tree)                             | Custom sections with display types (LIST, GRID, HIGHLIGHT, SWIMLANE)                                                |
| **Multi-store**    | One menu per store push                                                     | One menu assigned to many stores                                                                                    |
| **Publish flow**   | Immediate on sync                                                           | Draft → Publish workflow                                                                                            |
| **Status**         | Legacy — will be deprecated                                                 | Active — recommended for new integrations                                                                           |

## Data Flow Overview

<Tabs>
  <Tab title="JavaScript" id="cb-4-javascript">
    ```javascript
    POS Adapter (X-Provider header)
      ├── POST /api/v1/outlets/sync      → stores, store_credentials
      └── POST /api/v1/products/sync     → items, item_modifiers, item_modifier_options

    Cata Menu Editor (no provider)
      ├── Menu V2 Draft endpoints         → menu_drafts, menu_draft_*
      └── Publish                         → menus, menu_*, item_store
    ```
  </Tab>

  <Tab title="Python" id="cb-4-python">
    ```python
    POS Adapter (X-Provider header)
      ├── POST /api/v1/outlets/sync      → stores, store_credentials
      └── POST /api/v1/products/sync     → items, item_modifiers, item_modifier_options

    Cata Menu Editor (no provider)
      ├── Menu V2 Draft endpoints         → menu_drafts, menu_draft_*
      └── Publish                         → menus, menu_*, item_store
    ```
  </Tab>

  <Tab title="Java" id="cb-4-java">
    ```java
    POS Adapter (X-Provider header)
      ├── POST /api/v1/outlets/sync      → stores, store_credentials
      └── POST /api/v1/products/sync     → items, item_modifiers, item_modifier_options

    Cata Menu Editor (no provider)
      ├── Menu V2 Draft endpoints         → menu_drafts, menu_draft_*
      └── Publish                         → menus, menu_*, item_store
    ```
  </Tab>

  <Tab title="cURL" id="cb-4-curl">
    ```curl
    POS Adapter (X-Provider header)
      ├── POST /api/v1/outlets/sync      → stores, store_credentials
      └── POST /api/v1/products/sync     → items, item_modifiers, item_modifier_options

    Cata Menu Editor (no provider)
      ├── Menu V2 Draft endpoints         → menu_drafts, menu_draft_*
      └── Publish                         → menus, menu_*, item_store
    ```
  </Tab>

  <Tab title="Shell" id="cb-4-bash">
    ```bash
    POS Adapter (X-Provider header)
      ├── POST /api/v1/outlets/sync      → stores, store_credentials
      └── POST /api/v1/products/sync     → items, item_modifiers, item_modifier_options

    Cata Menu Editor (no provider)
      ├── Menu V2 Draft endpoints         → menu_drafts, menu_draft_*
      └── Publish                         → menus, menu_*, item_store
    ```
  </Tab>
</Tabs>

Contact Support:
Name: CATA Projects
Email: [support+api@cata.sg](mailto:support+api@cata.sg)