API endpoint reference
Every v1 endpoint with its scope, parameters, request body and an example response.
Base URL https://api.leisurenexus.app/v1. All examples send Authorization: Bearer lnk_….
Stores
GET /locations
Scope catalog.read. Active stores, by name.
{ "data": [ { "id": "9f2c…2e11", "name": "Main Street", "code": "001", "address_line1": "120 Main St",
"city": "Austin", "state": "TX", "postal_code": "78701", "phone": "5125550142", "timezone": "America/Chicago" } ] }
Departments
GET /departments
Scope catalog.read. Active departments with their active categories.
{ "data": [ { "id": "1b7e…90aa", "name": "Beverages", "image_url": "https://…/beverages.webp",
"categories": [ { "id": "44c1…0b21", "name": "Soda" }, { "id": "8e02…77d4", "name": "Energy" } ] } ] }
Items
GET /items
Scope catalog.read. Active items, paged, sorted by name.
| Parameter | Type | Meaning |
|---|---|---|
location_id | uuid | Store whose price to show; the key's store by default |
department_id | uuid | Only this department |
barcode | string | Items with this barcode (unit or pack) |
updated_since | date-time | Changed after this time |
include_inactive | boolean | Include inactive items |
search | string | Name or SKU |
sort / dir | name, updated, sku / asc, desc | Order |
page / per_page | integer | Paging (≤ 200) |
{
"data": [
{
"id": "c3d0…51f9", "sku": "100245", "plu": null, "name": "Coca-Cola 20 oz", "description": null,
"department_id": "1b7e…90aa", "category_id": "44c1…0b21", "department": "Beverages",
"unit": "each", "is_weighed": false, "age_restriction": null, "ebt_eligible": true, "is_active": true,
"price_cents": 249, "open_price": false, "image_url": "https://…/coke-20.webp",
"barcodes": [
{ "barcode": "049000028911", "pack_qty": 1, "price_cents": null },
{ "barcode": "10049000028918", "pack_qty": 24, "price_cents": 4999 }
],
"updated_at": "2026-10-16T18:22:41+00:00"
}
],
"meta": { "page": 1, "pages": 1, "per_page": 50, "total": 1, "from": 1, "to": 1 }
}
price_cents is the store's price when it has one, otherwise the item's base price. A barcode with pack_qty above 1 rings that many units, at its own price_cents if set. age_restriction is 18 or 21 for restricted items.
GET /items/{id}
Scope catalog.read. One item, same shape. Optional location_id.
Stock
GET /stock
Scope stock.read. Stock on hand for items that track inventory, at one store. Needs inventory switched on for the business.
| Parameter | Type | Meaning |
|---|---|---|
location_id | uuid | Store; the key's store by default |
item_id[] | uuid, up to 200 | Only these items |
page / per_page | integer | Paging |
{ "data": [ { "item_id": "c3d0…51f9", "sku": "100245", "name": "Coca-Cola 20 oz",
"location_id": "9f2c…2e11", "on_hand": 46, "in_stock": true, "updated_at": "2026-10-17 13:58:02" } ],
"meta": { "page": 1, "pages": 1, "per_page": 50, "total": 1, "from": 1, "to": 1 } }
Customers
GET /customers
Scope customers.read. Active customers.
| Parameter | Type | Meaning |
|---|---|---|
email | string | Exact email address (emails are stored encrypted, so partial matches aren't possible) |
phone | string | 10-digit US phone, any format |
updated_since | date-time | Changed after this time |
search | string | First name, last name or company |
sort / dir | name, updated | Order |
{ "data": [ { "id": "5a1e…c0d2", "first_name": "Ana", "last_name": "Lopez", "company": null,
"email": "ana@example.com", "phone": "5125550199", "loyalty_number": "5125550199", "is_active": true,
"name": "Ana Lopez", "points_balance": 320, "house_account_balance_cents": null,
"created_at": "2026-09-02T16:40:11+00:00", "updated_at": "2026-10-15T20:03:55+00:00" } ],
"meta": { "page": 1, "pages": 1, "per_page": 50, "total": 1, "from": 1, "to": 1 } }
house_account_balance_cents is what the customer owes on their house account, or null when they have none.
GET /customers/{id}
Scope customers.read. One customer, same shape.
POST /customers
Scope customers.write. Adds a customer. Returns 201 with the customer.
| Field | Rules |
|---|---|
first_name | Required, up to 60 characters |
last_name | Up to 60 |
company | Up to 120 |
email | Valid email, unique in the business; saved lower-case |
phone | 10-digit US number in any format; unique in the business |
notes | Up to 2,000 |
curl -X POST https://api.leisurenexus.app/v1/customers \
-H "Authorization: Bearer lnk_…" -H "Content-Type: application/json" \
-d '{"first_name":"Ana","last_name":"Lopez","email":"ana@example.com","phone":"(512) 555-0199"}'
PATCH /customers/{id}
Scope customers.write. Send only the fields to change. Returns 200 with the customer.
Sales and refunds
GET /orders
Scope orders.read. Completed and voided sales and refunds, newest first.
| Parameter | Type | Meaning |
|---|---|---|
location_id | uuid | One store; every store when empty |
customer_id | uuid | One customer's sales |
since / until | date-time | Completed at or after / before |
search | string | Receipt number |
sort / dir | completed, total | Order |
{
"data": [
{
"id": "e7a9…1c33", "receipt_number": "001-01-000123", "type": "sale", "status": "completed",
"location_id": "9f2c…2e11", "customer_id": "5a1e…c0d2", "order_type": null, "order_label": null,
"subtotal_cents": 798, "discount_cents": 0, "tax_cents": 66, "total_cents": 864, "tip_cents": 0,
"original_sale_id": null,
"completed_at": "2026-10-17T14:05:00+00:00", "voided_at": null,
"lines": [
{ "item_id": "c3d0…51f9", "sku": "100245", "name": "Coca-Cola 20 oz", "kind": "item",
"quantity": 2, "unit_price_cents": 249, "total_cents": 498, "discount_cents": 0, "modifiers": [], "note": null },
{ "item_id": "7f11…aa20", "sku": "100871", "name": "Doritos Nacho 9.25 oz", "kind": "item",
"quantity": 1, "unit_price_cents": 300, "total_cents": 300, "discount_cents": 0, "modifiers": [], "note": null }
],
"payments": [
{ "type": "cash", "amount_cents": 500, "card_brand": null },
{ "type": "card", "amount_cents": 364, "card_brand": "visa" }
]
}
],
"meta": { "page": 1, "pages": 1, "per_page": 50, "total": 1, "from": 1, "to": 1 }
}
| Field | Values |
|---|---|
type | sale or refund (a refund's original_sale_id points to the sale) |
status | completed or voided (voided sales are left out of every total) |
payments[].type | e.g. cash, card, ebt_snap, gift_card, house_account |
Card numbers and payment-processor references are never included.
GET /orders/{id}
Scope orders.read. One sale, same shape.
Wholesale orders
Needs the Wholesale ERP profile.
POST /sales-orders
Scope orders.write. Places a draft sales order for a business customer; prices come from the customer's price level. Returns 201.
| Field | Rules |
|---|---|
customer_id | Required: the business customer |
location_id | Store or warehouse; the key's store by default |
customer_po | Their purchase-order number, up to 40 |
notes | Up to 1,000 |
lines | 1–500 lines, each with item_id or sku, and quantity (units) |
{
"customer_id": "0d4b…8e19",
"customer_po": "PO-55120",
"lines": [
{ "sku": "100245", "quantity": 48 },
{ "item_id": "7f11…aa20", "quantity": 24 }
]
}
Response:
{ "data": { "id": "a61c…f402", "number": "SO00042", "customer_id": "0d4b…8e19", "status": "draft",
"customer_po": "PO-55120", "subtotal_cents": 18552, "tax_cents": 0, "total_cents": 18552,
"invoice_number": null, "location_id": "9f2c…2e11", "customer": "Bar One", "contact": "Sam Ortiz",
"order_date": "2026-10-17", "due_on": null, "shipped_at": null, "invoiced_at": null, "overdue": false,
"notes": null, "lines": [ { "id": "…", "item_id": "c3d0…51f9", "name": "Coca-Cola 20 oz",
"qty_milli": 48000, "unit_price_cents": 212, "line_cents": 10176, "tax_cents": 0,
"unit_id": null, "unit_name": null, "unit_qty_milli": null } ], "source": "api" } }
Statuses move draft → confirmed → shipped → invoiced (or cancelled) as your team works the order in the back office. qty_milli is the quantity × 1,000.
GET /sales-orders/{id}
Scope orders.write. One wholesale order, same shape.
Still stuck? Email support@leisurenexus.com or send us a message — we're happy to help.