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": []
  }
}
A points program takes bill_amount in halalas (100 = 1.00 SAR) instead of stamps. Read program.type from the card.

5. Next

  1. Authentication & permissions: what each permission allows.
  2. Idempotency: retry safely.
  3. Give loyalty from your POS: the whole checkout flow.
  4. API reference: every endpoint, field and error.

All endpoints

Paths are relative to https://serveu-psi.vercel.app.

MethodPathPermissionIdempotency-KeyWhat it does
GET/api/v1/businessbusiness:read—The business this key belongs to
GET/api/v1/branchesbusiness:read—Branches
GET/api/v1/ordersorders:read—List orders
GET/api/v1/orders/summaryorders:read—Sales summary
GET/api/v1/orders/{id}orders:read—Get an order
POST/api/v1/orders/{id}/actionsorders:writeRequiredMove an order along
POST/api/v1/cards/lookuployalty:read—Find a card by phone or scanned card
POST/api/v1/cardsloyalty:writeOptionalJoin a customer
GET/api/v1/cards/{id}loyalty:read—Get a card
GET/api/v1/cards/{id}/eventsloyalty:read—A card's history
POST/api/v1/cards/{id}/earnloyalty:writeRequiredAdd stamps or points for a sale
POST/api/v1/cards/{id}/redeemloyalty:writeRequiredRedeem a reward
POST/api/v1/loyalty-events/{id}/voidloyalty:writeRequiredUndo an earn or redeem
GET/api/v1/menumenu:read—The whole menu
POST/api/v1/menu/categoriesmenu:writeOptionalCreate a category
PATCH/api/v1/menu/categories/{id}menu:writeOptionalUpdate a category
DELETE/api/v1/menu/categories/{id}menu:writeOptionalDelete an empty category
PUT/api/v1/menu/categories/by-external-id/{externalId}menu:writeOptionalCreate or update a category by your code
POST/api/v1/menu/itemsmenu:writeOptionalCreate an item
GET/api/v1/menu/items/{id}menu:read—Get an item
PATCH/api/v1/menu/items/{id}menu:writeOptionalUpdate an item
DELETE/api/v1/menu/items/{id}menu:writeOptionalDelete an item
PUT/api/v1/menu/items/by-external-id/{externalId}menu:writeOptionalCreate or update an item by your code
POST/api/v1/menu/items/{id}/availabilitymenu:writeOptionalMark sold out or back
PUT/api/v1/menu/items/{id}/stockstock:writeOptionalSet an item's stock at a branch
PUT/api/v1/menu/items/by-external-id/{externalId}/stockstock:writeOptionalSet stock by your code
POST/api/v1/stockstock:writeOptionalSet stock for many items at once
GET/api/v1/webhook-endpointswebhooks:manage—List webhook endpoints
POST/api/v1/webhook-endpointswebhooks:manage—Create a webhook endpoint
GET/api/v1/webhook-endpoints/{id}webhooks:manage—Get a webhook endpoint
PATCH/api/v1/webhook-endpoints/{id}webhooks:manageOptionalUpdate or turn a webhook endpoint on/off
DELETE/api/v1/webhook-endpoints/{id}webhooks:manageOptionalDelete a webhook endpoint
POST/api/v1/webhook-endpoints/{id}/testwebhooks:manage—Send a signed test event (ping)
POST/api/v1/webhook-endpoints/{id}/rotate-secretwebhooks:manage—Rotate the signing secret

Next: Authentication & permissions →