العنوان الأساسي https://cashier.basmtak.com/api/partner/v1 · OpenAPI 3.0.3 · تنزيل ملف OpenAPI
Read sales and the catalogue, write items, send orders to the till and receive signed webhooks. Money is always integer halalas (1 SAR = 100). Errors are JSON {error, message, details}; the message follows Accept-Language (ar or en). Lists answer {data, meta}.
المصادقة
ينشئ المالك أو المدير عميل واجهة من لوحة الإدارة ← المطورون ويختار صلاحياته. يظهر الرمز مرة واحدة. أرسله مع كل طلب:
curl -H "Authorization: Bearer bmc_…" -H "Accept-Language: ar" https://cashier.basmtak.com/api/partner/v1/ping
الصلاحيات
pos:read | GET branches · categories · items · price-lists · customers · receipts · sessions/{id}/z-report · stock/levels |
pos:write | POST /items · PUT /items/{id} |
pos:orders | POST /orders · GET /orders/{id} |
pos:webhooks | GET|POST|DELETE /webhooks |
حد الطلبات
120 طلبًا في الدقيقة لكل عميل واجهة. الرد 429 يحمل Retry-After، وكل رد يحمل X-RateLimit-Limit و X-RateLimit-Remaining.
الأخطاء
كل خطأ JSON بالشكل {error, message, details}، والرسالة بلغة Accept-Language (ar أو en). المبالغ أعداد صحيحة بالهللة دائمًا (1 ريال = 100).
{"error": "insufficient_ability", "message": "…", "details": {"required_ability": "pos:write"}}
الإشعارات (REST hooks)
اشترك عبر POST /webhooks {target_url, event} وألغِ عبر DELETE /webhooks/{id} — نمط Zapier و Make. كل إشعار طلب POST بالشكل {id, event, tenant_id, occurred_at, api_version, data}، ويُعاد بتأخير متضاعف؛ 20 إخفاقًا متتاليًا (أو رد 410) توقف الاشتراك.
التحقق من الإشعار
X-Basmtak-Signature = "sha256=" + HMAC-SHA256 بصيغة hex بالسر على (X-Basmtak-Timestamp + "\n" + "POST" + "\n" + المسار + "\n" + SHA-256 للجسم الخام بصيغة hex). ارفض الطوابع الأقدم من خمس دقائق. X-Basmtak-Event-Id فريد لكل حدث؛ استخدمه لإسقاط المكرر.
// Node.js
const crypto = require('crypto');
const canonical = ts + "\nPOST\n" + new URL(targetUrl).pathname + "\n" + crypto.createHash('sha256').update(rawBody).digest('hex');
const ok = 'sha256=' + crypto.createHmac('sha256', secret).update(canonical).digest('hex') === req.headers['x-basmtak-signature'];
الأحداث
receipt.issued
بيانات مثال
{
"id": "01J9ZK3Q7R8S9T0V1W2X3Y4Z5A",
"number": "R1-418",
"kind": "simplified",
"branch_id": 1,
"register_id": 3,
"session_id": "01J9ZJ0000000000000000000S",
"issued_at": "2027-04-02T18:21:07+03:00",
"business_date": "2027-04-02",
"currency": "SAR",
"subtotal_halalas": 3130,
"tax_halalas": 470,
"total_halalas": 3600,
"prepaid_applied": null,
"amount_due_halalas": 3600,
"lines": [
{
"line_no": 1,
"variant_id": 21,
"name": "كابتشينو كبير",
"qty": "2",
"unit_price_halalas": 1800,
"total_halalas": 3600
}
],
"payments": [
{
"tender": "card",
"amount_halalas": 3600,
"scheme": "mada",
"last4": "4242"
}
]
}
credit_note.issued
بيانات مثال
{
"kind": "credit_note",
"refund_of": "01J9ZK3Q7R8S9T0V1W2X3Y4Z5A",
"total_halalas": 1800,
"id": "01J9ZK3Q7R8S9T0V1W2X3Y4Z5A",
"number": "R1-418",
"branch_id": 1,
"register_id": 3,
"session_id": "01J9ZJ0000000000000000000S",
"issued_at": "2027-04-02T18:21:07+03:00",
"business_date": "2027-04-02",
"currency": "SAR",
"subtotal_halalas": 3130,
"tax_halalas": 470,
"prepaid_applied": null,
"amount_due_halalas": 3600,
"lines": [
{
"line_no": 1,
"variant_id": 21,
"name": "كابتشينو كبير",
"qty": "2",
"unit_price_halalas": 1800,
"total_halalas": 3600
}
],
"payments": [
{
"tender": "card",
"amount_halalas": 3600,
"scheme": "mada",
"last4": "4242"
}
]
}
session.closed
بيانات مثال
{
"id": "01J9ZJ0000000000000000000S",
"branch_id": 1,
"register_id": 3,
"status": "closed",
"business_date": "2027-04-02",
"receipts": 57,
"total_halalas": 412300,
"net_sales_halalas": 358522,
"variance_halalas": -50,
"z_report": {
"sales": {
"total_halalas": 412300
}
}
}
stock.low
بيانات مثال
{
"branch_id": 1,
"variant_id": 21,
"sku": "MILK-1L",
"name_ar": "حليب ١ لتر",
"qty": "3.0000",
"reorder_point": "5.0000",
"suggested_qty": "7.0000"
}
order.status_changed
بيانات مثال
{
"from": "received",
"to": "accepted",
"id": "01J9ZM0000000000000000000O",
"external_id": "web-1001",
"provider": "partner:4",
"status": "accepted",
"total_halalas": 5400
}
customer.created
بيانات مثال
{
"id": 77,
"marketing_consent": true,
"name": "نورة",
"phone": "+966501234567",
"email": null,
"vat_number": null
}
نقاط الوصول
meta Connectivity
GET
/ping
Check the token and see the client's abilities
الصلاحية: any
الردود
200 | The client, its abilities and the account | |
401 | {error, message, details} | Error |
catalogue Branches, categories, items, price lists and customers
GET
/branches
List branches
الصلاحية: pos:read
المعاملات
page | query | integer | |
per_page | query | integer | |
الردود
200 | A page of branches | BranchPage |
GET
/categories
List categories
الصلاحية: pos:read
المعاملات
page | query | integer | |
per_page | query | integer | |
الردود
GET
/items
List items with their variants
الصلاحية: pos:read
Each variant carries `plu` (V + variant id), the reference to use in orders.
المعاملات
page | query | integer | |
per_page | query | integer | |
category_id | query | integer | |
updated_since | query | string | ISO-8601; only items changed since then |
sku | query | string | |
barcode | query | string | |
الردود
200 | A page of items | ItemPage |
POST
/items
Create an item (one payload with its variants)
الصلاحية: pos:write
Same payload as the back office. Barcodes and SKUs are unique per account. Without variants one default variant is made at default_price_halalas.
جسم الطلب ItemWrite
{
"name_ar": "كابتشينو",
"name_en": "Cappuccino",
"category_id": 3,
"variants": [
{
"name_ar": "صغير",
"sku": "CAP-S",
"price_halalas": 1400,
"is_default": true
},
{
"name_ar": "كبير",
"sku": "CAP-L",
"price_halalas": 1800
}
]
} الردود
201 | The item | object |
403 | {error, message, details} | Error |
422 | {error, message, details} | Error |
GET
/items/{id}
One item
الصلاحية: pos:read
المعاملات
الردود
200 | The item | |
404 | {error, message, details} | Error |
PUT
/items/{id}
Update an item
الصلاحية: pos:write
Arrays present in the body (variants, modifier_group_ids, combo_slots) replace what is stored; rows without id are created, missing ids are deleted. Arrays absent are untouched.
المعاملات
جسم الطلب ItemWrite
الردود
200 | The item | |
422 | {error, message, details} | Error |
GET
/price-lists
Price lists with their prices
الصلاحية: pos:read
المعاملات
page | query | integer | |
per_page | query | integer | |
الردود
GET
/customers
Customers (consented fields only)
الصلاحية: pos:read
Name, phone, email and address are returned only while the customer's marketing consent is granted (PDPL); otherwise they are null. VAT and CR numbers are always returned.
المعاملات
page | query | integer | |
per_page | query | integer | |
updated_since | query | string | |
consented_only | query | boolean | Only customers whose latest marketing consent in the consent ledger is granted (never erased ones). |
consent_channel | query | string | With consented_only, only consents that cover this channel. |
الردود
200 | A page of customers | object |
sales Receipts, Z-reports and stock
GET
/receipts
Receipts and credit notes, oldest first, with a since_id cursor
الصلاحية: pos:read
Pass the last receipt id you saw as since_id to get what arrived after it; meta.next_since_id is the value for the next call and meta.has_more says whether to call again now. The order is the server's arrival order, so receipts a till sold offline and synced later are never skipped. Receipts that arrived in the last few seconds are held back.
المعاملات
since_id | query | string | |
branch_id | query | integer | |
kind | query | string | |
per_page | query | integer | |
الردود
200 | Receipts after the cursor | ReceiptCursorPage |
422 | {error, message, details} | Error |
GET
/receipts/{id}
One receipt or credit note
الصلاحية: pos:read
المعاملات
الردود
200 | The receipt | object |
404 | {error, message, details} | Error |
GET
/sessions/{id}/z-report
The Z-report of a closed till session
الصلاحية: pos:read
The Z as stored at close; deposits already invoiced are netted (prepayments_applied).
المعاملات
الردود
200 | The session with z_report, expected and counted | |
409 | {error, message, details} | Error |
GET
/stock/levels
Stock on hand per branch and variant
الصلاحية: pos:read
المعاملات
page | query | integer | |
per_page | query | integer | |
branch_id | query | integer | |
variant_id | query | integer | |
low_only | query | boolean | |
الردود
200 | A page of stock levels | |
orders Orders sent to the till's incoming-orders panel
POST
/orders
Send an order to the till
الصلاحية: pos:orders
The order appears on the branch till's incoming-orders panel; the cashier accepts it and the receipt is issued on the till. Idempotent on external_id per API client: a retry answers 200 with duplicate true. Lines reference a variant by variant_id, or plu / sku / barcode; a line nothing matches stays an open item with your name and price. Prices are VAT-inclusive halalas; a line without a price takes the catalogue price. Status changes come back as order.status_changed webhooks.
جسم الطلب OrderWrite
{
"external_id": "web-1001",
"branch_id": 1,
"order_type": "pickup",
"prepaid": true,
"customer": {
"name": "Noura",
"phone": "+966501234567"
},
"lines": [
{
"variant_id": 21,
"qty": 2,
"unit_price_halalas": 1800
}
]
} الردود
201 | The order | object |
200 | The same order again (a retry) | |
422 | {error, message, details} | Error |
GET
/orders/{id}
The status of one of your orders
الصلاحية: pos:orders
المعاملات
الردود
200 | The order | |
404 | {error, message, details} | Error |
POST
/orders/{id}/cancel
Cancel one of your orders
الصلاحية: pos:orders
Withdraws the order from the till's incoming-orders panel. Cancelling an order that is already cancelled answers the same order again. If the till already issued its receipt, the receipt stands and the cashier is asked to decide on a refund.
المعاملات
جسم الطلب object
الردود
200 | The order, now cancelled | object |
404 | {error, message, details} | Error |
409 | {error, message, details} | Error |
webhooks REST hooks (Zapier and Make)
GET
/webhooks
Your subscriptions
الصلاحية: pos:webhooks
الردود
200 | A page of subscriptions | |
POST
/webhooks
Subscribe (REST hook)
الصلاحية: pos:webhooks
The answer carries the subscription id and its signing secret. The secret is shown only here.
جسم الطلب object
{
"target_url": "https://hooks.zapier.com/hooks/standard/123/abc",
"event": "receipt.issued"
} الردود
201 | {id, secret, data} | |
422 | {error, message, details} | Error |
GET
/webhooks/events
The events, each with sample data
الصلاحية: pos:webhooks
الردود
200 | {data: [{event, sample}]} | |
GET
/webhooks/{id}
One subscription
الصلاحية: pos:webhooks
المعاملات
الردود
DELETE
/webhooks/{id}
Unsubscribe (REST hook)
الصلاحية: pos:webhooks
المعاملات
الردود
204 | Removed | |
404 | {error, message, details} | Error |