منصة المطورين — بصمتك كاشير

واجهة الشركاء v1: قراءة المبيعات والكتالوج، وكتابة الأصناف، وإرسال الطلبات إلى الكاشير، واستقبال إشعارات موقّعة.

English

العنوان الأساسي 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:readGET branches · categories · items · price-lists · customers · receipts · sessions/{id}/z-report · stock/levels
pos:writePOST /items · PUT /items/{id}
pos:ordersPOST /orders · GET /orders/{id}
pos:webhooksGET|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

الردود

200The 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

المعاملات

pagequeryinteger
per_pagequeryinteger

الردود

200A page of branchesBranchPage
GET /categories List categories الصلاحية: pos:read

المعاملات

pagequeryinteger
per_pagequeryinteger

الردود

200A page of categories
GET /items List items with their variants الصلاحية: pos:read

Each variant carries `plu` (V + variant id), the reference to use in orders.

المعاملات

pagequeryinteger
per_pagequeryinteger
category_idqueryinteger
updated_sincequerystringISO-8601; only items changed since then
skuquerystring
barcodequerystring

الردود

200A page of itemsItemPage
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
        }
    ]
}

الردود

201The itemobject
403{error, message, details}Error
422{error, message, details}Error
GET /items/{id} One item الصلاحية: pos:read

المعاملات

idpathinteger · required

الردود

200The 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.

المعاملات

idpathinteger · required

جسم الطلب ItemWrite

الردود

200The item
422{error, message, details}Error
GET /price-lists Price lists with their prices الصلاحية: pos:read

المعاملات

pagequeryinteger
per_pagequeryinteger

الردود

200A page of price lists
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.

المعاملات

pagequeryinteger
per_pagequeryinteger
updated_sincequerystring
consented_onlyquerybooleanOnly customers whose latest marketing consent in the consent ledger is granted (never erased ones).
consent_channelquerystringWith consented_only, only consents that cover this channel.

الردود

200A page of customersobject
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_idquerystring
branch_idqueryinteger
kindquerystring
per_pagequeryinteger

الردود

200Receipts after the cursorReceiptCursorPage
422{error, message, details}Error
GET /receipts/{id} One receipt or credit note الصلاحية: pos:read

المعاملات

idpathstring · required

الردود

200The receiptobject
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).

المعاملات

idpathstring · required

الردود

200The session with z_report, expected and counted
409{error, message, details}Error
GET /stock/levels Stock on hand per branch and variant الصلاحية: pos:read

المعاملات

pagequeryinteger
per_pagequeryinteger
branch_idqueryinteger
variant_idqueryinteger
low_onlyqueryboolean

الردود

200A 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
        }
    ]
}

الردود

201The orderobject
200The same order again (a retry)
422{error, message, details}Error
GET /orders/{id} The status of one of your orders الصلاحية: pos:orders

المعاملات

idpathstring · required

الردود

200The 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.

المعاملات

idpathstring · required

جسم الطلب object

الردود

200The order, now cancelledobject
404{error, message, details}Error
409{error, message, details}Error
webhooks REST hooks (Zapier and Make)
GET /webhooks Your subscriptions الصلاحية: pos:webhooks

الردود

200A 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

المعاملات

idpathinteger · required

الردود

200The subscription
DELETE /webhooks/{id} Unsubscribe (REST hook) الصلاحية: pos:webhooks

المعاملات

idpathinteger · required

الردود

204Removed
404{error, message, details}Error