Getting Started¶
Authentication¶
Partner API calls authenticate with an API key in the X-Api-Key header:
X-Api-Key: your-partner-api-key
Request an API key from Cata
API keys are issued per tenant by the Cata team — they are not
self-serve. Contact your Cata account manager (or email
support+api@cata.sg) to request a key for your tenant. The key is
long-lived; rotate it by asking Cata to issue a new one and revoke
the old one.
OAuth 2.0 coming soon
A future release will add OAuth 2.0 with per-operation scopes
(e.g. products:read, orders:write, loyalty:*) for partners
that need granular access control. API-key auth stays supported
as the simple path — no migration required for today's partners.
Dashboard users (Cata-owned UI) authenticate with a JWT Bearer token instead:
Authorization: Bearer your-jwt-token
Required Headers¶
| Header | Required | Description |
|---|---|---|
X-Api-Key |
Yes (partners) | Partner API key issued by Cata. |
Authorization |
Yes (dashboard) | Bearer <jwt> for dashboard users. |
X-Store-ID |
Yes (some endpoints) | The outlet/store identifier when the endpoint needs it. |
Tenant routing happens via subdomain — no header needed
The tenant is resolved from the request hostname
(https://{tenant}.sgp1.samba-technologies.xyz/service/pos-integration/...).
Partners do not pass a tenant header — pointing the request at
the correct subdomain selects the tenant.
Base URL¶
| Environment | URL |
|---|---|
| Local | http://localhost:8080 |
| Development | https://api-dev.cataprojects.com |
| Staging | https://api-staging.cataprojects.com |
| Production | https://api.cataprojects.com |
Quick smoke test¶
Two one-liners to confirm your environment is wired correctly before integrating any business endpoint.
1. Health check — no auth¶
Confirms the service is reachable.
curl -i "$BASE/health"
HTTP/1.1 200 OK
Content-Type: application/json
{ "status": "healthy" }
No headers needed — /health is public.
2. Auth probe — verify your API key¶
Confirms your key works and the tenant subdomain resolves correctly.
curl -sS "$BASE/api/v1/auth/whoami" -H "X-Api-Key: $KEY" | jq .
{
"authenticated": true,
"tenantId": "7090aaaa-ba2e-4129-b7ed-345804f2f016",
"subdomain": "demo",
"apiKeyId": 37,
"expiresAt": "2046-04-21T09:03:11Z",
"serverTime": "2026-04-23T02:24:50Z"
}
A 401 here means the key is missing, invalid, or expired — re-check
the value with Cata before moving on. A 400 invalid tenant means the
subdomain didn't map to a known tenant; confirm the $BASE URL.
Error Handling¶
Overview¶
At-a-glance reference for the response shapes you'll see in practice. Details follow below.
| Case | Source | HTTP | Key field in body |
|---|---|---|---|
| Happy path (earn / use / balance) | loyalty | 200 |
isSuccess: true |
| Insufficient balance | loyalty | 200 |
code: 1012, isSuccess: false, message: "insufficient_points" |
Missing / non-positive points |
proxy | 400 |
error.message: "invalid request", error.details: "points must be greater than 0" |
Missing customerUuid |
proxy | 400 |
error.message: "missing required field", error.details: "customerUuid query parameter is required" |
| Missing API key | proxy | 401 |
error.message: "missing api key" |
| Invalid / expired API key | proxy | 401 |
error.message: "invalid api key" |
| Tenant not configured (loyalty) | proxy | 503 |
error.message: "loyalty not configured" |
| Upstream loyalty failure | proxy | 502 |
error.message: "loyalty upstream error" |
| Upstream loyalty timeout | proxy | 504 |
error.message: "loyalty upstream timeout" |
Two response envelopes you'll see depending on which layer produced the error.
1. Standard error envelope (most endpoints)¶
Validation, auth, not-found, and server-side errors return an HTTP
4xx / 5xx with this shape:
{
"error": {
"code": 401,
"message": "invalid api key",
"details": "the provided X-Api-Key is invalid or expired"
}
}
code mirrors the HTTP status. Partner-relevant statuses:
| HTTP | Retry? | Meaning |
|---|---|---|
400 |
No — fix input | Validation failed (missing/invalid field). |
401 |
No — re-auth | API key missing, invalid, or expired. |
404 |
Depends | Referenced resource does not exist. |
409 |
Depends | Conflict (e.g. duplicate externalId). |
429 |
Yes, backoff | Rate limited (reserved; no limit today). |
500 |
Yes, backoff | Server-side error. |
502 |
Yes, backoff | Upstream dependency (e.g. loyalty) returned an error. |
503 |
Wait for admin | Tenant not fully configured. |
504 |
Yes, backoff | Upstream timeout. |
2. Proxied-upstream envelope (loyalty endpoints)¶
HTTP 200 does not always mean success
Proxied endpoints (currently: /api/v1/loyalty/*) forward the
upstream service's own response envelope. Business errors come
back as HTTP 200 with isSuccess: false and a non-zero code.
Always check isSuccess, not just the HTTP status.
Example — redeeming more points than the customer has:
HTTP/1.1 200 OK
{
"code": 1012,
"isSuccess": false,
"message": "insufficient_points"
}
Validation handled at the proxy layer (missing customerUuid, non-positive
points, missing refCode) still returns the standard 4xx envelope
above — loyalty business rules are the ones that use 200 + isSuccess.
See Loyalty Points API Example for the full happy-path + error walkthrough.
Async Operations¶
Some operations are long-running and return immediately with a jobId:
{
"jobId": "job_01HXYZ123ABC",
"status": "queued",
"submittedAt": "2026-02-26T10:00:00Z"
}
Poll the status via GET /api/v1/jobs/{jobId} or register a webhook to be notified automatically.
Async endpoints
POST /api/v1/orders— Create orderPOST /api/v1/menu— Submit menu
Next: full API reference¶
Every partner-callable endpoint, request/response schema, and example lives in the public API portal: