Quick start
The ServeU API lets a POS, accounting system, kitchen screen or automation tool work with one ServeU business: its orders, loyalty cards and menu. Every call goes to https://serveu-psi.vercel.app/api/v1 with the business's API key.
1. Create a key
The business owner opens Settings → Developers, taps Create key, names it after your system, ticks Give stamps and points and copies the key. It is shown once: store it in your system's secrets right away.
2. Check it
curl https://serveu-psi.vercel.app/api/v1/business -H "Authorization: Bearer serveu_live_XXXX…"The answer is the business and the key itself (its name, permissions and branch):
{
"id": "3f1c2a9e-5b7d-4c11-9a2e-7d4b8c6e1f20",
"slug": "nakhla-coffee",
"name": {
"ar": "نخلة كوفي",
"en": "Nakhla Coffee"
},
"currency": "SAR",
"timezone": "Asia/Riyadh",
"prices_include_vat": true,
"vat_rate": 15,
"paid_gate": "none",
"program": {
"type": "stamps"
},
"key": {
"id": "a9c1e3f5-b7d9-4e1f-8a3c-5e7a9c1e3f5b",
"name": "Olaya POS",
"scopes": [
"loyalty:write"
],
"branch_id": "8a2d4e6f-1b3c-4d5e-8f70-9a1b2c3d4e5f"
}
}3. Find a card
curl -X POST https://serveu-psi.vercel.app/api/v1/cards/lookup \
-H "Authorization: Bearer serveu_live_XXXX…" \
-H "Content-Type: application/json" \
-d '{"phone":"0500000000"}'The answer is the card (its id, balance and program), or 404 not_found when this phone has no card yet.
4. Add stamps
curl -X POST https://serveu-psi.vercel.app/api/v1/cards/<card id>/earn \
-H "Authorization: Bearer serveu_live_XXXX…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: receipt-10422" \
-d '{"stamps":2,"reference":"R-10422"}'The answer (201) is the new event and the updated card. Send the same request again with the same Idempotency-Key and you get the same answer back, never a second earn.
{
"replayed": false,
"capped": false,
"event": {
"id": "c9e1a3b5-d7f9-4b1c-8d3e-5f7a9b1c3d5e",
"card_id": "5b7d9f1a-3c5e-4f7a-9b1c-3d5e7f9a1b3c",
"kind": "earn",
"source": "api",
"program_type": "stamps",
"amount": 2,
"bill_amount": null,
"reward": null,
"branch_id": "8a2d4e6f-1b3c-4d5e-8f70-9a1b2c3d4e5f",
"order_id": null,
"api_key": {
"id": "a9c1e3f5-b7d9-4e1f-8a3c-5e7a9c1e3f5b",
"name": "Olaya POS"
},
"reference": "R-10422",
"reason": null,
"voided": false,
"voided_at": null,
"created_at": "2026-11-08T09:41:12.482Z"
},
"card": {
"id": "5b7d9f1a-3c5e-4f7a-9b1c-3d5e7f9a1b3c",
"card_number": "•••• 4821 7730",
"verified": true,
"joined_via": "poster",
"joined_at": "2026-11-08T09:41:12.482Z",
"balance": 5,
"visits": 12,
"program": {
"type": "stamps",
"stamps_per_reward": 7,
"max_stamps_per_visit": 5,
"points_per_sar": null,
"min_bill": null,
"max_points_per_visit": null,
"reward_name": {
"ar": "قهوة مجانية",
"en": "Free coffee"
}
},
"progress": {
"type": "stamps",
"stamps_to_next": 2,
"rewards_available": 0,
"points_to_next": null,
"next_reward_id": null,
"affordable_reward_ids": []
},
"rewards": []
}
}bill_amount in halalas (100 = 1.00 SAR) instead of stamps. Read program.type from the card.5. Next
- Authentication & permissions: what each permission allows.
- Idempotency: retry safely.
- Give loyalty from your POS: the whole checkout flow.
- API reference: every endpoint, field and error.
All endpoints
Paths are relative to https://serveu-psi.vercel.app.
| Method | Path | Permission | Idempotency-Key | What it does |
|---|---|---|---|---|
| GET | /api/v1/business | business:read | — | The business this key belongs to |
| GET | /api/v1/branches | business:read | — | Branches |
| GET | /api/v1/orders | orders:read | — | List orders |
| GET | /api/v1/orders/summary | orders:read | — | Sales summary |
| GET | /api/v1/orders/{id} | orders:read | — | Get an order |
| POST | /api/v1/orders/{id}/actions | orders:write | Required | Move an order along |
| POST | /api/v1/cards/lookup | loyalty:read | — | Find a card by phone or scanned card |
| POST | /api/v1/cards | loyalty:write | Optional | Join a customer |
| GET | /api/v1/cards/{id} | loyalty:read | — | Get a card |
| GET | /api/v1/cards/{id}/events | loyalty:read | — | A card's history |
| POST | /api/v1/cards/{id}/earn | loyalty:write | Required | Add stamps or points for a sale |
| POST | /api/v1/cards/{id}/redeem | loyalty:write | Required | Redeem a reward |
| POST | /api/v1/loyalty-events/{id}/void | loyalty:write | Required | Undo an earn or redeem |
| GET | /api/v1/menu | menu:read | — | The whole menu |
| POST | /api/v1/menu/categories | menu:write | Optional | Create a category |
| PATCH | /api/v1/menu/categories/{id} | menu:write | Optional | Update a category |
| DELETE | /api/v1/menu/categories/{id} | menu:write | Optional | Delete an empty category |
| PUT | /api/v1/menu/categories/by-external-id/{externalId} | menu:write | Optional | Create or update a category by your code |
| POST | /api/v1/menu/items | menu:write | Optional | Create an item |
| GET | /api/v1/menu/items/{id} | menu:read | — | Get an item |
| PATCH | /api/v1/menu/items/{id} | menu:write | Optional | Update an item |
| DELETE | /api/v1/menu/items/{id} | menu:write | Optional | Delete an item |
| PUT | /api/v1/menu/items/by-external-id/{externalId} | menu:write | Optional | Create or update an item by your code |
| POST | /api/v1/menu/items/{id}/availability | menu:write | Optional | Mark sold out or back |
| PUT | /api/v1/menu/items/{id}/stock | stock:write | Optional | Set an item's stock at a branch |
| PUT | /api/v1/menu/items/by-external-id/{externalId}/stock | stock:write | Optional | Set stock by your code |
| POST | /api/v1/stock | stock:write | Optional | Set stock for many items at once |
| GET | /api/v1/webhook-endpoints | webhooks:manage | — | List webhook endpoints |
| POST | /api/v1/webhook-endpoints | webhooks:manage | — | Create a webhook endpoint |
| GET | /api/v1/webhook-endpoints/{id} | webhooks:manage | — | Get a webhook endpoint |
| PATCH | /api/v1/webhook-endpoints/{id} | webhooks:manage | Optional | Update or turn a webhook endpoint on/off |
| DELETE | /api/v1/webhook-endpoints/{id} | webhooks:manage | Optional | Delete a webhook endpoint |
| POST | /api/v1/webhook-endpoints/{id}/test | webhooks:manage | — | Send a signed test event (ping) |
| POST | /api/v1/webhook-endpoints/{id}/rotate-secret | webhooks:manage | — | Rotate the signing secret |