SysPOS MCP Server — API Docs

Everything AI agents need to use SysPOS MCP tools

← Back to Home

Authentication

Two ways in: the Claude custom connector uses OAuth 2.1 (you paste your key once on a login page); every other client sends the raw key as an Authorization: Bearer <key> header. Both resolve to the same tenant, scopes, and revocation.
Key formatsyspos_<64 hex chars>
Claude connectorOAuth 2.1 (DCR + PKCE) — paste your syspos_... key on the login page when connecting https://pos-mcp.syspos.ae/mcp
StandardAuthorization: Bearer syspos_... HTTP header (Claude Desktop config, Cursor, SDKs, curl, n8n)
Escape hatch?api_key=syspos_... on the URL — for clients that cannot set a Bearer header
Legacy fallback"api_key": "syspos_..." inside the JSON body / tool arguments
ScopingEach key is tied to a customer (tenant) and optionally limited to specific locations and tools

How to Call Tools

Option 1: Claude.ai custom connector (no code)

In Claude → Settings → Connectors → Add custom connector:

Name:                  SysPOS Product Tools
Remote MCP server URL: https://pos-mcp.syspos.ae/mcp
OAuth Client ID/Secret: (leave blank)

→ Add → Connect → paste your syspos_... key → Authorize
OAuth is automatic: Claude self-registers (DCR + PKCE) and walks you through a login page where you paste your syspos_... key. The key is exchanged for a short-lived access token that Claude refreshes on its own — it never appears in the chat.

Option 2: REST API (any HTTP client)

Simple HTTP POST — works with curl, fetch, n8n, Make, anything.

POST https://pos-mcp.syspos.ae/api/call
Authorization: Bearer syspos_your_key_here
Content-Type: application/json

{
  "tool": "list_products"
}

Option 3: MCP Protocol — SSE (Claude Desktop, Cursor, custom agents)

Use the remote MCP URL plus a Bearer header — the standard MCP-over-HTTP shape:

# MCP endpoint URL
https://pos-mcp.syspos.ae/sse

# Claude Desktop / Cursor / generic MCP client config
{
  "mcpServers": {
    "syspos": {
      "url": "https://pos-mcp.syspos.ae/sse",
      "headers": { "Authorization": "Bearer syspos_your_key_here" }
    }
  }
}

# Claude Code CLI
claude mcp add syspos --transport sse https://pos-mcp.syspos.ae/sse \
  --header "Authorization: Bearer syspos_your_key_here"
Tip: The Bearer header is sent once on the SSE connection and is automatically used for every tool call on that session — no need to pass an api_key argument.

Machine-Readable Docs for Agents

AI agents can fetch full tool schemas as JSON — no HTML parsing needed:

# Full docs with parameters, types, and example responses
curl https://pos-mcp.syspos.ae/api/docs

# Tool list with full parameter schemas
curl https://pos-mcp.syspos.ae/api/tools

Error Handling

StatusMeaningExample Response
200SuccessJSON result data
400Bad request / Auth error{ "error": "Invalid API key" }
404Tool not found{ "error": "Tool 'x' not found", "available": [...] }

Agent Integration Guide

System prompt template

Add this to your agent's instructions:

You have access to SysPOS product tools.

API endpoint:  https://pos-mcp.syspos.ae/api/call
Method:        POST
Content-Type:  application/json
Authorization: Bearer syspos_your_key_here

Rules:
- Use list_products to fetch the catalog. Pass limit if you need a page size other than the full catalog.
- Do not send customer_id. Tenant comes from the MCP API key.
- Create/update bodies match the dashboard product payload (name, prices, location_prices, modifiers, …).
Do send the API key as Authorization: Bearer syspos_... on every request.
Do use list_products to fetch the catalog; pass limit to page, or omit it for the full catalog.
Note: /api/call is a regular HTTP POST endpoint; /sse is the MCP Server-Sent-Events stream — use one or the other, not both.

Tool Reference

Table of Contents

Products

list_selling_products

GET /api/v2/products. Why: The menu the session branch sells — exactly what web-pos loads: live products of this branch with the branch price (actual_price, VAT incl.), option-group links (modifiers[]), sold-out flag and combo slots. Use it to show a menu or build a cart; use list_products only to manage the catalog. Paginated: loop current_page until you have total_records rows. Pair with list_selling_modifiers (option groups) and list_selling_addons (add-ons). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_products"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
page_limit number optional Optional.
current_page number optional Optional.
k string optional Optional.
cat string optional Optional.
addon enum: 0, 1 optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_products"
}'

Example Response

{
  "status": true,
  "message": "Data found ",
  "data": {
    "current_page": 1,
    "records_per_page": 50,
    "total_records": 1066,
    "products": [
      {
        "id": 257509,
        "category_id": 15430,
        "name": "Vanilla Latte",
        "description": "Authentic Flavors",
        "price": 10,
        "stock": 0,
        "type": "Specialty & Flavored Coffees",
        "image": "",
        "customer_id": 437,
        "location_id": 907,
        "is_addon": 0,
        "addons": "[]",
        "bundle_items": "[]",
        "is_disabled": 0,
        "discount_pct": 0,
        "modifiers": [
          {
            "priority": 3,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3687
          },
          {
            "priority": 2,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3703
          },
          {
            "priority": 1,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3710
          },
          {
            "priority": 4,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3986
          },
          {
            "priority": 5,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3987
          },
          {
            "priority": 6,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3988
          },
          {
            "priority": 7,
            "max_allowed": 3,
            "min_allowed": 0,
            "modifier_id": 3989
          },
          {
            "priority": 5,
            "max_allowed": 1,
            "min_allowed": 0,
            "modifier_id": 5622
          }
        ],
        "is_archive": 0,
        "barcode_type": null,
        "barcode_value": null,
        "is_open_price": 0,
        "is_combo": 0,
        "sync_deliverect": 0,
        "sync_otter": 0,
        "new_cost_date": null,
        "product_cost": null,
        "is_weightage_item": 0,
        "unit_id": 0,
        "actual_price": 10,
        "old_price": null,
        "vat": 0.48,
        "localized": null,
        "base_price": null,
        "menu_sort_order": 0,
        "plu": null,
        "calories": null,
        "is_stock_product": 0,
        "is_auto_apply": null,
        "combo_price": 0,
        "localize_name": "Vanilla Latte",
        "localize_description": "Coffee",
        "label_id": 50,
        "recipes": null,
        "sku_no": "prod-437-257509",
        "tax_id": 16,
        "preparation_time": 0,
        "product_recipe_total": 0,
        "product_material_total": 0,
        "is_overridden": 0,
        "is_deleted": 0,
        "category_sort_order": null,
        "is_sold_out": 0,
        "auto_selected_modifier_items": [],
        "price_levels": [],
        "tax_name": "Food Card Tax",
        "tax_value": 5,
        "image_path": "",
        "web_image": ""
      }
    ]
  }
}

list_products

GET /api/products/webProducts. Why: Catalog management view: every product of the tenant with its master price, category, label, branch list and recipe/channel flags — this is the dashboard product list, not the POS selling menu (use list_selling_products to show or sell from a branch menu; it has branch prices, sold-out and modifier links). Use it to search/filter products and get ids for get_product → update_product / archive_product / delete_product. Filters take ids from list_categories (category_id), list_brands (brand_id), list_labels (label_id), list_channels (channel_id), list_locations (loc). `type` is the category name the POS files the product under (list_selling_categories, sync_invoice line type). Add-ons: list_addons. Per-branch prices, modifier links, BOM and recipes are NOT here — call get_product. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_products"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated branch ids (list_locations).
k string optional Case-insensitive substring match on the master product name (products.name, not branch names).
category_id string optional Category id or comma-separated ids (list_categories); a product matches if its category_id is any of them.
cat string optional Comma-separated category NAMES (exact, case-insensitive, trimmed).
addon enum: 0, 1 optional "1" → ONLY add-on products (is_addon = 1).
is_archive number optional 0 → active products, 1 → archived products (never both).
brand_id number optional Brand id (list_brands): products whose category is in a group linked to that brand.
label_id number optional Label id (list_labels): exact match on label_id.
channel_id number optional Channel id (list_channels): only products that have a price row for this channel.
page number optional 1-based page, used only when limit < 10000.
limit number optional Page size.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_products"
}'

Example Response

{
  "data": [
    {
      "id": 257508,
      "category_id": 15231,
      "name": "Mocha",
      "description": "Authentic Flavors",
      "price": 10,
      "stock": 0,
      "type": "Espresso-Based Drinks",
      "image": "https://dev1.syspos.ae/api/uploads/1780385109749-apple.png",
      "customer_id": 437,
      "location_id": [
        908,
        907
      ],
      "is_addon": 0,
      "addons": "[]",
      "bundle_items": "[]",
      "is_disabled": 0,
      "discount_pct": 0,
      "modifiers": null,
      "is_archive": 0,
      "barcode_type": "",
      "barcode_value": "",
      "is_open_price": 0,
      "is_combo": 0,
      "sync_deliverect": 0,
      "sync_otter": 0,
      "new_cost_date": null,
      "product_cost": null,
      "is_weightage_item": 0,
      "unit_id": 0,
      "actual_price": 10,
      "old_price": null,
      "vat": 0.48,
      "localized": null,
      "base_price": null,
      "menu_sort_order": 0,
      "plu": 0,
      "calories": null,
      "is_stock_product": 0,
      "is_auto_apply": null,
      "combo_price": 0,
      "localize_name": "Mocha",
      "localize_description": "Coffee",
      "label_id": 50,
      "recipes": null,
      "sku_no": "pd-437-257508",
      "tax_id": 16,
      "preparation_time": 0,
      "product_recipe_total": 50,
      "product_material_total": 0,
      "is_overridden": 0,
      "is_deleted": 0,
      "category_sort_order": null,
      "override_location_id": 908,
      "override_name": "Mocha",
      "override_description": "Authentic Flavors",
      "override_price": 10,
      "override_actual_price": 10,
      "image_path": "1780385109749-apple.png",
      "web_image": "1780385109749-apple.png",
      "channel_price": [],
      "nameChannels": {
        "channels": []
      },
      "total_locations": 2,
      "has_recipe": 1
    }
  ],
  "total": 2745
}

get_product

GET /api/products/:id. Why: Full catalog record of one product — per-branch prices (location_prices), modifier links per branch, channel prices, BOM materials and recipes — which list_products does not return. Always call it before update_product: update is not a patch, so you resend location_prices, modifiers, materials, recipes, channel_price and the columns from this record. For selling/menu display use list_selling_products instead. Ids here link to list_locations (location_prices), list_modifiers / list_selling_modifiers and list_modifier_items (modifiers), list_channels (channel_price), list_materials, list_recipes, list_categories (category_id; type = category name), list_labels (label_id). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_product"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Product id (list_products data[].id).
loc string optional Branch id or comma-separated ids.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_product",
  "id": 11
}'

Example Response

[
  {
    "id": 257508,
    "category_id": 15231,
    "name": "Mocha",
    "description": "Authentic Flavors",
    "price": 10,
    "stock": 0,
    "type": "Espresso-Based Drinks",
    "image": "https://dev1.syspos.ae/api/uploads/1780385109749-apple.png",
    "customer_id": 437,
    "location_id": [
      907,
      908
    ],
    "is_addon": 0,
    "addons": [],
    "bundle_items": [],
    "is_disabled": 0,
    "discount_pct": 0,
    "modifiers": [
      {
        "id": 136528,
        "modifier_id": 3687,
        "max_allowed": 3,
        "min_allowed": 0,
        "priority": 4,
        "location_id": 907,
        "customer_id": 437,
        "selection_type": "multiple",
        "auto_selected_modifier_item_ids": [],
        "mandatory_modifier_item_ids": [],
        "unavailable_modifier_item_ids": [],
        "modifier_items_order": [
          18005,
          18052,
          22145,
          22146
        ]
      }
    ],
    "is_archive": 0,
    "barcode_type": "",
    "barcode_value": "",
    "is_open_price": 0,
    "is_combo": 0,
    "sync_deliverect": 0,
    "sync_otter": 0,
    "new_cost_date": null,
    "product_cost": null,
    "is_weightage_item": 0,
    "unit_id": 0,
    "actual_price": 10,
    "old_price": null,
    "vat": 0.48,
    "localized": null,
    "base_price": null,
    "menu_sort_order": 0,
    "plu": 0,
    "calories": null,
    "is_stock_product": 0,
    "is_auto_apply": null,
    "combo_price": 0,
    "localize_name": "Mocha",
    "localize_description": "Coffee",
    "label_id": 50,
    "recipes": [
      {
        "id": 16779,
        "name": "Test",
        "yield_qty": 0.8,
        "mode": "stockable",
        "recipe_id": 16779,
        "product_id": 257508,
        "portion_qty": 0.8,
        "unit": "Kilogram",
        "active_from": null,
        "active_to": null,
        "qty_used": 1
      }
    ],
    "sku_no": "pd-437-257508",
    "tax_id": 16,
    "preparation_time": 0,
    "product_recipe_total": 50,
    "product_material_total": 0,
    "is_overridden": 0,
    "is_deleted": 0,
    "category_sort_order": null,
    "image_path": "1780385109749-apple.png",
    "web_image": "1780385109749-apple.png",
    "channel_price": [],
    "materials": [],
    "location_prices": [
      {
        "location_id": 907,
        "location_name": "Default location",
        "location_price": 10,
        "location_actual_price": 10,
        "is_available": true
      }
    ]
  }
]

list_selling_categories

GET /api/v2/categories. Why: Menu categories of a branch — what web-pos loads for its category rail. Use it to group list_selling_products into a menu. A product belongs to a category when product.type == category.name (web-pos's main link) or product.category_id == category.id. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_categories"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.
k string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_categories"
}'

Example Response

[
  {
    "id": 17557,
    "name": "Footwear",
    "description": "Shoes",
    "customer_id": 437,
    "location_id": 907,
    "color": null,
    "image": "https://dev1.syspos.ae/api/uploads/1767085134601-shoes.jpg",
    "is_active": 1,
    "sync_otter": 0,
    "sync_deliverect": 0,
    "sync_menu": 1,
    "menu_sort_order": 12,
    "sync_bitebell": 1,
    "localize_name": null,
    "localize_description": null,
    "sku_no": "cat-437-17557",
    "is_deleted": 0,
    "sync_grubtech": 0,
    "web_image": "api/uploads/1767085134601-shoes.jpg"
  }
]

list_categories

GET /api/categories. Why: Dashboard catalog view of categories (the Products → Categories list), not the POS selling rail — use list_selling_categories to see what a branch actually sells. Use it to search by name, filter by branch / group / brand / active flag, get category ids for get_category → update_category / delete_category, and the id + name a product needs (product.category_id = id, product.type = name). Shows each category's branches (categories_locations) and groups (group_categories). Includes inactive categories unless is_active=1. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_categories"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv of ids (list_locations).
k string optional Case-insensitive partial match on the category name (LIKE %k%, parameterised; % and _ act as wildcards).
group_id number optional Group id (list_groups): only categories in that group (group_categories, inactive links included).
brand_id number optional Brand id (list_brands / list_selling_brands): only categories in a group linked to that brand (brands_groups_relation → group_categories).
is_active number optional 1 = only active, 0 = only inactive.
is_archive number optional Inverse alias: 0 = only active, 1 = only inactive.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_categories"
}'

Example Response

[
  {
    "id": 17557,
    "name": "Footwear",
    "description": "Shoes",
    "customer_id": 437,
    "sku_no": "cat-437-17557",
    "location_id": [
      909,
      908,
      922,
      910,
      907,
      1131
    ],
    "color": null,
    "image": "1767085134601-shoes.jpg",
    "web_image": "api/uploads/1767085134601-shoes.jpg",
    "is_active": 1,
    "sync_otter": 0,
    "sync_deliverect": 0,
    "sync_menu": 1,
    "sync_bitebell": 1,
    "sync_grubtech": 0,
    "menu_sort_order": 12,
    "total_categories": 39,
    "groups": [
      {
        "id": 1465,
        "group_name": "Accessories",
        "categories_count": 1
      }
    ]
  }
]

get_category

GET /api/categories/:id. Why: Full dashboard record of ONE category (the Edit Category page): every column, its branches, its groups, and the active products filed under it by category_id (linked_products) — list_categories has no product list. Use it (a) as the base body for update_category (image / is_active / sync_* / menu_sort_order reset if omitted), (b) to check linked_products_count and groups before delete_category. For what a branch sells use list_selling_categories / list_selling_products instead. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_category"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Category id (list_categories [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_category",
  "id": 11
}'

Example Response

[
  {
    "id": 17557,
    "name": "Footwear",
    "description": "Shoes",
    "customer_id": 437,
    "location_id": [
      909,
      908,
      922,
      910,
      907,
      1131
    ],
    "color": null,
    "image": "https://dev1.syspos.ae/api/uploads/1767085134601-shoes.jpg",
    "is_active": 1,
    "sync_otter": 0,
    "sync_deliverect": 0,
    "sync_menu": 1,
    "menu_sort_order": 12,
    "sync_bitebell": 1,
    "localize_name": null,
    "localize_description": null,
    "sku_no": "cat-437-17557",
    "is_deleted": 0,
    "sync_grubtech": 0,
    "web_image": "api/uploads/1767085134601-shoes.jpg",
    "groups": [
      {
        "id": 1465,
        "group_name": "Accessories",
        "categories_count": 1
      }
    ],
    "linked_products": [
      {
        "id": 266406,
        "name": "Flip-Flop",
        "sku_no": "prod-437-266406",
        "price": 10,
        "is_archive": 0,
        "category_sort_order": 0,
        "image": "",
        "web_image": ""
      }
    ],
    "linked_products_count": 2
  }
]

list_selling_modifiers

POST /api/v2/modifiers. Why: All option groups (Size, Milk, Toppings…) available at a branch, each with its items and item prices — what web-pos loads once, then looks up by product.modifiers[].modifier_id from list_selling_products. Read-only despite POST; no body. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_modifiers"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_modifiers"
}'

Example Response

{
  "status": true,
  "message": "Record Found",
  "data": [
    {
      "id": 3687,
      "name": "Buntie",
      "description": "",
      "selection_allowed": 1,
      "selection_min_allowed": -1,
      "priority": 2,
      "items": [
        {
          "id": 18005,
          "name": "Chocolate buntie",
          "description": "",
          "price": 5,
          "modifier_id": 3687,
          "actual_price": 5,
          "vat": 0,
          "priority": 1,
          "image": "",
          "tax_id": 16,
          "tax_name": "Food Card Tax",
          "tax_value": 5,
          "instructions": [
            {
              "id": 39,
              "instruction": "Modifier 123",
              "priority": 0
            }
          ],
          "web_image": ""
        },
        {
          "id": 18052,
          "name": "Rainbow",
          "description": "",
          "price": 5,
          "modifier_id": 3687,
          "actual_price": 5,
          "vat": 0,
          "priority": 2,
          "image": "",
          "tax_id": null,
          "tax_name": null,
          "tax_value": null,
          "instructions": [
            {
              "id": 40,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 140,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 141,
              "instruction": "Modifier are extra",
              "priority": 1
            }
          ],
          "web_image": ""
        },
        {
          "id": 22145,
          "name": "Rainbow Buntie",
          "description": "",
          "price": 5,
          "modifier_id": 3687,
          "actual_price": 5,
          "vat": 0,
          "priority": 3,
          "image": "",
          "tax_id": null,
          "tax_name": null,
          "tax_value": null,
          "instructions": [
            {
              "id": 42,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 142,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 143,
              "instruction": "Modifier are extra",
              "priority": 1
            }
          ],
          "web_image": ""
        },
        {
          "id": 22146,
          "name": "Plain Buntie",
          "description": "",
          "price": 5,
          "modifier_id": 3687,
          "actual_price": 5,
          "vat": 0,
          "priority": 4,
          "image": "",
          "tax_id": null,
          "tax_name": null,
          "tax_value": null,
          "instructions": [
            {
              "id": 43,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 144,
              "instruction": "Modifier",
              "priority": 0
            },
            {
              "id": 145,
              "instruction": "Modifier are extra",
              "priority": 1
            }
          ],
          "web_image": ""
        }
      ]
    }
  ]
}

list_modifiers

GET /api/modifiers. Why: Catalog management (dashboard Products › Modifiers list): every modifier GROUP of the tenant with its branch list and group defaults — no items, no product links. Use it to find a group id for get/update/delete_modifier, list_modifier_items modifier_id=, create/update_modifier_item modifier_ids and update_product modifiers[].modifier_id. To SELL (a group with its options and prices at a branch) use list_selling_modifiers; items of a group: list_modifier_items modifier_id=<id>; which products use a group: get_product / list_selling_products modifiers[]. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_modifiers"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv (e.g.
k string optional Substring search on the group name (LIKE %k%; dashboard: search box).
brand_id number optional Only groups linked (product_modifiers) to products in categories of this brand (list_brands id; dashboard: "Brand" select).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_modifiers"
}'

Example Response

[
  {
    "id": 4865,
    "name": "Basic Accessory",
    "description": "",
    "location_id": [
      922,
      907,
      908,
      909,
      910,
      1131
    ],
    "customer_id": 437,
    "selection_allowed": 0,
    "selection_min_allowed": 0,
    "priority": 0,
    "is_deleted": 0,
    "localize_name": null,
    "localize_description": null,
    "sku_no": null,
    "is_mandatory": 0
  }
]

get_modifier

GET /api/modifiers/:id. Why: Catalog management: load ONE modifier group (name, group min/max defaults, priority, translations, branch list) before update_modifier — update is a full overwrite, so read first and resend every field. No items (list_modifier_items modifier_id=<id>) and no linked products (get_product modifiers[]). For selling use list_selling_modifiers. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_modifier"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier group id (list_modifiers [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_modifier",
  "id": 11
}'

Example Response

[
  {
    "id": 3687,
    "name": "Buntie",
    "description": "",
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      1131
    ],
    "customer_id": 437,
    "selection_allowed": 1,
    "selection_min_allowed": -1,
    "priority": 2,
    "is_deleted": 0,
    "localize_name": null,
    "localize_description": null,
    "sku_no": "mod-437-3687",
    "is_mandatory": 0
  }
]

list_modifier_items

GET /api/modifierItems. Why: Catalog management (dashboard Products › Modifier Items list): options flat, with their groups, branches and price — to find an item id for get/update/delete_modifier_item or for update_product default / mandatory / unavailable item ids. Filter one group with modifier_id (also returns items linked to it as a second group). To SELL (groups with nested options + kitchen instructions at a branch) use list_selling_modifiers, which nests each option only under its primary modifier_id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_modifier_items"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv (e.g.
k string optional Substring search on the option name (LIKE %k%, escaped; dashboard: search box).
modifier_id number optional Only options of this group (list_modifiers [].id; dashboard: "Modifiers" select): primary modifier_id = it OR linked via modifier_item_assignments.
brand_id number optional Only options whose PRIMARY group is linked to a product in a category of this brand (list_brands id; dashboard: "Brand" select, which clears modifier_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_modifier_items"
}'

Example Response

[
  {
    "id": 18005,
    "name": "Chocolate buntie",
    "description": "",
    "price": 5,
    "customer_id": 437,
    "location_id": [
      1131,
      922,
      910,
      909,
      908,
      907
    ],
    "modifier_id": 3687,
    "actual_price": 5,
    "old_price": null,
    "vat": 0,
    "base_price": null,
    "localize_name": "Chocolate bunties",
    "localize_description": null,
    "sku_no": "moditem-437-18005",
    "tax_id": 16,
    "priority": 1,
    "is_deleted": 0,
    "image": "",
    "modifier_name": "Buntie",
    "mapped_location_ids": "1131,922,910,909,908,907",
    "tax_name": "Food Card Tax",
    "tax_value": 5,
    "modifier_ids": [
      3687
    ],
    "web_image": ""
  }
]

get_modifier_item

GET /api/modifierItems/:id. Why: Load ONE option before update_modifier_item (dashboard Modifier Items › Edit loads exactly this) — update resets price / priority / tax and replaces groups and branches, so read first and resend actual_price, priority, modifier_ids, location_ids and tax_id. No kitchen instructions (see list_selling_modifiers items[].instructions) and no recipes. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_modifier_item"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier item id (list_modifier_items [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_modifier_item",
  "id": 11
}'

Example Response

[
  {
    "id": 18005,
    "name": "Chocolate buntie",
    "description": "",
    "price": 5,
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      1131
    ],
    "modifier_id": 3687,
    "actual_price": 5,
    "old_price": null,
    "vat": 0,
    "base_price": null,
    "localize_name": "Chocolate bunties",
    "localize_description": null,
    "sku_no": "moditem-437-18005",
    "tax_id": 16,
    "priority": 1,
    "is_deleted": 0,
    "image": "",
    "modifier_name": "Buntie",
    "modifier": "3687",
    "modifier_ids": [
      3687
    ],
    "web_image": ""
  }
]

list_selling_brands

GET /api/v2/brands. Why: Brands of a branch WITH their groups nested (there is no separate groups route) — what web-pos loads to choose the brand, show the group rail and scope the menu. Each group lists the category ids it contains. Step 1 of building a menu: pick a brand, pick a group, then filter list_selling_categories / list_selling_products by the group's category ids. The chosen brand_id goes on every cart line and on the invoice. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_brands"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_brands"
}'

Example Response

{
  "status": true,
  "message": "brands found",
  "data": [
    {
      "brand_id": 459,
      "brand_name": "Zara",
      "brand_thumbnail": "",
      "location_id": 907,
      "brand_color": "#000",
      "brand_address": "Pakistan",
      "brand_email": "Zara@gmail.com",
      "brand_phone": "12345678",
      "brand_vat_no": "",
      "groups": [
        {
          "group_id": 1462,
          "group_name": "Clothing Brand",
          "group_thumbnail": "",
          "group_description": "",
          "brand_id": 459,
          "categories": [
            17539
          ]
        },
        {
          "group_id": 1465,
          "group_name": "Accessories",
          "group_thumbnail": "",
          "group_description": "",
          "brand_id": 459,
          "categories": [
            15481,
            17556,
            17557,
            15230
          ]
        }
      ]
    }
  ]
}

list_brands

GET /api/brands. Why: Dashboard catalog view of brands (brand management), not the POS selling view — use list_selling_brands to build a branch's menu (it nests each group's category ids). Use this to find brand ids for get_brand / update_brand / delete_brand, and to see each brand's branches (brands_locations) and group names (brands_groups_relation). A brand reaches categories only through its groups: brand → groups (get_brand groups[].id) → categories (list_categories group_id=… or brand_id=…&loc=…) → products. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_brands"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id(s), csv.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_brands"
}'

Example Response

{
  "status": true,
  "message": "Brand found here",
  "data": [
    {
      "id": 459,
      "brand_name": "Zara",
      "brand_thumbnail": "",
      "description": "Clothing Brand",
      "brand_groups": [
        "Clothing Brand",
        "Accessories"
      ],
      "location_id": [
        922,
        907,
        908,
        909,
        910,
        1131
      ],
      "brand_color": "#000",
      "brand_address": "Pakistan",
      "brand_email": "Zara@gmail.com",
      "brand_phone": "12345678",
      "brand_vat_no": ""
    }
  ]
}

get_brand

GET /api/brands/:id. Why: Full dashboard record of ONE brand: every column, branch ids and its groups with ids. Required before update_brand (which overwrites every field, group link and branch link) and to get the brand's group ids for list_categories group_id / get_group. For the POS menu use list_selling_brands. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_brand"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Brand id (list_brands data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_brand",
  "id": 11
}'

Example Response

[
  {
    "id": 459,
    "brand_name": "Zara",
    "brand_thumbnail": "",
    "location_id": [
      922,
      907,
      908,
      909,
      910,
      1131
    ],
    "customer_id": 437,
    "description": "Clothing Brand",
    "brand_groups": "1462,1465",
    "brand_color": "#000",
    "user_id": 4734,
    "brand_address": "Pakistan",
    "brand_email": "Zara@gmail.com",
    "brand_phone": "12345678",
    "brand_vat_no": "",
    "sku_no": "brd-437-459",
    "groups": [
      {
        "id": 1462,
        "group_name": "Clothing Brand"
      }
    ]
  }
]

list_groups

GET /api/groups. Why: List the tenant's groups with their linked categories, brands and branches — use it to see how a brand maps to categories, to find the group id to edit, or to check which categories a brand's menu will show. Produces group ids (get_group / update_group / delete_group); categories[].id feed list_categories/list_products filters; brand[].id are list_brands ids. Catalog management (dashboard) tool. For menu scoping on the POS use list_selling_brands, which already returns each brand's groups with their category ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_groups"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv.
brand_id number optional Brand id (list_brands): keeps only groups linked to this brand in brands_groups_relation.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_groups"
}'

Example Response

{
  "status": true,
  "message": "Groups found",
  "data": [
    {
      "id": 1341,
      "group_name": "Flavor you need",
      "customer_id": 437,
      "location_id": [
        907,
        908,
        909,
        910,
        922,
        907,
        908,
        909,
        910,
        922,
        1131
      ],
      "menu_sort_order": 0,
      "categories": [
        {
          "id": 15481,
          "name": "Alternate Options"
        },
        {
          "id": 15230,
          "name": "Brewed Coffee"
        },
        {
          "id": 15231,
          "name": "Espresso-Based Drinks"
        },
        {
          "id": 15266,
          "name": "Iced & Cold Coffees"
        },
        {
          "id": 16686,
          "name": "Sandwiches"
        },
        {
          "id": 15430,
          "name": "Specialty & Flavored Coffees"
        },
        {
          "id": 16707,
          "name": "Coffee"
        }
      ],
      "brand": [
        {
          "id": 412,
          "brand_name": "Nexus Cafe"
        }
      ]
    }
  ]
}

get_group

GET /api/groups/:id. Why: Load one group with every column plus its categories, brands and branches — required before update_group (which replaces everything). Consumes a group id from list_groups or list_selling_brands. Catalog management (dashboard) tool. For menu scoping on the POS use list_selling_brands, which already returns each brand's groups with their category ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_group"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Group id (list_groups data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_group",
  "id": 11
}'

Example Response

[
  {
    "id": 1341,
    "group_name": "Flavor you need",
    "group_thumbnail": "1759910641566-coffee.jpg",
    "group_description": "Flavor you need",
    "categories": [
      {
        "id": 15481,
        "name": "Alternate Options"
      },
      {
        "id": 15230,
        "name": "Brewed Coffee"
      },
      {
        "id": 15231,
        "name": "Espresso-Based Drinks"
      },
      {
        "id": 15266,
        "name": "Iced & Cold Coffees"
      },
      {
        "id": 16686,
        "name": "Sandwiches"
      },
      {
        "id": 15430,
        "name": "Specialty & Flavored Coffees"
      },
      {
        "id": 16707,
        "name": "Coffee"
      }
    ],
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      907,
      908,
      909,
      910,
      922,
      1131
    ],
    "customer_id": 437,
    "brand": [
      {
        "id": 412,
        "brand_name": "Nexus Cafe"
      }
    ],
    "user_id": 4734,
    "sku_no": "grp-437-1341",
    "menu_sort_order": 0
  }
]

list_selling_addons

GET /api/v2/products. Why: Add-ons of the session branch (web-pos: GET /api/v2/products?addon=1). An add-on is a small product added ON TOP of a cart line (e.g. extra sandwich with a coffee). Show an add-on for a line only when the add-on's `type` (comma-separated category names) contains the parent product's category name (`type`). Each chosen add-on is charged actual_price × qty and becomes its own invoice line with is_addon = 1 linked to the parent. Same row shape and paging as list_selling_products. web-pos offers NO add-ons when a product is added from search or favourites (no category selected). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_addons"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
page_limit number optional Optional.
current_page number optional Optional.
k string optional Optional.
cat string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_addons"
}'

Example Response

{
  "status": true,
  "message": "Data found ",
  "data": {
    "current_page": 1,
    "records_per_page": 50,
    "total_records": 7,
    "products": [
      {
        "id": 257559,
        "category_id": null,
        "name": "Classic Sandwich",
        "description": "",
        "price": 95,
        "stock": 0,
        "type": "Alternate Options,Brewed Coffee,Espresso-Based Drinks,Iced & Cold Coffees,Specialty & Flavored Coffees",
        "image": "",
        "customer_id": 437,
        "location_id": 907,
        "is_addon": 1,
        "addons": "[]",
        "bundle_items": "[]",
        "is_disabled": 0,
        "discount_pct": 0,
        "modifiers": [],
        "is_archive": 0,
        "barcode_type": "",
        "barcode_value": "",
        "is_open_price": 0,
        "is_combo": 0,
        "sync_deliverect": 0,
        "sync_otter": 0,
        "new_cost_date": null,
        "product_cost": 0,
        "is_weightage_item": 0,
        "unit_id": null,
        "actual_price": 100,
        "old_price": null,
        "vat": 5,
        "localized": null,
        "base_price": null,
        "menu_sort_order": 0,
        "plu": 0,
        "calories": null,
        "is_stock_product": null,
        "is_auto_apply": null,
        "combo_price": 0,
        "localize_name": "",
        "localize_description": "",
        "label_id": null,
        "recipes": null,
        "sku_no": "prod-437-257559",
        "tax_id": null,
        "preparation_time": 0,
        "product_recipe_total": 0,
        "product_material_total": 0,
        "is_overridden": 0,
        "is_deleted": 0,
        "category_sort_order": null,
        "is_sold_out": 0,
        "auto_selected_modifier_items": [],
        "price_levels": [],
        "tax_name": null,
        "tax_value": null,
        "image_path": "",
        "web_image": ""
      }
    ]
  }
}

list_addons

GET /api/products/webProducts. Why: Dashboard view of the tenant's add-ons (products with is_addon = 1): ids, master and branch prices, offer list (type), branch count, channel prices. Use it to find add-on ids for get_addon → update_addon / archive_product / delete_addon. Catalog management tool — what the POS offers on a cart line is list_selling_addons. Parent products: list_products / list_selling_products; `type` holds category names from list_categories / list_selling_categories; loc takes list_locations ids; channel_id takes list_channels ids. Per-branch availability and modifier/recipe links are NOT here — call get_addon. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_addons"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv of ids (list_locations).
k string optional Name search, case-insensitive substring (LIKE %k%) on the MASTER name (not branch names).
category_id string optional Category id or csv of ids (list_categories): keeps add-ons whose OWN category_id is one of them.
cat string optional Category NAME or csv of names, exact match (case-insensitive, trimmed) on the add-on's OWN category; used only when category_id is omitted/All.
brand_id number optional Brand id (list_brands): add-ons whose own category is in a group linked to the brand (brands_groups_relation → group_categories).
label_id number optional Label id (list_labels): exact products.label_id match.
channel_id number optional Channel id (list_channels): keeps add-ons that have a channel price row for it.
is_archive number optional 0 = active add-ons (default), 1 = archived ones (never both).
page number optional 1-based page; used only when limit < 10000.
limit number optional Rows per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_addons"
}'

Example Response

{
  "data": [
    {
      "id": 257559,
      "category_id": null,
      "name": "Classic Sandwich",
      "description": "",
      "price": 100,
      "stock": 0,
      "type": "Alternate Options,Brewed Coffee,Espresso-Based Drinks,Iced & Cold Coffees,Specialty & Flavored Coffees",
      "image": "",
      "customer_id": 437,
      "location_id": 907,
      "is_addon": 1,
      "addons": "[]",
      "bundle_items": "[]",
      "is_disabled": 0,
      "discount_pct": 0,
      "modifiers": null,
      "is_archive": 0,
      "barcode_type": "",
      "barcode_value": "",
      "is_open_price": 0,
      "is_combo": 0,
      "sync_deliverect": 0,
      "sync_otter": 0,
      "new_cost_date": null,
      "product_cost": 0,
      "is_weightage_item": 0,
      "unit_id": null,
      "actual_price": 100,
      "old_price": null,
      "vat": 5,
      "localized": null,
      "base_price": null,
      "menu_sort_order": 0,
      "plu": 0,
      "calories": null,
      "is_stock_product": null,
      "is_auto_apply": null,
      "combo_price": 0,
      "localize_name": "",
      "localize_description": "",
      "label_id": null,
      "recipes": null,
      "sku_no": "prod-437-257559",
      "tax_id": null,
      "preparation_time": 0,
      "product_recipe_total": 0,
      "product_material_total": 0,
      "is_overridden": 0,
      "is_deleted": 0,
      "category_sort_order": null,
      "override_location_id": 907,
      "override_name": "Classic Sandwich",
      "override_description": "",
      "override_price": 95,
      "override_actual_price": 100,
      "image_path": "",
      "web_image": "",
      "channel_price": [],
      "nameChannels": {
        "channels": []
      },
      "total_locations": 4,
      "has_recipe": 0
    }
  ],
  "total": 7
}

get_addon

GET /api/products/:id. Why: Load one add-on with everything update_addon needs: master prices, offer list (type), image path, per-branch price and availability (location_prices) and modifier/recipe/channel links. Call it before every update_addon (update rewrites the whole record). Takes an id from list_addons. Catalog management tool — what a branch's POS offers is list_selling_addons; branch ids/names come from list_locations, channel ids from list_channels. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_addon"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Add-on id (list_addons data[].id).
loc string optional Branch id or csv.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_addon",
  "id": 11
}'

Example Response

[
  {
    "id": 257559,
    "category_id": null,
    "name": "Classic Sandwich",
    "description": "",
    "price": 95,
    "stock": 0,
    "type": "Alternate Options,Brewed Coffee,Espresso-Based Drinks,Iced & Cold Coffees,Specialty & Flavored Coffees",
    "image": "",
    "customer_id": 437,
    "location_id": [
      908,
      907,
      910,
      1131
    ],
    "is_addon": 1,
    "addons": [],
    "bundle_items": [],
    "is_disabled": 0,
    "discount_pct": 0,
    "modifiers": [],
    "is_archive": 0,
    "barcode_type": "",
    "barcode_value": "",
    "is_open_price": 0,
    "is_combo": 0,
    "sync_deliverect": 0,
    "sync_otter": 0,
    "new_cost_date": null,
    "product_cost": 0,
    "is_weightage_item": 0,
    "unit_id": null,
    "actual_price": 100,
    "old_price": null,
    "vat": 5,
    "localized": null,
    "base_price": null,
    "menu_sort_order": 0,
    "plu": 0,
    "calories": null,
    "is_stock_product": null,
    "is_auto_apply": null,
    "combo_price": 0,
    "localize_name": "",
    "localize_description": "",
    "label_id": null,
    "recipes": [],
    "sku_no": "prod-437-257559",
    "tax_id": null,
    "preparation_time": 0,
    "product_recipe_total": 0,
    "product_material_total": 0,
    "is_overridden": 0,
    "is_deleted": 0,
    "category_sort_order": null,
    "image_path": "",
    "web_image": "",
    "channel_price": [],
    "materials": [],
    "location_prices": [
      {
        "location_id": 908,
        "location_name": "Johar Town",
        "location_price": null,
        "location_actual_price": 100,
        "is_available": true
      },
      {
        "location_id": 907,
        "location_name": "Default location",
        "location_price": 95,
        "location_actual_price": 100,
        "is_available": true
      },
      {
        "location_id": 910,
        "location_name": "Bahira Town",
        "location_price": 95,
        "location_actual_price": 100,
        "is_available": true
      },
      {
        "location_id": 1131,
        "location_name": "Test",
        "location_price": 95,
        "location_actual_price": 100,
        "is_available": true
      }
    ]
  }
]

list_labels

GET /api/label. Why: Every product label of the tenant with its branch list — the dashboard Products → Labels page and the source of label_id for create_product / update_product, the list_products label_id filter and the labels shown on sold lines (sync_invoice / list_orders_history items[].label_id). Search by name with k, filter by branch with loc (ids from list_locations). Get ids here for get_label / update_label / delete_label. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_labels"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated branch ids (list_locations).
k string optional Case-insensitive substring match on the label name, done in code (not SQL): % and _ are literal characters.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_labels"
}'

Example Response

{
  "data": [
    {
      "id": 50,
      "name": "Coffee Seeds",
      "description": "",
      "customer_id": 437,
      "location_id": [
        907,
        908,
        909,
        910,
        922,
        1131
      ]
    },
    {
      "id": 51,
      "name": "Coffee beans",
      "description": "",
      "customer_id": 437,
      "location_id": [
        907,
        909,
        908,
        910,
        922,
        1131
      ]
    },
    {
      "id": 53,
      "name": "Chai seeds",
      "description": "",
      "customer_id": 437,
      "location_id": [
        907,
        908,
        909,
        922,
        910,
        1131
      ]
    },
    {
      "id": 60,
      "name": "Garments",
      "description": "",
      "customer_id": 437,
      "location_id": [
        922,
        907,
        908,
        909,
        910,
        1131
      ]
    }
  ]
}

get_label

GET /api/label/:id. Why: One label with its branch list, to confirm a create_label / update_label or read the full record (name, description, location_id) that update_label must resend. Get the id from list_labels (or a product's label_id from list_products / get_product). The products using it: list_products label_id=<id>. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_label"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Label id (list_labels data[].id, or a product's label_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_label",
  "id": 11
}'

Example Response

{
  "label": {
    "id": 50,
    "name": "Coffee Seeds",
    "description": "",
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      1131
    ]
  },
  "message": "Label retrieved successfully"
}

list_deals

GET /api/deals. Why: Dashboard deal list: the tenant's live combos (is_combo 1, not archived/disabled/add-on) with price, branch list, channel prices and BOM cost — the catalog-management view, not the POS menu (list_selling_products shows what a branch sells, with is_combo / categories[] slots / combo_price). Get ids here for get_deal → update_deal / delete_deal. loc takes branch ids from list_locations. Slot ids + max_allowed are only in get_deal. Paginated with _page/_limit (default 10 per page). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_deals"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv of ids (list_locations): deals assigned to any of them (deals_locations, or legacy products.location_id).
k string optional Substring match on the deal name (LIKE %k%; % and _ are wildcards).
is_combo enum: 1 optional Any value → adds categories = the slot category NAMES (string[]) to each row.
_page number optional 1-based page.
_limit number optional Page size.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_deals"
}'

Example Response

{
  "items": [],
  "total": 0,
  "page": 1,
  "perPage": 10
}

get_deal

GET /api/deals/:id. Why: Full deal record for an edit: branch list, choice slots (categories[] with category_id + max_allowed — the same slots list_selling_products gives the POS), channel prices and image. Always call it before update_deal and resend its values (update_deal overwrites every column). Slot category ids resolve with list_categories / list_selling_categories; channel ids with list_channels; branch ids with list_locations. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_deal"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Deal id (list_deals items[].id).
is_archive number optional 0 → only a live deal, 1 → only an archived deal (archive_product).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_deal",
  "id": 11
}'

Example Response

"No records found"

list_channels

GET /api/channels. Why: Dashboard list of the tenant's sales channels with their branch assignments (location_id), description and sku_no. Use it to find a channel id before get/update/delete, or to see which branches a channel is sold on. For what the POS offers at one branch use list_selling_channels. Linkage: a channel id is referenced by list_selling_products data.products[].price_levels[].channel_id (per-channel price, product_channel_price), by orders (channel_id + channel_name, stored verbatim by sync_invoice — web-pos sends 0/""), and by discount codes channel_ids (list_discounts / get_user_extras data.discount_code[]). Branch availability lives in channels_locations (location_id in these tools); master channels have channels.location_id NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_channels"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_channels"
}'

Example Response

[
  {
    "id": 463,
    "name": "Coffee Bar",
    "description": "",
    "customer_id": 437,
    "sku_no": "chn-437-463",
    "location_id": [
      1131,
      910,
      922,
      907,
      908,
      909
    ]
  },
  {
    "id": 509,
    "name": "Express",
    "description": "",
    "customer_id": 437,
    "sku_no": "chn-437-509",
    "location_id": 907
  }
]

get_channel

GET /api/channels/:id. Why: Load one channel (name, description, branches) — call it before update_channel, which fully replaces name, description and branch list. Linkage: a channel id is referenced by list_selling_products data.products[].price_levels[].channel_id (per-channel price, product_channel_price), by orders (channel_id + channel_name, stored verbatim by sync_invoice — web-pos sends 0/""), and by discount codes channel_ids (list_discounts / get_user_extras data.discount_code[]). Branch availability lives in channels_locations (location_id in these tools); master channels have channels.location_id NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_channel"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Channel id (list_channels [].id / list_selling_channels data[].channel_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_channel",
  "id": 11
}'

Example Response

{
  "id": 463,
  "name": "Coffee Bar",
  "description": "",
  "customer_id": 437,
  "sku_no": "chn-437-463",
  "location_id": [
    909,
    908,
    907,
    922,
    910,
    1131
  ]
}

list_menus

GET /api/menus. Why: List the tenant's named menus (dashboard Products → Menus) with their branches, brand, routing label and Active status, to get ids for get_menu / update_menu / delete_menu. Menus are dashboard records only: nothing else in SysPOS reads them today. The POS (web-pos) never calls /menus, list_selling_products / list_selling_categories do not look at menus, and the aggregator syncs (Deliverect, Grubtech, Otter, LimeTray) build their menus from categories (sync_* flags, list_categories) and products, not from these menus. Adding or removing a product here does not change what a branch sells; for that use update_product (location_prices) or the category tools. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_menus"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated branch ids (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_menus"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 4,
      "name": "classic menu",
      "description": "",
      "currency": "AED",
      "customer_id": 437,
      "location_id": [
        907,
        910,
        922,
        909,
        908
      ],
      "is_active": 1,
      "brand_id": 414,
      "label_id": 51,
      "tax_id": 16,
      "brand_name": null,
      "label_name": "Coffee beans",
      "tax_name": "Food Card Tax",
      "tax_rate": 5
    }
  ]
}

get_menu

GET /api/menus/:id. Why: Load one menu with its categories, branches and products — the dashboard View and Edit pages. Call it before update_menu (the update overwrites every field) and to see which products/categories a menu contains. Menus are dashboard records only: nothing else in SysPOS reads them today. The POS (web-pos) never calls /menus, list_selling_products / list_selling_categories do not look at menus, and the aggregator syncs (Deliverect, Grubtech, Otter, LimeTray) build their menus from categories (sync_* flags, list_categories) and products, not from these menus. Adding or removing a product here does not change what a branch sells; for that use update_product (location_prices) or the category tools. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_menu"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Menu id (list_menus data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_menu",
  "id": 11
}'

Example Response

{
  "success": true,
  "data": {
    "id": 4,
    "name": "classic menu",
    "description": "",
    "currency": "AED",
    "customer_id": 437,
    "location_id": [
      907,
      910,
      922,
      909,
      908
    ],
    "is_active": 1,
    "brand_id": 414,
    "label_id": 51,
    "tax_id": 16,
    "brand_name": null,
    "label_name": "Coffee beans",
    "tax_name": "Food Card Tax",
    "tax_rate": 5,
    "categories": [
      {
        "id": 4,
        "menu_id": 4,
        "category_id": 15481,
        "category_name": "Alternate Options",
        "category_description": null
      }
    ],
    "products": [
      {
        "id": 8,
        "name": "Chai",
        "actual_price": 13,
        "product_id": 247653,
        "customer_id": 437,
        "location_id": null,
        "menu_id": 4,
        "category_id": 15481,
        "product_name": null,
        "product_description": null,
        "product_image": null,
        "category_name": "Alternate Options"
      }
    ],
    "locations": [
      {
        "id": 34,
        "menu_id": 4,
        "location_id": 907,
        "customer_id": 437,
        "location_name": "Default location"
      },
      {
        "id": 51,
        "menu_id": 4,
        "location_id": 910,
        "customer_id": 437,
        "location_name": "Bahira Town"
      },
      {
        "id": 52,
        "menu_id": 4,
        "location_id": 922,
        "customer_id": 437,
        "location_name": "Park View City"
      },
      {
        "id": 53,
        "menu_id": 4,
        "location_id": 909,
        "customer_id": 437,
        "location_name": "Iqbal Town"
      },
      {
        "id": 54,
        "menu_id": 4,
        "location_id": 908,
        "customer_id": 437,
        "location_name": "Johar Town"
      }
    ]
  }
}

list_selling_coupons

GET /api/v2/coupons. Why: Coupon list exactly as web-pos loads it into its local coupon cache when the Coupon dialog opens. Look the typed/scanned code up here FIRST (case-insensitive exact match on code): it tells you whether the coupon is product-scoped (product_ids non-empty → send product_ids to validate_coupon) or basket-wide (send order_total), and gives discount_type / discount_value to compute the discount — validate_coupon does not return an amount. Then ALWAYS call validate_coupon before applying. Not paginated. Product ids = list_selling_products data.products[].id; customer = list_pos_crm_users. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_coupons"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.
code string optional Optional.
is_active enum: 0, 1 optional Optional.
discount_type enum: percentage, value optional Optional.
min_order_value number optional Optional.
start_date string optional Optional.
end_date string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_coupons"
}'

Example Response

{
  "status": true,
  "message": "All coupons retrieved successfully.",
  "data": [
    {
      "id": 987,
      "customer_id": 437,
      "location_id": null,
      "code": "MASTER50",
      "description": "MASTER50",
      "discount_type": "value",
      "discount_value": 10,
      "min_order_value": 10,
      "start_date": "2026-04-01T00:00:00.000Z",
      "end_date": "2026-04-30T00:00:00.000Z",
      "usage_limit": 10,
      "per_user_limit": 2,
      "is_active": 1,
      "created_at": "2026-04-01T06:56:40.000Z",
      "updated_at": "2026-04-01T06:56:40.000Z",
      "current_usage": 0,
      "location_ids": [],
      "product_ids": [],
      "category_ids": []
    }
  ]
}

list_coupons

GET /api/coupons. Why: Dashboard coupon list with branch, product and category links — ids for get_coupon / update_coupon / delete_coupon. Same handler and same response as list_selling_coupons. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_coupons"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.
code string optional Optional.
is_active enum: 0, 1 optional Optional.
discount_type enum: percentage, value optional Optional.
min_order_value number optional Optional.
start_date string optional Optional.
end_date string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_coupons"
}'

Example Response

{
  "status": true,
  "message": "All coupons retrieved successfully.",
  "data": [
    {
      "id": 987,
      "customer_id": 437,
      "location_id": null,
      "code": "MASTER50",
      "description": "MASTER50",
      "discount_type": "value",
      "discount_value": 10,
      "min_order_value": 10,
      "start_date": "2026-04-01T00:00:00.000Z",
      "end_date": "2026-04-30T00:00:00.000Z",
      "usage_limit": 10,
      "per_user_limit": 2,
      "is_active": 1,
      "created_at": "2026-04-01T06:56:40.000Z",
      "updated_at": "2026-04-01T06:56:40.000Z",
      "current_usage": 0,
      "location_ids": [],
      "product_ids": [],
      "category_ids": []
    }
  ]
}

get_coupon

GET /api/coupons/:id. Why: Load one coupon with its branch, product and category links — read it before update_coupon (update replaces links). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_coupon"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Coupon id (list_coupons data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_coupon",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Coupon by id retrieved successfully.",
  "data": {
    "id": 987,
    "customer_id": 437,
    "location_id": null,
    "code": "MASTER50",
    "description": "MASTER50",
    "discount_type": "value",
    "discount_value": 10,
    "min_order_value": 10,
    "start_date": "2026-04-01T00:00:00.000Z",
    "end_date": "2026-04-30T00:00:00.000Z",
    "usage_limit": 10,
    "per_user_limit": 2,
    "is_active": 1,
    "created_at": "2026-04-01T06:56:40.000Z",
    "updated_at": "2026-04-01T06:56:40.000Z",
    "current_usage": 0,
    "product_ids": [],
    "category_ids": [],
    "location_ids": []
  }
}

list_coupon_products_by_locations

GET /api/coupons/products-by-locations. Why: Product picker for create_coupon / update_coupon product_ids: live (not archived, not disabled) products linked to ANY of the given branches, sorted by name. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_coupon_products_by_locations"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id(s), comma-separated — the coupon's branches.
search string optional Optional.
limit number optional Optional.
page number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_coupon_products_by_locations"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 270965,
      "name": "(1) Cold Brew"
    },
    {
      "id": 270946,
      "name": "(A) RADAR EV PATH REPL LENS CLEAR"
    },
    {
      "id": 270966,
      "name": "*Ice Cola"
    }
  ],
  "total": 1075,
  "page": 1,
  "limit": 3
}

list_coupon_categories_by_locations

GET /api/coupons/categories-by-locations. Why: Category picker for create_coupon / update_coupon category_ids: active categories linked to ANY of the given branches, sorted by name. Note category links are not enforced at checkout. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_coupon_categories_by_locations"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id(s), comma-separated — the coupon's branches.
search string optional Optional.
limit number optional Optional.
page number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_coupon_categories_by_locations"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 15481,
      "name": "Alternate Options"
    },
    {
      "id": 17556,
      "name": "Bag Accessory"
    },
    {
      "id": 15230,
      "name": "Brewed Coffee"
    }
  ],
  "total": 24,
  "page": 1,
  "limit": 3
}

list_discounts

GET /api/discounts/web. Why: Every discount (discount code) of the tenant linked to the given branches — the dashboard Discounts page. These are the rows web-pos gets as get_user_extras data.discount_code[] (active and in date only) and offers in its discount dialog; they are NOT coupons (list_selling_coupons / validate_coupon). Use it to find a discount id for update_discount / delete_discount — also instead of get_discount, which is broken. Scope ids: products list_selling_products, categories list_selling_categories, channels list_selling_channels, payment methods list_selling_payment_methods, order types list_order_types, branches list_locations. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_discounts"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations); keeps discounts linked to any of them.
category_ids number optional ONE category id: only discounts whose category_ids contain it (a csv of several ids matches nothing).
is_available_on string optional ONE lowercase weekday name (e.g.
start_time string optional "HH:MM:SS".
end_time string optional "HH:MM:SS".
complimentary string optional Any non-empty value (even "0") → only discounts with discount_percent = 100.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_discounts"
}'

Example Response

{
  "status": true,
  "message": "All discounts retrieved successfully.",
  "data": [
    {
      "id": 2376,
      "name": "Method Test",
      "discount_percent": 50,
      "max_cap_amount": 100,
      "min_basket_value": 20,
      "location_id": [
        907,
        908,
        909,
        922
      ],
      "is_active": 1,
      "start_date": "2026-03-01T00:00:00.000Z",
      "end_date": "2026-03-31T00:00:00.000Z",
      "discount_type": 0,
      "category_ids": "15481,15230",
      "apply_discount_to": "category",
      "product_ids": null,
      "start_time": "00:00:00",
      "end_time": "23:59:00",
      "is_available_on": "monday,tuesday,wednesday,thursday,friday,saturday,sunday",
      "is_auto_apply": 1,
      "is_promotion": 0,
      "payment_methods": null,
      "channel_ids": "463,509,482,515",
      "customer_id": 437,
      "order_type_ids": "1139,1174,1129,1127",
      "discount_method": "percentage",
      "discount_value": 50,
      "linked_location_id": 907,
      "location_name": "Default location"
    }
  ]
}

get_discount

GET /api/discounts/:id. Why: Meant to load one discount by id, but the server handler is broken: it reads the id from a route param that does not exist and ALWAYS answers 400 "Please enter valid discount id". Use list_discounts (loc="All") and pick the row by id instead — that is also what the dashboard does before an edit. BROKEN on the server: always returns 400 "Please enter valid discount id" — use list_discounts (loc="All") and pick the row by id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_discount"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Discount id (list_discounts data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_discount",
  "id": 11
}'

Example Response

{
  "status": false,
  "message": "Please enter valid discount id",
  "data": null
}

list_selling_payment_methods

GET /api/v2/payment-methods. Why: Tenders the branch can take (Cash, Card, Wallet…) — what web-pos loads for checkout, change-payment and refund. The method NAME is the identity: it is the key in the order's payment_method list, e.g. [{ "CASH": 30 }]. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_payment_methods"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_payment_methods"
}'

Example Response

[
  {
    "id": 1632,
    "name": "card",
    "is_enable": 1,
    "priority": 0,
    "type": "cash",
    "customer_id": 437,
    "location_id": 907,
    "sync_locations": 1
  },
  {
    "id": 2135,
    "name": "Wallet",
    "is_enable": 1,
    "priority": 0,
    "type": "wallet",
    "customer_id": 437,
    "location_id": 907,
    "sync_locations": 1
  }
]

list_payment_methods

GET /api/payment-methods. Why: Dashboard management list of tender types (Cash, Card, Wallet …) with the branches each is linked to — use it for ids (get/update/delete_payment_method), to audit which branches accept which methods, and to find methods linked to no branch. Not the POS selling view: web-pos reads GET /api/v2/payment-methods (list_selling_payment_methods), which defaults to the cashier's branch. Linkage: the payment method NAME is the key in an invoice's payment_method[] (e.g. [{"cash": 30}]) and is matched case-insensitively against the methods linked to the invoice's branch — so renaming or deleting a method changes what the POS can settle with. Branch availability lives in payment_methods_locations (location_id[] in these tools); on master tenants payment_methods.location_id is NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_payment_methods"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id(s), csv ("907,908").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_payment_methods"
}'

Example Response

[
  {
    "id": 1632,
    "name": "card",
    "is_enable": 1,
    "priority": 0,
    "type": "cash",
    "customer_id": 437,
    "location_id": 907,
    "sync_locations": 1
  },
  {
    "id": 2135,
    "name": "Wallet",
    "is_enable": 1,
    "priority": 0,
    "type": "wallet",
    "customer_id": 437,
    "location_id": 907,
    "sync_locations": 1
  }
]

get_payment_method

GET /api/payment-methods/:id. Why: Load one payment method with its full branch list before update_payment_method (update is a full replace — resend is_enable, priority, type, sync_locations and the complete location_id list). Linkage: the payment method NAME is the key in an invoice's payment_method[] (e.g. [{"cash": 30}]) and is matched case-insensitively against the methods linked to the invoice's branch — so renaming or deleting a method changes what the POS can settle with. Branch availability lives in payment_methods_locations (location_id[] in these tools); on master tenants payment_methods.location_id is NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_payment_method"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Payment method id (list_payment_methods).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_payment_method",
  "id": 11
}'

Example Response

[
  {
    "id": 2135,
    "name": "Wallet",
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      1100,
      1101,
      1102,
      1117,
      1131
    ],
    "is_enable": 1,
    "priority": 0,
    "type": "wallet",
    "sync_locations": 1
  }
]

list_selling_order_types

GET /api/v2/order-types. Why: Order types (Dine-in, Takeaway, Delivery, Drive-thru…) the POS offers — what web-pos loads for the order-type chips and checkout step 1. The chosen type's TITLE (a free-text string) is what goes on the order as order_type; nothing uses the id or code. Tenant-wide list (not per branch). Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_order_types"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
icon_pixel_size number optional Optional.
icon_format enum: png, jpg, jpeg, webp, gif optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_order_types"
}'

Example Response

{
  "status": true,
  "message": "data found",
  "data": [
    {
      "id": 1139,
      "title": "delivery",
      "icon_key": "carbon:delivery",
      "icon_url": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fcarbon%2Fdelivery.svg%3Fwidth%3D128%26height%3D128&output=png",
      "order_type_code": "DELIVERY",
      "is_system_default": 1
    },
    {
      "id": 1174,
      "title": "dine-in",
      "icon_key": "boxicons:fork-knife",
      "icon_url": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fboxicons%2Ffork-knife.svg%3Fwidth%3D128%26height%3D128&output=png",
      "order_type_code": "DINE_IN",
      "is_system_default": 1
    },
    {
      "id": 1129,
      "title": "drive-thru",
      "icon_key": "ic:baseline-drive-eta",
      "icon_url": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fic%2Fbaseline-drive-eta.svg%3Fwidth%3D128%26height%3D128&output=png",
      "order_type_code": "DRIVE_THRU",
      "is_system_default": 1
    },
    {
      "id": 1127,
      "title": "takeaway",
      "icon_key": "boxicons:takeaway",
      "icon_url": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fboxicons%2Ftakeaway.svg%3Fwidth%3D128%26height%3D128&output=png",
      "order_type_code": "TAKEAWAY",
      "is_system_default": 1
    }
  ]
}

list_order_types

GET /api/order-type. Why: Dashboard management list of the tenant's order types (dine-in, takeaway, delivery, drive-thru and custom ones) — use it to get ids for update_order_type / delete_order_type, check whether a name exists before create_order_type, and see which rows are protected system defaults. Not the POS selling view: web-pos reads GET /api/v2/order-types (list_selling_order_types), which returns the same rows as `title` plus a rendered icon_url. Order types are tenant-wide (order_type.customer_id) — NOT per branch; every branch sees the same list and there is no loc filter. The order type NAME (order_type here, `title` in GET /api/v2/order-types) is the string stored in an invoice's order_type; order_type_code is the fixed behaviour (DINE_IN / TAKEAWAY / DELIVERY / DRIVE_THRU) the POS uses to decide table/driver/address flow. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_order_types"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_order_types"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 1139,
      "order_type": "delivery",
      "order_type_code": "DELIVERY",
      "is_system_default": 1,
      "customer_id": 437,
      "icon_key": "carbon:delivery"
    },
    {
      "id": 1174,
      "order_type": "dine-in",
      "order_type_code": "DINE_IN",
      "is_system_default": 1,
      "customer_id": 437,
      "icon_key": "boxicons:fork-knife"
    }
  ]
}

list_order_type_codes

GET /api/order-type/codes. Why: The fixed set of behaviour codes accepted by create_order_type.order_type_code (DINE_IN, DRIVE_THRU, TAKEAWAY, DELIVERY). Call before creating a custom order type (e.g. "Talabat" with code DELIVERY) to pick its behaviour. Static list — same for every tenant. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_order_type_codes"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_order_type_codes"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "code": "DINE_IN",
      "default_label": "Dine-in"
    },
    {
      "code": "DRIVE_THRU",
      "default_label": "Drive-thru"
    },
    {
      "code": "TAKEAWAY",
      "default_label": "Takeaway"
    },
    {
      "code": "DELIVERY",
      "default_label": "Delivery"
    }
  ]
}

list_kds_orders

GET /api/v2/kds-orders. Why: The kitchen board — what web-pos's KDS screen polls every 4 s: today's orders of the session branch that are not bumped and not cancelled, each with its lines (is_prepared = ticked) and accept state (is_accepted). Use it to see what the kitchen still has to make, per station (c = category name), and to get uuid / order_items[].id for the three KDS write tools. is_order_rebump=1 returns the bumped orders instead (recall list). Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_kds_orders"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
c string optional Optional.
is_order_rebump number optional Optional.
brand_id number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_kds_orders"
}'

Example Response

[
  {
    "id": "a0b1c2d3e4f5",
    "oid": 57,
    "order_no": 57,
    "customer_invoice_id": 10433,
    "token_number": null,
    "is_accepted": 1,
    "external_order_ready": 0,
    "exchange_rate": null,
    "is_wastage": 0,
    "paid_in_usd": null,
    "is_discount_on_total": 0,
    "items_number": 2,
    "aggregator_order_id": null,
    "subtotal": 48,
    "vat": 0,
    "channel_id": 0,
    "payable": 48,
    "discountamount": 0,
    "items_discount_amount": 0,
    "is_ticket_available": 0,
    "is_wallet_used": 0,
    "discountpct": 0,
    "paid": 0,
    "change": 0,
    "note": "Guest allergic to sesame",
    "uuid": "c1d2e3f4-5a6b-4c7d-8e9f-a0b1c2d3e4f5",
    "created_at": 1790227800,
    "updated_at": 1790227890,
    "customer_id": 437,
    "location_id": 907,
    "row_discount": 0,
    "order_notes": null,
    "info_name": null,
    "info_address": null,
    "info_phone": null,
    "info_email": null,
    "status": "open",
    "by": "web-pos",
    "external_order_id": null,
    "burn_points": 0,
    "earn_points": 0,
    "burn_points_amount": 0,
    "available_points": 0,
    "available_points_amount": 0,
    "charges_fee": 0,
    "delivery_charges": 0,
    "phone_number": "",
    "loyalty_mobile_number": "",
    "loyalty_mobile_code": "",
    "crm_delivery_area": null,
    "user": "cashier@example.com",
    "table_id": 1204,
    "plate_number": null,
    "is_vat_on_discount_exclude": 0,
    "created_by_user_id": 3101,
    "close_by_user_id": null,
    "modified_by_user_ids": [],
    "crm_user_id": 0,
    "crm_user_name": "",
    "order_type": "Dine In",
    "tip_amount": 0,
    "brand_id": null,
    "dining_guests": 2,
    "channel_name": "",
    "discount_reason": null,
    "paid_at": null,
    "coupon_amount": 0,
    "coupon_id": 0,
    "coupon_code": null,
    "printed_invoice_receipts": 0,
    "printed_kds_receipts": 0,
    "app_version": null,
    "app_os_version": null,
    "build_number": null,
    "os_type": null,
    "round": 1,
    "rider_pickup_at": null,
    "rider_arrived_at": null,
    "order_type_icon_key": "mdi:silverware-fork-knife",
    "notes": [],
    "payment_method": null,
    "payment_methods": null,
    "payments": null,
    "order_items": [
      {
        "id": 1583204,
        "identifier": "8f7c2a10-3b1e-4c55-9a7e-0d4b6f1e2a31",
        "name": "Chicken Shawarma",
        "name_localize": null,
        "actual_price": 18,
        "base_price": 18,
        "price": 18,
        "is_weightage_item": 0,
        "unit_id": null,
        "qty": 2,
        "subtotal": 36,
        "line_total": null,
        "line_vat": null,
        "line_total_without_vat": null,
        "invoice_uuid": "c1d2e3f4-5a6b-4c7d-8e9f-a0b1c2d3e4f5",
        "product_id": 52110,
        "extras": null,
        "vat": 0,
        "is_updated": 0,
        "updated_qty": 0,
        "is_addon": 0,
        "is_refunded": 0,
        "refund_quantity": 0,
        "refund_reason": null,
        "note": "no pickles",
        "addon_linked_with": null,
        "discount_pct": 0,
        "discount_amount": 0,
        "modifier": [
          {
            "id": 881,
            "name": "Spicy",
            "price": 0,
            "qty": 1
          }
        ],
        "transferred_to": 0,
        "transferred_to_uuid": null,
        "transferred_to_uuid_list": [],
        "transferred_quantities": [],
        "transfer_recipients": [],
        "created_at": 1790227800,
        "updated_at": 1790227800,
        "is_prepared": null,
        "preparation_time": 0,
        "type": "Hot Kitchen",
        "label_id": null,
        "is_combo": 0,
        "is_deleted": 0,
        "deleted_at": null,
        "deleted_qty": null,
        "coupon_amount": 0,
        "coupon_id": 0,
        "coupon_code": null,
        "updated_by_user_ids": [],
        "re": null,
        "ri": null,
        "is_wastage": null,
        "discount_id": null,
        "discount_name": null,
        "round": 1,
        "combo_items": null
      },
      {
        "id": 1583205,
        "identifier": "2b9e5d71-6c0a-4f18-b3d2-7e1a9c4f8d02",
        "name": "Mint Lemonade",
        "name_localize": null,
        "actual_price": 12,
        "base_price": 12,
        "price": 12,
        "is_weightage_item": 0,
        "unit_id": null,
        "qty": 1,
        "subtotal": 12,
        "line_total": null,
        "line_vat": null,
        "line_total_without_vat": null,
        "invoice_uuid": "c1d2e3f4-5a6b-4c7d-8e9f-a0b1c2d3e4f5",
        "product_id": 52187,
        "extras": null,
        "vat": 0,
        "is_updated": 0,
        "updated_qty": 0,
        "is_addon": 0,
        "is_refunded": 0,
        "refund_quantity": 0,
        "refund_reason": null,
        "note": null,
        "addon_linked_with": null,
        "discount_pct": 0,
        "discount_amount": 0,
        "modifier": [],
        "transferred_to": 0,
        "transferred_to_uuid": null,
        "transferred_to_uuid_list": [],
        "transferred_quantities": [],
        "transfer_recipients": [],
        "created_at": 1790227800,
        "updated_at": 1790227890,
        "is_prepared": 1,
        "preparation_time": 0,
        "type": "Beverages",
        "label_id": null,
        "is_combo": 0,
        "is_deleted": 0,
        "deleted_at": null,
        "deleted_qty": null,
        "coupon_amount": 0,
        "coupon_id": 0,
        "coupon_code": null,
        "updated_by_user_ids": [],
        "re": null,
        "ri": null,
        "is_wastage": null,
        "discount_id": null,
        "discount_name": null,
        "round": 1,
        "combo_items": null
      }
    ],
    "order_type_icon": null
  }
]

list_sold_out_products

GET /api/v2/products/sold-out. Why: Ids of the products switched to sold out at the branch (products_locations.is_sold_out = 1, set by update_product_availability). web-pos polls it every 60 s (and listens on Firestore) to grey out tiles. It is the same flag as is_sold_out on list_selling_products, but cheaper to refresh. It does NOT include products whose quota is used up (see list_product_quota). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_sold_out_products"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_sold_out_products"
}'

Example Response

{
  "status": true,
  "message": "Sold out products fetched",
  "data": []
}

list_product_quota

GET /api/v2/user-product-quota. Why: Product quotas of the branch: for each product with a quota, how many units may be sold (total_quota), how many were sold (sold_count) and what is left (remaining_stock). web-pos loads it to show 'N left' on tiles, to block adding more than remaining_stock to a cart, and on the Product Sold Counter page. A quota is per product per branch (not per user, despite the route name). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_product_quota"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional Optional.
product_id string optional Optional.
assignment_id string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_product_quota"
}'

Example Response

{
  "status": true,
  "message": "All product quotas retrieved successfully",
  "data": [
    {
      "counter_id": null,
      "assignment_id": 4,
      "product_id": 257497,
      "product_name": "Espresso",
      "total_quota": 10,
      "sold_count": 0,
      "remaining_stock": 10,
      "history": []
    },
    {
      "counter_id": null,
      "assignment_id": 5,
      "product_id": 257499,
      "product_name": "Caramel Latte",
      "total_quota": 10,
      "sold_count": 0,
      "remaining_stock": 10,
      "history": []
    },
    {
      "counter_id": 15848,
      "assignment_id": 25,
      "product_id": 257508,
      "product_name": "Mocha",
      "total_quota": 20,
      "sold_count": 7,
      "remaining_stock": 13,
      "history": [
        {
          "id": 27,
          "quantity": -1,
          "invoice_id": 0,
          "counter_id": 15848,
          "identifier": "58556941-8651-4fbe-a683-8bf32143dfe7",
          "order_uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
          "product_id": 257508,
          "created_at": 1787738590
        }
      ]
    }
  ]
}

get_active_shift

GET /api/v2/shifts/active. Why: Get the active shift for this device/user. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_active_shift"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_active_shift"
}'

Example Response

{
  "id": 1,
  "status": "open"
}

list_shifts

GET /api/v2/shifts. Why: List shifts. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_shifts"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
page string optional Page number (1-based).
limit string optional Rows per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_shifts"
}'

Example Response

[
  {
    "id": 1
  }
]

list_drawer_categories

GET /api/v2/shifts/drawer-categories. Why: Cash in/out category list. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_drawer_categories"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_drawer_categories"
}'

Example Response

[
  {
    "id": 1,
    "name": "Petty cash"
  }
]

list_drawer_transactions

GET /api/v2/shifts/drawer-transactions. Why: List cash drawer transactions for the shift. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_drawer_transactions"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
shift_id string optional Shift id (list_shifts / get_active_shift).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_drawer_transactions"
}'

Example Response

[
  {
    "id": 1,
    "amount": 10
  }
]

get_tills_summary

GET /api/v2/shifts/tills-summary. Why: Per-user tills summary for a shift. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_tills_summary"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
shift_id string optional Shift id (list_shifts / get_active_shift).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_tills_summary"
}'

Example Response

[
  {
    "user_id": 1,
    "total": 100
  }
]

get_ol_info

GET /api/v2/ol-info. Why: Last order of the branch's CURRENT business day (its daily order_no and running receipt number customer_invoice_id) plus that day's UTC window. web-pos caches it at boot but reads nothing from it. Use it to see the last numbers before/after sync_invoice (the server assigns both) or the UTC bounds of 'today'. Hours/time zone come from get_pos_configs. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_ol_info"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_ol_info"
}'

Example Response

{
  "id": 0,
  "invoice_id": 0,
  "order_id": 0,
  "username": "cashier@example.com",
  "customer_id": 437,
  "location_id": 907,
  "fromDate": "2026-09-24 05:00:00",
  "toDate": "2026-09-25 05:00:00"
}

get_user_extras

GET /api/v2/user-extras-v3. Why: Branch + user configuration web-pos loads at boot: receipt header (name, address, VAT no.), flat VAT %, currency, money decimals, business-day hours, the user's max manual discount %, active discount codes, the void PIN and the ios_modules feature flags (is_loyalty_enable, is_branding_enable, is_activities_enabled). Read it before pricing an order for sync_invoice (VAT fallback, decimal_points) or offering discounts. The per-user POS switches (is_channels_enable, is_soldout, discount_codes, is_change_payment_method_enabled, tip_amount, dining_guest_count, combo_options) are NOT here: they are list_pos_users data[].setting. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_user_extras"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_user_extras"
}'

Example Response

{
  "status": true,
  "message": "Data found",
  "data": {
    "today_guests": 0,
    "can_preview_invoice": 0,
    "max_discount_permit": 50,
    "discount_code": [
      {
        "id": 101,
        "name": "Happy Hour 10",
        "discount_percent": null,
        "discount_method": "percentage",
        "discount_value": 10,
        "max_cap_amount": 20,
        "min_basket_value": 30,
        "location_id": null,
        "is_active": 1,
        "start_date": "2026-09-01T00:00:00.000Z",
        "end_date": "2026-12-31T00:00:00.000Z",
        "discount_type": 0,
        "category_ids": "",
        "apply_discount_to": "entire_basket",
        "product_ids": "",
        "start_time": "15:00:00",
        "end_time": "18:00:00",
        "is_available_on": "monday,tuesday,wednesday,thursday,friday",
        "is_auto_apply": 0,
        "is_promotion": 0,
        "payment_methods": "",
        "channel_ids": "",
        "customer_id": 437,
        "order_type_ids": ""
      }
    ],
    "opening_hour": "10:00 AM",
    "closing_hour": "08:00 AM",
    "business_logo": "https://dev1.syspos.ae/api/uploads/example-logo.png",
    "business_config": {
      "vat": "5",
      "info_name": "Example Cafe",
      "info_address": "Shop 1, Example Street",
      "info_email": "info@example-cafe.test",
      "vat_no": "100000000000003",
      "info_phone": "+971500000000",
      "legal_name": null,
      "exchange_rate": 12,
      "pincode": 1234,
      "currency": {
        "id": 10103,
        "name": "currency",
        "value": "",
        "customer_id": 437,
        "location_id": 907
      }
    },
    "decimal_points": 0,
    "ios_modules": {
      "is_kds_enable": 1,
      "is_daily_sale_report_enable": 1,
      "is_order_hisotry_screen_enable": 1,
      "is_sms_enable": 1,
      "is_cloud_print_enable": 1,
      "is_user_blocked": 0,
      "is_branding_enable": 1,
      "is_actual_price": 1,
      "is_usd_base": 0,
      "is_dine_in_enable": 1,
      "is_wastage_enable": 1,
      "is_material_stock_enable": 1,
      "is_balance_enable": 1,
      "is_product_scanner_enable": 1,
      "is_bar_scanner_enable": 1,
      "is_devices_enable": 1,
      "is_peer_enable": 1,
      "is_setting_enable": 1,
      "is_loyalty_enable": 1,
      "is_ticket_enabled": 0,
      "is_wallet_enabled": 1,
      "is_location_service_enabled": 1,
      "is_crm_mobile_mandatory": 0,
      "reload_dine_order": 1,
      "order_dispatch_feature": 0,
      "is_activities_enabled": 0
    },
    "tos": "<ul><li>Optional note</li></ul>",
    "tos_links": {},
    "receipt_header": "",
    "receipt_header_links": {},
    "is_shift_enabled": 1,
    "business_day_start": "10:00"
  }
}

get_user_location

GET /api/v2/user-location. Why: The session user's home branch row (locations table): id, name and integration ids. web-pos caches it at boot for the N-Genius card terminal ids. Use it to learn which branch the session sells in (the branch sync_invoice and every list_selling_* tool use). For all branches in the key's scope use list_locations. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_user_location"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_user_location"
}'

Example Response

[
  {
    "id": 907,
    "name": "Default location",
    "customer_id": 437,
    "is_ware_house": "1",
    "is_primary": null,
    "otter_location_id": null,
    "deliverect_location_id": null,
    "enable_menu_checkout": null,
    "invoice_service_version": "v1",
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "enable_grubtech": 0,
    "allow_grubtech_status_updates": 0
  }
]

list_pos_users

GET /api/v2/users-list. Why: POS users of the session branch with their POS switches (data[].setting). Use it to pick the cashier ids for sync_invoice (created_by_user_id / close_by_user_id / user email), for get_today_sales close_by_user_id and log_invoice_activities, and to read the per-user feature switches web-pos obeys: is_channels_enable + channel_id (list_selling_channels), is_soldout (update_product_availability), discount_codes (discount codes + list_selling_coupons / validate_coupon), is_change_payment_method_enabled (update_invoice_payment_methods), tip_amount, dining_guest_count, combo_options, pay_zero_amount. The session user's own row = the switches that apply to MCP orders. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_pos_users"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_pos_users"
}'

Example Response

{
  "status": true,
  "message": "users list",
  "data": [
    {
      "id": 4734,
      "username": "Cashier One",
      "email": "cashier@example.com",
      "pin": "1234",
      "role": "admin",
      "role_id": 31,
      "location_id": 907,
      "thumbnail": "",
      "is_multi_location_allowed": 0,
      "is_master_locations_enabled": 1,
      "shift_z_report": 0,
      "view_estimated_cash": 0,
      "external_order_ready": 0,
      "ios_modules": {
        "is_kds_enable": 1,
        "is_daily_sale_report_enable": 1,
        "is_order_hisotry_screen_enable": 1,
        "is_sms_enable": 1,
        "is_cloud_print_enable": 1,
        "is_user_blocked": 0,
        "is_branding_enable": 1,
        "is_actual_price": 1,
        "is_usd_base": 0,
        "is_dine_in_enable": 1,
        "is_wastage_enable": 1,
        "is_material_stock_enable": 1,
        "is_balance_enable": 1,
        "is_product_scanner_enable": 1,
        "is_bar_scanner_enable": 1,
        "is_devices_enable": 1,
        "is_peer_enable": 1,
        "is_setting_enable": 1,
        "is_loyalty_enable": 1,
        "is_ticket_enabled": 0,
        "is_wallet_enabled": 1,
        "is_location_service_enabled": 1,
        "is_crm_mobile_mandatory": 0,
        "reload_dine_order": 1,
        "order_dispatch_feature": 0
      },
      "device_info": [
        {
          "id": 123,
          "device_id": "device-0001",
          "device_name": "Front iPad",
          "app_os_version": null,
          "app_version": null,
          "build_number": null,
          "user_id": 4734,
          "username": "Cashier One",
          "customer_id": 437,
          "location_id": 907,
          "last_online": "2026-09-18T13:19:53.000Z"
        }
      ],
      "token": "<jwt>",
      "setting": {
        "is_multi_location_allowed": 0,
        "is_master_locations_enabled": 1,
        "print_receipt": 1,
        "print_no_of_times": 1,
        "is_ingredient_disabled": 0,
        "kds_auto_receipt": 1,
        "invoice_auto_receipt": 1,
        "cart_options": 1,
        "print_and_save": 1,
        "cloud_print": 1,
        "quit_printers_with_cloud_enabled": 1,
        "invoice_printer_act_as_kds": 1,
        "cart_note": 1,
        "cart_car_plate": 1,
        "token_number": 1,
        "discount_codes": 1,
        "transfer_order_pincode": 1,
        "daily_sale_input_check": 1,
        "daily_sale_media_check": 1,
        "active_pincode": 1,
        "label_printing": 1,
        "qrcode_on_label_print": 1,
        "is_kds_device": 1,
        "open_drawer": 1,
        "user_card_digits": 1,
        "invoice_qrcode": 1,
        "force_adons": 1,
        "combo_options": 1,
        "ensure_combo_minimum_item_requirement": 1,
        "row_discount": 0,
        "sms_invoice": 0,
        "language": "",
        "zebra_ip": "",
        "zebra_port": 0,
        "offline_order_sync": 0,
        "orderhistory_print_receipt": 0,
        "fraction_digits": 2,
        "brand_id": 0,
        "dining_guest_count": 1,
        "is_brand_groups": 0,
        "is_extras_enble": 0,
        "display_modifier_titles": 0,
        "modifier_sort_min_required_desc": 1,
        "print_only_save_kds_orders": 0,
        "print_note_on_receipt": 0,
        "show_receipt_screen": 0,
        "order_type": 0,
        "open_dine_in": 0,
        "hide_cart_pay_button": 0,
        "delivery_detail_enable": 0,
        "is_attributed_receipt": 0,
        "is_cds_enable": 0,
        "external_order_print_receipt": 0,
        "text_receipt": 0,
        "each_category_kds_print": 0,
        "exchange_format": 0,
        "has_void_order_permission": 0,
        "hide_clear_checkout": 1,
        "kds_instruction": 1,
        "pay_default_usd": 0,
        "rpt_business_logo": 0,
        "rpt_order_type": 0,
        "rpt_is_modified": 0,
        "rpt_biller_info": 0,
        "rpt_total_before_vat": 0,
        "rpt_vat_info": 0,
        "rpt_net_total": 0,
        "rpt_exchange_rate": 0,
        "rpt_dine_guest_count": 0,
        "rpt_paid_at": 0,
        "esc_command": 0,
        "tip_amount": 0,
        "print_void_orders": 0,
        "qr_code_on_print_receipt": 0,
        "is_channels_enable": 0,
        "channel_id": 463,
        "unify_brands": 0,
        "ingredient_stay_standalone": 0,
        "allegy_item": 0,
        "dinein_serving_role": 0,
        "draft_order": 0,
        "is_kds_expeditor": 0,
        "is_qr_payment": 0,
        "pay_zero_amount": 0,
        "print_deleted_item_on_receipt": 0,
        "receipt_format": 0,
        "receipt_translation": 0,
        "product_translation": 0,
        "category_translation": 0,
        "print_invoice_translation": 0,
        "print_kds_translation": 0,
        "reservation_with_dine_in": 0,
        "splitefire": 0,
        "station_id": 0,
        "cloud_target_user_id": 0,
        "cloud_target_device_id": null,
        "one_table_one_order": 0,
        "is_socket_server": 0,
        "is_client": 0,
        "exchange_rate": 12,
        "currency": {
          "id": 10103,
          "name": "currency",
          "value": "",
          "customer_id": 437,
          "location_id": 907
        },
        "web_local_network_printer": 0,
        "open_price_pincode": 0,
        "is_transfer_enabled": 0,
        "block_print_until_order_open": 0,
        "user_product_sales_counter": 0,
        "is_change_payment_method_enabled": 0,
        "skip_zero_quantity_items": 0,
        "skip_zero_amount_items": 0,
        "settle_wallet_topup": 1,
        "is_shift_management": 0,
        "acknowledge_dine_table_edit": 0,
        "lock_screen": 0,
        "is_soldout": 0,
        "force_crm_user": 0,
        "shift_transactions": 0,
        "eod_shift_report": 0,
        "override_shift": 0
      }
    }
  ]
}

list_selling_channels

GET /api/v2/get-channels. Why: Sales channels offered at the cashier's branch — what web-pos loads at boot and shows in Settings → Channel. The channel_id is the key for per-channel product prices (list_selling_products data.products[].price_levels[].channel_id), for discount-code channel scope (channel_ids) and for an order's channel_id/channel_name. Not paginated. Management (description, branches, create/update/delete): list_channels / get_channel in the channels catalog. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_channels"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_channels"
}'

Example Response

{
  "status": true,
  "message": "Channels data found",
  "data": [
    {
      "channel_id": 463,
      "channel_name": "Coffee Bar"
    },
    {
      "channel_id": 509,
      "channel_name": "Express"
    },
    {
      "channel_id": 482,
      "channel_name": "Food Panda"
    }
  ]
}

get_master_data

GET /api/v2/master-data. Why: Raw table dump used by the POS apps for offline sync. Useful rows: table=locations (id + name + integration ids of the branches the session USER may open — web-pos branch picker) and table=user (the session user's discount limit). table=product_materials / materials are for NON-master (inventory-per-branch) tenants and out of scope; table=products is a multi-MB raw dump (use list_selling_products); table=categories is broken. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_master_data"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
table enum: locations, user, products, product_materials, materials, categories required Required.
location-only string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_master_data",
  "table": "example"
}'

Example Response

[
  {
    "id": 907,
    "name": "Default location",
    "customer_id": 437,
    "is_ware_house": "1",
    "is_primary": null,
    "otter_location_id": null,
    "deliverect_location_id": null,
    "enable_menu_checkout": null,
    "invoice_service_version": "v1",
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "enable_grubtech": 0,
    "allow_grubtech_status_updates": 0
  },
  {
    "id": 908,
    "name": "Johar Town",
    "customer_id": 437,
    "is_ware_house": "1",
    "is_primary": null,
    "otter_location_id": null,
    "deliverect_location_id": null,
    "enable_menu_checkout": null,
    "invoice_service_version": "v1",
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "enable_grubtech": 0,
    "allow_grubtech_status_updates": 0
  }
]

get_today_sales

GET /api/v2/today-sales-v3. Why: The web-pos Daily Sales (Z-style) report of one branch and business day: net sales, orders, VAT, tenders, order types, items, discounts, voids, open orders and per-user totals. Business day = opening_hour → next opening_hour in the branch time_zone (get_pos_configs). Filter by cashier with close_by_user_id (list_pos_users). Order-level drill-down: list_orders_history; audit trail: get_invoice_activities. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_today_sales"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
date string optional Optional.
from_date string optional Optional.
to_date string optional Optional.
location_id string optional Optional.
close_by_user_id string optional Optional.
paid_at string optional Optional.
brand_id string optional Optional.
brands string optional Optional.
shift_id string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_today_sales"
}'

Example Response

{
  "order_type": {
    "Delivery": 2,
    "Takeaway": 1,
    "void_orders": 5,
    "refunded_items": 0
  },
  "dining_guests": 2,
  "item_transferred": 0,
  "items": [
    {
      "type": "Espresso-Based Drinks",
      "name": "Flat White",
      "unit_id": 0,
      "extras": null,
      "is_addon": 0,
      "is_refunded": 0,
      "total": 33,
      "qty": 3,
      "period": "2026-09-24T13:37:06.484Z",
      "list": [],
      "extra": []
    }
  ],
  "addons": [],
  "refund_items": [],
  "modifiers": [],
  "itemsCat": [
    {
      "type": "Espresso-Based Drinks",
      "total": 33
    }
  ],
  "itemsPayment": [
    {
      "payment_method": "cash",
      "total": 22,
      "transactions": 2,
      "tip_amount": 0
    }
  ],
  "totalAmount": 33,
  "total_sales": 33,
  "total_orders": 3,
  "hourly": [
    {
      "hour": 15,
      "value": 11,
      "order_count": 1
    }
  ],
  "time_distribution": [
    {
      "hour": 15,
      "value": 11,
      "order_count": 1
    }
  ],
  "opening_hour": "10:00",
  "time_zone": "+05:00",
  "location_id": 907,
  "wallet_orders": {
    "total_amount": 0,
    "total_count": 0
  },
  "wallet_topups": {
    "transaction_type": "debit",
    "amount": 0,
    "count": 0
  },
  "avg_invoice_bill": 11,
  "avg_per_order": 11,
  "avg_per_guest": 11,
  "total_vat": 1.04,
  "void_orders": {
    "title": "Void orders",
    "no_of_cancelled_order": 5,
    "no_of_canceled_order": 5,
    "no_of_items_refund": 0,
    "total": 424,
    "transaction": [
      {
        "payment_method": "cash",
        "total": 424,
        "transactions": 4
      }
    ]
  },
  "ts": 1790257026487,
  "time": "06:37:06 PM",
  "withdraw_amount": 0,
  "credit_amount": 0,
  "active_shift_id": null,
  "active_shift": null,
  "shift_filter_applied": false,
  "discount_summary": [],
  "invoice_begin": 16,
  "invoice_end": 24,
  "invoices_count": 9,
  "order_types": [
    {
      "name": "Delivery",
      "count": 2,
      "amount": 22
    }
  ],
  "user_statistics": {
    "open_orders": {
      "users": [
        {
          "user": "cashier@example.com",
          "total_amount": 31,
          "order_count": 1,
          "orders": [
            {
              "id": 8085857,
              "uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
              "payable": 31,
              "created_at": "2026-08-26T10:03:08.000Z",
              "order_type": "Dine In",
              "dining_guests": 2
            }
          ]
        }
      ],
      "total_amount": 31,
      "total_orders": 1
    },
    "completed_orders": {
      "users": [
        {
          "user": "cashier@example.com",
          "total_amount": 33,
          "order_count": 3,
          "orders": [
            {
              "id": 8085852,
              "uuid": "f5bd46f3-377a-477d-b748-876e9aab0063",
              "payable": 11,
              "created_at": "2026-08-20T11:07:42.000Z",
              "order_type": "Delivery",
              "dining_guests": 1
            }
          ]
        }
      ],
      "total_amount": 33,
      "total_orders": 3
    },
    "created_by_users": {
      "users": [
        {
          "user": "cashier@example.com",
          "user_id": "4734",
          "total_amount": 499,
          "order_count": 9,
          "orders": [
            {
              "id": 8085848,
              "uuid": "fdf751c3-f3f8-4cbe-9af0-76fd0d8482c8",
              "payable": 106,
              "created_at": "2026-08-17T06:25:20.000Z",
              "order_type": "takeaway",
              "dining_guests": null
            }
          ]
        }
      ],
      "total_amount": 499,
      "total_orders": 9
    },
    "closed_by_users": {
      "users": [
        {
          "user": "cashier@example.com",
          "user_id": "4734",
          "total_amount": 33,
          "order_count": 3,
          "orders": [
            {
              "id": 8085852,
              "uuid": "f5bd46f3-377a-477d-b748-876e9aab0063",
              "payable": 11,
              "created_at": "2026-08-20T11:07:42.000Z",
              "order_type": "Delivery",
              "dining_guests": 1
            }
          ]
        }
      ],
      "total_amount": 33,
      "total_orders": 3
    }
  },
  "tip_amount": {
    "title": "Tip Amount",
    "total": 0,
    "transactions": 0
  },
  "points": {
    "title": "Points",
    "total": 0,
    "transactions": 0
  },
  "discount_amount": {
    "title": "Discounts",
    "total": 0,
    "transactions": 0
  },
  "unpaid_order": {
    "title": "Unpaid Order",
    "total": 31,
    "transactions": 1
  },
  "pending_orders": [
    {
      "invoice_number": 23,
      "order_type": "Dine In",
      "amount": 31,
      "currency": "AED",
      "created_date": "Aug 26",
      "user": "cashier@example.com"
    }
  ],
  "late_closure": [],
  "total_without_vat": 31.96,
  "total_payable": 33,
  "gross_sales": 33,
  "order_charges": {
    "charges_fee": 0,
    "delivery_charges": 0
  },
  "date_range": {
    "from": "2026-08-01",
    "to": "2026-09-24"
  },
  "total_customers": {
    "count": 1
  },
  "total_discounts": {
    "total_amount": 0,
    "total_applications": 0,
    "references": []
  },
  "complete_order_summary": {
    "rows": [],
    "total_discount_amount": 0
  },
  "order_type_summary": {
    "rows": [
      {
        "type_name": "Delivery",
        "orders": 2,
        "share_percent": 66.67,
        "amount": 22
      }
    ],
    "totals": {
      "orders": 3,
      "share_percent": 100,
      "amount": 33
    }
  },
  "order_discount_summary": {
    "rows": [],
    "totals": {
      "applied_times": 0,
      "avg_discount": 0,
      "total_amount": 0
    },
    "footer": {
      "discount_percent_of_gross_sales": 0,
      "gross_sales_amount": 31.96,
      "gross_sales_currency": "AED",
      "message": "Discounts represent 0% of gross sales (31.96 AED) · Deducted from net collected"
    }
  },
  "item_discount_summary": {
    "rows": [],
    "totals": {
      "qty": 0,
      "original": 0,
      "discount": 0,
      "net": 0
    }
  }
}

get_invoice_activities

GET /api/v2/activities/:uuid. Why: Audit trail of one order by uuid — what web-pos shows in the order's "Activity" section: who did what and when (created, items added/removed, paid, voided, payment method changed…). uuid comes from list_orders_history or list_dine_orders. Rows are written by the POS apps; cancel_invoice / refund_invoice done by an agent do NOT add rows. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_invoice_activities"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_invoice_activities",
  "uuid": "example"
}'

Example Response

{
  "status": true,
  "message": "No activities found against the order.",
  "data": []
}

get_maintenance_status

GET /api/v2/maintenance/status. Why: Global (all tenants) maintenance switch. web-pos polls it every 2 minutes and blocks the whole POS with a maintenance screen while is_maintenance = 1 or the server answers 500/502/503. Check it before a batch of writes (sync_invoice etc.) or when calls start failing. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_maintenance_status"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_maintenance_status"
}'

Example Response

{
  "status": true,
  "data": {
    "is_maintenance": 0,
    "message": "All systems are up and running smoothly."
  }
}

get_loyalty_card

GET /api/v2/loyalty-program/card/:crmUserId. Why: A customer's loyalty card — what web-pos loads for the cart balance banner and before any redemption at payment. The tenant has ONE loyalty program (type point, stamp or membership) shared by every branch; a customer joins it only by enrolling (the card is NOT created by the POS, by create_pos_crm_user or by a sale). point: current_points and their money value (available_points_amount). stamp: current_stamps / stamps_required and the reward. membership: expiry and active flag. Read-only. Use it only when get_user_extras data.ios_modules.is_loyalty_enable = 1 and a customer is on the order (crm_user_id > 0 from list_pos_crm_users / create_pos_crm_user). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_loyalty_card"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
crmUserId string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_loyalty_card",
  "crmUserId": "example"
}'

Example Response

{
  "status": true,
  "message": "Loyalty card retrieved successfully.",
  "data": {
    "card_id": 57,
    "crm_user_id": 63821,
    "loyalty_program_id": 16,
    "loyalty_type": "point",
    "customer_name": "Sara Ahmed",
    "email": "sara.ahmed@example.com",
    "phone_number": "+971500000001",
    "is_active": 1,
    "total_redeemed": 0,
    "last_redeemed_date": null,
    "current_points": 3,
    "available_points": 3,
    "available_points_amount": 0.3,
    "program": {
      "id": 16,
      "name": "Loyalty 1",
      "description": null,
      "reward_description": null,
      "is_active": 1
    },
    "pass_info": {
      "serial_number": "00000000-0000-4000-8000-000000000057",
      "type_identifier": "pass.com.syspos.pos"
    },
    "card_created_at": "2026-06-29T10:27:28.000Z",
    "card_updated_at": "2026-08-20T11:07:43.000Z"
  }
}

validate_loyalty_burn

POST /api/v2/loyalty-program/burn-validation. Why: Server check of a loyalty redemption before you apply it at payment — what web-pos calls from the Redeem points dialog (point program) and the Redeem stamps button (stamp program). Uses the tenant's program and the customer's card (from get_loyalty_card). point: checks burn_points <= current_points and returns burn_amount = burn_points × redeem_amount / redeem_points. stamp / membership: checks the card is full / not expired, the minimum order and returns discount_amount. Does NOT reserve or deduct anything: it only reads (no OTP is sent, nothing is stored); the points are burned when the PAID order is saved (sync_invoice). No side effects. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"validate_loyalty_burn"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
crm_user_id string required Required.
order_total number required Required, > 0.
burn_points number optional Required for a point program: points to burn, > 0 and <= get_loyalty_card data.available_points (missing / 0 → 400 "Burn points must be a positive number").
loyalty_card_id string optional Optional.
product_ids array<number> optional Required only for a free_product reward (stamp or membership): must contain the reward product (get_loyalty_card data.product_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "validate_loyalty_burn",
  "crm_user_id": 11,
  "order_total": 1
}'

Example Response

{
  "status": true,
  "message": "Burn validation successful",
  "data": {
    "status": "Success",
    "available_points": 30,
    "burn_points": 20,
    "burn_amount": 2,
    "points_after_burn": 10,
    "conversion_rate": 0.1,
    "loyalty_program_id": 16,
    "loyalty_card_id": 57,
    "crm_user_id": 63821,
    "order_total": 45.5,
    "discount_reason": "Loyalty discount"
  }
}

list_locations

GET /api/locations. Why: Every branch (location) of the tenant this key may use, with its id and name — the id is the loc / location_id that every other tool takes (get_pos_configs, get_location_config, get_business_info, get_business_hours, get_location_days_hours, list_* catalog tools, create_* location_id lists). Call it first whenever the user names a branch, to turn the name into an id. Also shows the warehouse flag and the per-branch payment settings. This is the unpaginated list every dashboard picker uses; the dashboard Locations page uses the paginated /api/locations/v2 (same rows, fewer columns, newest first) — not exposed because this tool returns the same data in one call and MCP filters it to the key's branches. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_locations"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_locations"
}'

Example Response

[
  {
    "id": 907,
    "name": "Default location",
    "is_ware_house": "1",
    "enable_menu_checkout": null,
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "invoice_service_version": "v1"
  },
  {
    "id": 908,
    "name": "Johar Town",
    "is_ware_house": "1",
    "enable_menu_checkout": null,
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "invoice_service_version": "v1"
  },
  {
    "id": 909,
    "name": "Iqbal Town",
    "is_ware_house": "1",
    "enable_menu_checkout": null,
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "invoice_service_version": "v1"
  },
  {
    "id": 910,
    "name": "Bahira Town",
    "is_ware_house": "0",
    "enable_menu_checkout": null,
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "invoice_service_version": "v1"
  }
]

get_location

GET /api/locations/:id. Why: The full row of ONE branch — every column list_locations shows plus the integration ids (otter, deliverect, grubtech flags). Read it before update_location: that tool overwrites name, isWareHouse, enable_menu_checkout, payment_provider and both N-Genius ids, so you must send the current values of the ones you are not changing. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_location"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Location id (list_locations[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_location",
  "id": 11
}'

Example Response

[
  {
    "id": 907,
    "name": "Default location",
    "customer_id": 437,
    "is_ware_house": "1",
    "is_primary": null,
    "otter_location_id": null,
    "deliverect_location_id": null,
    "enable_menu_checkout": null,
    "invoice_service_version": "v1",
    "payment_provider": null,
    "ngenius_terminal_id": null,
    "ngenius_merchant_id": null,
    "enable_grubtech": 0,
    "allow_grubtech_status_updates": 0
  }
]

get_business_info

GET /api/config/business-info. Why: The branch's business profile — what the receipt header prints (name, address, VAT No, logo) plus contact, legal name, delivery area and T&C. Read it before create_business_info / update_business_info and to check what a receipt will show. Pass strict="1": without it an EMPTY branch silently returns ANOTHER branch's profile. Returns rows { id, name, value }; name is one of: info_name, legal_name, info_address, info_phone, info_email, info_vat_no, logo_image, license_image, location_latitude, location_longitude, polygon_coordinates, area_coordinates, receipt_footer_text, reciept_header_text (sic), currency, terms_and_conditions. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_business_info"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_business_info"
}'

Example Response

[
  {
    "id": 9993,
    "value": "Example Cafe",
    "name": "info_name"
  },
  {
    "id": 9994,
    "value": "Shop 1, Example Street",
    "name": "info_address"
  },
  {
    "id": 9995,
    "value": "info@example-cafe.test",
    "name": "info_email"
  },
  {
    "id": 9996,
    "value": "100000000000003",
    "name": "info_vat_no"
  },
  {
    "id": 9997,
    "value": "1762167059218-example-logo.png",
    "name": "logo_image"
  },
  {
    "id": 10103,
    "value": "",
    "name": "currency"
  },
  {
    "id": 10237,
    "value": "+971500000000",
    "name": "info_phone"
  },
  {
    "id": 11174,
    "value": "<ul><li>Optional note</li></ul>",
    "name": "receipt_footer_text"
  }
]

get_location_config

GET /api/config/all. Why: Read a branch's settings exactly as the dashboard Settings page shows them: VAT %, currency, decimals, time zone, business hours, exchange rate, receipt texts, POS flags, plus the main business-info keys. Use it to check a branch before selling (sync_invoice needs vat, decimal_points, time_zone) and before save_config. Branch rows only — no fallback. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_location_config"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_location_config"
}'

Example Response

[
  {
    "id": 9905,
    "value": "+05:00",
    "name": "time_zone"
  },
  {
    "id": 9943,
    "value": "5",
    "name": "vat"
  },
  {
    "id": 9993,
    "value": "Example Cafe",
    "name": "info_name"
  },
  {
    "id": 9994,
    "value": "Shop 1, Example Street",
    "name": "info_address"
  },
  {
    "id": 9995,
    "value": "info@example-cafe.test",
    "name": "info_email"
  },
  {
    "id": 9996,
    "value": "100000000000003",
    "name": "info_vat_no"
  },
  {
    "id": 9997,
    "value": "1762167059218-example-logo.png",
    "name": "logo_image"
  },
  {
    "id": 10101,
    "value": "10:00",
    "name": "opening_hour"
  },
  {
    "id": 10102,
    "value": "08:00",
    "name": "closing_hour"
  },
  {
    "id": 10103,
    "value": "",
    "name": "currency"
  },
  {
    "id": 10237,
    "value": "+971500000000",
    "name": "info_phone"
  },
  {
    "id": 10977,
    "value": "Asia/Karachi",
    "name": "time_zone_code"
  },
  {
    "id": 11174,
    "value": "<ul><li>Optional note</li></ul>",
    "name": "receipt_footer_text"
  },
  {
    "id": 11578,
    "value": "12",
    "name": "exchange_rate"
  },
  {
    "id": 11583,
    "value": "20",
    "name": "max_order_time"
  }
]

get_currency

GET /api/config/currency. Why: Currency code of one branch (display only). Same route as get_config_value with name=currency. Pass loc — without a branch the server returns the first currency row of ANY branch. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_currency"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_currency"
}'

Example Response

{
  "name": "currency",
  "value": ""
}

get_config_value

GET /api/config/:name. Why: Read ONE config key of one branch (any key, including ones get_location_config does not list). Use before save_config to see the current value. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_config_value"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Config key, exact spelling and case (e.g.
loc string optional ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_config_value",
  "name": "Iced Latte"
}'

Example Response

{
  "name": "vat",
  "value": "5"
}

get_business_hours

GET /api/config/business-hours. Why: The branch's BUSINESS DAY as a UTC window — the same opening_hour + time_zone config rule the POS uses for 'today' (get_today_sales, get_ol_info, list_kds_orders, daily reports). Use start_utc / end_utc to bound order or invoice queries for a date range in the branch's local business days. It says nothing about per-weekday open/closed times — that is get_location_days_hours. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_business_hours"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional Optional.
from string optional Optional.
to string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_business_hours"
}'

Example Response

{
  "success": true,
  "data": {
    "start_utc": "2026-09-25 05:00:00",
    "end_utc": "2026-09-26 05:00:00",
    "time_zone": "+00:00",
    "opening_hour": "10:00",
    "closing_hour": "08:00"
  }
}

get_location_days_hours

GET /api/location-days-hours-config. Why: The branch's weekly OPERATING HOURS as set on the dashboard Business Info > Operating Hours tab: one row per weekday with open / close time or 'closed all day'. Use it to answer 'when is branch X open on Sunday' or before upsert_location_days_hours. It is independent from the business-day hours (opening_hour config → get_business_hours), which drive POS 'today' and reports. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_location_days_hours"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional ONE branch id — pass it.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_location_days_hours"
}'

Example Response

[
  {
    "id": 29,
    "customer_id": 437,
    "location_id": 909,
    "day": "monday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 30,
    "customer_id": 437,
    "location_id": 909,
    "day": "tuesday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 31,
    "customer_id": 437,
    "location_id": 909,
    "day": "wednesday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 32,
    "customer_id": 437,
    "location_id": 909,
    "day": "thursday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 33,
    "customer_id": 437,
    "location_id": 909,
    "day": "friday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 34,
    "customer_id": 437,
    "location_id": 909,
    "day": "saturday",
    "open": "12:00",
    "close": "00:00",
    "is_off": 0,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  },
  {
    "id": 35,
    "customer_id": 437,
    "location_id": 909,
    "day": "sunday",
    "open": null,
    "close": null,
    "is_off": 1,
    "created_at": "2025-10-03T05:50:48.000Z",
    "updated_at": "2025-10-03T05:50:48.000Z"
  }
]

get_pos_configs

GET /api/v2/config/get-all-configs. Why: Every config row (name/value) of ONE branch — the source web-pos uses at boot for the flat VAT % (key vat), currency (currency) and time zone (time_zone, else time_zone_code). Read it before building a sync_invoice order: vat → line/order vat and vat_pct, time_zone → seconds_from_gmt. Also holds business hours (opening_hour / closing_hour → get_today_sales, get_ol_info), receipt header keys and exchange_rate. Tax groups are out of scope. For a named subset use get_location_config / get_business_info. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_pos_configs"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_pos_configs"
}'

Example Response

{
  "status": true,
  "message": "Configs retrieved successfully",
  "data": [
    {
      "id": 9905,
      "name": "time_zone",
      "value": "+05:00",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9943,
      "name": "vat",
      "value": "5",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9993,
      "name": "info_name",
      "value": "Example Cafe",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9994,
      "name": "info_address",
      "value": "Shop 1, Example Street",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9995,
      "name": "info_email",
      "value": "info@example-cafe.test",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9996,
      "name": "info_vat_no",
      "value": "100000000000003",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 9997,
      "name": "logo_image",
      "value": "example-logo.png",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 10101,
      "name": "opening_hour",
      "value": "10:00",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 10102,
      "name": "closing_hour",
      "value": "08:00",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 10103,
      "name": "currency",
      "value": "",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 10237,
      "name": "info_phone",
      "value": "+971500000000",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 10977,
      "name": "time_zone_code",
      "value": "Asia/Karachi",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 11174,
      "name": "receipt_footer_text",
      "value": "<ul><li>Optional note</li></ul>",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 11578,
      "name": "exchange_rate",
      "value": "12",
      "customer_id": 437,
      "location_id": 907
    },
    {
      "id": 11583,
      "name": "max_order_time",
      "value": "20",
      "customer_id": 437,
      "location_id": 907
    }
  ]
}

list_selling_floors

GET /api/v2/floors. Why: Floors (dining areas) of the cashier's session branch — what web-pos loads at boot for the floor tabs on the Dine screen and in the "Select table" dialog of a dine-in order. A floor only groups tables: tables point to it with floor_id (list_selling_tables), and the live occupancy per table comes from list_dine_orders. Session branch only (no params); for another branch use list_floors_by_location. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_floors"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_floors"
}'

Example Response

{
  "status": true,
  "message": "Record Found",
  "data": [
    {
      "id": 606,
      "name": "Ground Floor",
      "customer_id": 437,
      "location_id": 907,
      "image": "",
      "width": 0,
      "length": 0,
      "floor_design": "",
      "web_image": ""
    },
    {
      "id": 615,
      "name": "1st Floor",
      "customer_id": 437,
      "location_id": 907,
      "image": "",
      "width": 0,
      "length": 0,
      "floor_design": "false",
      "web_image": ""
    },
    {
      "id": 619,
      "name": "Second Floor",
      "customer_id": 437,
      "location_id": 907,
      "image": "",
      "width": 0,
      "length": 0,
      "floor_design": "",
      "web_image": ""
    }
  ]
}

list_selling_tables

GET /api/v2/tables. Why: Tables of the cashier's session branch — what web-pos loads at boot to resolve table names (tickets, order list, bill header) and each table's floor. The table id is what a dine-in order carries as table_id. Occupancy is NOT here: list_dine_orders returns the same tables with their open tickets (dinerOrderDetail) and is what the Dine screen actually draws; get_table_order gives the latest ticket of one table. Tables have no brand_id. Session branch only (no params); for another branch use list_tables_by_location. Not paginated. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_selling_tables"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_selling_tables"
}'

Example Response

[
  {
    "id": 5935,
    "name": "Table A5",
    "customer_id": 437,
    "location_id": 907,
    "x_pos": null,
    "y_pos": null,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 1,
    "plate_number": null,
    "is_plate": 1,
    "guest_name": null,
    "floor_id": 606,
    "width": 30,
    "height": 30,
    "rotation": 0,
    "is_rounded": 0,
    "x": 72,
    "y": 49,
    "pgo_restaurant_short_name": null,
    "pgo_table_short_id": null,
    "is_active": 1,
    "capacity": 4,
    "current_status": "available",
    "qr_code_link": null,
    "today_activity_percentage": 0,
    "last_activity_at": null,
    "table_number": null,
    "created_at": "2026-02-24T11:13:55.000Z",
    "updated_at": "2026-02-24T11:13:55.000Z"
  },
  {
    "id": 6194,
    "name": "Table 1",
    "customer_id": 437,
    "location_id": 907,
    "x_pos": null,
    "y_pos": null,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 0,
    "plate_number": null,
    "is_plate": 0,
    "guest_name": null,
    "floor_id": 606,
    "width": 30,
    "height": 30,
    "rotation": 0,
    "is_rounded": 0,
    "x": 42,
    "y": 29,
    "pgo_restaurant_short_name": null,
    "pgo_table_short_id": null,
    "is_active": 1,
    "capacity": 4,
    "current_status": "available",
    "qr_code_link": null,
    "today_activity_percentage": 0,
    "last_activity_at": null,
    "table_number": null,
    "created_at": "2026-06-24T08:03:09.000Z",
    "updated_at": "2026-06-24T08:03:09.000Z"
  }
]

list_floors_by_location

GET /api/floors-location/:location_id. Why: Floors of one branch (dashboard route; any branch of the tenant, not only the session branch). Tables link to a floor with floor_id (list_tables_by_floor / list_tables_by_location); live occupancy per table is in list_dine_orders. POS view of the session branch: list_selling_floors. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_floors_by_location"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_floors_by_location",
  "location_id": 11
}'

Example Response

[
  {
    "id": 606,
    "name": "Ground Floor",
    "customer_id": 437,
    "location_id": 907,
    "image": "",
    "width": 0,
    "length": 0,
    "floor_design": "",
    "web_image": ""
  },
  {
    "id": 615,
    "name": "1st Floor",
    "customer_id": 437,
    "location_id": 907,
    "image": "",
    "width": 0,
    "length": 0,
    "floor_design": "false",
    "web_image": ""
  },
  {
    "id": 619,
    "name": "Second Floor",
    "customer_id": 437,
    "location_id": 907,
    "image": "",
    "width": 0,
    "length": 0,
    "floor_design": "",
    "web_image": ""
  }
]

get_floor

GET /api/floors/:id. Why: One floor by id (dashboard route). Scoped to your tenant; any branch. Its tables: list_tables_by_floor with floor_id = id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_floor"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_floor",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Record Found",
  "data": [
    {
      "id": 606,
      "name": "Ground Floor",
      "customer_id": 437,
      "location_id": 907,
      "image": "",
      "width": 0,
      "length": 0,
      "floor_design": "",
      "web_image": ""
    }
  ]
}

list_tables_by_location

GET /api/tables-location/:location_id. Why: Tables of one branch (dashboard route; any branch of the tenant). Same rows as list_selling_tables but for a chosen branch. Each table's floor is floor_id (get_floor); its open tickets are in list_dine_orders (session branch only) / get_table_order. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_tables_by_location"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_tables_by_location",
  "location_id": 11
}'

Example Response

[
  {
    "id": 5935,
    "name": "Table A5",
    "customer_id": 437,
    "location_id": 907,
    "x_pos": null,
    "y_pos": null,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 1,
    "plate_number": null,
    "is_plate": 1,
    "guest_name": null,
    "floor_id": 606,
    "width": 30,
    "height": 30,
    "rotation": 0,
    "is_rounded": 0,
    "x": 72,
    "y": 49,
    "pgo_restaurant_short_name": null,
    "pgo_table_short_id": null,
    "is_active": 1,
    "capacity": 4,
    "current_status": "available",
    "qr_code_link": null,
    "today_activity_percentage": 0,
    "last_activity_at": null,
    "table_number": null,
    "created_at": "2026-02-24T11:13:55.000Z",
    "updated_at": "2026-02-24T11:13:55.000Z"
  },
  {
    "id": 6194,
    "name": "Table 1",
    "customer_id": 437,
    "location_id": 907,
    "x_pos": null,
    "y_pos": null,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 0,
    "plate_number": null,
    "is_plate": 0,
    "guest_name": null,
    "floor_id": 606,
    "width": 30,
    "height": 30,
    "rotation": 0,
    "is_rounded": 0,
    "x": 42,
    "y": 29,
    "pgo_restaurant_short_name": null,
    "pgo_table_short_id": null,
    "is_active": 1,
    "capacity": 4,
    "current_status": "available",
    "qr_code_link": null,
    "today_activity_percentage": 0,
    "last_activity_at": null,
    "table_number": null,
    "created_at": "2026-06-24T08:03:09.000Z",
    "updated_at": "2026-06-24T08:03:09.000Z"
  }
]

list_tables_by_floor

GET /api/tables-floor/:floor_id. Why: Tables on one floor (dashboard route). Scoped to your tenant but NOT to a branch: floor_id 0 returns every table of the tenant across all branches, and the API key allow_locations is not applied to this tool — filter on location_id yourself. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_tables_by_floor"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
floor_id number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_tables_by_floor",
  "floor_id": 11
}'

Example Response

[
  {
    "id": 5935,
    "name": "Table A5",
    "customer_id": 437,
    "location_id": 907,
    "x_pos": null,
    "y_pos": null,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 1,
    "plate_number": null,
    "is_plate": 1,
    "guest_name": null,
    "floor_id": 606,
    "width": 30,
    "height": 30,
    "rotation": 0,
    "is_rounded": 0,
    "x": 72,
    "y": 49,
    "pgo_restaurant_short_name": null,
    "pgo_table_short_id": null,
    "is_active": 1,
    "capacity": 4,
    "current_status": "available",
    "qr_code_link": null,
    "today_activity_percentage": 0,
    "last_activity_at": null,
    "table_number": null,
    "created_at": "2026-02-24T11:13:55.000Z",
    "updated_at": "2026-02-24T11:13:55.000Z"
  }
]

get_table

GET /api/tables/:id. Why: BROKEN on the server — do not rely on it: the handler never binds the id, so the SQL fails and the response is HTTP 200 with an EMPTY body for every id. To read one table use list_selling_tables (session branch) or list_tables_by_location and match id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_table"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_table",
  "id": 11
}'

Example Response

"<empty body: HTTP 200, no JSON>"

list_dine_orders

GET /api/v2/dine-orders-v3. Why: The live dine board of the JWT session branch — exactly what web-pos draws on the Dine screen: EVERY table of the branch (id = table id, floor_id, name, people_size, x, y …) with its open/done tickets in dinerOrderDetail. A table is OCCUPIED when dinerOrderDetail has at least one ticket, FREE when it is null or []. Group rows by floor_id (list_selling_floors). Optional table_id filter (list_selling_tables). The location cannot be changed via query. For one table's latest ticket use get_table_order; for a ticket's lines use get_invoice_items with its uuid. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_dine_orders"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
table_id number optional Optional.
brand_id number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_dine_orders"
}'

Example Response

[
  {
    "floor_id": 606,
    "id": 6194,
    "name": "Table 1",
    "customer_id": 437,
    "location_id": 907,
    "x": 42,
    "y": 29,
    "is_rounded": 0,
    "rotation": 0,
    "height": 30,
    "width": 30,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 0,
    "is_plate": 0,
    "plate_number": null,
    "uuid": [
      "b7b056e0-3c5f-437d-9258-968b6cc34a6e"
    ],
    "dinerOrderDetail": [
      {
        "id": 23,
        "customer_invoice_id": 23,
        "uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
        "status": "open",
        "order_no": 1,
        "table_id": 6194,
        "floor_id": 606,
        "order_type": "Dine In",
        "is_paid": null,
        "payable": 31,
        "payment_method": null,
        "created_at": 1787738588,
        "order_items": [
          {
            "id": 25504110,
            "identifier": "5f1c0c66-2efb-4335-ab40-99ce6ceaa433",
            "name": "Flat White",
            "name_localize": null,
            "actual_price": 11,
            "price": 11,
            "qty": 1,
            "subtotal": 11,
            "line_total": null,
            "line_vat": null,
            "line_total_without_vat": null,
            "invoice_uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
            "product_id": 257506,
            "extras": null,
            "vat": 0,
            "is_updated": 0,
            "updated_qty": 0,
            "is_addon": 0,
            "is_refunded": 0,
            "refund_quantity": 0,
            "refund_reason": null,
            "note": null,
            "addon_linked_with": "5f1c0c66-2efb-4335-ab40-99ce6ceaa433",
            "discount_pct": 0,
            "discount_amount": 0,
            "modifier": [],
            "transferred_to": 0,
            "transferred_to_uuid": null,
            "transferred_to_uuid_list": [
              "3ccd598a-f022-4939-931f-eedd132e79b6",
              "f2ab957f-2cba-4b48-a534-8681b8934a1a",
              "0ea2c766-fd6d-418d-9e04-70aa6a60fa1a"
            ],
            "transferred_quantities": [
              2,
              4,
              1.5
            ],
            "transfer_recipients": [
              12,
              43,
              60
            ],
            "created_at": 1787738588,
            "updated_at": 1787738588,
            "is_prepared": 0,
            "preparation_time": null,
            "is_weightage_item": 0,
            "unit_id": 0,
            "type": "Espresso-Based Drinks",
            "label_id": 50,
            "item_status": "active",
            "linked_uuid": null,
            "original_linked_uuid": null,
            "disposition_type": null,
            "image": null,
            "is_combo": 0,
            "is_deleted": 0,
            "deleted_at": null,
            "deleted_qty": 0,
            "coupon_amount": 0,
            "coupon_id": 0,
            "coupon_code": "",
            "updated_by_user_ids": [],
            "re": null,
            "ri": 0,
            "is_wastage": 0,
            "discount_id": null,
            "discount_name": null,
            "round": null,
            "combo_items": null
          }
        ]
      }
    ]
  },
  {
    "floor_id": 606,
    "id": 6195,
    "name": "Table 2",
    "customer_id": 437,
    "location_id": 907,
    "x": 29,
    "y": 46,
    "is_rounded": 0,
    "rotation": 0,
    "height": 30,
    "width": 30,
    "people_size": 0,
    "attended_by": null,
    "unavailable": 0,
    "is_plate": 0,
    "plate_number": null,
    "uuid": [],
    "dinerOrderDetail": null
  }
]

get_table_order

GET /api/dine-table-order/:table. Why: The latest open (or done) ticket of ONE table at the JWT session branch, with its lines — what to reopen when adding items to an occupied table. Table id comes from list_selling_tables / list_dine_orders. Only the newest ticket is returned; when a table has several open tickets use list_dine_orders (dinerOrderDetail lists them all). Session branch only (a table of another branch returns []). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_table_order"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
table number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_table_order",
  "table": 1
}'

Example Response

[
  {
    "token_number": null,
    "id": 23,
    "items_number": 3,
    "aggregator_order_id": null,
    "is_accepted": 0,
    "exchange_rate": 0,
    "subtotal": 31,
    "vat": 0,
    "payable": 31,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 0,
    "channel_id": 0,
    "change": 0,
    "note": "dev1 transfer-fields e2e dine",
    "uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
    "created_at": "2026-08-26T10:03:08.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "",
    "info_address": "",
    "info_phone": "",
    "info_email": "",
    "status": "open",
    "user": "admin@devnexus.co",
    "user_id": 4734,
    "table_id": 6194,
    "payment_method": null,
    "order_id": null,
    "invoice_id": null,
    "done_at": null,
    "period": null,
    "is_kds_printed": 0,
    "is_invoice_printed": 0,
    "plate_number": null,
    "order_no": 1,
    "is_paid": "0",
    "is_refunded": 0,
    "guest_name": "Transfer E2E Dine",
    "is_updated": 0,
    "card_digits": "",
    "mac_addresses": "",
    "crm_user_id": 0,
    "updated_at": "2026-08-26T10:03:10.000Z",
    "crm_user_name": "",
    "created_by_user_id": 4734,
    "close_by_user_id": null,
    "modified_by_user_ids": [],
    "discount_id": null,
    "order_type": "Dine In",
    "is_vat_on_discount_exclude": 0,
    "dining_guests": 2,
    "channel_name": "",
    "order_items": [
      {
        "id": 25504110,
        "identifier": "5f1c0c66-2efb-4335-ab40-99ce6ceaa433",
        "name": "Flat White",
        "name_localize": null,
        "actual_price": 11,
        "base_price": null,
        "price": 11,
        "is_weightage_item": 0,
        "unit_id": 0,
        "qty": 1,
        "subtotal": 11,
        "line_total": null,
        "line_vat": null,
        "line_total_without_vat": null,
        "invoice_uuid": "b7b056e0-3c5f-437d-9258-968b6cc34a6e",
        "product_id": 257506,
        "extras": null,
        "vat": 0,
        "is_updated": 0,
        "updated_qty": 0,
        "is_addon": 0,
        "is_refunded": 0,
        "refund_quantity": 0,
        "refund_reason": null,
        "note": null,
        "addon_linked_with": "5f1c0c66-2efb-4335-ab40-99ce6ceaa433",
        "discount_pct": 0,
        "discount_amount": 0,
        "modifier": [],
        "transferred_to": 0,
        "transferred_to_uuid": null,
        "transferred_to_uuid_list": [
          "3ccd598a-f022-4939-931f-eedd132e79b6",
          "f2ab957f-2cba-4b48-a534-8681b8934a1a",
          "0ea2c766-fd6d-418d-9e04-70aa6a60fa1a"
        ],
        "transferred_quantities": [
          2,
          4,
          1.5
        ],
        "transfer_recipients": [
          12,
          43,
          60
        ],
        "created_at": 1787738588,
        "updated_at": 1787738588,
        "is_prepared": 0,
        "preparation_time": null,
        "type": "Espresso-Based Drinks",
        "label_id": 50,
        "is_combo": 0,
        "is_deleted": 0,
        "deleted_at": null,
        "deleted_qty": 0,
        "coupon_amount": 0,
        "coupon_id": 0,
        "coupon_code": "",
        "updated_by_user_ids": [],
        "re": null,
        "ri": 0,
        "is_wastage": 0,
        "discount_id": null,
        "discount_name": null,
        "round": null,
        "combo_items": null
      }
    ],
    "items_discount_amount": 0
  }
]

list_orders_history

GET /api/v2/orders-history. Why: The order list of the session branch — exactly what the web-pos Invoices screen loads: every order (open, completed, cancelled) newest first, 30 per page, with lines, payments, customer and a summary. Use it to find an order (by status, invoice number, customer, phone, plate, shift, brand or business day) and read it in full; then act on its uuid with cancel_invoice, refund_invoice, update_invoice_payment_methods or get_invoice_activities. Paginated with current_page / page_limit. order_type cannot be filtered on the server — filter the rows yourself by list_selling_order_types title. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_orders_history"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id string optional Optional.
status string optional Optional.
date string optional Optional.
current_page number optional Optional.
page_limit number optional Optional.
invoice_no string optional Optional.
crm_user_id number optional Optional.
crm_user_name string optional Optional.
mobile_number string optional Optional.
phone_number string optional Optional.
guest_name string optional Optional.
plate_number string optional Optional.
brand_id string optional Optional.
shift_id number optional Optional.
user_id number optional Optional.
is_order_rebump enum: 1 optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_orders_history"
}'

Example Response

{
  "status": true,
  "message": "Orders history",
  "data": {
    "current_page": "1",
    "records_per_page": 10,
    "total_records": 5,
    "force_location_ids": [
      907
    ],
    "summary": {
      "total_orders": 5,
      "total_revenue": 461,
      "completed_orders": 0,
      "completed_revenue": 0,
      "open_orders": 0,
      "open_revenue": 0,
      "void_orders": 5,
      "void_revenue": 461
    },
    "orders": [
      {
        "id": "3609c849e2e4",
        "oid": 19,
        "customer_invoice_id": 19,
        "brand_id": null,
        "exchange_rate": null,
        "token_number": null,
        "is_wastage": 0,
        "paid_in_usd": null,
        "is_accepted": null,
        "external_order_ready": 0,
        "is_discount_on_total": null,
        "items_number": 2,
        "aggregator_order_id": null,
        "subtotal": 106,
        "vat": 0,
        "payable": 106,
        "discountamount": 0,
        "items_discount_amount": 1.1,
        "is_ticket_available": 1,
        "is_wallet_used": 0,
        "discountpct": 0,
        "paid": 106,
        "change": 0,
        "note": "qa cancel **canceled by: Nexus Cafe",
        "uuid": "3a00fb3a-0c78-444a-abef-3609c849e2e4",
        "deliverect_id": null,
        "created_at": 1786948030,
        "updated_at": 1786948031,
        "customer_id": 437,
        "location_id": 907,
        "guest_name": "QA rd 0",
        "info_name": "QA",
        "info_address": "",
        "info_phone": "",
        "info_email": "cashier@example.com",
        "status": "cancelled",
        "by": null,
        "external_order_id": null,
        "user": "cashier@example.com",
        "table_id": 0,
        "order_id": null,
        "payment_method": [
          {
            "cash": 106
          }
        ],
        "burn_points": 0,
        "earn_points": 0,
        "burn_points_amount": 0,
        "available_points": 0,
        "available_points_amount": 0,
        "charges_fee": 0,
        "delivery_charges": 0,
        "phone_number": "",
        "loyalty_mobile_number": "",
        "loyalty_mobile_code": "",
        "crm_delivery_area": null,
        "invoice_id": null,
        "done_at": null,
        "period": null,
        "is_kds_printed": 0,
        "is_invoice_printed": 0,
        "plate_number": null,
        "order_no": 4,
        "created_by_user_id": 4734,
        "close_by_user_id": 4734,
        "modified_by_user_ids": [],
        "is_vat_on_discount_exclude": 0,
        "order_type": "takeaway",
        "channel_id": 0,
        "tip_amount": 0,
        "dining_guests": null,
        "channel_name": "",
        "is_paid": 1,
        "card_digits": "",
        "crm_user_id": 0,
        "crm_user_name": "",
        "discount_reason": null,
        "row_discount": 0,
        "paid_at": null,
        "coupon_amount": 0,
        "coupon_id": 0,
        "coupon_code": null,
        "printed_invoice_receipts": 0,
        "printed_kds_receipts": 0,
        "app_version": "qa-rd",
        "app_os_version": "qa",
        "build_number": "1",
        "os_type": "qa",
        "round": null,
        "rider_pickup_at": null,
        "rider_arrived_at": null,
        "shift_id": null,
        "cashier_user_id": null,
        "cashier_name": null,
        "shift_clock_in_at": null,
        "is_order_rebump": 1,
        "order_type_icon_key": "boxicons:takeaway",
        "notes": [],
        "grand_total": 106,
        "payment_methods": [
          {
            "cash": {
              "amount": 106,
              "tip_amount": null
            }
          }
        ],
        "payments": [
          {
            "payment_method": "cash",
            "amount": 106,
            "tip_amount": 0
          }
        ],
        "items": [
          {
            "id": 25504102,
            "identifier": "7c53c3c5-569d-47ae-84b4-7f2e28cbcee6",
            "name": "QA rd 0 Flat White",
            "name_localize": null,
            "base_price": null,
            "actual_price": 11,
            "preparation_time": null,
            "price": 11,
            "qty": 1,
            "is_weightage_item": 0,
            "unit_id": 0,
            "subtotal": 11,
            "line_total": null,
            "line_vat": null,
            "line_total_without_vat": null,
            "invoice_uuid": "3a00fb3a-0c78-444a-abef-3609c849e2e4",
            "product_id": 257506,
            "extras": [
              {
                "name": "QA Extra",
                "actual_price": 5,
                "price": 5,
                "quantity": 1,
                "discount_pct": 0
              }
            ],
            "vat": 0,
            "is_updated": 0,
            "updated_qty": 0,
            "is_addon": 0,
            "is_refunded": 0,
            "refund_quantity": 0,
            "refund_reason": null,
            "note": null,
            "addon_linked_with": "7c53c3c5-569d-47ae-84b4-7f2e28cbcee6",
            "discount_pct": 10,
            "discount_amount": 1.1,
            "modifier": [
              {
                "name": "QA Modifier",
                "actual_price": 3,
                "price": 3,
                "quantity": 1,
                "discount_pct": 0
              }
            ],
            "transferred_to": 0,
            "transferred_to_uuid": null,
            "transferred_quantity": 0,
            "transferred_to_uuid_list": [],
            "transferred_quantities": [],
            "transfer_recipients": [],
            "created_at": 1786948030,
            "updated_at": 1786948030,
            "is_prepared": 0,
            "type": "Espresso-Based Drinks",
            "label_id": 50,
            "item_status": "active",
            "linked_uuid": null,
            "original_linked_uuid": null,
            "disposition_type": null,
            "image": null,
            "is_combo": 0,
            "is_deleted": null,
            "deleted_at": null,
            "deleted_qty": null,
            "coupon_amount": 0,
            "coupon_id": 0,
            "coupon_code": "",
            "updated_by_user_ids": [],
            "re": null,
            "ri": 0,
            "is_wastage": 0,
            "discount_id": null,
            "discount_name": null,
            "round": null,
            "combo_items": null
          },
          {
            "id": 25504103,
            "identifier": "cba24a8b-6e50-4798-a047-05834e9d9d6a",
            "name": "QA addon Classic Sandwich",
            "name_localize": null,
            "base_price": null,
            "actual_price": 100,
            "preparation_time": null,
            "price": 95,
            "qty": 1,
            "is_weightage_item": 0,
            "unit_id": null,
            "subtotal": 95,
            "line_total": null,
            "line_vat": null,
            "line_total_without_vat": null,
            "invoice_uuid": "3a00fb3a-0c78-444a-abef-3609c849e2e4",
            "product_id": 257559,
            "extras": null,
            "vat": 0,
            "is_updated": 0,
            "updated_qty": 0,
            "is_addon": 1,
            "is_refunded": 0,
            "refund_quantity": 0,
            "refund_reason": null,
            "note": null,
            "addon_linked_with": "7c53c3c5-569d-47ae-84b4-7f2e28cbcee6",
            "discount_pct": 0,
            "discount_amount": 0,
            "modifier": [],
            "transferred_to": 0,
            "transferred_to_uuid": null,
            "transferred_quantity": 0,
            "transferred_to_uuid_list": [],
            "transferred_quantities": [],
            "transfer_recipients": [],
            "created_at": 1786948030,
            "updated_at": 1786948030,
            "is_prepared": 0,
            "type": "Alternate Options,Brewed Coffee,Espresso-Based Drinks,Iced & Cold Coffees,Specialty & Flavored Coffees",
            "label_id": null,
            "item_status": "active",
            "linked_uuid": null,
            "original_linked_uuid": null,
            "disposition_type": null,
            "image": "https://dev1.syspos.ae/api/uploads/",
            "is_combo": 0,
            "is_deleted": null,
            "deleted_at": null,
            "deleted_qty": null,
            "coupon_amount": 0,
            "coupon_id": 0,
            "coupon_code": "",
            "updated_by_user_ids": [],
            "re": null,
            "ri": 0,
            "is_wastage": 0,
            "discount_id": null,
            "discount_name": null,
            "round": null,
            "combo_items": null
          }
        ],
        "order_type_icon": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fboxicons%2Ftakeaway.svg%3Fwidth%3D128%26height%3D128&output=png"
      }
    ]
  }
}

get_invoice

GET /api/invoice-v2/:uuid. Why: One order header by uuid: status, totals, payments, customer_invoice_id and the branch's receipt header (business_info). web-pos never calls it (it backs the public receipt page). Use it when you only hold a uuid; for the full order (lines, customer, is_paid, order_type) call list_orders_history with invoice_no = customer_invoice_id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_invoice"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_invoice",
  "uuid": "example"
}'

Example Response

[
  {
    "logo_image": "1762167059218-Nexus_logo_2015.svg.png",
    "id": 8085851,
    "items_number": 2,
    "subtotal": 106,
    "vat": 0,
    "payable": 106,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 106,
    "change": 0,
    "note": "qa cancel **canceled by: Nexus Cafe",
    "uuid": "3a00fb3a-0c78-444a-abef-3609c849e2e4",
    "created_at": "2026-08-17T06:27:10.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "QA",
    "info_address": "",
    "info_phone": "",
    "info_email": "cashier@example.com",
    "status": "cancelled",
    "user": "cashier@example.com",
    "table_id": 0,
    "plate_number": null,
    "customer_invoice_id": 19,
    "business_info": {
      "time_zone": "+05:00",
      "vat": "5",
      "info_name": "Nexus",
      "info_address": "Nexus located at Dev",
      "info_email": "info@example.com",
      "info_vat_no": "1423",
      "logo_image": "1762167059218-Nexus_logo_2015.svg.png",
      "opening_hour": "10:00",
      "closing_hour": "08:00",
      "currency": "",
      "info_phone": "500000000",
      "receipt_footer_text": "<ul><li>Optional note</li></ul>",
      "exchange_rate": "12",
      "max_order_time": "20"
    },
    "payments": [
      {
        "payment_method": "cash",
        "amount": 106
      }
    ]
  }
]

get_invoice_items

GET /api/invoice-v2/:uuid/items. Why: Raw database lines of one order by uuid (every invoice_items column). web-pos never calls it. Prefer list_orders_history items[] (parsed, correct extras, nested combos); use this only for database-only columns such as refund_pm_type, refund_amount, item_price_after_discount or cost snapshots. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_invoice_items"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_invoice_items",
  "uuid": "example"
}'

Example Response

[
  {
    "id": 25504102,
    "name": "QA rd 0 Flat White",
    "price": 11,
    "qty": 1,
    "subtotal": 11,
    "invoice_uuid": "3a00fb3a-0c78-444a-abef-3609c849e2e4",
    "customer_id": 437,
    "location_id": 907,
    "product_id": 257506,
    "extras": [],
    "vat": 0,
    "vat_pct": null,
    "vat_name": null,
    "is_addon": 0,
    "note": null,
    "is_refunded": 0,
    "refund_quantity": 0,
    "refund_reason": null,
    "is_updated": 0,
    "updated_qty": 0,
    "addon_linked_with": [
      {
        "id": 25504103,
        "name": "QA addon Classic Sandwich",
        "actual_price": 100,
        "vat": 0,
        "price": 95,
        "subtotal": 95,
        "qty": 1
      }
    ],
    "discount_pct": 10,
    "refund_pm_type": "cash",
    "modifier": "[{\"name\":\"QA Modifier\",\"actual_price\":3,\"price\":3,\"quantity\":1,\"discount_pct\":0}]",
    "transferred_to": 0,
    "transferred_to_uuid": null,
    "created_at": "2026-08-17T06:27:10.000Z",
    "updated_at": "2026-08-17T06:27:10.000Z",
    "combo_item_id": null,
    "is_combo": 0,
    "product_type": null,
    "is_internal": null,
    "sort_order": null,
    "plu": null,
    "otter_account_id": null,
    "delivery_cost": null,
    "tip": null,
    "by": null,
    "otter_id": null,
    "transferred_quantity": 0,
    "actual_price": 11,
    "is_prepared": 0,
    "discount_amount": 1.1,
    "base_price": null,
    "brand_id": 0,
    "is_deleted": null,
    "deleted_qty": null,
    "deleted_at": null,
    "label_id": 50,
    "preparation_time": null,
    "linked_uuid": null,
    "item_type": null,
    "item_price_after_discount": 9.9,
    "line_vat": null,
    "line_total": null,
    "line_total_without_vat": null,
    "identifier": "7c53c3c5-569d-47ae-84b4-7f2e28cbcee6",
    "coupon_amount": 0,
    "coupon_id": 0,
    "coupon_code": "",
    "product_recipe_cost": 0,
    "total_cost": 0,
    "updated_by_user_ids": "",
    "re": null,
    "ri": 0,
    "is_wastage": 0,
    "discount_id": null,
    "discount_name": null,
    "round": null,
    "name_localize": null,
    "original_linked_uuid": null,
    "item_status": "active",
    "disposition_type": null,
    "disposition_reason": null,
    "refund_amount": null,
    "modifiers_cost_amount": 0,
    "cost_snapshot_at": "2026-08-17T06:27:10.000Z",
    "transferred_to_uuid_list": null,
    "transferred_quantities": null,
    "transfer_recipients": null
  }
]

list_materials

GET /api/materials. Why: Material (inventory SKU) catalog of the master inventory: raw materials (material_type mat) and recipe-output materials (rm) with branch stock, branch unit cost (avg_price), storage/ingredient units, category, suppliers and brands — the dashboard Inventory → Materials list. Use it to find material ids for get_material / update_material / delete_material, recipe ingredients (list_recipes / get_recipe), supplier catalogs and draft purchase orders (list_supplier_materials, create_draft_purchase_order), wastage, counts, transfers and work orders. Stock here is the branch on-hand qty; the movements behind it are in get_material_movements / list_inventory_movements, the ledger sum in get_material_stock. Filter by category with `cat` (id from list_material_categories), branch with `loc` (list_locations). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_materials"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations).
k string optional Case-insensitive substring of the material name (LIKE %k%; % and _ act as wildcards).
cat string optional Category filter.
material_type enum: mat, rm optional "mat" = raw/purchased materials, "rm" = recipe-output materials.
mode enum: stockable, phantom optional "stockable" or "phantom" (exact materials.mode).
is_prepared enum: 0, 1 optional "1" = produced in-house (recipe/preparation outputs), "0" = purchased (exact match).
is_active enum: 0, 1 optional "1" → active materials (default when omitted), "0" → deactivated ones (delete_material).
brand_id number optional Brand id (list_brands): only materials explicitly limited to that brand (material_brands).
_page number optional 1-based page.
_limit number optional Page size.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_materials"
}'

Example Response

{
  "data": [
    {
      "id": 119158,
      "name": "Cherry Blosom",
      "description": "Cherry",
      "price": 0,
      "stock": -12762188.785166,
      "unit": "Kilogram",
      "customer_id": 437,
      "location_id": [
        907,
        908,
        909,
        910,
        922
      ],
      "low_stock": 0,
      "physical_stock": null,
      "is_active": 1,
      "barcode": "",
      "ingredientunit": "Gram",
      "storageunit": "Kilogram",
      "is_prepared": 0,
      "conversionrate": "1000",
      "minimum_level": "0",
      "maximum_level": "0",
      "par_level": "0",
      "costingmethod": "average",
      "actual_stock": 0,
      "type": "raw",
      "avg_price": 1.6474,
      "materials": null,
      "sale_price": 0,
      "selling_price": 0,
      "created_at": "2026-02-09T07:05:18.000Z",
      "updated_at": "2026-08-05T07:03:56.000Z",
      "wholesale_price": 0,
      "tax_id": null,
      "material_type": "mat",
      "linked_recipe": null,
      "linked_sku": null,
      "mode": "stockable",
      "unit_id": 2,
      "ingredient_unit_id": 1,
      "custom_unit_id": null,
      "category_id": 2,
      "yield_percentage": 100,
      "sku_no": null,
      "tax_name": null,
      "tax_value": null,
      "suppliers": [
        970
      ],
      "suppliers_prices": [
        {
          "supplier_id": 970,
          "purchase_price": 1.1092
        }
      ],
      "brand_ids": [
        412,
        473
      ]
    }
  ],
  "pagination": {
    "total": 1,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}

get_material

GET /api/materials/:id. Why: One material with its stock, reservations and unit cost at one branch (`loc`), units, costing method, suppliers with purchase prices, brands and branch list — the dashboard material edit/detail page. Load it before update_material (the update overwrites every field, so you pass this record back) and to read reserved/available stock before a transfer, wastage or count. Per-branch stock/cost: call once per branch (loc). Recipe outputs (material_type rm) link to their recipe via linked_recipe (get_recipe). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_material"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material id (list_materials).
loc number optional ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_material",
  "id": 11
}'

Example Response

[
  {
    "id": 119158,
    "name": "Cherry Blosom",
    "description": "Cherry",
    "price": 0,
    "selling_price": 0,
    "wholesale_price": 0,
    "stock": -12762188.785166,
    "unit": "Kilogram",
    "unit_id": 2,
    "custom_unit_id": null,
    "ingredient_unit_id": 1,
    "avg_price": 1.6474,
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909,
      910,
      922
    ],
    "low_stock": 0,
    "physical_stock": null,
    "actual_stock": 0,
    "is_active": 1,
    "barcode": "",
    "sku_no": "",
    "conversionrate": "1000",
    "ingredientunit": "Gram",
    "storageunit": "Kilogram",
    "costingmethod": "average",
    "minimum_level": "0",
    "maximum_level": "0",
    "par_level": "0",
    "type": "raw",
    "category_id": 2,
    "material_type": "mat",
    "mode": "stockable",
    "linked_recipe": null,
    "tax_id": null,
    "tax_name": null,
    "tax_value": null,
    "suppliers": [
      970
    ],
    "suppliers_prices": [
      {
        "supplier_id": 970,
        "purchase_price": 1.1092
      }
    ],
    "reserved_stock": 0,
    "committed_stock": 0,
    "available_for_sale": -12762188.785166,
    "brands": [
      {
        "id": 412,
        "name": "Nexus Cafe"
      },
      {
        "id": 473,
        "name": "Test "
      }
    ]
  }
]

get_material_stock

GET /api/inventory/:material_id/stock. Why: Ledger check: the sum of every inventory movement qty of one material at one branch (storage unit). Use it to cross-check the branch stock shown by get_material / list_materials (materials_locations) — they can drift — or when only the movement ledger counts. For history use get_material_movements; for all materials list_inventory / list_materials. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_material_stock"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
material_id number required Material id (list_materials).
location number required ONE branch id (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_material_stock",
  "material_id": 11,
  "location": 1
}'

Example Response

[
  {
    "stock": 67.1985
  }
]

list_inventory

GET /api/inventory. Why: Legacy stock board of one branch: active materials linked to the branch with ledger stock, tenant average cost, unit, low-stock level and stock value. CURRENTLY RETURNS 500 FOR MASTER TENANTS (server count query bug) — for branch stock use list_inventory_movements (current_stock, avg_cost, total_value per material × branch), get_material_stock (one material) or list_materials. No page of the dashboard menu uses it any more. Master tenants: always HTTP 500 "Unknown column 'm.is_active'" (backend bug) — use list_inventory_movements or get_material_stock instead. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_inventory"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string required ONE branch id (list_locations).
k string optional Case-insensitive substring match on the material name (LIKE %k%; `%` and `_` act as wildcards).
cat string optional PRODUCT category name (exact, trimmed): keeps materials used in the recipe/BOM (product_materials) of a non-archived product whose type equals it.
brand_id string optional Brand id (list_brands): keeps materials used by non-archived products whose category is in a group linked to that brand.
_page number optional 1-based page, used only with _limit.
_limit number optional Page size.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_inventory",
  "loc": "example"
}'

Example Response

{
  "data": [
    {
      "id": 120802,
      "name": "Gotcha Recipe",
      "description": null,
      "stock": 9689,
      "physical_stock": 0,
      "price": 2.4616,
      "unit": "Kilogram",
      "low_stock": 0,
      "diff": 9689,
      "total": 23850.44
    }
  ],
  "total": 1
}

get_materials_stats

GET /api/materials/stats. Why: The four header numbers of the dashboard Materials page for a branch scope: total materials, active, recipe materials (rm) and low-stock count (branch stock below low_stock). Quick context before list_materials; for the items themselves use list_materials (and get_material for one). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_materials_stats"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_materials_stats"
}'

Example Response

{
  "total": 2250,
  "active": 2247,
  "recipes": 902,
  "low_stock": 0
}

list_material_categories

GET /api/inventory/categories. Why: Material category tree of the tenant (top-level categories and their subcategories, max 2 levels; separate from product categories). Use the ids as category_id on create_material / update_material and as `cat` to filter list_materials; pick parents for create_material_category. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_material_categories"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
format enum: flat, tree optional "flat" → flat array (each row has parent_id).
k string optional Case-insensitive substring of the category name (LIKE %k%).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_material_categories"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 2,
      "name": "Farm",
      "description": "",
      "parent_id": null,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    },
    {
      "id": 4,
      "name": "Frozen",
      "description": "",
      "parent_id": null,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    },
    {
      "id": 6,
      "name": "Meat",
      "description": "",
      "parent_id": null,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    },
    {
      "id": 3,
      "name": "Dairy",
      "description": "",
      "parent_id": 2,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    },
    {
      "id": 1,
      "name": "Meat",
      "description": "",
      "parent_id": 2,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    },
    {
      "id": 5,
      "name": "Meat",
      "description": "",
      "parent_id": 4,
      "customer_id": 437,
      "color": "",
      "image": "",
      "is_deleted": 0
    }
  ]
}

get_material_used_in_recipes

GET /api/materials/:id/used-in-recipes. Why: Where-used for one material: every recipe that has it as an ingredient, with the quantity and unit per recipe batch, and the recipe's own output material. Run it before changing units/conversion/yield/cost, deactivating (delete_material) or advising substitutions; then get_recipe for the full recipe and list_products / get_product to see which products and modifier items sell through those recipes (sync_invoice deducts along them). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_material_used_in_recipes"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material id (list_materials).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_material_used_in_recipes",
  "id": 11
}'

Example Response

{
  "message": "Used-in recipes fetched successfully",
  "data": [
    {
      "recipe_id": 17224,
      "recipe_name": "Gotcha Recipe",
      "yield_qty": 0.782,
      "recipe_unit": "Kilogram",
      "recipe_mode": "stockable",
      "qty": 1000,
      "qty_unit": "Gram",
      "wastage_pct": 0,
      "item_type": "mat",
      "material_id": 120802,
      "material_name": "Gotcha Recipe",
      "material_sku": "mat-437-120802",
      "material_mode": "stockable",
      "material_type": "rm"
    },
    {
      "recipe_id": 16779,
      "recipe_name": "Test",
      "yield_qty": 0.8,
      "recipe_unit": "Kilogram",
      "recipe_mode": "stockable",
      "qty": 500,
      "qty_unit": "Gram",
      "wastage_pct": 0,
      "item_type": "mat",
      "material_id": 119197,
      "material_name": "Test",
      "material_sku": "mat-437-119197",
      "material_mode": "stockable",
      "material_type": "rm"
    }
  ]
}

list_inventory_movements

GET /api/inventory/movements-v3. Why: Stock movement summary (dashboard Inventory → Movement page): one row per material × branch that moved in the date range, with opening stock, totals per movement type (purchased in, transferred in/out, produced, sold, consumed, wasted, adjusted …), closing stock, live current stock, branch average cost and stock value, plus variance between ledger closing and live stock. Use it to answer "how much X came in / went out / is on hand at branch Y" and for stock valuation. Drill into single transactions with get_material_movements (one material) or get_inventory_movements_ledger (all materials). Read only — it never changes stock. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_inventory_movements"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations).
material_name string optional Case-insensitive substring match on the material name (LIKE %…%; `%`/`_` are wildcards).
start_date string optional From date YYYY-MM-DD, inclusive (movements are stored per calendar day).
end_date string optional To date YYYY-MM-DD, inclusive.
_page number optional 1-based page.
_limit number optional Rows (material × branch pairs) per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_inventory_movements"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "material_id": 120802,
      "material_name": "Gotcha Recipe",
      "material_unit": "Kilogram",
      "location_id": 907,
      "location_name": "Default location",
      "opening_stock": -111,
      "stock_in": 0,
      "stock_out": 0,
      "transferred_in": 0,
      "transferred_out": 200,
      "transfer_return": 0,
      "consumed": 0,
      "produced": 10000,
      "waste": 0,
      "sold": 0,
      "purchase_return": 0,
      "adjustment_type_in": 0,
      "adjustment_type_out": 0,
      "closing_stock": 9689,
      "current_stock": 9700,
      "avg_cost": 2.4616,
      "total_value": 23877.52,
      "expected_closing_stock": "9689.000",
      "variance": "-11.000",
      "variance_percentage": -0.11
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 3,
    "total": 3,
    "totalPages": 1
  }
}

get_inventory_movements_ledger

GET /api/inventory/movements-ledger. Why: Transaction ledger across materials (dashboard Reports → Movement Report): every stock movement row — sale deductions, purchase receipts, transfers, wastage, count adjustments, work-order consumption/production, returns — with signed quantity, unit cost, value, reference document, user and a running balance per material × branch. Use it to audit what changed stock, by whom and why (filter by branch, materials, movement types, dates). For per-period totals per material use list_inventory_movements; for one material's history get_material_movements. Read only. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_inventory_movements_ledger"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations).
material_ids string optional Material id or comma-separated ids (list_materials).
mov_types string optional Comma-separated movement types (exact): in, out, sold, refund, wastage, waste, transferred_in, transferred_out, transfer_return, consumed, produced, purchase_r…
k string optional Case-insensitive substring match on the material name (LIKE %k%; `%`/`_` are wildcards).
start_date string optional From date YYYY-MM-DD, inclusive (created_at ≥ start_date 00:00).
end_date string optional To date YYYY-MM-DD, inclusive (created_at < end_date + 1 day).
group_by enum: none, location, material, movementType, day, week optional Only changes the sort: location → by branch name, material → by material name, movementType → by type, then newest first.
_page number optional 1-based page.
_limit number optional Rows per page, clamped to 1–1000.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_inventory_movements_ledger"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 12472803,
      "timestamp": "2026-08-12T10:48:20.000Z",
      "material_id": 120802,
      "material_name": "Gotcha Recipe",
      "material_unit": "Kilogram",
      "location_id": 907,
      "location_name": "Default location",
      "movement_type": "transferred_out",
      "quantity_delta": -200,
      "unit_cost": "18.1956",
      "total_value": -3639.12,
      "reference_id": "Stock transfer fe1ecd13-f615-4326-b6a1-482b5353a3c6 - Approved quantity",
      "expiry_date": null,
      "user_id": 4734,
      "recorded_by": "Nexus Cafe",
      "running_balance": 9689
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 3290,
    "totalPages": 165
  }
}

get_material_movements

GET /api/inventory/:id/movs. Why: Stock history of ONE material at a branch (dashboard material page History tab and the Movement page drawer): every ledger movement newest first with signed qty, unit cost, reference document, user and brand, plus the material's current stock at that branch on the first row. Use it to explain why a material's stock is what it is. Material id from list_materials; for all materials use get_inventory_movements_ledger, for period totals list_inventory_movements. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_material_movements"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material id (list_materials / get_material).
location string required Branch id (list_locations).
start_date string optional From date YYYY-MM-DD, inclusive.
end_date string optional To date YYYY-MM-DD, inclusive (created_at < end_date + 1 day).
_page number optional 1-based page.
_limit number optional Rows per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_material_movements",
  "id": 11,
  "location": "example"
}'

Example Response

{
  "data": [
    {
      "id": 12472802,
      "mov_type": "transferred_out",
      "material_id": 119196,
      "qty": -200,
      "price": "11.0136",
      "reference": "Stock transfer fe1ecd13-f615-4326-b6a1-482b5353a3c6 - Approved quantity",
      "created_at": "2026-08-12 10:48:20",
      "expiry_date": null,
      "user_id": 4734,
      "brand_id": null,
      "location_id": 907,
      "location_name": "Default location",
      "brand_name": null,
      "username": "Nexus Cafe",
      "stock": -21387897.14439
    },
    {
      "id": 12472793,
      "mov_type": "in",
      "material_id": 119196,
      "qty": 1000.921387,
      "price": "0.1256",
      "reference": "Supplier Order d39ca7ba-1468-4f0b-855e-94d042731c3f",
      "created_at": "2026-08-04 16:49:29",
      "expiry_date": null,
      "user_id": 4734,
      "brand_id": null,
      "location_id": 907,
      "location_name": "Default location",
      "brand_name": null,
      "username": "Nexus Cafe"
    }
  ],
  "total": 41
}

list_suppliers

GET /api/suppliers. Why: Supplier (vendor) directory of the tenant with per-supplier counters: how many active materials are linked to it (items_in_catalog), how many purchase orders it has (total_orders) and how many are still pending delivery. Use it to find a supplier id before get_supplier / update_supplier / delete_supplier, before reading its price list (list_supplier_materials sup=<id>) or its purchase orders (list_supplier_invoices sid=<id>). Supplier ↔ material links and purchase prices live in list_supplier_materials; material master data in list_materials / get_material. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_suppliers"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated ids (list_locations).
k string optional Case-insensitive substring match on supplier name (LIKE %k%; `%` and `_` act as wildcards).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_suppliers"
}'

Example Response

[
  {
    "id": 825,
    "name": "Grocery Supplier",
    "description": "Grocery items",
    "customer_id": 437,
    "location_id": 907,
    "address": "Warehouse 4, Al Quoz, Dubai",
    "logo": "",
    "tax_pct": 5,
    "payments": "{\"is_cash\":true,\"is_credit\":false,\"is_cheque\":false}",
    "trade_license": "",
    "contact_name": "Sample Contact",
    "contact_email": "orders@supplier.example",
    "contact_phone": "+971500000001",
    "trn_certificate": "",
    "can_recieve_email": 1,
    "items_in_catalog": 5,
    "total_orders": 7,
    "pending_deliveries": 1
  }
]

list_supplier_materials

GET /api/supplier-materials. Why: A supplier's price list: the materials linked to a supplier at a branch with the supplier's last purchase price (purchase_price) and the material's average cost. Use it to pick material ids and unit prices for draft PO lines (create_draft_purchase_order / update_draft_purchase_order_lines) — the dashboard PO pickers show exactly this list. Supplier ids from list_suppliers, branch ids from list_locations; material details and units via get_material, stock via list_inventory. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_supplier_materials"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
sup number required Supplier id (list_suppliers).
loc string optional Branch id or csv (list_locations).
k string optional Case-insensitive substring match on material name (LIKE %k%; `%`/`_` are wildcards).
filter enum: linked, unlinked, all optional "linked" → only materials linked to the supplier; "unlinked" → only materials NOT linked; "all" (or any other value) → both.
mode enum: stockable, phantom, all optional Material mode filter: "stockable" (default — what POs use), "phantom", or "all" for no filter.
exclude_prepared enum: 0, false, 1, true optional Prepared / stockable-recipe materials are excluded by default (they are made in-house, not purchased).
include_recipes enum: 0, 1, true, false optional "1"/"true" → include prepared / stockable-recipe materials (same as exclude_prepared=0).
_page number optional 1-based page.
_limit number optional Page size.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_supplier_materials",
  "sup": 1
}'

Example Response

[
  {
    "id": 112157,
    "name": "\t SRC-BT-86CBPR-SG3 BB86-BB92 INTERLOCK SHIMANO CERAMIC COATED BLACK",
    "unit": "Pieces",
    "type": "CEMA BEARINGS",
    "material_type": "mat",
    "mode": "stockable",
    "avg_price": 1.2343,
    "sku": "mat-437-112157",
    "supplier_id": 825,
    "material_id": 112157,
    "purchase_price": 2,
    "brand_ids": []
  },
  {
    "id": 112295,
    "name": " Atx 27.5 In Vibrant Blue",
    "unit": "Pieces",
    "type": "SUN AND SAND SPORTS LLC",
    "material_type": "mat",
    "mode": "stockable",
    "avg_price": 3,
    "sku": "mat-437-112295",
    "supplier_id": 825,
    "material_id": 112295,
    "purchase_price": 3,
    "brand_ids": []
  }
]

list_supplier_invoices

GET /api/supplier-invoices. Why: Purchase-order board of the tenant (the dashboard "Purchase Orders" list): confirmed POs, drafts, partially received and received ones, with supplier snapshot, totals, PO status and supplier response. Use it to find a PO/draft uuid for get_purchase_order and the draft tools, to see a supplier's orders (sid from list_suppliers) or what is still pending delivery. Read-only: approving, sending, receiving and cancelling POs happen only in the dashboard. Stock arrives only when the dashboard receives a PO (then visible in list_inventory / list_inventory_movements). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_supplier_invoices"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations): exact match on location_id.
st enum: all, draft, awaiting, pending_approval, pending, partially_received, delivered, canceled, cancelled, returned optional Status filter: all | draft | awaiting | pending_approval | pending | partially_received | delivered | canceled | cancelled | returned (POs with at least one su…
sid number optional Supplier id (list_suppliers): only POs of that supplier (table_id).
from string optional Start date YYYY-MM-DD on created_at in the tenant time zone (inclusive).
to string optional End date YYYY-MM-DD (inclusive).
k string optional Partial match on PO number (oid), supplier invoice number, system supplier invoice id or your_order_ref.
is_archive enum: 0, 1, all optional "0" (default) → active POs only; "1" → archived only; "all" → both.
type string optional Exact payment_method filter; "All" → none.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_supplier_invoices"
}'

Example Response

[
  {
    "id": "aa9412cc3eb9",
    "oid": 6187,
    "items_number": 1,
    "subtotal": 12,
    "vat": 0.6,
    "payable": 12.6,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 0,
    "change": 0,
    "note": "",
    "published_date": null,
    "uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
    "created_at": "2026-05-19T16:16:14.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "Faisal Suppliers",
    "info_address": "Industrial Area 2, Sharjah",
    "info_phone": "+971500000003",
    "info_email": "sales@faisal.example",
    "status": "draft",
    "user": "Nexus Cafe",
    "table_id": 881,
    "payment_method": null,
    "your_order_ref": "",
    "supplier_order_ref": "",
    "is_draft": 1,
    "parent_purchase_uuid": "6f6db90f-2a7d-4910-82e9-3a0dae7e6e70",
    "is_archive": 0
  },
  {
    "id": "3a0dae7e6e70",
    "oid": 6180,
    "items_number": 2,
    "subtotal": 10,
    "vat": 0.5,
    "payable": 10.5,
    "discountamount": 0,
    "discountpct": null,
    "paid": null,
    "change": 2,
    "note": "",
    "published_date": "2026-05-19T00:00:00.000Z",
    "uuid": "6f6db90f-2a7d-4910-82e9-3a0dae7e6e70",
    "created_at": "2026-05-19T14:41:09.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "Grocery Supplier",
    "info_address": "Warehouse 4, Al Quoz, Dubai",
    "info_phone": "+971500000001",
    "info_email": "orders@supplier.example",
    "status": "partially_received",
    "user": "Nexus Cafe",
    "table_id": 825,
    "payment_method": null,
    "your_order_ref": "",
    "supplier_order_ref": null,
    "is_draft": 0,
    "parent_purchase_uuid": null,
    "is_archive": 0
  }
]

list_stock_transfers

GET /api/stock-transfer-invoices. Why: Inter-branch stock transfer board (Inventory → Transfer Request): every transfer between the tenant's branches with status, source/destination branch, transfer_no, value and who requested/approved/dispatched/received it. Use it to find a transfer id for get_stock_transfer (lines, on-hand, history) → update_stock_transfer / update_stock_transfer_items (pending only), to find the transfer you just created with create_stock_transfer (its response has no id), and to answer 'what is in transit / waiting for approval'. Branch ids come from list_locations. Server-side scope: only transfers whose source or destination is in the calling user's allowed branches. Read only — approve/dispatch/receive/complete/reject are dashboard actions. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_stock_transfers"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
status enum: draft, pending, approved, dispatched, received, completed, rejected, cancelled, delivered optional Exact status filter.
from_location_id number optional Source branch id (list_locations): only transfers leaving this branch.
to_location_id number optional Destination branch id (list_locations): only transfers arriving at this branch.
from string optional Start date YYYY-MM-DD, compared to DATE(created_at) in UTC (inclusive).
to string optional End date YYYY-MM-DD, compared to DATE(created_at) in UTC (inclusive).
_page number optional 1-based page.
_limit number optional Rows per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_stock_transfers"
}'

Example Response

{
  "success": true,
  "message": "Stock transfers fetched successfully",
  "data": {
    "data": [
      {
        "id": 7777,
        "items_number": 2,
        "subtotal": 5841.84,
        "vat": 292.092,
        "payable": 6133.932,
        "discountamount": 0,
        "discountpct": 0,
        "paid": 0,
        "change": 0,
        "note": "",
        "uuid": "fe1ecd13-f615-4326-b6a1-482b5353a3c6",
        "created_at": "2026-08-12T10:48:07.000Z",
        "customer_id": 437,
        "from_location_id": 907,
        "info_name": "Nexus Cafe",
        "info_address": null,
        "info_email": "admin@devnexus.co",
        "info_phone": null,
        "status": "dispatched",
        "user": "Nexus Cafe",
        "table_id": null,
        "payment_method": null,
        "order_id": null,
        "invoice_id": 44,
        "to_location_id": 909,
        "customer_location_id": 907,
        "email": null,
        "delivery_date": null,
        "user_id": 4734,
        "is_edit": null,
        "username": "Nexus Cafe",
        "transfer_no": "STR-2026-01628",
        "requested_by": 4734,
        "approved_by": 4734,
        "dispatched_by": 4734,
        "received_by": null,
        "requested_at": "2026-08-12T10:48:07.000Z",
        "approved_at": "2026-08-12T10:48:16.000Z",
        "dispatched_at": "2026-08-12T10:48:21.000Z",
        "received_at": null,
        "notes": null,
        "priority": null,
        "expected_delivery_date": null,
        "updated_at": "2026-08-12T10:48:20.000Z",
        "rejected_by": null,
        "rejected_at": null,
        "parent_transfer_id": null,
        "from_location": "Default location",
        "to_location": "Iqbal Town",
        "requested_by_username": "Nexus Cafe",
        "approved_by_username": "Nexus Cafe",
        "dispatched_by_username": "Nexus Cafe",
        "received_by_username": null
      }
    ],
    "total": 47,
    "page": 1,
    "limit": "5"
  }
}

list_stock_order_requests

GET /api/stock-order-request. Why: Branch material order requests (Inventory → Order Requests): what a branch asked to buy, per request number REQ-<oid>, with status (Pending / Draft PO Created / Declined), total, item count and the draft PO it became (po_number). Use it to find a request uuid for get_stock_order_request → update_stock_order_request (Pending only), to check a request you created, or to see which requests already have POs (then get_purchase_order). Branch ids from list_locations. Read only — decline / create draft PO are dashboard actions. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_stock_order_requests"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE requesting-branch id (list_locations); only requests whose from_location_id equals it.
st string optional Exact status filter (case-insensitive): Pending | Draft PO Created | Declined (dashboard dropdown), or a legacy status.
k string optional Substring search on request number (invoice_id/id), user, username or info_name.
from string optional Start business day YYYY-MM-DD (from the branch opening hour, tenant timezone).
to string optional End business day YYYY-MM-DD (inclusive, up to the next day's opening hour).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_stock_order_requests"
}'

Example Response

[
  {
    "id": "4338bd97b70a",
    "oid": 13,
    "subtotal": 6000,
    "vat": 300,
    "payable": 6300,
    "change": 0,
    "note": "",
    "uuid": "59c4fb6e-fb85-4ab1-ac0b-4338bd97b70a",
    "delivery_date": "2026-08-06T12:52:39.000Z",
    "created_at": "2026-08-06T12:52:49.000Z",
    "customer_id": 437,
    "to_location_id": 907,
    "to_location": "Default location",
    "customer_location_id": 907,
    "from_location_id": 907,
    "from_location": "Default location",
    "location_name": "Default location",
    "info_name": "Nexus Cafe",
    "info_email": "admin@devnexus.co",
    "status": "Pending",
    "user": "Nexus Cafe",
    "username": "Nexus Cafe",
    "table_id": 0,
    "payment_method": "1",
    "items_count": 1,
    "purchase_uuid": null,
    "po_number": null
  },
  {
    "id": "8cbb626a9d44",
    "oid": 12,
    "subtotal": 125.72,
    "vat": 6.29,
    "payable": 132,
    "change": 0,
    "note": "",
    "uuid": "d5bc4cdc-f6b6-4d04-bdcf-8cbb626a9d44",
    "delivery_date": "2026-08-04T21:48:06.000Z",
    "created_at": "2026-08-04T21:48:44.000Z",
    "customer_id": 437,
    "to_location_id": 907,
    "to_location": "Default location",
    "customer_location_id": 907,
    "from_location_id": 907,
    "from_location": "Default location",
    "location_name": "Default location",
    "info_name": "Nexus Cafe",
    "info_email": "admin@devnexus.co",
    "status": "Draft PO Created",
    "user": "Nexus Cafe",
    "username": "Nexus Cafe",
    "table_id": 0,
    "payment_method": "1",
    "items_count": 1,
    "purchase_uuid": "d39ca7ba-1468-4f0b-855e-94d042731c3f",
    "po_number": "PO-55"
  }
]

list_wastage

GET /api/wastage. Why: Wastage batches (spoiled / expired / damaged stock write-offs) of the tenant with status, reason, cost and creator — the dashboard Wastage page. Use it to find batch ids for get_wastage / update_wastage_draft / delete_wastage_draft and to report waste cost. Read only; the stock-out happens when a batch is approved in the dashboard. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_wastage"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv of branch ids (list_locations).
from string optional Start date YYYY-MM-DD (created_at ≥ that day 00:00 in server time).
to string optional End date YYYY-MM-DD inclusive (created_at ≤ 23:59:59).
approval_status enum: draft, pending, approved, rejected optional Exact status.
user number optional Creator user id (list_users).
brand_filter number optional Brand id (list_brands): batches with at least one line of that brand.
page number optional 1-based page.
limit number optional Rows per page.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_wastage"
}'

Example Response

{
  "success": true,
  "message": "Wastage batches fetched successfully",
  "data": {
    "data": [
      {
        "id": 29,
        "batch_number": "WB-2026-00029",
        "wastage_reason": "invoice_cancellation",
        "items_count": 1,
        "total_cost": 1.1624,
        "approval_status": "pending",
        "requires_approval": 1,
        "created_by": 4734,
        "approved_by": null,
        "batch_notes": "Auto-created from invoice cancellation — 6CAC39F5-C142-4FF2-8970-DF090FEA68EE",
        "approval_notes": null,
        "location_id": 907,
        "customer_id": 437,
        "submitted_at": "2026-07-01T12:12:18.000Z",
        "approved_at": null,
        "created_at": "2026-07-01T12:12:18.000Z",
        "updated_at": "2026-07-01T12:12:18.000Z",
        "reference_type": "invoice_cancellation",
        "reference_id": "6CAC39F5-C142-4FF2-8970-DF090FEA68EE",
        "brand_id": null,
        "location_name": "Default location",
        "created_by_name": "Nexus Cafe",
        "approved_by_name": null,
        "item_brand_names_csv": null
      },
      {
        "id": 14,
        "batch_number": "WB-2026-00014",
        "wastage_reason": "expired",
        "items_count": 1,
        "total_cost": 5.4144,
        "approval_status": "approved",
        "requires_approval": 1,
        "created_by": 4734,
        "approved_by": 4734,
        "batch_notes": null,
        "approval_notes": null,
        "location_id": 907,
        "customer_id": 437,
        "submitted_at": "2026-04-30T09:16:16.000Z",
        "approved_at": "2026-04-30T14:16:46.000Z",
        "created_at": "2026-04-30T09:16:15.000Z",
        "updated_at": "2026-04-30T09:16:45.000Z",
        "reference_type": "manual",
        "reference_id": null,
        "brand_id": 473,
        "location_name": "Default location",
        "created_by_name": "Nexus Cafe",
        "approved_by_name": "Nexus Cafe",
        "item_brand_names_csv": "Test "
      }
    ],
    "pagination": {
      "total": 4,
      "limit": "5"
    }
  }
}

list_inventory_counts

GET /api/inventory-counts/list. Why: Physical inventory counts (stock-take sessions) of a branch with every line (expected vs counted). Use it to find a count id and its status before get_inventory_count / update_inventory_count_materials / add_inventory_count_item, or to report variances. Read only — approval (which adjusts stock) happens in the dashboard. Count-sheet templates are list_inventory_count_sheets. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_inventory_counts"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or csv of branch ids (list_locations); counts whose location_id is any of them.
status enum: draft, submitted, pending, approved, rejected optional Exact status filter.
type number optional Exact type filter: 0 partial, 1 full, 2 count sheet.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_inventory_counts"
}'

Example Response

{
  "status": true,
  "message": "Inventory count list found",
  "data": [
    {
      "id": 477,
      "start_date": "Apr 25, 2026",
      "start_time": "03:41 PM",
      "location_id": 907,
      "name": "Default location - Apr 25, 2026 03:41 PM",
      "type": 0,
      "is_partial": 1,
      "status": "pending",
      "created_at": 1777113069,
      "updated_at": 1777113069,
      "created_by_user_id": 4734,
      "materials": [
        {
          "material_id": 119197,
          "counted_quantity": 10.45,
          "expected_quantity": 0,
          "price": 0.2873,
          "name": "Test"
        }
      ]
    },
    {
      "id": 476,
      "start_date": "Apr 25, 2026",
      "start_time": "03:57 PM",
      "location_id": 907,
      "name": "Default location - Apr 25, 2026 03:57 PM",
      "type": 0,
      "is_partial": 1,
      "status": "approved",
      "created_at": 1777112893,
      "updated_at": 1777112893,
      "created_by_user_id": 4734,
      "materials": [
        {
          "material_id": 119197,
          "counted_quantity": 10.9985,
          "expected_quantity": 0,
          "price": 0.2873,
          "name": "Test"
        }
      ]
    }
  ]
}

get_inventory_count

GET /api/inventory-counts/by-id. Why: One inventory count with every line: line id, material, expected vs counted quantity, price and units. Needed before update_inventory_count_item (line ids), update_inventory_count_materials (full line list) or add_inventory_count_item (check the material is not already on the count), and to report variance. Read only. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_inventory_count"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Inventory count id (list_inventory_counts → data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_inventory_count",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Inventory count found",
  "data": [
    {
      "id": 156,
      "start_date": "Mar 03, 2026",
      "start_time": "01:16 PM",
      "location_id": 907,
      "name": "Default location - Mar 03, 2026 01:16 PM",
      "type": 2,
      "is_partial": 0,
      "status": "approved",
      "created_at": 1772524002,
      "updated_at": 1772524002,
      "created_by_user_id": 4734,
      "created_by_user_name": "Nexus Cafe",
      "materials": [
        {
          "id": 1400,
          "material_id": 119196,
          "counted_quantity": 50,
          "expected_quantity": 100,
          "price": 100,
          "name": "Test Material",
          "unit": "Kilogram",
          "ingredientunit": "Gram",
          "conversionrate": "1000"
        }
      ]
    }
  ]
}

list_recipes

GET /api/recipe. Why: Recipe (BOM) catalog of the tenant: name, output yield_qty + unit, mode (phantom | stockable), last computed cost and brand tags. Recipes are tenant-wide (no branch filter). Use it to find recipe ids for get_recipe → update_recipe / delete_recipe, to pick STOCKABLE recipes for create_work_order (mode=stockable), or phantom recipes to link to products (create_product / update_product recipes[]). Ingredient lines, product links and branches are only in get_recipe. A recipe's prepared material (its stock item) is in list_materials (linked_recipe = recipe id). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_recipes"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
page number optional 1-based page.
limit number optional Page size.
search string optional Case-insensitive substring of the recipe name (`%`/`_` act as wildcards).
mode enum: phantom, stockable optional Exact mode filter.
brand_id number optional Brand id (list_brands): only recipes tagged with this brand (brand_ids).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_recipes"
}'

Example Response

{
  "message": "Recipes Fetched Successfully",
  "data": [
    {
      "id": 16779,
      "customer_id": 437,
      "name": "Test",
      "yield_qty": 0.8,
      "unit": "Kilogram",
      "version": 1,
      "created_by": 4734,
      "mode": "stockable",
      "sku_no": "rcp-437-16779",
      "unit_id": 2,
      "unit_cost": 50,
      "total_cost": 50,
      "calculation_date": "2026-02-12T13:26:26.000Z",
      "material_count": 2,
      "waste_pct": 0,
      "notes": null,
      "brands": [
        {
          "id": 412,
          "name": "Nexus Cafe"
        },
        {
          "id": 473,
          "name": "Test "
        }
      ]
    }
  ],
  "pagination": {
    "total": 902,
    "page": 1,
    "limit": 1,
    "totalPages": 902
  }
}

get_recipe

GET /api/recipe/:id. Why: One recipe with its ingredient lines (material, qty in ingredient unit, yield override, material cost data), linked products (qty_used), brand tags and the branches of its prepared material. Always call it before update_recipe (update is a full replace) and before create_work_order (the work-order material lines are computed from these lines). Material ids → get_material / list_materials; product ids → get_product; branch ids → list_locations; brand ids → list_brands. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_recipe"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Recipe id (list_recipes data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_recipe",
  "id": 11
}'

Example Response

{
  "message": "Recipe Fetched Successfully",
  "data": [
    {
      "id": 16779,
      "customer_id": 437,
      "name": "Test",
      "yield_qty": 0.8,
      "unit": "Kilogram",
      "version": 1,
      "created_by": 4734,
      "mode": "stockable",
      "sku_no": "rcp-437-16779",
      "unit_id": 2,
      "recipe_items": [
        {
          "recipe_id": 16779,
          "material_id": 119196,
          "qty": 500,
          "unit": "Gram",
          "wastage_pct": 0,
          "id": 988201,
          "type": "mat",
          "unit_id": 1,
          "yield_pct": null,
          "name": "Test Material",
          "ingredientunit": "Gram",
          "conversionrate": "1000",
          "avg_price": 22.0271,
          "price": 0
        },
        {
          "recipe_id": 16779,
          "material_id": 119158,
          "qty": 500,
          "unit": "Gram",
          "wastage_pct": 0,
          "id": 988202,
          "type": "mat",
          "unit_id": 1,
          "yield_pct": null,
          "name": "Cherry Blosom",
          "ingredientunit": "Gram",
          "conversionrate": "1000",
          "avg_price": 0.3295,
          "price": 0
        }
      ],
      "product_ids": [
        257508
      ],
      "linked_products": [
        {
          "product_id": 257508,
          "id": 257508,
          "name": "Mocha",
          "qty_used": 1,
          "portion_qty": 0.8,
          "unit": "Kilogram"
        }
      ],
      "brand_ids": [
        412,
        473
      ],
      "location_id": [
        907,
        909,
        908,
        922,
        910,
        1101,
        1102,
        1100,
        1117
      ]
    }
  ]
}

get_material_category

GET /api/inventory/categories/:id. Why: One material category (name, parent, description, image) — load it before update_material_category or to resolve a material's category_id to a name. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_material_category"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material category id (list_material_categories).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_material_category",
  "id": 11
}'

Example Response

{
  "success": true,
  "data": {
    "id": 3,
    "name": "Dairy",
    "description": "",
    "parent_id": 2,
    "customer_id": 437,
    "color": "",
    "image": "",
    "is_deleted": 0
  }
}

get_supplier

GET /api/suppliers/:id. Why: Load one supplier record (all its columns) before update_supplier — the update overwrites every column, so you need the current values to send back — or to show its contact / payment details. Ids come from list_suppliers. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_supplier"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Supplier id from list_suppliers.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_supplier",
  "id": 11
}'

Example Response

[
  {
    "id": 825,
    "name": "Grocery Supplier",
    "description": "Grocery items",
    "customer_id": 437,
    "location_id": 907,
    "address": "Warehouse 4, Al Quoz, Dubai",
    "logo": "",
    "tax_pct": 5,
    "payments": "{\"is_cash\":true,\"is_credit\":false,\"is_cheque\":false}",
    "trade_license": "",
    "contact_name": "Sample Contact",
    "contact_email": "orders@supplier.example",
    "contact_phone": "+971500000001",
    "trn_certificate": "",
    "can_recieve_email": 1
  }
]

get_purchase_order

GET /api/supplier-invoices/:uuid. Why: Full purchase order or draft: header (supplier snapshot, dates, refs, status, totals, who received/approved), every line with material, unit, qty, requested qty, unit price and line total, plus the branch's business info. Load it before update_draft_purchase_order_lines (you must resend all lines and keep the VAT rate) or delete_draft_purchase_order (check is_draft = 1), and to answer "what's on PO-123". uuid from list_supplier_invoices. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_purchase_order"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required PO/draft uuid (list_supplier_invoices → uuid; not id/oid).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_purchase_order",
  "uuid": "example"
}'

Example Response

[
  {
    "id": 6187,
    "items_number": 1,
    "subtotal": 12,
    "vat": 0.6,
    "payable": 12.6,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 0,
    "change": 0,
    "note": "",
    "published_date": null,
    "delivery_date": null,
    "delivery_details": null,
    "uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
    "created_at": "2026-05-19T00:00:00.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "Faisal Suppliers",
    "info_address": "Industrial Area 2, Sharjah",
    "info_phone": "+971500000003",
    "info_email": "sales@faisal.example",
    "status": "draft",
    "user": "Nexus Cafe",
    "table_id": 881,
    "your_order_ref": "",
    "supplier_order_ref": "",
    "supplier_invoice_number": null,
    "supplier_invoice_id": 3,
    "received_by": null,
    "received_at": null,
    "received_by_name": null,
    "approved_by": null,
    "approved_at": null,
    "approved_by_name": null,
    "rejected_by": null,
    "rejected_at": null,
    "rejected_by_name": null,
    "is_draft": 1,
    "parent_purchase_uuid": "6f6db90f-2a7d-4910-82e9-3a0dae7e6e70",
    "order_id": 18,
    "is_archive": 0,
    "parent_order_id": 6180,
    "location_name": "Default location",
    "items": [
      {
        "id": 14292,
        "name": "\t SRC-BT-86CBPR-SG3 BB86-BB92 INTERLOCK SHIMANO CERAMIC COATED BLACK",
        "price": 2,
        "qty": 6,
        "subtotal": 12,
        "purchase_uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
        "customer_id": 437,
        "location_id": 907,
        "material_id": 112157,
        "extras": null,
        "custom_price": 2,
        "product_id": null,
        "unit": "Pieces",
        "wastage": null,
        "brand_id": null,
        "requested_qty": 6,
        "remark": null,
        "ingredientunit": "Pieces",
        "brand_name": null
      }
    ],
    "business_info": [
      {
        "id": 9993,
        "value": "Nexus",
        "name": "info_name"
      },
      {
        "id": 9994,
        "value": "Nexus located at Dev",
        "name": "info_address"
      },
      {
        "id": 9995,
        "value": "info@buyer.example",
        "name": "info_email"
      },
      {
        "id": 10237,
        "value": "+971500000009",
        "name": "info_phone"
      },
      {
        "id": 9996,
        "value": "100000000000003",
        "name": "info_vat_no"
      },
      {
        "id": 9997,
        "value": "1762167059218-logo.png",
        "name": "logo_image"
      }
    ]
  }
]

get_wastage

GET /api/wastage/:id. Why: One wastage batch with all its lines (material, qty, unit, cost). Needed before update_wastage_draft (it replaces the whole batch, so resend everything) or delete_wastage_draft (confirm it is still a draft). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_wastage"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Wastage batch id (list_wastage → data.data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_wastage",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Wastage batch fetched successfully",
  "data": [
    {
      "header": {
        "id": 33,
        "batch_number": "WB-2026-00030",
        "wastage_reason": "Customer complaint",
        "items_count": 7,
        "total_cost": 13.2238,
        "approval_status": "pending",
        "requires_approval": 1,
        "created_by": 4734,
        "approved_by": null,
        "batch_notes": "Refund wastage — invoice 79EEB56A-1E47-4DB1-9F24-9234A4FB017E",
        "approval_notes": null,
        "location_id": 907,
        "customer_id": 437,
        "submitted_at": "2026-07-01T13:17:21.000Z",
        "approved_at": null,
        "created_at": "2026-07-01T13:17:20.000Z",
        "updated_at": "2026-07-01T13:17:20.000Z",
        "reference_type": "invoice_refund",
        "reference_id": "79EEB56A-1E47-4DB1-9F24-9234A4FB017E",
        "brand_id": null,
        "location_name": "Default location",
        "created_by_name": "Nexus Cafe",
        "approved_by_name": null
      },
      "items": [
        {
          "id": 121,
          "wastage_batch_id": 33,
          "material_id": 111644,
          "qty": 3,
          "unit": "Pieces",
          "unit_price": 0.4512,
          "total_cost": 1.3536,
          "item_notes": null,
          "customer_id": 437,
          "created_at": "2026-07-01T13:17:20.000Z",
          "brand_id": null,
          "material_name": "[CBSH114I] Caliper Brake Road SHIMANO Ultegra BR-R8000 F/R ",
          "brand_name": null
        },
        {
          "id": 122,
          "wastage_batch_id": 33,
          "material_id": 111649,
          "qty": 3,
          "unit": "Pieces",
          "unit_price": 1.001,
          "total_cost": 3.003,
          "item_notes": null,
          "customer_id": 437,
          "created_at": "2026-07-01T13:17:20.000Z",
          "brand_id": null,
          "material_name": "[CBSH001FI] Caliper Brake Road SHIMANO 105 BR-R7000 IBRR7000AF82XL",
          "brand_name": null
        }
      ]
    }
  ]
}

get_stock_transfer

GET /api/stock-transfer-invoices/:id. Why: One transfer with its lines (material, requested/approved/dispatched/received qty, variance, cost, source on-hand and shortfall), status history, linked draft POs and parent/child re-supply transfers — the dashboard transfer page. Call it before update_stock_transfer / update_stock_transfer_items (status must be pending or draft, and you need items[].id), and to explain where a transfer stands. Ids come from list_stock_transfers; material ids link to get_material / list_materials; on_hand is the source branch stock (same as list_inventory for that branch). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_stock_transfer"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Numeric transfer id (list_stock_transfers data.data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_stock_transfer",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Stock transfer fetched successfully",
  "data": [
    {
      "id": 7772,
      "items_number": 1,
      "subtotal": 1101.36,
      "vat": 55.068,
      "payable": 1156.428,
      "discountamount": 0,
      "discountpct": 0,
      "paid": 0,
      "change": 0,
      "note": "",
      "uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
      "created_at": "2026-07-31T06:17:04.000Z",
      "customer_id": 437,
      "from_location_id": 907,
      "info_name": "Nexus Cafe",
      "info_address": null,
      "info_email": "admin@devnexus.co",
      "info_phone": null,
      "status": "completed",
      "user": "Nexus Cafe",
      "table_id": null,
      "payment_method": null,
      "order_id": null,
      "invoice_id": 41,
      "to_location_id": 909,
      "customer_location_id": 907,
      "email": null,
      "delivery_date": null,
      "user_id": 4734,
      "is_edit": null,
      "username": "Nexus Cafe",
      "transfer_no": "STR-2026-01623",
      "requested_by": 4734,
      "approved_by": 4734,
      "dispatched_by": 4734,
      "received_by": 4734,
      "requested_at": "2026-07-31T06:17:05.000Z",
      "approved_at": "2026-07-31T06:17:21.000Z",
      "dispatched_at": "2026-07-31T06:17:25.000Z",
      "received_at": "2026-07-31T06:17:38.000Z",
      "notes": null,
      "priority": null,
      "expected_delivery_date": null,
      "updated_at": "2026-07-31T06:17:45.000Z",
      "rejected_by": null,
      "rejected_at": null,
      "parent_transfer_id": null,
      "from_location": "Default location",
      "to_location": "Iqbal Town",
      "requested_by_username": "Nexus Cafe",
      "approved_by_username": "Nexus Cafe",
      "dispatched_by_username": "Nexus Cafe",
      "received_by_username": "Nexus Cafe",
      "items": [
        {
          "id": 67284,
          "name": "Test Material",
          "price": 11.0136,
          "qty": 100,
          "subtotal": 1101.36,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "location_id": 907,
          "customer_id": 437,
          "material_id": 119196,
          "extras": null,
          "is_edit": "0",
          "approved_qty": 100,
          "dispatched_qty": 100,
          "received_qty": 50,
          "variance_qty": -50,
          "variance_reason": "lost",
          "unit": "Kilogram",
          "unit_id": null,
          "item_status": null,
          "notes": null,
          "created_at": "2026-07-31T06:17:04.000Z",
          "updated_at": "2026-07-31T06:17:37.000Z",
          "stock_transfer_id": null,
          "brand_id": null,
          "disposition_type": null,
          "disposition_status": null,
          "resolved_by": null,
          "resolved_at": null,
          "disposition_notes": null,
          "po_meta": {},
          "purchase_uuid": null,
          "supplier_id": null,
          "supplier_name": null,
          "po_number": null,
          "moved_to_transfer_id": null,
          "moved_to_transfer_no": null,
          "on_hand": -21311072.05389,
          "shortfall": 21311172.05389,
          "resupply_draft_created": true,
          "resupply_draft_transfer_id": 7773,
          "resupply_draft_transfer_uuid": "808adb34-cc06-4c22-93bc-0589f3101788",
          "resupply_draft_transfer_no": "STR-2026-01624",
          "resupply_draft_status": "draft"
        }
      ],
      "history": [
        {
          "id": 9460,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:45.000Z",
          "notes": "Transfer completed",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "complete",
            "status": "completed",
            "old_status": "received"
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:45.000Z",
          "changed_by_username": "Nexus Cafe"
        },
        {
          "id": 9458,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:38.000Z",
          "notes": "Variance shortfall draft STR-2026-01624 created and linked to this transfer.",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "variance_draft_created",
            "shortfall_items": 1,
            "child_transfer_id": 7773,
            "child_transfer_no": "STR-2026-01624",
            "child_transfer_uuid": "808adb34-cc06-4c22-93bc-0589f3101788"
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:38.000Z",
          "changed_by_username": "Nexus Cafe"
        },
        {
          "id": 9456,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:37.000Z",
          "notes": "Transfer moved from DISPATCHED to RECEIVED. All items received with variance adjustments.",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "move_to_received",
            "new_status": "received",
            "old_status": "dispatched",
            "items_count": 1
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:37.000Z",
          "changed_by_username": "Nexus Cafe"
        },
        {
          "id": 9455,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:25.000Z",
          "notes": "Transfer moved from APPROVED to DISPATCHED. Approved quantities copied to dispatched quantities.",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "move_to_dispatched",
            "new_status": "dispatched",
            "old_status": "approved",
            "items_count": 1
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:25.000Z",
          "changed_by_username": "Nexus Cafe"
        },
        {
          "id": 9454,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:21.000Z",
          "notes": "Transfer moved from PENDING to APPROVED. All items approved.",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "move_to_approved",
            "new_status": "approved",
            "old_status": "pending",
            "items_count": 1
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:21.000Z",
          "changed_by_username": "Nexus Cafe"
        },
        {
          "id": 9453,
          "stock_transfer_id": 0,
          "changed_by": 4734,
          "changed_at": "2026-07-31T06:17:04.000Z",
          "notes": "Stock transfer created",
          "metadata": {
            "user": "Nexus Cafe",
            "action": "create",
            "status": "pending",
            "items_number": 1
          },
          "customer_id": 437,
          "stock_transfer_uuid": "799537a3-c13f-400b-a494-78aeb1a5bd79",
          "created_at": "2026-07-31T06:17:04.000Z",
          "changed_by_username": "Nexus Cafe"
        }
      ],
      "purchase_orders": [],
      "is_auto_created": false,
      "is_variance_draft": false,
      "source_transfer_id": null,
      "source_transfer_no": null,
      "source_transfer_uuid": null,
      "parent_transfer": null,
      "child_transfers": [
        {
          "id": 7773,
          "uuid": "808adb34-cc06-4c22-93bc-0589f3101788",
          "transfer_no": "STR-2026-01624",
          "status": "draft",
          "created_at": "2026-07-31T06:17:38.000Z",
          "parent_transfer_id": 7772
        }
      ],
      "originating_po_uuids": [],
      "originating_po_numbers": [],
      "transfer_meta": {},
      "linked_po_count": 0
    }
  ]
}

get_stock_order_request

GET /api/stock-order-request/:uuid. Why: One order request with its lines (material, qty in storage unit, unit cost, supplier) and the draft PO it produced — the dashboard request detail page. Call it before update_stock_order_request (status must be Pending; you must resend every line). uuids come from list_stock_order_requests; material ids link to get_material, supplier ids to list_suppliers, purchase_uuid to get_purchase_order. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_stock_order_request"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Order request uuid (list_stock_order_requests [].uuid).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_stock_order_request",
  "uuid": "example"
}'

Example Response

[
  {
    "id": 13,
    "username": "Nexus Cafe",
    "subtotal": 6000,
    "vat": 300,
    "email": null,
    "delivery_date": "2026-08-06T12:52:39.000Z",
    "payable": 6300,
    "change": 0,
    "note": "",
    "uuid": "59c4fb6e-fb85-4ab1-ac0b-4338bd97b70a",
    "reject_reason": null,
    "created_at": "2026-08-06T00:00:00.000Z",
    "customer_id": 437,
    "to_location_id": 907,
    "from_location_id": 907,
    "customer_location_id": 907,
    "info_name": "Nexus Cafe",
    "info_email": "admin@devnexus.co",
    "status": "Pending",
    "user": "Nexus Cafe",
    "table_id": 0,
    "purchase_uuid": null,
    "po_number": null,
    "from_location": "Default location",
    "to_location": "Default location",
    "location_name": "Default location",
    "items": [
      {
        "item_id": 35046,
        "order_uuid": "59c4fb6e-fb85-4ab1-ac0b-4338bd97b70a",
        "material_id": 112157,
        "name": "\t SRC-BT-86CBPR-SG3 BB86-BB92 INTERLOCK-W:86.5/89.5/92XOD:41 SHIMANO-CERAMIC COATED BLACK 4712910901471",
        "price": 3,
        "qty": 2000,
        "subtotal": 6000,
        "unit_name": "Pieces",
        "supplier_id": 881,
        "is_edit": "0",
        "reason": null,
        "old_qty": null,
        "new_qty": null,
        "material_table_id": 112157,
        "material_name": "\t SRC-BT-86CBPR-SG3 BB86-BB92 INTERLOCK-W:86.5/89.5/92XOD:41 SHIMANO-CERAMIC COATED BLACK 4712910901471",
        "material_unit": "Pieces",
        "ingredientunit": "Pieces",
        "storageunit": "Pieces",
        "id": 35046,
        "material": "\t SRC-BT-86CBPR-SG3 BB86-BB92 INTERLOCK-W:86.5/89.5/92XOD:41 SHIMANO-CERAMIC COATED BLACK 4712910901471"
      }
    ],
    "current_location_id": 907,
    "tax": "5.00"
  }
]

list_work_orders

GET /api/work-orders. Why: Production work orders (batches of stockable recipes to produce at a branch) with status, dates, branch, cost totals and a packed summary of their recipe lines. Filter by status, production date range, branch, recipe or text; get ids for get_work_order (lines) → update_work_order / delete_work_order (drafts only). Completed orders are the ones that moved stock (list_inventory_movements, reference WORK_ORDER_<uuid>). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_work_orders"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
_page number optional 1-based page.
_limit number optional Page size.
status enum: draft, in_progress, completed, cancelled optional Exact status.
from string optional production_date ≥ this day (YYYY-MM-DD).
to string optional production_date ≤ this day (YYYY-MM-DD, inclusive).
loc_id number optional ONE branch id (list_locations): orders of that branch.
recipe_id number optional Only orders that contain this recipe (list_recipes).
k string optional Substring match on work_order_no, id, notes or a line's recipe_name (`%`/`_` are wildcards).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_work_orders"
}'

Example Response

{
  "success": true,
  "message": "Work orders fetched successfully",
  "data": {
    "data": [
      {
        "id": 1737,
        "uuid": "66663B01-F720-4574-A3CF-65F6F1F02BB3",
        "public_slug": "8d090ad8-258f-41cd-91ae-ebab74853540",
        "work_order_no": "PRD-2026-01659",
        "status": "completed",
        "production_date": "2026-08-05T00:00:00.000Z",
        "expected_completion": null,
        "created_by": 4734,
        "notes": null,
        "net_total": 23017.9029,
        "location_id": 907,
        "customer_id": 437,
        "created_at": "2026-08-05T07:03:22.000Z",
        "updated_at": "2026-08-05T07:03:58.000Z",
        "total_materials_cost": 23017.9029,
        "total_recipes_cost": 23017.9029,
        "recipes_count": 1,
        "materials_count": 2,
        "recipe_data": "Gotcha Recipe|10000.000000|2.3018|23017.9029",
        "location_name": "Default location",
        "item_brand_names_csv": null
      }
    ],
    "pagination": {
      "total": 29,
      "page": 1,
      "limit": 1
    }
  }
}

get_work_order

GET /api/work-orders/:id. Why: One work order: header (status, dates, branch, notes, creator, QR image), its recipe lines (what is produced, qty in the recipe unit, cost) and material lines (what completion will consume, yield-adjusted qty, price). Call it before update_work_order (full replace) and to explain what completing it will do to stock. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_work_order"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Work order id (list_work_orders data.data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_work_order",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Work order fetched successfully",
  "data": [
    {
      "header": {
        "id": 1737,
        "uuid": "66663B01-F720-4574-A3CF-65F6F1F02BB3",
        "public_slug": "8d090ad8-258f-41cd-91ae-ebab74853540",
        "work_order_no": "PRD-2026-01659",
        "status": "completed",
        "production_date": "2026-08-05T00:00:00.000Z",
        "expected_completion": null,
        "created_by": 4734,
        "notes": null,
        "net_total": 23017.9029,
        "location_id": 907,
        "customer_id": 437,
        "qr_png": {
          "type": "Buffer",
          "data": [
            137,
            80,
            78,
            71,
            "…3870 byte values…"
          ]
        },
        "created_at": "2026-08-05T07:03:22.000Z",
        "updated_at": "2026-08-05T07:03:58.000Z",
        "location_name": "Default location",
        "created_by_name": "Nexus Cafe"
      },
      "materials": [
        {
          "id": 23605,
          "work_order_id": 1737,
          "recipe_id": 17224,
          "material_id": 119196,
          "unit": "Gram",
          "qty": 21312872.975277,
          "unit_price": 0.0001,
          "total_price": 2557.5448,
          "customer_id": 437,
          "created_at": "2026-08-05T07:03:22.000Z",
          "material_name": "Test Material",
          "brand_id": null,
          "brand_name": null
        },
        {
          "id": 23606,
          "work_order_id": 1737,
          "recipe_id": 17224,
          "material_id": 119158,
          "unit": "Gram",
          "qty": 12787723.785166,
          "unit_price": 0.0016,
          "total_price": 20460.3581,
          "customer_id": 437,
          "created_at": "2026-08-05T07:03:22.000Z",
          "material_name": "Cherry Blosom",
          "brand_id": null,
          "brand_name": null
        }
      ],
      "recipes": [
        {
          "id": 4470,
          "work_order_id": 1737,
          "recipe_id": 17224,
          "qty": 10000,
          "unit_cost": 2.3018,
          "total_cost": 23017.9029,
          "customer_id": 437,
          "created_at": "2026-08-05T07:03:22.000Z",
          "recipe_name": "Gotcha Recipe"
        }
      ]
    }
  ]
}

list_users

GET /api/users. Why: Staff users (the people who log into the dashboard and the POS) with their role, branches and every per-user POS switch — the admin view of the users table. Admin-only: the key's user must have is_admin 1 or role admin/super, else 401. Use it to get user ids for get_user / update_user / assign_user_role, for sync_invoice created_by_user_id / close_by_user_id and for list_orders_history user_id; role_id links to list_roles, location_id / allow_locations to list_locations, brand_id to list_selling_brands, channel_id to list_selling_channels. For the POS view of the session branch (setting object, devices) use list_pos_users; to change the session user's own POS switches use update_user_permissions; decoded ios_modules / max discount of the session user: get_user_extras. Customers are the CRM tools, not this. Rows carry the clear-text PIN — never show it. Without loc the server lists only the session user's branch (plus multi-branch users); for every user pass loc = comma-separated ids from list_locations. Bare array, no paging. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_users"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional Branch id or comma-separated branch ids (list_locations).
_limit number optional Max rows (1-50000); anything else is ignored (no limit).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_users"
}'

Example Response

[
  {
    "id": 4789,
    "username": "Sara Cashier",
    "email": "sara.cashier@example.com",
    "pin": "0000",
    "role": "staff",
    "role_id": 32,
    "is_admin": 0,
    "location_id": 907,
    "customer_id": 437,
    "allow_locations": "907,909,908",
    "organization_customer_ids": null,
    "organization_id": null,
    "is_daily_report": 0,
    "is_weekly_report": 0,
    "is_monthly_report": 1,
    "modules": "1;1;1;1;1;1;1;1;1;1;1",
    "ios_modules": "1;1;1;1;1;1;1;1;1",
    "can_preview_invoice": 1,
    "max_discount_permit": 26,
    "image": "",
    "permissions": "1;0;1;1;0;0;0;0;1;1;0;0;0;0;0;1;0;0;0;1;0;0;0",
    "print_no_of_times": 1,
    "is_ingredient_disabled": 0,
    "brand_id": 0,
    "channel_id": 0,
    "fraction_digits": 2,
    "dining_guest_count": 0,
    "is_brand_groups": 0,
    "is_extras_enble": 1,
    "display_modifier_titles": 1,
    "modifier_sort_min_required_desc": 1,
    "print_only_save_kds_orders": 1,
    "print_note_on_receipt": 1,
    "orderhistory_print_receipt": 1,
    "show_receipt_screen": 1,
    "order_type": 1,
    "open_dine_in": 0,
    "hide_cart_pay_button": 0,
    "qr_code_on_print_receipt": 0,
    "is_channels_enable": 0,
    "tip_amount": 1,
    "print_void_orders": 0,
    "has_void_order_permission": 1,
    "delivery_detail_enable": 0,
    "is_attributed_receipt": 1,
    "exchange_format": 0,
    "is_cds_enable": null,
    "external_order_print_receipt": 0,
    "is_multi_location_allowed": 1,
    "is_master_locations_enabled": 1,
    "is_qr_payment": 0,
    "is_kds_expeditor": 0,
    "unify_brands": 0,
    "text_receipt": 0,
    "each_category_kds_print": 0,
    "dinein_serving_role": 0,
    "receipt_format": 0,
    "ingredient_stay_standalone": 0,
    "reservation_with_dine_in": 0,
    "pay_zero_amount": 0,
    "receipt_translation": 0,
    "product_translation": 0,
    "category_translation": 0,
    "print_invoice_translation": 0,
    "print_kds_translation": 0,
    "print_deleted_item_on_receipt": 0,
    "draft_order": 0,
    "splitefire": 0,
    "allegy_item": 0,
    "hide_clear_checkout": 1,
    "kds_instruction": 0,
    "pay_default_usd": 1,
    "rpt_business_logo": 1,
    "rpt_order_type": 1,
    "rpt_is_modified": 1,
    "rpt_biller_info": 1,
    "rpt_total_before_vat": 1,
    "rpt_vat_info": 1,
    "rpt_net_total": 1,
    "rpt_exchange_rate": 0,
    "rpt_dine_guest_count": 0,
    "rpt_paid_at": 0,
    "esc_command": 0,
    "one_table_one_order": 0,
    "is_socket_server": 0,
    "is_client": 0,
    "web_local_network_printer": 0,
    "open_price_pincode": 1,
    "is_transfer_enabled": 0,
    "block_print_until_order_open": 0,
    "user_product_sales_counter": 0,
    "is_change_payment_method_enabled": 0,
    "skip_zero_quantity_items": 0,
    "skip_zero_amount_items": 0,
    "settle_wallet_topup": 0,
    "sms_invoice": 0,
    "offline_order_sync": 1,
    "is_shift_management": 0,
    "acknowledge_dine_table_edit": 0,
    "lock_screen": 0,
    "force_crm_user": 0,
    "shift_transactions": 0,
    "eod_shift_report": 0,
    "override_shift": 0,
    "web_image": "",
    "is_crm_mobile_mandatory": 0,
    "reload_dine_order": 0
  }
]

get_user

GET /api/users/:id. Why: One staff user's full record — what the dashboard user edit form loads before update_user: the list_users columns plus language, Zebra printer, cloud-print target, KDS station, is_soldout and the branch currency. Admin-only. Currently fails through MCP (HTTP 500): this route has no Bearer-token auth, it only works with a dashboard login cookie. Use list_users (loc = all branch ids) and pick the row by id instead. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_user"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required User id (list_users [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_user",
  "id": 11
}'

Example Response

[
  {
    "id": 4789,
    "username": "Sara Cashier",
    "email": "sara.cashier@example.com",
    "pin": "0000",
    "role": "staff",
    "role_id": 32,
    "is_admin": 0,
    "location_id": 907,
    "customer_id": 437,
    "allow_locations": "907,909,908",
    "organization_customer_ids": null,
    "organization_id": null,
    "is_daily_report": 0,
    "is_weekly_report": 0,
    "is_monthly_report": 1,
    "modules": "1;1;1;1;1;1;1;1;1;1;1",
    "ios_modules": "1;1;1;1;1;1;1;1;1",
    "can_preview_invoice": 1,
    "max_discount_permit": 26,
    "image": null,
    "permissions": "1;0;1;1;0;0;0;0;1;1;0;0;0;0;0;1;0;0;0;1;0;0;0",
    "print_no_of_times": 1,
    "is_ingredient_disabled": 0,
    "brand_id": 0,
    "channel_id": 0,
    "fraction_digits": 2,
    "dining_guest_count": 0,
    "is_brand_groups": 0,
    "is_extras_enble": 1,
    "display_modifier_titles": 1,
    "modifier_sort_min_required_desc": 1,
    "print_only_save_kds_orders": 1,
    "print_note_on_receipt": 1,
    "orderhistory_print_receipt": 1,
    "show_receipt_screen": 1,
    "order_type": 1,
    "open_dine_in": 0,
    "hide_cart_pay_button": 0,
    "qr_code_on_print_receipt": 0,
    "is_channels_enable": 0,
    "tip_amount": 1,
    "print_void_orders": 0,
    "has_void_order_permission": 1,
    "delivery_detail_enable": 0,
    "is_attributed_receipt": 1,
    "exchange_format": 0,
    "is_cds_enable": null,
    "external_order_print_receipt": 0,
    "is_multi_location_allowed": 1,
    "is_master_locations_enabled": 1,
    "is_qr_payment": 0,
    "is_kds_expeditor": 0,
    "unify_brands": 0,
    "text_receipt": 0,
    "each_category_kds_print": 0,
    "dinein_serving_role": 0,
    "receipt_format": 0,
    "ingredient_stay_standalone": 0,
    "reservation_with_dine_in": 0,
    "pay_zero_amount": 0,
    "receipt_translation": 0,
    "product_translation": 0,
    "category_translation": 0,
    "print_invoice_translation": 0,
    "print_kds_translation": 0,
    "print_deleted_item_on_receipt": 0,
    "draft_order": 0,
    "splitefire": 0,
    "allegy_item": 0,
    "hide_clear_checkout": 1,
    "kds_instruction": 0,
    "pay_default_usd": 1,
    "rpt_business_logo": 1,
    "rpt_order_type": 1,
    "rpt_is_modified": 1,
    "rpt_biller_info": 1,
    "rpt_total_before_vat": 1,
    "rpt_vat_info": 1,
    "rpt_net_total": 1,
    "rpt_exchange_rate": 0,
    "rpt_dine_guest_count": 0,
    "rpt_paid_at": 0,
    "esc_command": 0,
    "one_table_one_order": 0,
    "is_socket_server": 0,
    "is_client": 0,
    "web_local_network_printer": 0,
    "open_price_pincode": 1,
    "is_transfer_enabled": 0,
    "block_print_until_order_open": 0,
    "user_product_sales_counter": 0,
    "is_change_payment_method_enabled": 0,
    "skip_zero_quantity_items": 0,
    "skip_zero_amount_items": 0,
    "settle_wallet_topup": 0,
    "sms_invoice": 0,
    "offline_order_sync": 1,
    "is_shift_management": 0,
    "acknowledge_dine_table_edit": 0,
    "lock_screen": 0,
    "force_crm_user": 0,
    "ios_language": "en",
    "zebra_ip": "",
    "zebra_port": 0,
    "cloud_target_user_id": null,
    "cloud_target_device_id": null,
    "station_id": 0,
    "is_soldout": 0,
    "shift_transactions": 0,
    "eod_shift_report": 0,
    "row_discount": 0,
    "shift_z_report": 0,
    "view_estimated_cash": 0,
    "external_order_ready": 0,
    "currency_code": "AED"
  }
]

list_pos_crm_users

GET /api/v2/crm/users. Why: The customer list the POS cashier picks from to attach a customer to an order — exactly what web-pos loads (page_limit 500, order_by ASC) and caches. Each row carries what checkout needs: id (→ crm_user_id on the order), name, phone, delivery area, the wallet balance and the loyalty program. Customers are tenant-wide (every branch sees the same list). Use it to find/attach a customer; use list_crm_users_details / get_crm_user_details for analytics (visits, segment, wallet history). Paginated: loop current_page until you have total_records rows. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_pos_crm_users"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
page_limit number optional Optional.
current_page number optional Optional.
order_by enum: ASC, DESC optional Optional.
q string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_pos_crm_users"
}'

Example Response

{
  "status": true,
  "message": "Users found",
  "data": {
    "total_records": 2,
    "records_per_page": 500,
    "current_page": 1,
    "users": [
      {
        "id": 63821,
        "first_name": "Test",
        "last_name": "Customer",
        "mobile_number": "+971500000000",
        "email": "test@example.com",
        "dob": "1990-01-01",
        "customer_id": 437,
        "delivery_area": "Test Area",
        "city": "Test City",
        "country": "Pakistan",
        "is_push_notification_enable": 0,
        "preferred_currency": "USD",
        "latitude": 0,
        "longitude": 0,
        "company_name": "Test Company",
        "thumbnail": "",
        "is_mobile_number_verified": 0,
        "gender": "male",
        "created_at_tz": 1782320919,
        "number_of_purchases": 2,
        "average_basket_size": 11,
        "earn_points": 3,
        "total_spend": 22,
        "trn_number": "100000000000003",
        "loyalty_program_name": "Loyalty 1",
        "redeem_points": 10,
        "redeem_amount": 1,
        "wallet_id": 35,
        "wallet_current_balance": 80,
        "wallet_currency_code": null,
        "wallet_status": "active"
      },
      {
        "id": 64887,
        "first_name": "Second",
        "last_name": "Customer",
        "mobile_number": "+971500000001",
        "email": "second@example.com",
        "dob": null,
        "customer_id": 437,
        "delivery_area": null,
        "city": null,
        "country": null,
        "is_push_notification_enable": 0,
        "preferred_currency": null,
        "latitude": 0,
        "longitude": 0,
        "company_name": null,
        "thumbnail": "",
        "is_mobile_number_verified": 0,
        "gender": null,
        "created_at_tz": 1782747497,
        "number_of_purchases": 0,
        "average_basket_size": 0,
        "earn_points": 0,
        "total_spend": 0,
        "trn_number": null,
        "loyalty_program_name": "Loyalty 1",
        "redeem_points": 10,
        "redeem_amount": 1,
        "wallet_id": null,
        "wallet_current_balance": 0,
        "wallet_currency_code": null,
        "wallet_status": null
      }
    ]
  }
}

list_crm_users

GET /api/crm-users. Why: Dashboard customer (CRM) list — one slim row per customer of the tenant (name, phone, email, company, TRN). Same ids as list_pos_crm_users and the crm_user_id on orders. To attach a customer at the POS use list_pos_crm_users (it has wallet + loyalty); for visits/segments use list_crm_users_details. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_crm_users"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
current_page number optional Optional.
page_limit number optional Optional.
k string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_crm_users"
}'

Example Response

{
  "data": [
    {
      "id": 63821,
      "first_name": "Test",
      "last_name": "Customer",
      "company_name": "Test Company",
      "mobile_number": "+971500000000",
      "earn_points": 0,
      "email": "test@example.com",
      "gender": "male",
      "trn_number": "100000000000003",
      "thumbnail": " "
    },
    {
      "id": 64887,
      "first_name": "Second",
      "last_name": "Customer",
      "company_name": null,
      "mobile_number": "+971500000001",
      "earn_points": 0,
      "email": "second@example.com",
      "gender": null,
      "trn_number": null,
      "thumbnail": null
    }
  ],
  "total_records": 2
}

list_crm_users_details

GET /api/crm-users/details. Why: Dashboard CRM analytics list: every customer column plus an all-time order summary, RFM segment and churn risk per customer, with tenant-wide KPIs. Filters by order date window, branch, channel, spend, visit frequency, inactivity, segment. Sorted by last visit (most recent first, never-ordered last). Use it for marketing/retention questions; use list_pos_crm_users to attach a customer to an order. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_crm_users_details"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
k string optional Optional.
current_page number optional Optional.
page_limit number optional Optional.
from_date string optional Optional.
to_date string optional Optional.
location_id string optional Branch id.
channel_id number optional Optional.
spend_range string optional Optional.
visit_frequency enum: Low, Medium, High optional Optional.
inactive_days number optional Optional.
segment enum: VIP, Loyal, Regular, At Risk, Needs Attention, Lost, New, One-timer optional Optional.
churn_risk enum: Low, Medium, High optional Optional.
preferred_time enum: breakfast, brunch, lunch, evening, dinner optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_crm_users_details"
}'

Example Response

{
  "status": true,
  "message": "CRM users with detail fetched successfully",
  "data": [
    {
      "id": 63821,
      "first_name": "Test",
      "last_name": "Customer",
      "mobile_number": "+971500000000",
      "email": "test@example.com",
      "dob": "1990-01-01",
      "customer_id": 437,
      "delivery_area": "Test Area",
      "city": "Test City",
      "country": "Pakistan",
      "created_at": "2026-06-24T12:08:39.000Z",
      "gender": "male",
      "preferred_currency": "USD",
      "latitude": 0,
      "longitude": 0,
      "thumbnail": " ",
      "is_mobile_number_verified": 0,
      "is_push_notification_enable": 0,
      "number_of_purchases": 2,
      "average_basket_size": 11,
      "total_spend": 22,
      "earn_points": 0,
      "trn_number": "100000000000003",
      "company_name": "Test Company",
      "summary": {
        "total_spend": 22,
        "visit_count": 2,
        "last_visit": "2026-08-20T11:07:42.000Z",
        "avg_order_value": 11
      },
      "segment": "Needs Attention",
      "churn_risk": "Low"
    },
    {
      "id": 64887,
      "first_name": "Second",
      "last_name": "Customer",
      "mobile_number": "+971500000001",
      "email": "second@example.com",
      "dob": null,
      "customer_id": 437,
      "delivery_area": null,
      "city": null,
      "country": null,
      "created_at": "2026-06-29T10:38:17.000Z",
      "gender": null,
      "preferred_currency": null,
      "latitude": 0,
      "longitude": 0,
      "thumbnail": null,
      "is_mobile_number_verified": 0,
      "is_push_notification_enable": 0,
      "number_of_purchases": 0,
      "average_basket_size": 0,
      "total_spend": 0,
      "earn_points": 0,
      "trn_number": null,
      "company_name": null,
      "summary": {
        "total_spend": 0,
        "visit_count": 0,
        "last_visit": null,
        "avg_order_value": 0
      },
      "segment": "New",
      "churn_risk": null
    }
  ],
  "total_records": 2,
  "kpis": {
    "total_customers": 2,
    "total_revenue": 893,
    "avg_order_value": 49.61,
    "vip_customers": 0,
    "high_churn_risk_customers": 0,
    "avg_visits_per_month": 1.7
  }
}

get_crm_user

GET /api/crm-users/:id. Why: One customer's stored profile (every contact/address field + server-maintained purchase stats). Tenant-checked. For visits, segment and wallet history use get_crm_user_details; for wallet balance and loyalty in POS terms use list_pos_crm_users. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_crm_user"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Customer id (crm_user_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_crm_user",
  "id": 11
}'

Example Response

[
  {
    "id": 63821,
    "first_name": "Test",
    "last_name": "Customer",
    "company_name": "Test Company",
    "email": "test@example.com",
    "mobile_number": "+971500000000",
    "dob": "1990-01-01",
    "earn_points": 0,
    "customer_id": 437,
    "delivery_area": "Test Area",
    "city": "Test City",
    "is_push_notification_enable": 0,
    "preferred_currency": "USD",
    "trn_number": "100000000000003",
    "latitude": 0,
    "longitude": 0,
    "thumbnail": " ",
    "is_mobile_number_verified": 0,
    "gender": "male",
    "country": "Pakistan",
    "created_at": "2026-06-24T12:08:39.000Z",
    "number_of_purchases": 2,
    "average_basket_size": 11,
    "total_spend": 22
  }
]

get_crm_user_details

GET /api/crm-users/:id/details. Why: Customer intelligence for one customer: profile, all-time order summary, RFM segment, churn risk, value rank, visit patterns (time, days, order types), menu preferences (top items), spending trend, discount usage and the in-house wallet with its last 50 transactions. Tenant-checked. Use it to answer "who is this customer / how are they doing". Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_crm_user_details"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Customer id (crm_user_id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_crm_user_details",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "CRM user detail fetched successfully",
  "data": {
    "user": {
      "id": 63821,
      "first_name": "Test",
      "last_name": "Customer",
      "mobile_number": "+971500000000",
      "email": "test@example.com",
      "dob": "1990-01-01",
      "customer_id": 437,
      "delivery_area": "Test Area",
      "city": "Test City",
      "country": "Pakistan",
      "created_at": "2026-06-24T12:08:39.000Z",
      "gender": "male",
      "preferred_currency": "USD",
      "latitude": 0,
      "longitude": 0,
      "thumbnail": " ",
      "is_mobile_number_verified": 0,
      "is_push_notification_enable": 0,
      "number_of_purchases": 2,
      "average_basket_size": 11,
      "total_spend": 22,
      "earn_points": 0,
      "trn_number": "100000000000003",
      "company_name": "Test Company"
    },
    "summary": {
      "total_spend": 22,
      "visit_count": 2,
      "last_visit": "2026-08-20T11:07:42.000Z",
      "avg_order_value": 11,
      "visit_frequency": 2
    },
    "segment": "Needs Attention",
    "loyalty_tier": null,
    "churn_risk": "Low",
    "tags": null,
    "notes": null,
    "rfm_score": {
      "recency": 4,
      "frequency": 1,
      "monetary": 1,
      "rfm_segment": "Needs Attention"
    },
    "value_rank": "Mid Value",
    "value_rank_percentile": 50,
    "lifetime_value": 264,
    "visit_patterns": {
      "visit_frequency": 2,
      "preferred_time": "Lunch",
      "first_visit": "2026-08-20T11:07:42.000Z",
      "days_since_last_visit": 35,
      "avg_party_size": 1,
      "table_preference": "0",
      "preferred_days": [
        "Thursday"
      ],
      "order_type_split": {
        "Delivery": 100
      }
    },
    "menu_preferences": {
      "favorite_category": "espresso-based drinks",
      "avg_items_per_order": 1,
      "beverage_food_ratio": "100% beverages",
      "top_ordered_items": [
        "QA min Flat White"
      ],
      "dietary_preferences": null
    },
    "spending": {
      "total_spend": 22,
      "average_order_value": 11,
      "lowest_order": 11,
      "highest_order": 11,
      "lifetime_value": 264,
      "avg_items_per_order": 1,
      "spending_trend": "Decreasing",
      "trend_percentage": -100,
      "trend_label": "-100%"
    },
    "promotions_discounts": {
      "discount_usage_rate": 0,
      "discount_revenue_ratio": 0,
      "promo_responsiveness": 0
    },
    "wallet": {
      "current_balance": 80,
      "transactions": [
        {
          "id": 408,
          "wallet_id": 35,
          "user_id": 63821,
          "transaction_type": "credit",
          "amount": 60,
          "balance_before": 20,
          "balance_after": 80,
          "reference_type": "invoice",
          "reference_id": "8D27D146-767C-4E22-9FB3-0611991DAB63",
          "performed_by_user_id": 4734,
          "source": "invoice-sync-v3",
          "description": "Wallet debit for invoice 8D27D146-767C-4E22-9FB3-0611991DAB63",
          "customer_id": 437,
          "location_id": 907,
          "created_at": "2026-06-29T10:14:55.000Z",
          "payment_id": null,
          "payment_method_name": null
        }
      ]
    }
  }
}

list_roles

GET /api/roles. Why: Roles of the tenant with the permissions each grants — the left list of the dashboard Roles & Permissions page and the Roles dropdown of the user edit form. Use it to pick role_id for update_user / assign_user_role, to read which dashboard pages/actions a user's role (list_users role_id) allows, and to get permission ids for create_role / update_role. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"list_roles"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

No additional parameters — only the Authorization: Bearer header is required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "list_roles"
}'

Example Response

{
  "success": true,
  "data": [
    {
      "id": 32,
      "name": "Staff",
      "description": "Default staff role with module group permissions",
      "customer_id": 437,
      "is_deleted": 0,
      "created_at": "2025-09-29T08:05:50.000Z",
      "updated_at": "2025-09-29T08:05:50.000Z",
      "permissions": [
        {
          "id": 1,
          "name": "Manage Inventory Count",
          "value": "Inventory_Count",
          "group": "inventory",
          "sub_group": null,
          "action": null
        },
        {
          "id": 139,
          "name": "Attendance",
          "value": "module_attendance",
          "group": "module_access",
          "sub_group": null,
          "action": null
        },
        {
          "id": 629,
          "name": "Roles",
          "value": "users.roles",
          "group": "Users",
          "sub_group": "Roles",
          "action": null
        },
        {
          "id": 630,
          "name": "Add",
          "value": "users.roles.add",
          "group": "Users",
          "sub_group": "Roles",
          "action": "add"
        }
      ]
    },
    {
      "id": 182,
      "name": "Manager",
      "description": "",
      "customer_id": 437,
      "is_deleted": 0,
      "created_at": "2025-12-13T13:24:49.000Z",
      "updated_at": "2025-12-13T13:24:49.000Z",
      "permissions": [
        {
          "id": 139,
          "name": "Attendance",
          "value": "module_attendance",
          "group": "module_access",
          "sub_group": null,
          "action": null
        },
        {
          "id": 140,
          "name": "Configuration",
          "value": "module_config",
          "group": "module_access",
          "sub_group": null,
          "action": null
        }
      ]
    }
  ]
}

get_role

GET /api/roles/:id. Why: One role with its permissions — read it before update_role (which replaces the permission list) or to explain what a user's role (list_users role_id) lets them do in the dashboard. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"get_role"} when you need field meanings. Safety: read. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Role id (list_roles data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "get_role",
  "id": 11
}'

Example Response

{
  "success": true,
  "data": {
    "id": 182,
    "name": "Manager",
    "description": "",
    "customer_id": 437,
    "is_deleted": 0,
    "created_at": "2025-12-13T13:24:49.000Z",
    "updated_at": "2025-12-13T13:24:49.000Z",
    "permissions": [
      {
        "id": 139,
        "name": "Attendance",
        "value": "module_attendance",
        "group": "module_access",
        "sub_group": null,
        "action": null
      },
      {
        "id": 140,
        "name": "Configuration",
        "value": "module_config",
        "group": "module_access",
        "sub_group": null,
        "action": null
      },
      {
        "id": 141,
        "name": "Dashboard",
        "value": "module_dashboard",
        "group": "module_access",
        "sub_group": null,
        "action": null
      }
    ]
  }
}

describe_tool

Full documentation of another tool: every param, every response field with its meaning, the rules (how the POS/dashboard uses it and how it links to other tools), caveats and real examples. Read-only, no SysPOS call. Use it before any write tool and whenever you need to know what a response field means.

Parameters

ParameterTypeDescription
name string required Exact tool name, e.g. "sync_invoice" or "list_selling_products".

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "describe_tool",
  "name": "Iced Latte"
}'

Example Response

{
  "message": "JSON body returned by the matching SysPOS API"
}

Products (write)

create_product

POST /api/products. Why: Add a new catalog product (master tenant). Check list_products (k=name) first — duplicate active names are rejected. Get category_id and its name (type) from list_categories, label_id from list_labels, branch ids for location_prices / location_id from list_locations, modifier_id from list_modifiers (item ids from list_modifier_items), channel ids from list_channels, recipe ids from list_recipes (phantom only). For add-ons use create_addon. The new product shows on list_selling_products only at branches listed in location_prices. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_product"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Required.
description string optional Free-text description; also copied onto every branch row.
actual_price number optional Master price, VAT INCLUDED — the "Base Price" the user types (required by the dashboard form).
price number optional Price EXCLUDING VAT.
category_id number optional Category id (list_categories).
label_id number optional Label id (list_labels).
location_id array<number> optional Branch id or array of branch ids (list_locations).
location_prices array<object> optional Branches that sell the product, with their price.
modifiers array<object> optional Modifier groups attached to the product, written for each branch in location_id (or tenant-wide when none).
materials array<object> optional Direct raw-material (BOM) lines; drive product_material_total / product_cost.
recipes array<object> optional Recipe links (phantom recipes only); drive product_recipe_total / product_cost.
sku_no string optional SKU; trimmed.
plu number optional PLU / short code for the POS keypad (INT column).
image string optional Image path: an uploaded relative path (list_products image_path) or a URL on the SysPOS media host (normalised to a relative path).
is_addon string optional Truthy → 1 (add-on product), else 0.
is_open_price string optional Truthy → 1 (cashier types the price at sale time).
is_weightage_item string optional Truthy → 1 (sold by weight; set unit_id).
unit_id number optional Weight unit id for weighted items.
is_archive string optional Truthy → 1 (archived).
discount_pct number optional Product discount percent; missing, < 0 or > 100 → 0.
combo_price number optional Price when sold inside a combo.
localize_name string optional Second-language (e.g.
localize_description string optional Second-language description.
menu_sort_order number optional Sort position inside its category (ascending).
calories number optional Calories shown on menus.
preparation_time number optional Preparation time (kitchen display hint).
barcode_type string optional Barcode symbology label: the dashboard sends "EAN-13" or "QR Code".
barcode_value string optional Barcode value scanned at the POS.
is_stock_product number optional 1 = tracked as a stock item, 0 = not.
tax_id number optional Tax group id — tax groups are out of scope; omit on create.
vat number optional VAT AMOUNT inside actual_price (not a %).
price_level array<object> optional Sales-channel prices (delivery apps etc.).
stock number optional Legacy opening-stock column (DECIMAL 10,2), not live inventory.
type string optional Category NAME(s), comma-separated.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_product",
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Product created successfully",
  "productId": 257508
}

update_product

PUT /api/products/:id. Why: Edit one catalog product (name, price, category, branches + branch prices, modifier links, channel prices, BOM, recipes). NOT a patch: first call get_product and resend the whole record with your changes — omitted fields reset prices/image/tax to 0/empty, un-archive the product, wipe materials/recipes and rebuild or reprice branch rows. Same id sources as create_product (list_categories, list_labels, list_locations, list_modifiers, list_channels, list_recipes). Use archive_product to archive/restore, update_product_availability for the POS sold-out switch and delete_product to remove. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_product"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Product id (list_products / get_product).
name string optional Product name.
description string optional Free-text description; also copied onto every branch row.
actual_price number optional Master price, VAT INCLUDED (dashboard "Base Price", required by its form).
price number optional Price EXCLUDING VAT.
category_id number optional Category id (list_categories).
label_id number optional Label id (list_labels).
location_id array<number> optional Branch id or array of branch ids (list_locations).
location_prices array<object> optional FULL REPLACE of the product's branches: a non-empty array deletes all existing branch rows and inserts these (branch-specific names are reset to `name`).
modifiers array<object> optional Modifier groups attached to the product, written for each branch in location_id (or tenant-wide when none).
materials array<object> optional Direct raw-material (BOM) lines; drive product_material_total / product_cost.
recipes array<object> optional Recipe links (phantom recipes only); drive product_recipe_total / product_cost.
sku_no string optional SKU; trimmed.
plu number optional PLU / short code for the POS keypad (INT column).
image string optional Image path: an uploaded relative path (list_products image_path) or a URL on the SysPOS media host (normalised to a relative path).
is_addon string optional Truthy → 1 (add-on product), else 0.
is_open_price string optional Truthy → 1 (cashier types the price at sale time).
is_weightage_item string optional Truthy → 1 (sold by weight; set unit_id).
unit_id number optional Weight unit id for weighted items.
is_archive string optional Truthy → 1 (archived).
discount_pct number optional Product discount percent; missing, < 0 or > 100 → 0.
combo_price number optional Price when sold inside a combo.
localize_name string optional Second-language (e.g.
localize_description string optional Second-language description.
menu_sort_order number optional Sort position inside its category (ascending).
calories number optional Calories shown on menus.
preparation_time number optional Preparation time (kitchen display hint).
barcode_type string optional Barcode symbology label: the dashboard sends "EAN-13" or "QR Code".
barcode_value string optional Barcode value scanned at the POS.
is_stock_product number optional 1 = tracked as a stock item, 0 = not.
tax_id number optional Tax group id — tax groups are out of scope: never choose one.
vat number optional VAT AMOUNT inside actual_price (not a %).
channel_price array<object> optional Sales-channel prices.
modifiers_by_location string optional Per-branch modifier links: { "<location_id>": [modifier rows] }.
stock number optional Legacy opening-stock column (DECIMAL 10,2), not live inventory.
type string optional Category NAME(s), comma-separated.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_product",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Product updated successfully",
  "product": {
    "id": "257508",
    "name": "Mocha",
    "actual_price": 12,
    "category_id": 15231,
    "location_prices": [
      {
        "location_id": 907,
        "location_price": 12,
        "is_available": 1
      }
    ],
    "customer_id": 437
  }
}

archive_product

PUT /api/products/archive_product/:id. Why: Hide a product from the active catalog and every branch menu without deleting it (is_archive = 1), or restore it with is_archive="0". Archived products are listed with list_products is_archive=1 and dropped from list_selling_products. Use instead of delete_product when the item may come back; to hide it at one branch use update_product location_prices[].is_available=false, and for a temporary sell-out use update_product_availability. Note: update_product un-archives unless you send is_archive = 1. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"archive_product"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Product id (list_products; archived ones via is_archive=1).
is_archive enum: 0, 1 optional Exactly "0" → restore (is_archive = 0).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "archive_product",
  "id": 11
}'

Example Response

{
  "message": "Product archived successfully",
  "product": {
    "id": 113573,
    "category_id": 15481,
    "name": "Violet Whispers ",
    "description": null,
    "price": 0,
    "stock": 0,
    "type": "Alternate Options",
    "image": "https://dev1.syspos.ae/api/uploads/KOZ_3276 copy.jpg",
    "customer_id": 437,
    "location_id": [
      null
    ],
    "is_addon": 0,
    "addons": [],
    "bundle_items": [],
    "is_disabled": 0,
    "discount_pct": 0,
    "modifiers": [],
    "is_archive": 1,
    "barcode_type": null,
    "barcode_value": null,
    "is_open_price": 0,
    "is_combo": 0,
    "sync_deliverect": 0,
    "sync_otter": 0,
    "new_cost_date": null,
    "product_cost": null,
    "is_weightage_item": 0,
    "unit_id": 0,
    "actual_price": 0,
    "old_price": null,
    "vat": null,
    "localized": null,
    "base_price": null,
    "menu_sort_order": 0,
    "plu": 1236,
    "calories": null,
    "is_stock_product": 0,
    "is_auto_apply": null,
    "combo_price": 0,
    "localize_name": null,
    "localize_description": null,
    "label_id": null,
    "recipes": [],
    "sku_no": "prod-437-113573",
    "tax_id": null,
    "preparation_time": 0,
    "product_recipe_total": 0,
    "product_material_total": 0,
    "is_overridden": 1,
    "is_deleted": 0,
    "category_sort_order": null,
    "image_path": "KOZ_3276 copy.jpg",
    "web_image": "KOZ_3276 copy.jpg",
    "channel_price": [],
    "materials": [],
    "selectionLength": 0
  }
}

create_category

POST /api/categories. Why: Create a new catalog category (dashboard Add Category) linked to branches. Returns the new id, which you then use as product.category_id (and its name as product.type) in create_product / update_product, and add to a group with update_group so brands with groups show it on the POS. It sells only at the branches in location_id. Check list_categories k=<name> first — duplicate names are not rejected. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_category"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Category name (varchar 300), trimmed.
location_id array<number> required Branches that sell this category — one categories_locations row per id.
image string optional Image file name relative to /api/uploads (e.g.
description string optional Free-text description (categories.description, varchar 300).
color string optional Tile colour, hex like "#FF8800" (varchar 45).
menu_sort_order number optional Menu sort key, ascending (lower first).
localize_name string optional Name in the secondary language (dashboard "Local Name (Arabic)", right-to-left), varchar 255.
localize_description string optional Description in the secondary language (dashboard "Local Description (Arabic)"), varchar 255.
sync_menu number optional 0 | 1.
sync_grubtech number optional 0 | 1.
sync_otter number optional 0 | 1.
sync_deliverect number optional 0 | 1.
sync_bitebell number optional 0 | 1.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_category",
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "message": "Category created successfully",
  "category_id": 19250
}

update_category

PUT /api/categories/:id. Why: Edit one catalog category (dashboard Edit Category / row Activate-Deactivate / Grubtech sync screen): rename (product.type follows for products filed under this id), change branches, activate/deactivate, image, colour, sort order, channel sync flags. Deactivating (is_active 0) is the safe alternative to delete_category when products still use it. Call get_category first and send the whole record back — image, is_active, sync_* and menu_sort_order RESET when omitted. Group membership is edited with update_group, not here. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_category"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Category id (list_categories / get_category).
name string optional New name, trimmed; 400 if it contains a comma.
location_id array<number> required Full list of branches that sell this category.
image string optional Image.
is_active number optional 1 = active, 0 = inactive/archived (dropped from list_selling_categories, the POS rail).
description string optional Free-text description (categories.description, varchar 300).
color string optional Tile colour, hex like "#FF8800" (varchar 45).
menu_sort_order number optional Menu sort key, ascending (lower first).
localize_name string optional Name in the secondary language (dashboard "Local Name (Arabic)", right-to-left), varchar 255.
localize_description string optional Description in the secondary language (dashboard "Local Description (Arabic)"), varchar 255.
sync_menu number optional 0 | 1.
sync_grubtech number optional 0 | 1.
sync_otter number optional 0 | 1.
sync_deliverect number optional 0 | 1.
sync_bitebell number optional 0 | 1.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_category",
  "id": 11,
  "location_id": 11
}'

Example Response

{
  "name": "Footwear",
  "location_id": [
    909,
    908,
    922,
    910,
    907,
    1131
  ],
  "image": "1767085134601-shoes.jpg",
  "is_active": 1,
  "sync_menu": 1,
  "sync_bitebell": 1,
  "sync_grubtech": 0,
  "sync_otter": 0,
  "sync_deliverect": 0,
  "menu_sort_order": 12,
  "id": "17557"
}

create_modifier

POST /api/modifiers. Why: Create a modifier GROUP (e.g. "Milk") at one or more branches (dashboard Modifiers › Add). Next: add its options with create_modifier_item (modifier_ids [new id], location_ids = the same branches), then link it to products with update_product modifiers[] ({modifier_id, min_allowed, max_allowed, priority, selection_type, default/mandatory item ids}). It reaches the POS (list_selling_modifiers / list_selling_products modifiers[]) only once a product links it. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_modifier"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Group name, e.g.
description string optional Free-text description (max 100 chars; dashboard: "Description").
location_id array<number> required Branch ids that carry the group, as an ARRAY, e.g.
selection_allowed number optional Group default max picks (dashboard: "Selection Allowed", default 0; 0 = unlimited).
selection_min_allowed number optional Group default min picks (dashboard: "Minimum Selection Allowed", default 0; 0 = optional).
priority number optional Group order, ascending (dashboard: "Priority", default 0; list drag/sort writes 1..n).
localize_name string optional Translated (e.g.
localize_description string optional Translated description (dashboard: "Local Description").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_modifier",
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "name": "Milk",
  "description": "",
  "location_id": null,
  "customer_id": 437,
  "selection_allowed": 1,
  "selection_min_allowed": 0,
  "priority": 0,
  "localize_name": null,
  "localize_description": null,
  "id": 5701
}

update_modifier

PUT /api/modifiers/:id. Why: Rename a modifier group, change its group defaults / order / translations, or change which branches carry it (dashboard Modifiers › Edit). FULL overwrite: get_modifier first and resend every field; location_id replaces the branch list. Does not touch the group's items (update_modifier_item) or its product links and per-product min/max (update_product modifiers[]). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_modifier"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier group id to update (list_modifiers [].id).
name string required Group name, e.g.
description string optional Free-text description (max 100 chars; dashboard: "Description").
location_id array<number> required Branch ids that carry the group, as an ARRAY, e.g.
selection_allowed number optional Group default max picks (dashboard: "Selection Allowed", default 0; 0 = unlimited).
selection_min_allowed number optional Group default min picks (dashboard: "Minimum Selection Allowed", default 0; 0 = optional).
priority number optional Group order, ascending (dashboard: "Priority", default 0; list drag/sort writes 1..n).
localize_name string optional Translated (e.g.
localize_description string optional Translated description (dashboard: "Local Description").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_modifier",
  "id": 11,
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "id": "4417",
  "name": "Milk",
  "description": "",
  "location_id": [
    907,
    908
  ],
  "selection_allowed": 1,
  "selection_min_allowed": 0,
  "priority": 0,
  "localize_name": "حليب",
  "localize_description": ""
}

create_modifier_item

POST /api/modifierItems. Why: Add an option (e.g. "Oat milk" +2.00) to one or more modifier groups at given branches (dashboard Modifier Items › Add). Needs group ids from list_modifiers / create_modifier. Then it shows at the POS under its primary group (list_selling_modifiers) on products that link that group, and its id can be used in update_product modifiers[] default / mandatory / unavailable item lists. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_modifier_item"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Option name, e.g.
description string optional Free-text description (max 100 chars; dashboard: "Description").
actual_price number optional THE option price per unit, VAT INCLUDED (dashboard: "Actual Price", required, default 0, sent as typed — no VAT maths on either side).
modifier_ids array<number> required Group(s) the option belongs to (dashboard: "Modifiers" multi-select, at least one; options from list_modifiers loc=<branch>).
location_ids array<number> required Branches that carry the option (dashboard: "Locations" checkboxes, at least one; sent there as a csv location_id).
priority number optional Order inside the group, ascending (dashboard: "Priority", required, ≥ 0, default 0; the group screen's drag/sort writes 1..n).
image string optional Relative upload path (e.g.
tax_id number optional Tax id — tax is OUT OF SCOPE: never choose one.
localize_name string optional Translated (e.g.
localize_description string optional Translated description (dashboard: "Local Description").
sku_no string optional Not needed: create overwrites it with "moditem-<customer_id>-<id>" for master tenants.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_modifier_item",
  "name": "Iced Latte",
  "modifier_ids": [
    11
  ],
  "location_ids": [
    11
  ]
}'

Example Response

{
  "name": "Oat milk",
  "actual_price": 2,
  "modifier_ids": [
    4417
  ],
  "location_ids": [
    907,
    908
  ],
  "priority": 3,
  "id": 30211,
  "modifier_id": 4417
}

update_modifier_item

PUT /api/modifierItems/:id. Why: Change an option's name, price, order, groups or branches (dashboard Modifier Items › Edit). Read it with get_modifier_item first and resend actual_price, priority, modifier_ids, location_ids and tax_id — omitted ones are reset or replaced. Per-product defaults / availability of the option are set with update_product, not here; the POS sold-out switch for options (update_product_availability itemType MODIFIER_ITEM) only reaches Grubtech. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_modifier_item"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier item id to update (list_modifier_items [].id).
name string optional Option name (max 100 chars).
description string optional Free-text description (max 100 chars; dashboard: "Description").
actual_price number required THE option price per unit, VAT INCLUDED (dashboard: "Actual Price", required, default 0, sent as typed — no VAT maths on either side).
modifier_ids array<number> required Group(s) the option belongs to (dashboard: "Modifiers" multi-select, at least one; options from list_modifiers loc=<branch>).
location_ids array<number> required Branches that carry the option (dashboard: "Locations" checkboxes, at least one; sent there as a csv location_id).
priority number required Order inside the group, ascending (dashboard: "Priority", required, ≥ 0, default 0; the group screen's drag/sort writes 1..n).
image string optional Relative upload path (e.g.
tax_id number optional Tax id — tax is OUT OF SCOPE: never choose one.
localize_name string optional Translated (e.g.
localize_description string optional Translated description (dashboard: "Local Description").
sku_no string optional SKU (read-only on the dashboard).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_modifier_item",
  "id": 11,
  "actual_price": 1,
  "modifier_ids": [
    11
  ],
  "location_ids": [
    11
  ],
  "priority": 1
}'

Example Response

{
  "name": "Oat milk",
  "actual_price": 2.5,
  "modifier_ids": [
    4417
  ],
  "location_ids": [
    907,
    908
  ],
  "priority": 3,
  "tax_id": null,
  "id": "30211",
  "modifier_id": 4417
}

create_brand

POST /api/brands. Why: Create a new brand (dashboard catalog management) on one or more branches, optionally linking existing groups. The new id is what list_selling_brands returns as brand_id and what cart lines/invoices carry. A brand shows categories only through its groups — create groups (create_group) and link them here via brand_groups or later with update_brand. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_brand"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
brand_name string required Brand name (brands.brand_name, max 100).
location_id string required Branches of the brand as a CSV STRING, e.g.
brand_groups string optional Group ids as a CSV STRING ("1462,1465") — one brands_groups_relation row per id (brand ↔ group; a group's categories then define the brand's menu).
description string optional Free-text description (brands.description).
brand_thumbnail string optional Logo path relative to /api/uploads (e.g.
brand_color string optional Hex colour, e.g.
brand_address string optional Address printed in the receipt header for this brand (invoice info_address).
brand_email string optional Email printed in the receipt header (invoice info_email).
brand_phone string optional Phone printed in the receipt header (invoice info_phone).
brand_vat_no string optional Brand VAT registration number (text only; VAT itself comes from branch config).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_brand",
  "brand_name": "example",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "brand_ids": [
    480
  ]
}

update_brand

PUT /api/brands/:id. Why: Edit one brand: name, logo, receipt-header contact fields, branches and linked groups. It is a FULL REPLACE — call get_brand first and resend every field, brand_groups (else all groups are unlinked) and location_id (else branches are reset). Use it to attach/detach groups (and therefore categories) to a brand. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_brand"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Brand id (list_brands).
brand_name string required Brand name (brands.brand_name, max 100).
location_id string required Branches of the brand as a CSV STRING, e.g.
brand_groups string optional Group ids as a CSV STRING ("1462,1465") — REPLACES all brands_groups_relation rows of this brand.
description string optional Free-text description (brands.description).
brand_thumbnail string optional Logo path relative to /api/uploads.
brand_color string optional Hex colour, e.g.
brand_address string optional Address printed in the receipt header for this brand (invoice info_address).
brand_email string optional Email printed in the receipt header (invoice info_email).
brand_phone string optional Phone printed in the receipt header (invoice info_phone).
brand_vat_no string optional Brand VAT registration number (text only; VAT itself comes from branch config).
image string optional Alias for brand_thumbnail (used only when brand_thumbnail is absent); a full uploads URL is cut to the relative path.
name_update string optional Truthy = check that no OTHER brand of this tenant already has brand_name → 400 { success:false, message:"Brand name already exists" }.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_brand",
  "id": 11,
  "brand_name": "example",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "brand_id": 459
}

create_group

POST /api/groups. Why: Create a group to attach categories to a brand's menu (e.g. "Hot drinks" under brand Nexus Cafe). Pass categories and brand as csv strings and location_id as an array. Produces group_id for get_group / update_group. Catalog management (dashboard) tool. For menu scoping on the POS use list_selling_brands, which already returns each brand's groups with their category ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_group"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
group_name string required Required.
group_description string optional Free-text description (max 200 chars).
group_thumbnail string optional Image path or URL for the group tile (max 100 chars).
image string optional update_group only: alias of group_thumbnail, used when group_thumbnail is absent.
location_id array<number> required Branch ids the group belongs to (groups_locations rows).
categories string optional CSV string of category ids (list_categories), e.g.
brand string optional CSV string of brand ids (list_brands), e.g.
menu_sort_order number optional Sort position (ascending) in lists and menus.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_group",
  "group_name": "example",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "group_id": 1486
}

update_group

PUT /api/groups/:id. Why: Rename a group, change its categories, brands or branches. FULL REPLACE — read get_group first and send every field back (categories and brand as csv of ids, location_id as array), or links are wiped. Consumes a group id from list_groups/get_group. Catalog management (dashboard) tool. For menu scoping on the POS use list_selling_brands, which already returns each brand's groups with their category ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_group"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Group id (list_groups / get_group).
group_name string required Required.
group_description string optional Free-text description (max 200 chars).
group_thumbnail string optional Image path or URL for the group tile (max 100 chars).
image string optional update_group only: alias of group_thumbnail, used when group_thumbnail is absent.
location_id array<number> required Branch ids the group belongs to (groups_locations rows).
categories string optional CSV string of category ids (list_categories), e.g.
brand string optional CSV string of brand ids (list_brands), e.g.
menu_sort_order number optional Sort position (ascending) in lists and menus.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_group",
  "id": 11,
  "group_name": "example",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "message": "Group updated successfully",
  "group_id": "1341"
}

create_addon

POST /api/products. Why: Create a new add-on (products row with is_addon = 1). Set type to the parent category NAMES it is offered with (list_categories) and location_prices to the branches/prices it sells at (list_locations) — without location_prices a master add-on is in no branch and the POS never offers it. Channel prices: price_level (list_channels ids). Produces productId for get_addon / update_addon. Afterwards it shows in list_addons and, at those branches, in list_selling_addons. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_addon"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Required.
description string optional Optional description.
actual_price number optional VAT-inclusive selling price — the price the POS charges.
price number optional Ex-VAT price = actual_price − vat.
vat number optional VAT AMOUNT inside actual_price (not a percent), e.g.
type string optional Comma-separated CATEGORY NAMES of the parent products this add-on is offered with, e.g.
category_id number optional The add-on's OWN category id (list_categories).
image string optional Image relative upload path or URL; normalised to the relative path.
web_image string optional Alternative image field; wins over image when both are sent (not a column).
is_addon string optional Add-on flag.
location_id array<number> optional Branch ids (list_locations).
location_prices array<object> optional Branch rows (products_locations) — the ONLY way to put a new add-on in branches on master tenants; without it the add-on is in no branch and the POS never show…
is_archive string optional 1 = archived (hidden from list_addons and the POS).
is_open_price string optional 1 = cashier enters the price at sale time.
is_weightage_item string optional 1 = sold by weight.
discount_pct number optional Per-product discount percent 0–100; anything else or omitted → 0 (also on update).
sku_no string optional SKU, trimmed.
label_id number optional Label id (list_labels).
menu_sort_order number optional Menu sort position (ascending).
localize_name string optional Name in the second (localised) language.
localize_description string optional Description in the second language.
calories number optional Calories for digital menus.
preparation_time number optional Preparation minutes for KDS.
addons array<number> optional Legacy JSON list stored as text.
bundle_items array<number> optional Legacy bundle list stored as text.
modifiers array<object> optional Modifier groups to link (rare for add-ons).
modifiers_by_location string optional update_addon only.
recipes array<object> optional Recipe links (rare for add-ons).
price_level array<object> optional create_addon only: per-channel prices.
channel_price array<object> optional update_addon only: per-channel prices.
stock number optional Legacy opening-stock column (not live inventory).
barcode_type enum: , EAN-13, QR Code optional Product-code symbology.
barcode_value string optional Barcode value.
unit_id number optional Unit of measure id for weighted items.
plu number optional PLU code (INT column) for scales/integrations.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_addon",
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Product created successfully",
  "productId": 271199
}

update_addon

PUT /api/products/:id. Why: Edit an add-on: prices, name, offer list (type = category names from list_categories), branches and per-branch price/availability (location_prices, list_locations ids), channel prices (channel_price, list_channels ids), image. FULL REWRITE — read get_addon first and send every field back. Archive/unarchive only: archive_product is lighter. Sold-out at a branch: update_product_availability, not this tool. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_addon"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Add-on id (list_addons / get_addon).
name string required Required.
description string optional Optional description.
actual_price number optional VAT-inclusive selling price — the price the POS charges.
price number optional Ex-VAT price = actual_price − vat.
vat number optional VAT AMOUNT inside actual_price (not a percent), e.g.
type string optional Comma-separated CATEGORY NAMES of the parent products this add-on is offered with, e.g.
category_id number optional The add-on's OWN category id (list_categories).
image string optional Image relative upload path or URL; normalised to the relative path.
web_image string optional Alternative image field; wins over image when both are sent (not a column).
is_addon string optional Add-on flag.
location_id array<number> optional Branch ids (list_locations).
location_prices array<object> optional Branch rows (products_locations) — the ONLY way to put a new add-on in branches on master tenants; without it the add-on is in no branch and the POS never show…
is_archive string optional 1 = archived (hidden from list_addons and the POS).
is_open_price string optional 1 = cashier enters the price at sale time.
is_weightage_item string optional 1 = sold by weight.
discount_pct number optional Per-product discount percent 0–100; anything else or omitted → 0 (also on update).
sku_no string optional SKU, trimmed.
label_id number optional Label id (list_labels).
menu_sort_order number optional Menu sort position (ascending).
localize_name string optional Name in the second (localised) language.
localize_description string optional Description in the second language.
calories number optional Calories for digital menus.
preparation_time number optional Preparation minutes for KDS.
addons array<number> optional Legacy JSON list stored as text.
bundle_items array<number> optional Legacy bundle list stored as text.
modifiers array<object> optional Modifier groups to link (rare for add-ons).
modifiers_by_location string optional update_addon only.
recipes array<object> optional Recipe links (rare for add-ons).
price_level array<object> optional create_addon only: per-channel prices.
channel_price array<object> optional update_addon only: per-channel prices.
stock number optional Legacy opening-stock column (not live inventory).
barcode_type enum: , EAN-13, QR Code optional Product-code symbology.
barcode_value string optional Barcode value.
unit_id number optional Unit of measure id for weighted items.
plu number optional PLU code (INT column) for scales/integrations.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_addon",
  "id": 11,
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Product updated successfully",
  "product": {
    "id": "257559",
    "name": "Classic Sandwich",
    "description": "",
    "actual_price": 100,
    "price": 95,
    "vat": 5,
    "type": "Alternate Options,Brewed Coffee",
    "image": "",
    "is_addon": 1,
    "location_id": [
      907
    ],
    "location_prices": [
      {
        "location_id": 907,
        "location_price": 100,
        "is_available": 1
      }
    ],
    "customer_id": 437
  }
}

create_label

POST /api/label. Why: Add a product label (dashboard Products → Labels → Add). Check list_labels k=<name> first — duplicates are not rejected on master tenants. Branch ids from list_locations. Then put it on products with create_product / update_product label_id (full product from get_product); when product_unique_formula is "name,label_id" the same product name may exist once per label. Confirm with get_label. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_label"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Label name (varchar 255), stored as sent (not trimmed).
description string optional Free-text description (TEXT).
location_id array<number> required Branches the label is offered at — one labels_locations row per id (the label row's own location_id stays NULL on master tenants).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_label",
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

[
  {
    "label": {
      "name": "Coffee beans",
      "description": "",
      "customer_id": 437,
      "location_id": [
        907,
        908
      ],
      "id": 61
    }
  },
  {
    "labels_locations": [
      {
        "id": 5120,
        "label_id": 61,
        "location_id": 907
      },
      {
        "id": 5121,
        "label_id": 61,
        "location_id": 908
      }
    ]
  }
]

update_label

PUT /api/label/:id. Why: Rename a label, change its description or its branch list (dashboard Edit Label). NOT a patch — resend name, description and every branch from list_labels / get_label. Products keep label_id, so a rename shows on list_products, create_product / update_product label pickers and on sold lines (sync_invoice / list_orders_history items[].label_id). Branch ids from list_locations. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_label"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Label id (list_labels data[].id).
name string required Label name (varchar 255), stored as sent.
description string optional Free-text description (TEXT).
location_id array<number> required Branches the label is offered at — one labels_locations row per id (the label row's own location_id stays NULL on master tenants).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_label",
  "id": 11,
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "label": {
    "id": "50",
    "name": "Coffee Seeds",
    "description": "Whole-bean products",
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909,
      910,
      922,
      1131
    ]
  },
  "message": "Label updated successfully"
}

create_deal

POST /api/deals. Why: Add a fixed-price combo (is_combo 1, type "combo"): a name, a VAT-incl. actual_price, the branches (list_locations) and the choice slots — categories[] { category_id from list_categories, max_allowed }. At the POS the customer picks max_allowed products from each slot category (products whose type equals the category name, list_selling_products); the chosen children are sold as combo_items under one combo parent line in sync_invoice (BUILD 9); a child's combo_price (update_product) adds to its line. Channel prices: price_level with list_channels ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_deal"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Deal name (dashboard: required, trimmed, non-empty).
description string optional Free-text description.
actual_price number optional Deal price VAT included — what the POS charges for the combo (dashboard: required number).
price number optional Price without VAT.
vat number optional Tax AMOUNT (not a percent).
discount_pct number optional Deal discount percent.
barcode_type enum: EAN-13, QR Code, optional Barcode symbology (dashboard "Code Type" select).
barcode_value string optional Barcode / "Deal Code Number".
image string optional Relative upload file name (e.g.
location_id string optional Branch ids (list_locations) as a csv string, e.g.
categories array<object> optional Choice slots: one row per category the customer picks from, with max_allowed.
price_level array<object> optional Channel prices (product_channel_price), CREATE key — update uses channel_price.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_deal",
  "name": "Iced Latte"
}'

Example Response

{
  "name": "Lunch Combo",
  "description": "Burger + side + drink",
  "actual_price": 35,
  "price": 35,
  "discount_pct": 0,
  "location_id": "907",
  "categories": [
    {
      "category_id": 15231,
      "max_allowed": 1
    },
    {
      "category_id": 15240,
      "max_allowed": 2
    }
  ],
  "type": "combo",
  "customer_id": 437,
  "is_addon": false,
  "addons": [],
  "modifier": null,
  "barcode_type": null,
  "barcode_value": null,
  "image": "",
  "is_open_price": null,
  "vat": 0,
  "id": 260001,
  "plu": null
}

update_deal

PUT /api/deals/:id. Why: Edit one deal (price, name, branches, choice slots, channel prices, image). It is a FULL overwrite, not a patch: start from get_deal and resend every value; categories (slots → list_categories ids), location_id (branches → list_locations) and channel_price (list_channels) are fully replaced. Slot changes reach the POS through list_selling_products categories[]; to hide a deal use archive_product, to remove it delete_deal. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_deal"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Deal id (list_deals items[].id).
name string required Deal name (dashboard: required, trimmed, non-empty).
description string optional Free-text description.
actual_price number required Deal price VAT included — what the POS charges for the combo (dashboard: required number).
price number required Price without VAT.
vat number optional Tax AMOUNT (not a percent).
discount_pct number optional Deal discount percent.
barcode_type enum: EAN-13, QR Code, optional Barcode symbology.
barcode_value string optional Barcode / "Deal Code Number".
image string required REQUIRED STRING — the handler calls image.split() before its try block, so a missing/non-string image crashes the API process.
location_id string required REQUIRED csv STRING of ALL branch ids the deal should stay on, e.g.
selected_location_ids array<number> optional Alternative branch list as an array of ids; when present it wins over location_id.
categories array<object> optional Choice slots — FULL replace: every deal_categories row is deleted and these are inserted.
channel_price array<object> optional Channel prices, UPDATE key (create uses price_level) — FULL replace: omitted/[] deletes every channel price.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_deal",
  "id": 11,
  "name": "Iced Latte",
  "actual_price": 1,
  "price": 1,
  "image": "example",
  "location_id": 11
}'

Example Response

{
  "id": 260001,
  "name": "Lunch Combo",
  "description": "Burger + side + drink",
  "actual_price": 39,
  "price": 39,
  "vat": 0,
  "discount_pct": 0,
  "barcode_type": "",
  "barcode_value": "",
  "image": "1780385109749-combo.png",
  "location_id": "907,908",
  "categories": [
    {
      "category_id": 15231,
      "max_allowed": 1
    },
    {
      "category_id": 15240,
      "max_allowed": 2
    }
  ],
  "channel_price": [
    {
      "channel_id": 12,
      "channel_name": "Talabat",
      "channel_price": 42,
      "channel_vat": 0,
      "channel_actual_price": 42
    }
  ],
  "type": "combo",
  "customer_id": 437
}

create_channel

POST /api/channels. Why: Add a sales channel and assign it to branches. The POS (list_selling_channels) offers it at those branches; per-channel product prices are then set on products (update_product channel_price) and discounts can be limited to it (channel_ids). Check list_channels first — names must be unique. Linkage: a channel id is referenced by list_selling_products data.products[].price_levels[].channel_id (per-channel price, product_channel_price), by orders (channel_id + channel_name, stored verbatim by sync_invoice — web-pos sends 0/""), and by discount codes channel_ids (list_discounts / get_user_extras data.discount_code[]). Branch availability lives in channels_locations (location_id in these tools); master channels have channels.location_id NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_channel"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Channel name, max 100 chars.
description string optional Free text, max 100 chars.
selected_location_ids array<number> required Branch ids the channel is sold on (one channels_locations row each).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_channel",
  "name": "Iced Latte",
  "selected_location_ids": [
    11
  ]
}'

Example Response

{
  "message": "Channel created successfully",
  "data": {
    "name": "Talabat",
    "description": "Delivery app",
    "selected_location_ids": [
      907,
      908
    ],
    "customer_id": 437,
    "id": 516
  }
}

update_channel

PUT /api/channels/:id. Why: Rename a channel, change its description or the branches it is sold on. FULL REPLACE: name and description are both overwritten (omitted → NULL) and the branch list is replaced — call get_channel first and send everything back. Renaming also renames the channel on every product price row (price_levels[].channel_name); old orders keep the old channel_name. Linkage: a channel id is referenced by list_selling_products data.products[].price_levels[].channel_id (per-channel price, product_channel_price), by orders (channel_id + channel_name, stored verbatim by sync_invoice — web-pos sends 0/""), and by discount codes channel_ids (list_discounts / get_user_extras data.discount_code[]). Branch availability lives in channels_locations (location_id in these tools); master channels have channels.location_id NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_channel"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Channel id (list_channels / get_channel).
name string required Channel name.
description string optional Always overwritten: omitted → NULL.
selected_location_ids array<number> required The COMPLETE branch list after the update: all channels_locations rows are deleted and re-inserted from it.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_channel",
  "id": 11,
  "name": "Iced Latte",
  "selected_location_ids": [
    11
  ]
}'

Example Response

{
  "message": "Channel updated successfully",
  "success": true
}

create_menu

POST /api/menus. Why: Create a named menu: a name plus branches (list_locations), categories (list_categories) and products (list_products) with an optional stored price per product. Menus are dashboard records only: nothing else in SysPOS reads them today. The POS (web-pos) never calls /menus, list_selling_products / list_selling_categories do not look at menus, and the aggregator syncs (Deliverect, Grubtech, Otter, LimeTray) build their menus from categories (sync_* flags, list_categories) and products, not from these menus. Adding or removing a product here does not change what a branch sells; for that use update_product (location_prices) or the category tools. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_menu"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Menu name.
description string optional Free-text description.
currency string optional Currency code label.
is_active number optional 1 = Active, 0 = Inactive.
brand_id number optional Brand id (list_brands).
label_id number optional Routing label id (list_labels).
tax_id number optional Tax id.
location_id array<number> required Branches the menu is linked to (menu_locations), as an ARRAY of ids.
category_id array<number> optional Categories (menu sections) on the menu (menu_categories).
product_id array<number> optional Products on the menu, used ONLY when products_with_categories is omitted or [].
products_with_categories array<object> optional Explicit product rows; when non-empty it wins over product_id.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_menu",
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "message": "Menu created successfully",
  "menu_id": 5,
  "data": {
    "id": 5,
    "name": "Breakfast Menu",
    "description": "Served until noon",
    "currency": "AED",
    "customer_id": 437,
    "location_id": [
      907,
      910
    ],
    "is_active": 1,
    "brand_id": null,
    "label_id": 51,
    "tax_id": null,
    "brand_name": null,
    "label_name": "Coffee beans",
    "tax_name": null,
    "tax_rate": null,
    "categories": [
      {
        "category_id": 15481,
        "category_name": "Alternate Options"
      }
    ],
    "products": [
      {
        "product_id": 247653,
        "category_id": 15481,
        "actual_price": 13,
        "product_name": "Chai",
        "product_type": "Alternate Options"
      }
    ]
  }
}

update_menu

PUT /api/menus/:id. Why: Edit a menu: rename, (de)activate, change branches, categories or products. It is a full replace, not a patch — load get_menu first and send every field back. Menus are dashboard records only: nothing else in SysPOS reads them today. The POS (web-pos) never calls /menus, list_selling_products / list_selling_categories do not look at menus, and the aggregator syncs (Deliverect, Grubtech, Otter, LimeTray) build their menus from categories (sync_* flags, list_categories) and products, not from these menus. Adding or removing a product here does not change what a branch sells; for that use update_product (location_prices) or the category tools. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_menu"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Menu id (list_menus data[].id).
name string required Menu name.
description string optional Free-text description.
currency string optional Currency code label.
is_active number optional 1 = Active, 0 = Inactive.
brand_id number optional Brand id (list_brands).
label_id number optional Routing label id (list_labels).
tax_id number optional Tax id.
location_id array<number> required Branches the menu is linked to (menu_locations), as an ARRAY of ids.
category_id array<number> optional Categories (menu sections) on the menu (menu_categories).
product_id array<number> optional Products on the menu, used ONLY when products_with_categories is omitted or [].
products_with_categories array<object> optional Explicit product rows; when non-empty it wins over product_id.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_menu",
  "id": 11,
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "message": "Menu updated successfully",
  "menu_id": "4",
  "data": {
    "id": 4,
    "name": "classic menu",
    "description": "",
    "currency": "AED",
    "customer_id": 437,
    "location_id": [
      907,
      910,
      922,
      909,
      908
    ],
    "is_active": 1,
    "brand_id": 414,
    "label_id": 51,
    "tax_id": 16,
    "brand_name": null,
    "label_name": "Coffee beans",
    "tax_name": "Food Card Tax",
    "tax_rate": 5,
    "categories": [
      {
        "category_id": 15481,
        "category_name": "Alternate Options"
      }
    ],
    "products": [
      {
        "product_id": 247653,
        "category_id": 15481,
        "actual_price": 13,
        "product_name": null,
        "product_type": null
      }
    ]
  }
}

validate_coupon

POST /api/v2/coupons/validate. Why: Server check of a coupon code before you apply it — what web-pos calls after finding the code in list_selling_coupons. Checks: code exists for the tenant, linked to location_id, is_active = 1, now within start_date/end_date, total usage_limit, per-customer per_user_limit (only if crm_user_id is sent), fixed/percentage discount not larger than order_total (basket mode), and for product-scoped coupons that the basket holds a linked product. Returns the coupon row and, for product-scoped coupons, target_product_id. Does NOT return a discount amount and does NOT consume the coupon (usage is counted when sync_invoice saves the order). No side effects. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"validate_coupon"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
coupon_code string required Required.
location_id number required Required.
order_total number optional Required when product_ids is not sent (basket coupon) — missing, non-numeric or negative → 400 "Order total is required and cannot be negative.".
crm_user_id number optional Optional.
product_ids array<number> optional Send ONLY for a product-scoped coupon (list_selling_coupons product_ids non-empty): the product_id of every active cart line.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "validate_coupon",
  "coupon_code": "example",
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Coupon is valid.",
  "target_product_id": 270965,
  "data": {
    "id": 1001,
    "customer_id": 437,
    "location_id": null,
    "code": "COLDBREW5",
    "description": "5 off Cold Brew",
    "discount_type": "value",
    "discount_value": 5,
    "min_order_value": null,
    "start_date": "2026-09-01T00:00:00.000Z",
    "end_date": "2026-12-31T23:59:59.000Z",
    "usage_limit": 100,
    "per_user_limit": 1,
    "is_active": 1,
    "created_at": "2026-09-01T08:00:00.000Z",
    "updated_at": "2026-09-01T08:00:00.000Z",
    "current_usage": 3,
    "linked_products": [
      {
        "id": 270965,
        "name": "(1) Cold Brew"
      }
    ],
    "user_usage": 0
  }
}

create_coupon

POST /api/coupons. Why: Create a coupon linked to one or more branches, optionally limited to products (list_coupon_products_by_locations) or categories (list_coupon_categories_by_locations) — not both. Send is_active: 1, or the coupon is stored inactive and never validates. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_coupon"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
code string required Required.
discount_type enum: percentage, value required Required (NOT NULL column; missing → 500).
location_id array<number> required Required.
discount_value number optional Optional (null = no discount).
is_active number optional 1 = active, 0 = inactive.
description string optional Optional.
min_order_value number optional Optional.
start_date string optional Optional.
end_date string optional Optional.
usage_limit number optional Optional.
per_user_limit number optional Optional.
product_ids array<number> optional Optional.
category_ids array<number> optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_coupon",
  "code": "example",
  "discount_type": "example",
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Your coupon has been successfully created with multiple locations.",
  "data": {
    "id": 1001,
    "customer_id": 437,
    "location_id": 907,
    "code": "COLDBREW5",
    "description": "5 off Cold Brew",
    "discount_type": "value",
    "discount_value": 5,
    "min_order_value": null,
    "start_date": "2026-09-01T00:00:00.000Z",
    "end_date": "2026-12-31T23:59:59.000Z",
    "usage_limit": 100,
    "per_user_limit": 1,
    "is_active": 1,
    "created_at": "2026-09-01T08:00:00.000Z",
    "updated_at": "2026-09-01T08:00:00.000Z",
    "current_usage": 0
  }
}

update_coupon

PUT /api/coupons/:id. Why: Change a coupon. Coupon columns are PARTIAL (only the ones you send change), but branch links and product/category links are FULLY REPLACED: always send location_id, and send product_ids or category_ids again (from get_coupon) or they are removed. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_coupon"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Coupon id (list_coupons data[].id).
location_id array<number> required Required.
code string optional Optional.
description string optional Optional.
discount_type enum: percentage, value optional Optional.
discount_value number optional Optional.
min_order_value number optional Optional.
start_date string optional Optional.
end_date string optional Optional.
usage_limit number optional Optional.
per_user_limit number optional Optional.
is_active number optional Optional.
product_ids array<number> optional Product links.
category_ids array<number> optional Category links (not enforced at checkout).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_coupon",
  "id": 11,
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Your coupon has been successfully updated.",
  "data": {
    "id": 1001,
    "customer_id": 437,
    "location_id": 907,
    "code": "COLDBREW5",
    "description": "5 off Cold Brew",
    "discount_type": "value",
    "discount_value": 5,
    "min_order_value": null,
    "start_date": "2026-09-01T00:00:00.000Z",
    "end_date": "2026-12-31T23:59:59.000Z",
    "usage_limit": 200,
    "per_user_limit": 1,
    "is_active": 1,
    "created_at": "2026-09-01T08:00:00.000Z",
    "updated_at": "2026-09-10T08:00:00.000Z",
    "current_usage": 3
  }
}

create_discount

POST /api/discounts. Why: Add a discount (discount code) that web-pos offers in its discount dialog (get_user_extras data.discount_code[]) at the listed branches while active and in date. Not a coupon (create_coupon). Branch ids from list_locations; scope ids: products list_selling_products, categories list_selling_categories, channels list_selling_channels (channel_id), order types list_order_types, payment methods list_selling_payment_methods. Check list_discounts first so the name (= the code cashiers type) is not a duplicate. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_discount"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id array<number> required Branches the discount is valid at (list_locations ids).
name string required Display name, also the code the cashier types at the POS.
discount_type number optional Kind of value: 1 (or 2) = percentage, 0 = fixed amount.
discount_percent number optional The value: percent (0–100) when discount_type = 1, money amount (≥ 0) when 0.
is_changeable string optional 1/true = changeable (stored as discount_type = 1), 0/false/omitted = fixed (discount_type = 0).
max_cap_amount number optional Maximum money off (percentage discounts).
min_basket_value number optional Minimum order total for the code to be eligible at the POS.
is_active string optional 1 = active, 0 = inactive (Number(value); "true" as a string → 0).
start_date string required First valid day, "YYYY-MM-DD".
end_date string required Last valid day (inclusive), "YYYY-MM-DD".
start_time string optional Daily window start "HH:MM" or "HH:MM:SS" (HH:MM → HH:MM:00).
end_time string optional Daily window end, same format; earlier than start_time = wraps midnight.
is_available_on string optional CSV of lowercase full weekday names ("monday,tuesday,…,sunday"); "" → null = every day.
apply_discount_to enum: entire_basket, basket, category, product optional Scope: "entire_basket" (whole order; "basket" / "entire basket" are stored as "entire_basket"), "category" (send category_ids), "product" (send product_ids; "i…
category_ids array<number> optional Categories for apply_discount_to = "category" (csv string also accepted).
product_ids array<number> optional Products for apply_discount_to = "product" (csv string also accepted).
channel_ids array<number> optional Sales channels the code is valid on (csv also accepted).
order_type_ids array<number> optional Order types (csv also accepted).
payment_methods array<number> optional Payment methods (csv also accepted).
is_auto_apply string optional 1/true = auto apply, 0/false = cashier picks it.
is_promotion string optional 1/true = promotion.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_discount",
  "location_id": 11,
  "name": "Iced Latte",
  "start_date": "example",
  "end_date": "example"
}'

Example Response

{
  "status": true,
  "message": "Your discount has been successfully created.",
  "data": {
    "id": 2400,
    "name": "Weekend 10%",
    "discount_percent": 10,
    "max_cap_amount": 50,
    "min_basket_value": 30,
    "location_id": null,
    "is_active": 1,
    "start_date": "2026-10-01T00:00:00.000Z",
    "end_date": "2026-10-31T00:00:00.000Z",
    "discount_type": 0,
    "category_ids": null,
    "apply_discount_to": "entire_basket",
    "product_ids": null,
    "start_time": "00:00:00",
    "end_time": "23:59:00",
    "is_available_on": "friday,saturday,sunday",
    "is_auto_apply": 0,
    "is_promotion": 0,
    "payment_methods": null,
    "channel_ids": null,
    "customer_id": 437,
    "order_type_ids": null,
    "discount_method": "percentage",
    "discount_value": 10
  }
}

update_discount

PUT /api/discounts/:id. Why: Change a discount (discount code): value, scope, branches, dates, active flag. NOT a patch — read the row from list_discounts (loc="All"; get_discount is broken) and send every field again, or the omitted scope/value/time fields are cleared. The change reaches web-pos with its next get_user_extras. Scope ids: list_selling_products, list_selling_categories, list_selling_channels, list_selling_payment_methods, list_order_types; branches list_locations. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_discount"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Discount id (list_discounts data[].id).
location_id array<number> required Branches the discount is valid at (list_locations ids).
name string optional Display name, also the code the cashier types at the POS.
discount_type number optional Kind of value: 1 (or 2) = percentage, 0 = fixed amount.
discount_percent number optional The value: percent (0–100) when discount_type = 1, money amount (≥ 0) when 0.
is_changeable string optional 1/true = changeable (stored as discount_type = 1), 0/false/omitted = fixed (discount_type = 0).
max_cap_amount number optional Maximum money off (percentage discounts).
min_basket_value number optional Minimum order total for the code to be eligible at the POS.
is_active string optional 1 = active, 0 = inactive (Number(value); "true" as a string → 0).
start_date string optional First valid day, "YYYY-MM-DD".
end_date string optional Last valid day (inclusive), "YYYY-MM-DD".
start_time string optional Daily window start "HH:MM" or "HH:MM:SS" (HH:MM → HH:MM:00).
end_time string optional Daily window end, same format; earlier than start_time = wraps midnight.
is_available_on string optional CSV of lowercase full weekday names ("monday,tuesday,…,sunday"); "" → null = every day.
apply_discount_to enum: entire_basket, basket, category, product optional Scope: "entire_basket" (whole order; "basket" / "entire basket" are stored as "entire_basket"), "category" (send category_ids), "product" (send product_ids; "i…
category_ids array<number> optional Categories for apply_discount_to = "category" (csv string also accepted).
product_ids array<number> optional Products for apply_discount_to = "product" (csv string also accepted).
channel_ids array<number> optional Sales channels the code is valid on (csv also accepted).
order_type_ids array<number> optional Order types (csv also accepted).
payment_methods array<number> optional Payment methods (csv also accepted).
is_auto_apply string optional 1/true = auto apply, 0/false = cashier picks it.
is_promotion string optional 1/true = promotion.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_discount",
  "id": 11,
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Your discount has been successfully updated.",
  "data": {
    "id": 2400,
    "name": "Weekend 15%",
    "discount_percent": 15,
    "max_cap_amount": 50,
    "min_basket_value": 30,
    "location_id": null,
    "is_active": 1,
    "start_date": "2026-10-01T00:00:00.000Z",
    "end_date": "2026-10-31T00:00:00.000Z",
    "discount_type": 0,
    "category_ids": null,
    "apply_discount_to": "entire_basket",
    "product_ids": null,
    "start_time": "00:00:00",
    "end_time": "23:59:00",
    "is_available_on": "friday,saturday,sunday",
    "is_auto_apply": 0,
    "is_promotion": 0,
    "payment_methods": null,
    "channel_ids": null,
    "customer_id": 437,
    "order_type_ids": null,
    "discount_method": "percentage",
    "discount_value": 15
  }
}

create_payment_method

POST /api/payment-methods. Why: Add a tender type and enable it at chosen branches. The name becomes the invoice payment_method[] key, so match what cashiers/integrations will send (case-insensitive). Check list_payment_methods (no loc) first — names must be unique across the tenant. Linkage: the payment method NAME is the key in an invoice's payment_method[] (e.g. [{"cash": 30}]) and is matched case-insensitively against the methods linked to the invoice's branch — so renaming or deleting a method changes what the POS can settle with. Branch availability lives in payment_methods_locations (location_id[] in these tools); on master tenants payment_methods.location_id is NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_payment_method"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Payment method name (trimmed; blank/omitted → 400 "Payment method name is required").
location_id array<number> required Branch ids to enable this method at, as an array, e.g.
is_enable number optional 1 = enabled, 0 = disabled.
priority number optional Sort priority (lower first in the unfiltered list).
type string optional Free-form tender type, e.g.
sync_locations number optional Master flag: 1 (or true / "1") = shared master method; anything else → 0.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_payment_method",
  "name": "Iced Latte",
  "location_id": 11
}'

Example Response

{
  "success": true,
  "message": "Payment method created successfully",
  "data": {
    "name": "Wallet",
    "is_enable": 1,
    "priority": 3,
    "type": "wallet",
    "sync_locations": 1,
    "id": 2140,
    "customer_id": 437,
    "location_id": [
      907,
      908
    ]
  }
}

update_payment_method

PUT /api/payment-methods/:id. Why: Rename, enable/disable, re-prioritise or change the branches of a payment method. FULL REPLACE: name, is_enable, priority, type and sync_locations are all overwritten (omitted → NULL/0), so call get_payment_method first and resend everything. Renaming changes the invoice payment_method[] key — invoices/integrations still sending the old name will no longer match. Linkage: the payment method NAME is the key in an invoice's payment_method[] (e.g. [{"cash": 30}]) and is matched case-insensitively against the methods linked to the invoice's branch — so renaming or deleting a method changes what the POS can settle with. Branch availability lives in payment_methods_locations (location_id[] in these tools); on master tenants payment_methods.location_id is NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_payment_method"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Payment method id (list_payment_methods).
name string required Payment method name (trimmed; blank/omitted → 400 "Payment method name is required").
location_id array<number> optional FULL REPLACEMENT of the branch list, as an array of branch ids.
is_enable number optional 1 = enabled, 0 = disabled.
priority number optional Sort priority (lower first in the unfiltered list).
type string optional Free-form tender type, e.g.
sync_locations number optional Master flag: 1 (or true / "1") = shared master method; anything else → 0.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_payment_method",
  "id": 11,
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Payment method updated successfully",
  "data": {
    "name": "Wallet",
    "is_enable": 1,
    "priority": 0,
    "type": "wallet",
    "sync_locations": 1,
    "id": "2135",
    "customer_id": 437,
    "location_id": [
      907,
      908,
      909
    ]
  }
}

create_order_type

POST /api/order-type. Why: Add a custom order type for the whole tenant (all branches get it), e.g. "Talabat" behaving as DELIVERY. Its name becomes the order_type string invoices carry, so choose it deliberately. Check list_order_types first — names must be unique per tenant. Order types are tenant-wide (order_type.customer_id) — NOT per branch; every branch sees the same list and there is no loc filter. The order type NAME (order_type here, `title` in GET /api/v2/order-types) is the string stored in an invoice's order_type; order_type_code is the fixed behaviour (DINE_IN / TAKEAWAY / DELIVERY / DRIVE_THRU) the POS uses to decide table/driver/address flow. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_order_type"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
order_type string required Order type name shown on the POS and stored as the invoice order_type string, e.g.
order_type_code enum: DINE_IN, TAKEAWAY, DELIVERY, DRIVE_THRU required Behaviour code (list_order_type_codes): DINE_IN | TAKEAWAY | DELIVERY | DRIVE_THRU.
icon_key string required Iconify icon key "prefix:name" (e.g.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_order_type",
  "order_type": "example",
  "order_type_code": "example",
  "icon_key": "example"
}'

Example Response

{
  "success": true,
  "message": "Order type created successfully",
  "data": {
    "id": 1201,
    "order_type": "Talabat",
    "icon_key": "carbon:delivery",
    "order_type_code": "DELIVERY",
    "is_system_default": 0
  }
}

update_order_type

PUT /api/order-type/:id. Why: Rename a custom order type or change its icon. The 4 system defaults (is_system_default = 1) cannot be edited (403); the behaviour code can never change (delete and recreate instead). Renaming changes the order_type string that NEW invoices carry — old invoices keep the old name, so reports will show both. Send BOTH order_type and icon_key every time (full replace). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_order_type"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Order type id (list_order_types).
order_type string required New name (trimmed).
icon_key string required Iconify key "prefix:name" (same rules as create).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_order_type",
  "id": 11,
  "order_type": "example",
  "icon_key": "example"
}'

Example Response

{
  "success": true,
  "message": "Order type updated successfully",
  "data": {
    "id": 1201,
    "order_type": "Talabat Delivery",
    "icon_key": "carbon:delivery",
    "order_type_code": "DELIVERY",
    "is_system_default": 0
  }
}

sync_invoice

POST /api/v2/invoice-sync-v3. Why: Create or update a POS order — web-pos 'Pay' / 'Save order' (finalizeSyncPayload). One call = the whole order: header, every line, and (when paid) the tender. New uuid → new order on the session branch with order_no / customer_invoice_id; the same uuid → update of that OPEN order (full resend). status 'open' saves a pay-later / dine order (stock reserved), status 'completed' + is_paid 1 closes a paid sale (payments, stock commit, quota, loyalty, wallet, coupon usage, CRM totals, SMS, KDS push). The server stores the totals as sent — it computes no prices, VAT or totals and validates none of them. Feed it from list_selling_products / list_selling_modifiers / list_selling_addons / list_selling_brands / list_selling_order_types / list_selling_tables / list_pos_crm_users / list_selling_payment_methods, validate_coupon, validate_loyalty_burn and list_product_quota. Read it back with list_orders_history or list_dine_orders; afterwards cancel_invoice, refund_invoice or update_invoice_payment_methods. MCP wraps this one invoice into the array the backend expects. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"sync_invoice"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.
request_id string optional Optional but recommended.
status enum: open, completed required Required.
is_paid number optional Optional.
items array<object> required Required.
items_number number optional Optional.
subtotal number required Required.
vat number required Required.
payable number required Required.
discountamount number optional Optional.
discountpct number optional Optional.
discount_id number optional Optional.
discount_reason string optional Optional.
is_vat_on_discount_exclude number optional Optional.
is_discount_on_total number optional Optional.
row_discount number optional Optional.
charges_fee number optional Optional.
delivery_charges number optional Optional.
paid number optional Optional (send when is_paid = 1).
change number optional Optional.
tip_amount number optional Optional.
payment_method array<string> optional Tender rows (send with is_paid = 1).
card_digits string optional Optional.
is_wallet_used number optional Optional.
wallet_current_balance number optional Optional.
created_at number optional Optional.
paid_at number optional Optional (send when is_paid = 1).
order_type string optional Optional.
table_id number optional Optional.
dining_guests number optional Optional.
token_number number optional Optional.
round number optional Optional.
brand_id number optional Optional.
channel_id number optional Optional.
channel_name string optional Optional.
shift_id number optional Optional.
guest_name string optional Optional.
note string optional Optional.
notes array<string> optional Optional.
info_name string optional Optional.
info_address string optional Optional.
info_phone string optional Optional.
info_email string optional Optional.
phone_number string optional Optional.
crm_user_id number optional Optional.
crm_delivery_area string optional Optional.
plate_number string optional Optional.
coupon_id number optional Optional.
coupon_code string optional Optional.
coupon_amount number optional Optional.
burn_points number optional Optional.
created_by_user_id number optional Optional.
close_by_user_id number optional Optional.
modified_by_user_ids array<number> optional Optional.
is_kds_printed number optional Optional, read on CREATE only.
app_version string optional Optional.
app_os_version string optional Optional.
build_number string optional Optional.
os_type string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "sync_invoice",
  "uuid": "example",
  "status": "example",
  "items": [
    11
  ],
  "subtotal": 1,
  "vat": 1,
  "payable": 1
}'

Example Response

{
  "status": true,
  "message": "order saved successfully",
  "data": [
    {
      "id": "5d0b7a6c4e19",
      "oid": 25,
      "customer_invoice_id": 25,
      "items_number": 1,
      "aggregator_order_id": null,
      "exchange_rate": null,
      "paid_in_usd": null,
      "is_wastage": null,
      "is_accepted": null,
      "external_order_ready": 0,
      "is_discount_on_total": null,
      "subtotal": 10.48,
      "vat": 0.52,
      "payable": 11,
      "discountamount": 0,
      "items_discount_amount": 0,
      "is_ticket_available": 1,
      "is_wallet_used": 0,
      "discountpct": 0,
      "paid": 11,
      "change": 0,
      "note": null,
      "uuid": "3f6c1d2a-8b4e-4c7a-9e21-5d0b7a6c4e19",
      "created_at": 1790242200,
      "updated_at": 1790242201,
      "customer_id": 437,
      "location_id": 907,
      "guest_name": "Walk-in",
      "info_name": null,
      "info_address": null,
      "info_phone": null,
      "info_email": null,
      "status": "completed",
      "user": "owner@example.com",
      "table_id": 0,
      "order_id": null,
      "payment_method": [
        {
          "CASH": 11
        }
      ],
      "earn_points": 0,
      "burn_points": 0,
      "burn_points_amount": 0,
      "available_points": 0,
      "available_points_amount": 0,
      "charges_fee": 0,
      "delivery_charges": 0,
      "phone_number": "",
      "loyalty_mobile_number": "",
      "loyalty_mobile_code": "",
      "crm_delivery_area": null,
      "row_discount": 0,
      "invoice_id": null,
      "done_at": null,
      "paid_at": 1790242200,
      "period": null,
      "is_kds_printed": 0,
      "is_invoice_printed": 0,
      "plate_number": null,
      "crm_user_id": 0,
      "crm_user_name": "",
      "order_no": 3,
      "token_number": null,
      "brand_id": null,
      "order_type": "Takeaway",
      "tip_amount": 0,
      "created_by_user_id": null,
      "close_by_user_id": null,
      "modified_by_user_ids": [],
      "channel_id": null,
      "is_vat_on_discount_exclude": 0,
      "discount_id": null,
      "dining_guests": null,
      "channel_name": null,
      "is_paid": 1,
      "discount_reason": null,
      "coupon_id": 0,
      "coupon_amount": 0,
      "coupon_code": null,
      "printed_invoice_receipts": 0,
      "printed_kds_receipts": 0,
      "round": null,
      "rider_pickup_at": null,
      "rider_arrived_at": null,
      "order_type_icon_key": "boxicons:takeaway",
      "notes": [],
      "order_type_icon": "https://images.weserv.nl/?url=https%3A%2F%2Fapi.iconify.design%2Fboxicons%2Ftakeaway.svg%3Fwidth%3D128%26height%3D128&output=png",
      "payment_methods": [
        {
          "CASH": {
            "amount": 11,
            "tip_amount": null
          }
        }
      ],
      "payments": [
        {
          "payment_method": "CASH",
          "amount": 11,
          "tip_amount": 0
        }
      ],
      "items": [
        {
          "id": 25504190,
          "identifier": "9a4f0c3e-6d21-4b8a-8e5f-2c7d1b0a9e44",
          "name": "Flat White",
          "name_localize": null,
          "base_price": null,
          "actual_price": 11,
          "preparation_time": null,
          "price": 11,
          "qty": 1,
          "is_weightage_item": 0,
          "unit_id": 0,
          "subtotal": 11,
          "line_total": null,
          "line_vat": null,
          "line_total_without_vat": null,
          "invoice_uuid": "3f6c1d2a-8b4e-4c7a-9e21-5d0b7a6c4e19",
          "product_id": 257506,
          "extras": null,
          "vat": 0.52,
          "is_updated": 0,
          "updated_qty": 0,
          "is_addon": 0,
          "is_refunded": 0,
          "refund_quantity": 0,
          "refund_reason": null,
          "note": null,
          "addon_linked_with": "9a4f0c3e-6d21-4b8a-8e5f-2c7d1b0a9e44",
          "discount_pct": 0,
          "discount_amount": 0,
          "modifier": [],
          "transferred_to": null,
          "transferred_to_uuid": null,
          "transferred_quantity": null,
          "transferred_to_uuid_list": [],
          "transferred_quantities": [],
          "transfer_recipients": [],
          "created_at": 1790242200,
          "updated_at": 1790242200,
          "is_prepared": null,
          "type": "Espresso-Based Drinks",
          "label_id": 50,
          "item_status": "active",
          "linked_uuid": null,
          "original_linked_uuid": null,
          "disposition_type": null,
          "image": null,
          "is_combo": null,
          "is_deleted": null,
          "deleted_at": null,
          "deleted_qty": null,
          "coupon_amount": 0,
          "coupon_id": 0,
          "coupon_code": "",
          "updated_by_user_ids": [],
          "re": null,
          "ri": 0,
          "is_wastage": 0,
          "discount_id": null,
          "discount_name": null,
          "round": null,
          "combo_items": null
        }
      ]
    }
  ]
}

cancel_invoice

PUT /api/v2/invoice-cancel/:uuid. Why: Void / cancel an order — the web-pos 'Cancel Order' button (order history: status open, opened, completed or partially_refunded) and 'Void Order' on an open dine table. Works on OPEN (unpaid) and COMPLETED (paid) orders. Sets invoices.status = 'cancelled' (the order drops out of sales, list_dine_orders and list_kds_orders), releases or returns the order's stock (master-inventory tenants), books every payment of the order as a cash-out (withdrawal), reverses a wallet payment, gives product-quota units back (list_product_quota), refreshes the CRM customer's totals and tells Grubtech / Limetray / Kiply. It CANNOT be undone and a cancelled order can no longer be refunded or have its payment changed. Use refund_pos_invoice (pending — route not deployed) instead when the sale stands but money (all or some lines) goes back to the guest. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"cancel_invoice"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.
note string optional Optional.
re string optional Optional.
is_wastage number optional Stock disposition.
ri number optional Stock disposition.
close_by_user_id number optional Optional.
brand_id number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "cancel_invoice",
  "uuid": "example"
}'

Example Response

{
  "status": true,
  "message": "Order canceled successfully"
}

refund_invoice

POST /api/v2/orders-refund. Why: LEGACY money-only full refund (the iOS route; web-pos does not call it). is_refund=1 marks the whole invoice refunded (invoices.is_refunded = 1 — status stays 'completed') and adds ONE negative payment of the full payable; undo_refund=1 reverses exactly that. No stock, lines, quota, wallet, loyalty or KDS change. Use it only when the user wants the money recorded as refunded without touching stock, or to undo a refund made by this tool. For web-pos parity (status 'refunded'/'partially_refunded', per-line refunds, stock returned or wasted) use refund_pos_invoice (pending — route not deployed). Never use both tools on the same order. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"refund_invoice"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
order_uuid string required Required.
is_refund number optional Send 1 to refund the whole invoice.
undo_refund number optional Send 1 to undo a full refund previously made with is_refund=1 (only when is_refund is not sent).
refund_payment_method string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "refund_invoice",
  "order_uuid": "example"
}'

Example Response

{
  "status": true,
  "message": "Invoice refunded successfully"
}

update_invoice_payment_methods

PUT /api/v2/invoice/:uuid/payment-methods. Why: Re-book the tender of a PAID order (web-pos 'Change Payment Method' in order history, e.g. the guest paid by card, not cash). REPLACES all payments rows of the order with payment_methods and rewrites invoices.payment_method / paid / change / tip_amount / card_digits (and shift_id). The order total, lines and status do not change. Method names come from list_selling_payment_methods. Logged as an order activity 'Payment Method Updated' (get_invoice_activities). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_invoice_payment_methods"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.
payment_methods array<string> required Required.
paid number optional Optional.
change number optional Optional.
tip_amount number optional Optional.
card_digits string optional Optional.
shift_id number optional Optional.
reason string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_invoice_payment_methods",
  "uuid": "example",
  "payment_methods": [
    11
  ]
}'

Example Response

{
  "status": true,
  "http_status": 200,
  "message": "Payment method updated successfully",
  "data": {
    "invoice_id": 9412231,
    "invoice_uuid": "0b7c2f4e-3a91-4d2e-9c55-7e1f0a6b8d21",
    "customer_invoice_id": 1187,
    "invoice_status": "completed",
    "shift_id": null,
    "payment_methods": [
      {
        "card": {
          "amount": 11,
          "tip_amount": 0
        }
      }
    ],
    "previous_payment_methods": [
      {
        "payment_method": "Cash",
        "payment_method_id": 1631,
        "amount": 11,
        "tip_amount": 0
      }
    ],
    "change_rows": [
      {
        "change": "Cash → card",
        "from_payment_method": "Cash",
        "to_payment_method": "card",
        "from_is_cash": true,
        "to_is_cash": false,
        "amount": 11,
        "cash_impact": -11
      }
    ],
    "cash_before": 11,
    "cash_after": 0,
    "cash_impact": -11,
    "reason": null,
    "paid": "11.00",
    "change": "0.00",
    "tip_amount": "0.00",
    "total_amount": "11.00",
    "invoice_payable": "11.00",
    "card_digits": null
  }
}

log_invoice_activities

POST /api/v2/activities. Why: Write ONE audit row to an order's Activity trail — the batch activity writer web-pos uses (the server never logs cashier actions itself, except update_invoice_payment_methods). Log what you did through other tools so the trail matches a web-pos till: e.g. after cancel_invoice title "Void" message "Cancelled order reason: X"; after sync_invoice paid title "Paid" message "paid [30] using [CASH]"; title "Order Created" for a new order. Read back with get_invoice_activities (same uuid). Skip it when get_user_extras ios_modules.is_activities_enabled = 0. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"log_invoice_activities"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
identifier string required Required (by this tool).
uuid string required Required.
title string required Required.
message string optional Optional.
user_id number optional Optional.
username string optional Optional.
role string optional Optional.
timestamp number optional Optional but send it.
platform string optional Optional.
device_model string optional Optional.
device_id string optional Optional.
app_version string optional Optional.
location_id number optional Optional.
session_id string optional Optional.
order_detail string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "log_invoice_activities",
  "identifier": "example",
  "uuid": "example",
  "title": "example"
}'

Example Response

{
  "request": {
    "identifier": "5b0f1c2e-8a4d-4b7e-9d3a-2f6c1e0a9b71",
    "uuid": "f5bd46f3-377a-477d-b748-876e9aab0063",
    "title": "Void",
    "message": "Cancelled order reason: wrong item",
    "user_id": 4734,
    "username": "Cashier One",
    "role": "admin",
    "timestamp": 1790257026,
    "platform": "mcp",
    "device_model": "MCP agent",
    "location_id": 907,
    "session_id": ""
  },
  "response": {
    "status": true,
    "message": "Activities have been successfully saved.",
    "data": [
      {
        "identifier": "5b0f1c2e-8a4d-4b7e-9d3a-2f6c1e0a9b71",
        "uuid": "f5bd46f3-377a-477d-b748-876e9aab0063",
        "title": "Void",
        "message": "Cancelled order reason: wrong item",
        "user_id": 4734,
        "username": "Cashier One",
        "role": "admin",
        "timestamp": 1790257026,
        "platform": "mcp",
        "device_model": "MCP agent",
        "location_id": 907,
        "session_id": "",
        "id": 1790257026487
      }
    ]
  },
  "note": "Code-derived (write route not called live)."
}

update_kds_item_status

PUT /api/v2/kds-item-status. Why: Tick (or un-tick) one line on the kitchen board — the web-pos KDS line tap. Writes invoice_items.is_prepared for that line only; the order stays on the board until it is bumped with change_kds_order_status. Single line per call (no bulk): call once per line. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_kds_item_status"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
item_id number required Required.
is_prepared number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_kds_item_status",
  "item_id": 11,
  "is_prepared": 1
}'

Example Response

{
  "status": true,
  "message": "KDS item Chicken Shawarma is ready to served."
}

update_kds_order_status

PUT /api/v2/kds-order-status. Why: Accept or reject a ticket on the kitchen board — the web-pos KDS ACCEPT / REJECT buttons shown on PENDING tickets (is_accepted 0/null: online/aggregator orders and, in web-pos, new POS orders too). Writes invoices.is_accepted. Accepting an order whose status is 'pending_acceptance' also completes it (status 'completed'). Reject only flags the ticket (greyed CANCELLED on the KDS) — it does NOT cancel the sale; use cancel_invoice for that. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_kds_order_status"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.
status number required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_kds_order_status",
  "uuid": "example",
  "status": 1
}'

Example Response

{
  "status": true,
  "message": "Kds Order 10433 is Accepted"
}

change_kds_order_status

PUT /api/v2/kds-change-status/:uuid. Why: BUMP a ticket off the kitchen board (order ready) or RECALL it back — the web-pos KDS DONE/bump button, dismiss of a rejected ticket, UNDO and the recall list. Bump sets invoices.done_at = now: the order leaves list_kds_orders and shows in the is_order_rebump=1 list; recall (is_order_rebump=1) clears done_at and it is back on the board. Bump also notifies: 'order ready' push to the branch's POS devices (with the table name for dine-in), Ably 'ready', a one-time WhatsApp 'order ready' to an opted-in guest, and — when the user's external_order_ready setting is on — 'prepared' to Grubtech / Limetray. It does NOT change the invoice status or payment. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"change_kds_order_status"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Required.
is_order_rebump number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "change_kds_order_status",
  "uuid": "example"
}'

Example Response

{
  "status": true,
  "message": "Order status updated"
}

update_product_availability

POST /api/v2/products/availability. Why: The POS sold-out switch (long-press on a tile in web-pos): marks one product sold out (UNAVAILABLE) or back in stock (AVAILABLE) at one branch. Writes products_locations.is_sold_out (what list_sold_out_products and list_selling_products[].is_sold_out return), updates the Firestore list other tills listen to, and also pushes the change to Grubtech (delivery aggregator). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_product_availability"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
itemId string required Required.
availability enum: AVAILABLE, UNAVAILABLE, HIDDEN required Required (case-insensitive).
itemType enum: PRODUCT, MODIFIER_ITEM optional Optional.
location_id string optional Optional.
brandId string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_product_availability",
  "itemId": "example",
  "availability": "example"
}'

Example Response

{
  "status": true,
  "message": "Product have been updated.",
  "data": {
    "message": "Grubtech update availability failed",
    "menuId": "location-store-907-menu",
    "itemId": "PRO-257508",
    "storeId": "store-907",
    "availability": "UNAVAILABLE",
    "itemType": "PRODUCT",
    "is_sold_out": 1,
    "soldOutPersist": {
      "updated": true,
      "affectedRows": 1,
      "is_sold_out": 1
    },
    "firestoreSync": {
      "ok": true,
      "action": "set",
      "docId": "257508_907"
    },
    "grubtechUrl": "https://api.grubtech.io/menu/v1/menus/location-store-907-menu/items/PRO-257508/availability",
    "hint": "menuId/storeId must match a menu synced via sync/products (e.g. storeId: store-59, menuId: location-store-59-menu). Send location_id to derive them automatically.",
    "grubtechStatus": 404,
    "grubtechError": {}
  }
}

update_product_quota

POST /api/v2/user-product-quota. Why: Create a quota for one product at the session branch: it may sell total_quota units, the sold counter starts at 0. CREATE ONLY — if the product already has an active quota the server answers 409 (it cannot change or delete one through MCP). Not used by web-pos (quotas are set on iOS / the dashboard). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_product_quota"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
product_id string required Required.
total_quota string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_product_quota",
  "product_id": 11,
  "total_quota": "example"
}'

Example Response

{
  "status": true,
  "message": "Quota created successfully",
  "data": {
    "counter_id": 15848,
    "assignment_id": 26,
    "product_id": 257508,
    "product_name": "Mocha",
    "total_quota": 20,
    "sold_count": 0,
    "remaining_stock": 20,
    "history": []
  }
}

mark_product_sold_out

POST /api/v2/user-product-quota/mark-sold-out. Why: Use up a product's quota at the session branch: sets sold_count = total_quota so remaining_stock becomes 0 (sold out on the menu). Only works for products that HAVE a quota (404 otherwise). It does NOT set is_sold_out — for a product without a quota use update_product_availability. Not used by web-pos. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"mark_product_sold_out"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
product_id string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "mark_product_sold_out",
  "product_id": 11
}'

Example Response

{
  "status": true,
  "message": "Product marked as sold out",
  "data": {
    "product_id": 257508,
    "total_quota": 20,
    "sold_count": 20,
    "remaining_stock": 0
  }
}

reset_product_quota

POST /api/v2/user-product-quota/:counterId/reset. Why: Start a quota over: sets the counter's sold_count to 0 (remaining_stock = total_quota again) and deletes its sale history. The quota itself (total_quota) is kept. This is the only 'daily reset' — nothing resets counters automatically. web-pos has this button on the Product Sold Counter page but it is currently hidden. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"reset_product_quota"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
counterId string required Required.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "reset_product_quota",
  "counterId": "example"
}'

Example Response

{
  "status": true,
  "message": "Counter reset successfully",
  "data": {
    "counter_id": 15848,
    "product_id": 257508,
    "sold_count": 0
  }
}

shift_clock_in

POST /api/v2/shifts/clock-in. Why: Clock in / open shift. Body typically opening cash + device_id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"shift_clock_in"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
opening_cash string optional Cash in the drawer at clock-in (float).
device_id string optional POS device identifier the shift runs on (optional).
location_id string optional Location id, csv, or array — the branch(es) this row belongs to.
notes string optional Opening note for the shift.
denominations string optional Cash-count per note: { "500": n, "200": n, "100": n, "50": n, "20": n, "10": n, "5": n, "1": n, "coins": amount }.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "shift_clock_in"
}'

Example Response

{
  "status": true
}

shift_clock_out

POST /api/v2/shifts/clock-out. Why: Clock out / close shift. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"shift_clock_out"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
closing_cash string optional Cash counted at clock-out.
notes string optional Closing note for the shift.
denominations string optional Cash-count per note: { "500": n, "200": n, "100": n, "50": n, "20": n, "10": n, "5": n, "1": n, "coins": amount }.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "shift_clock_out"
}'

Example Response

{
  "status": true
}

create_drawer_transaction

POST /api/v2/shifts/drawer-transactions. Why: Record cash in/out. Include uuid for idempotency when POS does. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_drawer_transaction"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Client-generated UUID for this cash movement (idempotency key).
shift_id string required Open shift id (get_active_shift).
direction enum: in, out required 'in' = cash added to the drawer (paid-in), 'out' = cash removed (paid-out / expense).
category_id string required Drawer category id (list_drawer_categories), e.g.
amount string required Cash amount, > 0.
description string required What the movement was for — printed on the shift report.
receipt_ref string optional External receipt / voucher reference (optional).
brand_id string optional Brand id when the location runs multiple brands (optional).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_drawer_transaction",
  "uuid": "example",
  "shift_id": 11,
  "direction": "example",
  "category_id": 11,
  "amount": "example",
  "description": "example"
}'

Example Response

{
  "status": true
}

update_user_permissions

POST /api/v2/update-user-permissions. Why: Save POS switches of the SESSION user (the API key user) — what web-pos Settings does: one key per call (e.g. { discount_codes: 1 }, { tip_amount: 0 }, { channel_id: 463 } from Settings → Channel). PARTIAL merge: only the keys you send change. Turns features on/off for this user's till: is_channels_enable + channel_id (list_selling_channels), is_soldout (update_product_availability), discount_codes (discount codes + list_selling_coupons / validate_coupon), is_change_payment_method_enabled (update_invoice_payment_methods), tip_amount, dining_guest_count, combo_options. Read the current values first with list_pos_users (own row). Only role admin may call it (else 401). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_user_permissions"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
print_receipt number optional Optional.
kds_auto_receipt number optional Optional.
invoice_auto_receipt number optional Optional.
cart_options number optional Optional.
print_and_save number optional Optional.
cloud_print number optional Optional.
quit_printers_with_cloud_enabled number optional Optional.
invoice_printer_act_as_kds number optional Optional.
cart_note number optional Optional.
cart_car_plate number optional Optional.
token_number number optional Optional.
discount_codes number optional Optional.
transfer_order_pincode number optional Optional.
daily_sale_input_check number optional Optional.
daily_sale_media_check number optional Optional.
active_pincode number optional Optional.
label_printing number optional Optional.
qrcode_on_label_print number optional Optional.
is_kds_device number optional Optional.
open_drawer number optional Optional.
user_card_digits number optional Optional.
invoice_qrcode number optional Optional.
force_adons number optional Optional.
combo_options number optional Optional.
ensure_combo_minimum_item_requirement number optional Optional.
sms_invoice number optional Optional.
offline_order_sync number optional Optional.
orderhistory_print_receipt number optional Optional.
dining_guest_count number optional Optional.
is_brand_groups number optional Optional.
is_extras_enble number optional Optional.
display_modifier_titles number optional Optional.
modifier_sort_min_required_desc number optional Optional.
print_only_save_kds_orders number optional Optional.
print_note_on_receipt number optional Optional.
show_receipt_screen number optional Optional.
open_dine_in number optional Optional.
hide_cart_pay_button number optional Optional.
tip_amount number optional Optional.
print_void_orders number optional Optional.
has_void_order_permission number optional Optional.
hide_clear_checkout number optional Optional.
kds_instruction number optional Optional.
pay_default_usd number optional Optional.
rpt_business_logo number optional Optional.
rpt_order_type number optional Optional.
rpt_is_modified number optional Optional.
rpt_biller_info number optional Optional.
rpt_total_before_vat number optional Optional.
rpt_vat_info number optional Optional.
rpt_net_total number optional Optional.
rpt_exchange_rate number optional Optional.
rpt_dine_guest_count number optional Optional.
rpt_paid_at number optional Optional.
esc_command number optional Optional.
one_table_one_order number optional Optional.
qr_code_on_print_receipt number optional Optional.
is_channels_enable number optional Optional.
delivery_detail_enable number optional Optional.
is_attributed_receipt number optional Optional.
is_cds_enable number optional Optional.
external_order_print_receipt number optional Optional.
text_receipt number optional Optional.
each_category_kds_print number optional Optional.
unify_brands number optional Optional.
ingredient_stay_standalone number optional Optional.
allegy_item number optional Optional.
dinein_serving_role number optional Optional.
draft_order number optional Optional.
is_kds_expeditor number optional Optional.
is_qr_payment number optional Optional.
pay_zero_amount number optional Optional.
print_deleted_item_on_receipt number optional Optional.
receipt_format number optional Optional.
receipt_translation number optional Optional.
product_translation number optional Optional.
category_translation number optional Optional.
print_invoice_translation number optional Optional.
print_kds_translation number optional Optional.
reservation_with_dine_in number optional Optional.
splitefire number optional Optional.
is_ingredient_disabled number optional Optional.
is_socket_server number optional Optional.
is_client number optional Optional.
open_price_pincode number optional Optional.
is_transfer_enabled number optional Optional.
block_print_until_order_open number optional Optional.
web_local_network_printer number optional Optional.
user_product_sales_counter number optional Optional.
is_change_payment_method_enabled number optional Optional.
skip_zero_quantity_items number optional Optional.
skip_zero_amount_items number optional Optional.
settle_wallet_topup number optional Optional.
is_shift_management number optional Optional.
acknowledge_dine_table_edit number optional Optional.
is_soldout number optional Optional.
force_crm_user number optional Optional.
shift_transactions number optional Optional.
eod_shift_report number optional Optional.
shift_z_report number optional Optional.
view_estimated_cash number optional Optional.
external_order_ready number optional Optional.
row_discount number optional Optional.
channel_id number optional Optional.
brand_id number optional Optional.
print_no_of_times number optional Optional.
language string optional Optional.
zebra_ip string optional Optional.
zebra_port number optional Optional.
fraction_digits number optional Optional.
order_type number optional Optional.
exchange_format number optional Optional.
station_id number optional Optional.
lock_screen number optional Optional.
cloud_target_user_id number optional Optional.
cloud_target_device_id string optional Optional.
exchange_rate number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_user_permissions"
}'

Example Response

{
  "request": {
    "discount_codes": 1
  },
  "response": {
    "status": true,
    "message": "setting saved successfully.",
    "data": {
      "setting": {
        "print_receipt": 1,
        "discount_codes": 1,
        "tip_amount": 0,
        "dining_guest_count": 1,
        "is_channels_enable": 0,
        "channel_id": 463,
        "is_soldout": 0,
        "is_change_payment_method_enabled": 0,
        "combo_options": 1,
        "exchange_rate": 0
      }
    }
  },
  "note": "Code-derived (write route not called live); data.setting is abbreviated."
}

save_config

POST /api/config. Why: Set ONE setting of ONE branch — exactly what each card of the dashboard Settings page saves (Tax, Currency, Decimal Points, Time Zone, Opening Hours, Base Price, receipt texts, POS flags…). Upsert by (tenant, branch, name); other keys are untouched. Call once per key and per branch (the dashboard saves time_zone + time_zone_code, and opening_hour + closing_hour, as two calls). Read the current value first (get_config_value / get_location_config) and confirm with the user. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"save_config"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).
name string required Config key, exact spelling (see config_keys): e.g.
value string required New value AS A STRING in the key's format (vat "5", decimal_points "2", time_zone "+04:00", opening_hour "06:00", flags "0"/"1").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "save_config",
  "name": "Iced Latte",
  "value": "example"
}'

Example Response

{
  "name": "decimal_points",
  "value": "2",
  "customer_id": 437,
  "location_id": "907"
}

create_business_info

POST /api/config/business-info. Why: Save a branch's business profile (receipt name, address, VAT No, logo, contact, delivery area). POST and PUT run the SAME handler (update_business_info is identical): an upsert of each field you send. Send only the fields to change; fields you omit are untouched. Read get_business_info (strict="1") first and confirm with the user. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_business_info"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).
info_name string optional Optional.
legal_name string optional Optional.
info_address string optional Optional.
info_phone string optional Optional.
info_email string optional Optional.
info_vat_no string optional Optional.
logo_image string optional Optional.
license_image string optional Optional.
location_latitude string optional Optional.
location_longitude string optional Optional.
polygon_coordinates string optional Optional.
area_coordinates string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_business_info"
}'

Example Response

{
  "msg": "Business info saved successfully"
}

update_business_info

PUT /api/config/business-info. Why: Save a branch's business profile (receipt name, address, VAT No, logo, contact, delivery area). PUT and POST run the SAME handler (create_business_info is identical): an upsert of each field you send. Send only the fields to change; fields you omit are untouched. Read get_business_info (strict="1") first and confirm with the user. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_business_info"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
loc string optional ONE branch id (list_locations).
info_name string optional Optional.
legal_name string optional Optional.
info_address string optional Optional.
info_phone string optional Optional.
info_email string optional Optional.
info_vat_no string optional Optional.
logo_image string optional Optional.
license_image string optional Optional.
location_latitude string optional Optional.
location_longitude string optional Optional.
polygon_coordinates string optional Optional.
area_coordinates string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_business_info"
}'

Example Response

{
  "msg": "Business info saved successfully"
}

create_location

POST /api/locations. Why: Open a new branch (location) for the tenant — the dashboard Locations > Add form. It only inserts the locations row (name + warehouse flag), links every synced payment method to it, optionally links all master products / categories, and appends the new id to the CALLING USER's allow_locations. It does NOT create business info, VAT, currency, time zone, opening hours, weekday hours, menus, brands, tables or users for the branch — configure those afterwards (business-info / config tools, upsert_location_days_hours, update_location with sync_* flags, create_* tools with the new location_id). Counts against the tenant's max_locations subscription limit. Confirm the name with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_location"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Branch name.
isWareHouse string optional Optional.
sync_products string optional Optional.
sync_categories string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_location",
  "name": "Iced Latte"
}'

Example Response

{
  "message": "Location created successfully",
  "data": {
    "name": "Marina Walk",
    "isWareHouse": "0",
    "sync_products": "1",
    "sync_categories": "1",
    "customer_id": 437,
    "id": 1132
  },
  "syncedProductsCount": 152,
  "syncMessage": "",
  "syncedCategoriesCount": 18,
  "categoriesSyncMessage": "",
  "syncedMaterialsCount": 0,
  "materialsSyncMessage": ""
}

update_location

PUT /api/locations/:id. Why: Rename a branch, toggle its warehouse flag, set its payment provider / N-Genius terminal / menu-checkout / invoice-service version, and optionally link ALL master items of a catalog entity to it (sync_* flags — the way to fill a new or incomplete branch on a MASTER tenant). It is a full overwrite of name, isWareHouse, enable_menu_checkout, payment_provider, ngenius_terminal_id and ngenius_merchant_id: call get_location first and send every one of those with its current value unless you mean to change or clear it. Business info, hours, VAT, currency are config rows, not this tool. Confirm with the user before any sync_* flag — it can reset per-branch overrides. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_location"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Location id (list_locations[].id).
name string required Branch name — ALWAYS send it (current name if unchanged): the UPDATE writes it, so omitting it blanks the name.
isWareHouse string optional "1" = warehouse, "0" = not.
enable_menu_checkout number optional 1 = QR/online menu checkout on, 0 = off.
payment_provider string optional Online payment gateway id (e.g.
ngenius_terminal_id string optional N-Genius terminal id (TID).
ngenius_merchant_id string optional N-Genius merchant id (MID).
invoice_service_version enum: v1, v2 optional Optional.
sync_products string optional Optional.
sync_categories string optional Optional.
sync_brands string optional Optional.
sync_groups string optional Optional.
sync_menus string optional Optional.
sync_labels string optional Optional.
sync_deals string optional Optional.
sync_modifiers string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_location",
  "id": 11,
  "name": "Iced Latte"
}'

Example Response

{
  "message": "Location updated successfully",
  "data": {
    "name": "Marina Walk",
    "isWareHouse": "0",
    "enable_menu_checkout": 0,
    "invoice_service_version": "v1",
    "sync_menus": 1,
    "customer_id": 437,
    "id": "1132"
  },
  "syncedMenusCount": 3,
  "menusSyncMessage": "Successfully synced 3 Menus"
}

upsert_location_days_hours

POST /api/location-days-hours-config/bulk-upsert. Why: Set a branch's weekly operating hours — exactly what the dashboard Operating Hours 'Save' does (it always sends all 7 days). Each config updates that weekday's row if it exists, else creates it. Days you leave out are untouched. This does NOT change the business day / opening_hour config used by the POS and reports (that is a config row). Confirm the schedule with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"upsert_location_days_hours"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
location_id number required ONE branch id (list_locations[].id).
configs array<object> required 1–7 day entries; send each weekday at most once (a new day listed twice creates two rows).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "upsert_location_days_hours",
  "location_id": 11,
  "configs": [
    11
  ]
}'

Example Response

{
  "message": "Bulk upsert completed. Created: 0, Updated: 2, Failed: 0",
  "data": [
    {
      "id": 29,
      "customer_id": 437,
      "location_id": 909,
      "day": "monday",
      "open": "09:00",
      "close": "23:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 30,
      "customer_id": 437,
      "location_id": 909,
      "day": "tuesday",
      "open": "09:00",
      "close": "23:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 31,
      "customer_id": 437,
      "location_id": 909,
      "day": "wednesday",
      "open": "12:00",
      "close": "00:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 32,
      "customer_id": 437,
      "location_id": 909,
      "day": "thursday",
      "open": "12:00",
      "close": "00:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 33,
      "customer_id": 437,
      "location_id": 909,
      "day": "friday",
      "open": "12:00",
      "close": "00:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 34,
      "customer_id": 437,
      "location_id": 909,
      "day": "saturday",
      "open": "12:00",
      "close": "00:00",
      "is_off": 0,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    },
    {
      "id": 35,
      "customer_id": 437,
      "location_id": 909,
      "day": "sunday",
      "open": null,
      "close": null,
      "is_off": 1,
      "created_at": "2025-10-03T05:50:48.000Z",
      "updated_at": "2025-10-03T05:50:48.000Z"
    }
  ],
  "results": {
    "created": 0,
    "updated": 2,
    "failed": 0,
    "errors": []
  }
}

create_material

POST /api/materials. Why: Add a raw material (metadata only): name, storage unit (unit_id) and ingredient unit + conversionrate for recipes, costing method and cost, branches (location_id; one stock row per branch at 0), suppliers with purchase prices, category and brands. Stock starts at 0 everywhere — receive it with create_draft_purchase_order / create_stock_transfer; then use the id in recipes (create_recipe), supplier catalogs and counts. Recipe-output materials are created by the recipe tools, not here. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_material"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
is_prepared enum: 1 optional Any non-empty value (even "0") → materials.is_prepared = 1 (in-house prepared item).
name string required Material name.
description string optional Free text.
unit_id number optional Standard storage unit id (units_of_measure; on dev1 1 Gram, 2 Kilogram, 3 Pound, 4 Ounce, 5 Milliliter, 6 Liter, 7 Gallon, 8 Fluid Ounce, 9 Pieces, 10 Dozen, 1…
custom_unit_id number optional Customer-defined storage unit id (customer_uom_conversions; copy from a material that uses it).
unit string optional Storage unit display label stored in materials.unit, e.g.
ingredientunit string optional Ingredient (recipe) unit label, e.g.
conversionrate string optional Ingredient units per 1 storage unit, e.g.
costingmethod enum: average, fixed optional "average" (default; cost follows purchases/receipts) or "fixed" (cost = price, per-branch override via location_prices).
price number optional Cost price per storage unit.
selling_price number optional Retail price when the material is sold directly (0 = none).
wholesale_price number optional Wholesale price (0 = none).
low_stock number optional Low-stock alert threshold in the storage unit (0 = no alert; drives get_materials_stats.low_stock).
minimum_level number optional Minimum stock level (storage unit).
maximum_level number optional Maximum stock level.
par_level number optional Par (target) stock level.
barcode string optional Barcode.
category_id number optional Material category id (list_material_categories; must exist and not be deleted, else 400 INVALID_MATERIAL_CATEGORY).
type string optional Legacy free-text kind: "raw" for normal materials (dashboard always sends raw on create and keeps the current value on edit).
material_type enum: mat, rm optional "mat" = raw/purchased material (default).
yield_percentage number optional Usable yield % after prep, 0.01–100+ (100 = no loss).
tax_id number optional Tax id — tax groups are out of scope: omit on create; on update resend get_material.tax_id unchanged (omitting it sets the tax to NULL).
location_id array<number> optional Branch ids to stock the material at (list_locations), as an ARRAY of ints (or csv string).
location_prices array<object> optional Per-branch opening unit cost.
suppliers array<number> optional Supplier ids (list_suppliers) to link.
suppliers_prices array<object> optional Per-supplier purchase prices; also links those suppliers.
brand_ids array<number> optional Brand ids (list_brands) the material is limited to; unknown ids are dropped.
ingredient_unit_id number optional Standard unit id of the ingredient unit (units_of_measure).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_material",
  "name": "Iced Latte"
}'

Example Response

[
  {
    "material": {
      "name": "Whole Milk",
      "unit_id": 6,
      "unit": "Liter",
      "ingredientunit": "Milliliter",
      "conversionrate": 1000,
      "costingmethod": "fixed",
      "price": 3.2,
      "location_id": [
        907,
        908
      ],
      "location_prices": [
        {
          "location_id": 907,
          "avg_cost": 3.2
        },
        {
          "location_id": 908,
          "avg_cost": 3.4
        }
      ],
      "suppliers_prices": [
        {
          "supplier_id": 970,
          "purchase_price": 3.1
        }
      ],
      "category_id": 3,
      "yield_percentage": 100,
      "stock": 0,
      "is_master_user": 1,
      "id": 121001,
      "sku_no": "mat-437-121001",
      "mode": "stockable",
      "customer_id": 437,
      "brand_ids": []
    }
  },
  {
    "materials_locations": [
      {
        "id": 0,
        "material_id": 121001,
        "location_id": 907,
        "customer_id": 437,
        "material_type": "mat",
        "avg_cost": 3.2,
        "stock": 0
      },
      {
        "id": 1,
        "material_id": 121001,
        "location_id": 908,
        "customer_id": 437,
        "material_type": "mat",
        "avg_cost": 3.4,
        "stock": 0
      }
    ]
  }
]

update_material

PUT /api/materials/:id. Why: Edit a material's metadata — name, units/conversion, costing method and cost (fixed: per-branch costs via location_prices), selling/wholesale price, stock levels, barcode, category, yield, branches, suppliers + purchase prices, brands. It is a FULL overwrite: load get_material {id, loc} first and send that record back with your changes (including stock, location_id, unit_id, tax_id, suppliers_prices, brand_ids = brands[].id). Never use it to change on-hand stock — use counts, wastage, transfers or purchase orders. FULL OVERWRITE: every field you omit (except category_id) is set to NULL/default, omitted suppliers/brands are unlinked, and branches missing from location_id lose their stock rows. Always start from get_material. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_material"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material id (list_materials).
name string required Material name.
description string optional Free text.
unit_id number optional Standard storage unit id (units_of_measure; on dev1 1 Gram, 2 Kilogram, 3 Pound, 4 Ounce, 5 Milliliter, 6 Liter, 7 Gallon, 8 Fluid Ounce, 9 Pieces, 10 Dozen, 1…
custom_unit_id number optional Customer-defined storage unit id (customer_uom_conversions; copy from a material that uses it).
unit string optional Storage unit display label stored in materials.unit, e.g.
ingredientunit string optional Ingredient (recipe) unit label, e.g.
conversionrate string optional Ingredient units per 1 storage unit, e.g.
costingmethod enum: average, fixed optional "average" (default; cost follows purchases/receipts) or "fixed" (cost = price, per-branch override via location_prices).
price number optional Cost price per storage unit.
selling_price number optional Retail price when the material is sold directly (0 = none).
wholesale_price number optional Wholesale price (0 = none).
low_stock number optional Low-stock alert threshold in the storage unit (0 = no alert; drives get_materials_stats.low_stock).
minimum_level number optional Minimum stock level (storage unit).
maximum_level number optional Maximum stock level.
par_level number optional Par (target) stock level.
barcode string optional Barcode.
category_id number optional Material category id (list_material_categories; must exist and not be deleted, else 400 INVALID_MATERIAL_CATEGORY).
type string optional Legacy free-text kind: "raw" for normal materials (dashboard always sends raw on create and keeps the current value on edit).
material_type enum: mat, rm optional "mat" = raw/purchased material (default).
yield_percentage number optional Usable yield % after prep, 0.01–100+ (100 = no loss).
tax_id number optional Tax id — tax groups are out of scope: omit on create; on update resend get_material.tax_id unchanged (omitting it sets the tax to NULL).
location_id array<number> required REQUIRED on update: the COMPLETE list of branch ids the material must stay on (get_material.location_id, plus/minus the branches you change).
location_prices array<object> optional Fixed costing only (ignored for average): per-branch unit cost.
suppliers array<number> optional Supplier ids (list_suppliers).
suppliers_prices array<object> optional Per-supplier purchase prices.
brand_ids array<number> optional Brand ids (list_brands) the material is limited to; unknown ids are dropped.
stock number required Pass get_material.stock back unchanged.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_material",
  "id": 11,
  "name": "Iced Latte",
  "location_id": 11,
  "stock": 1
}'

Example Response

{
  "id": 119158,
  "name": "Cherry Blossom",
  "description": "Cherry",
  "price": 0,
  "selling_price": 0,
  "wholesale_price": 0,
  "stock": -12762188.785166,
  "unit": "Kilogram",
  "unit_id": 2,
  "custom_unit_id": null,
  "ingredientunit": "Gram",
  "conversionrate": "1000",
  "costingmethod": "average",
  "low_stock": 0,
  "minimum_level": "0",
  "maximum_level": "0",
  "par_level": "0",
  "barcode": "",
  "type": "raw",
  "material_type": "mat",
  "category_id": 2,
  "yield_percentage": 100,
  "tax_id": null,
  "location_id": null,
  "suppliers": [
    970
  ],
  "suppliers_prices": [
    {
      "supplier_id": 970,
      "purchase_price": 1.1092
    }
  ],
  "brand_ids": [
    412,
    473
  ],
  "customer_id": 437
}

create_material_category

POST /api/inventory/categories. Why: Add a material category or subcategory (taxonomy only; no stock impact) so materials can be grouped via create_material / update_material category_id and filtered in list_materials (cat). Two levels max: a subcategory's parent must be a top-level category. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_material_category"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Category name, trimmed.
description string optional Free-text description.
parent_id number optional Parent category id for a subcategory (must be an existing non-deleted TOP-LEVEL category, else 400 CATEGORY_PARENT_INVALID / CATEGORY_MAX_DEPTH_EXCEEDED).
image string optional Image path or URL stored verbatim (upload URL from the dashboard uploader).
color string optional Colour string, stored verbatim (not shown by the dashboard).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_material_category",
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Material category created successfully",
  "data": {
    "id": 7,
    "name": "Cheese",
    "description": "",
    "parent_id": 2,
    "customer_id": 437,
    "color": "",
    "image": "",
    "is_deleted": 0
  }
}

update_material_category

PUT /api/inventory/categories/:id. Why: Rename, re-describe, change the image of, or re-parent a material category (taxonomy only; materials keep their category_id). Partial update: only the keys you send change. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_material_category"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material category id (list_material_categories).
name string optional New name, trimmed (blank → 400).
description string optional Free-text description.
parent_id number optional Move under this top-level category (null / "" / 0 → top level).
image string optional Image path or URL stored verbatim (upload URL from the dashboard uploader).
color string optional Colour string, stored verbatim (not shown by the dashboard).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_material_category",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Material category updated successfully",
  "data": {
    "id": 3,
    "name": "Dairy & Eggs",
    "description": "",
    "parent_id": 2,
    "customer_id": 437,
    "color": "",
    "image": "",
    "is_deleted": 0
  }
}

create_supplier

POST /api/suppliers. Why: Add a vendor to the supplier directory (no PO, no stock). Check list_suppliers k=<name> first — the API does not reject duplicates. After creating, link the materials it sells in the dashboard (Suppliers → Link materials; no MCP tool) and read them back with list_supplier_materials. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_supplier"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Supplier display name (max 300).
description string optional Free-text description (max 300).
address string optional Postal address (max 200).
tax_pct number optional Supplier tax % (≥ 0, 2 decimals).
contact_name string optional Contact person (max 200).
contact_email string optional Contact email (max 200); must be a valid email if given.
contact_phone string optional Contact phone (max 45).
payments string optional Accepted payment methods as a JSON STRING (not an object): "{\"is_cash\":true,\"is_credit\":false,\"is_cheque\":false}".
can_recieve_email string optional 1/true = email POs to contact_email, 0/false = don't.
logo string optional Relative upload path of a logo image (from the dashboard file upload).
trade_license string optional Relative upload path of the trade-licence document (dashboard file upload, max 200).
trn_certificate string optional Relative upload path of the TRN certificate document (dashboard file upload, max 45).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_supplier",
  "name": "Iced Latte"
}'

Example Response

{
  "name": "Fresh Farms Trading",
  "description": "Vegetables and dairy",
  "address": "Warehouse 12, Al Quoz, Dubai",
  "tax_pct": 5,
  "contact_name": "Sample Contact",
  "contact_email": "orders@freshfarms.example",
  "contact_phone": "+971500000002",
  "payments": "{\"is_cash\":true,\"is_credit\":true,\"is_cheque\":false}",
  "can_recieve_email": 1,
  "logo": "",
  "trade_license": "",
  "trn_certificate": "",
  "location_id": 907,
  "customer_id": 437
}

update_supplier

PUT /api/suppliers/:id. Why: Change a supplier's directory fields (name, contacts, address, payment methods, PO-email flag). The API overwrites ALL columns, so load the row with get_supplier first and send every field back with your changes. Does not touch POs already issued (they keep their info_* snapshot). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_supplier"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Supplier id from list_suppliers.
name string required Supplier display name.
description string optional Free-text description (max 300).
address string optional Postal address (max 200).
tax_pct number optional Supplier tax % (≥ 0, 2 decimals).
contact_name string optional Contact person (max 200).
contact_email string optional Contact email (max 200); must be a valid email if given.
contact_phone string optional Contact phone (max 45).
payments string optional Accepted payment methods as a JSON STRING (not an object): "{\"is_cash\":true,\"is_credit\":false,\"is_cheque\":false}".
can_recieve_email string optional 1/true = email POs to contact_email, 0/false = don't.
logo string optional Relative upload path of a logo image (from the dashboard file upload).
trade_license string optional Relative upload path of the trade-licence document (dashboard file upload, max 200).
trn_certificate string optional Relative upload path of the TRN certificate document (dashboard file upload, max 45).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_supplier",
  "id": 11,
  "name": "Iced Latte"
}'

Example Response

{
  "name": "Grocery Supplier LLC",
  "description": "Grocery items",
  "address": "Warehouse 4, Al Quoz, Dubai",
  "tax_pct": 5,
  "contact_name": "Sample Contact",
  "contact_email": "orders@supplier.example",
  "contact_phone": "+971500000001",
  "payments": "{\"is_cash\":true,\"is_credit\":false,\"is_cheque\":false}",
  "can_recieve_email": 1,
  "logo": "",
  "trade_license": "",
  "trn_certificate": "",
  "location_id": 907,
  "customer_id": 437
}

create_recipe

POST /api/recipe. Why: Create a recipe (BOM): output yield_qty + unit, mode, ingredient lines (materials from list_materials, qty in their ingredient unit, optional yield %), branches, brand tags and — phantom only — product links. The server also creates the recipe's prepared material (list_materials, material_type "rm", linked_recipe = new id) at the chosen branches with the computed cost, and saves the cost snapshot. No stock moves: a phantom recipe deducts ingredients when a linked product is sold (sync_invoice); a stockable one is produced via create_work_order (completed in the dashboard). Send name, yield_qty, unit, unit_id, mode, recipe_items [{material_id, qty (ingredient unit), unit, yield_pct}] and location_id; products only for phantom recipes. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_recipe"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Recipe name.
yield_qty number required Output of one batch in `unit` (> 0).
unit string required Output unit label; the dashboard sends the UOM name (e.g.
unit_id number optional UOM id matching `unit` (no MCP units tool: copy from another recipe/material with the same unit, else null).
mode enum: phantom, stockable optional phantom (default) = no own stock, sales of linked products deduct the ingredients; stockable = produced by work orders into the prepared material's stock, cann…
recipe_items array<object> required Ingredient lines, at least one (the dashboard requires ≥ 1).
location_id array<number> required Branches (list_locations) where the prepared material (its stock/cost row) is created, as an array like the dashboard (the server also accepts a number or csv).
brand_ids array<number> optional Brand ids (list_brands) to tag the recipe with; [] / omitted = none.
products array<object> optional Product links, PHANTOM recipes only: [{ id, qty_used }].
product_ids array<number> optional Product ids to link (each gets qty_used 1).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_recipe",
  "name": "Iced Latte",
  "yield_qty": 1,
  "unit": "example",
  "recipe_items": [
    11
  ],
  "location_id": 11
}'

Example Response

{
  "message": "Recipe Created Successfully",
  "data": {
    "id": 17225,
    "customer_id": 437,
    "name": "Tomato sauce",
    "yield_qty": 2,
    "unit": "Kilogram",
    "version": 1,
    "created_by": 4734,
    "mode": "stockable",
    "sku_no": "rcp-437-17225",
    "unit_id": 2,
    "recipe_items": [
      {
        "recipe_id": 17225,
        "material_id": 119196,
        "qty": 1500,
        "unit": "Gram",
        "wastage_pct": 0,
        "id": 1044600,
        "type": "mat",
        "unit_id": null,
        "yield_pct": null,
        "name": "Test Material",
        "ingredientunit": "Gram",
        "conversionrate": "1000",
        "avg_price": 22.0271,
        "price": 0,
        "material_yield": null,
        "effective_yield": 100
      },
      {
        "recipe_id": 17225,
        "material_id": 119158,
        "qty": 700,
        "unit": "Gram",
        "wastage_pct": 0,
        "id": 1044601,
        "type": "mat",
        "unit_id": null,
        "yield_pct": 90,
        "name": "Cherry Blosom",
        "ingredientunit": "Gram",
        "conversionrate": "1000",
        "avg_price": 0.3295,
        "price": 0,
        "material_yield": null,
        "effective_yield": 90
      }
    ],
    "product_ids": [],
    "linked_products": [],
    "brand_ids": [
      412
    ]
  }
}

update_recipe

PUT /api/recipe/:id. Why: Edit a recipe: name, yield, unit, ingredient lines, product links (phantom), brand tags and branches of its prepared material; the cost snapshot and the prepared material (name, unit, branch avg cost) are recomputed. NOT a patch — omitted lines, links, brands and header fields are wiped — so always start from get_recipe. The mode cannot be changed. No stock moves. Full replace, not a patch: resend every field from get_recipe (lines, products, brand_ids, location_id, unit_id, current mode). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_recipe"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Recipe id (list_recipes / get_recipe).
name string required Recipe name (NOT NULL; omitted → 500, rolled back).
yield_qty number required Output of one batch in `unit` (> 0).
unit string required Output unit label; the dashboard sends the UOM name (e.g.
unit_id number optional UOM id matching `unit` (no MCP units tool: copy from another recipe/material with the same unit, else null).
mode enum: phantom, stockable required Resend the recipe's CURRENT mode (get_recipe).
recipe_items array<object> required FULL list of ingredient lines — replaces all lines.
products array<object> optional Product links for a PHANTOM recipe: [{ id, qty_used }].
product_ids array<number> optional Product ids to link (each gets qty_used 1).
brand_ids array<number> optional Brand ids (list_brands).
location_id array<number> optional Branches (list_locations) of the prepared material, as an array (the server also accepts a number or csv).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_recipe",
  "id": 11,
  "name": "Iced Latte",
  "yield_qty": 1,
  "unit": "example",
  "mode": "example",
  "recipe_items": [
    11
  ]
}'

Example Response

{
  "message": "Recipe Updated Successfully",
  "data": {
    "recipe": {
      "id": 16779,
      "customer_id": 437,
      "name": "Test",
      "yield_qty": 0.8,
      "unit": "Kilogram",
      "version": 1,
      "created_by": 4734,
      "mode": "stockable",
      "sku_no": "rcp-437-16779",
      "unit_id": 2,
      "recipe_items": [
        {
          "recipe_id": 16779,
          "material_id": 119196,
          "qty": 450,
          "unit": "Gram",
          "wastage_pct": 0,
          "id": 988201,
          "type": "mat",
          "unit_id": 1,
          "yield_pct": null,
          "name": "Test Material",
          "ingredientunit": "Gram",
          "conversionrate": "1000",
          "avg_price": 22.0271,
          "price": 0
        },
        {
          "recipe_id": 16779,
          "material_id": 119158,
          "qty": 500,
          "unit": "Gram",
          "wastage_pct": 0,
          "id": 988202,
          "type": "mat",
          "unit_id": 1,
          "yield_pct": null,
          "name": "Cherry Blosom",
          "ingredientunit": "Gram",
          "conversionrate": "1000",
          "avg_price": 0.3295,
          "price": 0
        }
      ],
      "product_ids": [
        257508
      ],
      "linked_products": [
        {
          "product_id": 257508,
          "id": 257508,
          "name": "Mocha",
          "qty_used": 1,
          "portion_qty": 0.8,
          "unit": "Kilogram"
        }
      ],
      "brand_ids": [
        412,
        473
      ]
    }
  },
  "materialId": 119300
}

create_inventory_count_sheet

POST /api/inventory-count-sheets/create-sheet. Why: Save a reusable count-sheet template: a named list of materials (list_materials ids) assigned to branches (list_locations ids). A sheet only pre-selects materials for a later sheet-based count (create_inventory_count type 2); creating it does not create a count or touch stock. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_inventory_count_sheet"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Sheet name.
reference string optional Free-text reference / sheet code.
material_ids array<number> required Every material on the sheet (the dashboard requires ≥ 1 and picks from stockable materials of the selected branches).
location_id array<number> required Branches the sheet is assigned to — ALWAYS an array of numbers (the dashboard sends e.g.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_inventory_count_sheet",
  "name": "Iced Latte",
  "material_ids": [
    11
  ],
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Inventory count sheet created successfully"
}

update_inventory_count_sheet

PUT /api/inventory-count-sheets/update-sheet/:id. Why: Rename a count-sheet template or change its materials / branches. NOT a patch: name, reference and material_ids are overwritten and branches are synced to exactly location_id — resend the full sheet with your change. Template only; no count or stock change. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_inventory_count_sheet"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Count sheet id (list_inventory_count_sheets → data[].id).
name string required Sheet name.
reference string optional Free-text reference / sheet code.
material_ids array<number> required Every material on the sheet (the dashboard requires ≥ 1 and picks from stockable materials of the selected branches).
location_id array<number> required Branches the sheet is assigned to — ALWAYS an array of numbers (the dashboard sends e.g.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_inventory_count_sheet",
  "id": 11,
  "name": "Iced Latte",
  "material_ids": [
    11
  ],
  "location_id": 11
}'

Example Response

{
  "status": true,
  "message": "Inventory count sheet updated successfully"
}

create_draft_purchase_order

POST /api/supplier-invoices/draft. Why: Create a DRAFT purchase order (is_draft = 1, status draft) for a supplier, split from an existing PO — the dashboard does this when a delivery is short: the missing quantities become drafts (possibly for another supplier) that a user later confirms in the dashboard. Nothing is sent to the supplier and no stock moves. Needs the parent PO uuid (get_purchase_order / list_supplier_invoices), the supplier (list_suppliers), material ids and prices (list_supplier_materials, list_materials). The draft only becomes a real PO when a user presses Confirm in the dashboard; stock arrives only when that PO is received there (list_inventory / list_inventory_movements). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_draft_purchase_order"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
parent_purchase_uuid string required uuid of the existing (non-archived) purchase order this draft is split from (list_supplier_invoices / get_purchase_order).
location_id number optional Branch of the draft — send the parent PO's location_id (one id).
table_id number optional Supplier id of the draft (list_suppliers).
info_name string optional Supplier name snapshot = supplier.name.
info_phone string optional Supplier phone snapshot = supplier.contact_phone.
info_email string optional Supplier email snapshot = supplier.contact_email (where the PO email goes on confirm).
info_address string optional Supplier address snapshot = supplier.address.
items array<object> required Draft lines (at least one; 400 "items array is required" otherwise).
subtotal number required Header subtotal = Σ line subtotals (4 dp).
vat number required VAT AMOUNT (not a percent) = round4(subtotal × VAT% / 100), stored with 2 decimals.
payable number required Grand total = subtotal + vat (4 dp).
discountamount number optional Header discount amount.
note string optional Header note.
your_order_ref string optional Your reference.
supplier_order_ref string optional Supplier's reference.
delivery_details string optional Multi-line delivery block printed on the PO.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_draft_purchase_order",
  "parent_purchase_uuid": "example",
  "items": [
    11
  ],
  "subtotal": 1,
  "vat": 1,
  "payable": 1
}'

Example Response

{
  "success": true,
  "draft": {
    "id": 6187,
    "items_number": 1,
    "subtotal": 12,
    "vat": 0.6,
    "payable": 12.6,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 0,
    "change": 0,
    "note": "",
    "uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
    "created_at": "2026-05-19T16:16:14.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "Faisal Suppliers",
    "info_address": "Industrial Area 2, Sharjah",
    "info_phone": "+971500000003",
    "info_email": "sales@faisal.example",
    "status": "draft",
    "user": "Nexus Cafe",
    "user_id": 1201,
    "table_id": 881,
    "payment_method": null,
    "order_id": 18,
    "invoice_id": null,
    "custom_price": null,
    "your_order_ref": "",
    "supplier_order_ref": "",
    "published_date": null,
    "is_draft": 1,
    "parent_purchase_uuid": "6f6db90f-2a7d-4910-82e9-3a0dae7e6e70",
    "delivery_date": null,
    "order_request_uuid": null,
    "supplier_invoice_number": null,
    "received_by": null,
    "received_at": null,
    "approved_by": null,
    "approved_at": null,
    "rejected_by": null,
    "rejected_at": null,
    "supplier_invoice_id": null,
    "delivery_details": "Nexus\nNexus located at Dev\nEmail: info@buyer.example\nVAT: 100000000000003\nPhone: +971500000009",
    "is_archive": 0,
    "items": [
      {
        "id": 14292,
        "name": "SRC-BT-86CBPR-SG3 BB86-BB92",
        "price": 2,
        "qty": 6,
        "subtotal": 12,
        "purchase_uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
        "customer_id": 437,
        "location_id": 907,
        "material_id": 112157,
        "extras": null,
        "custom_price": 2,
        "product_id": null,
        "unit": null,
        "wastage": null,
        "brand_id": null,
        "requested_qty": 6,
        "remark": null,
        "material_name": "SRC-BT-86CBPR-SG3 BB86-BB92",
        "unit_id": null,
        "unit_name": "Pieces"
      }
    ]
  }
}

update_draft_purchase_order_lines

PUT /api/supplier-invoices/:uuid/price-quantity. Why: Replace the lines (materials, quantities, unit prices) and totals of a DRAFT purchase order — the dashboard draft editor. Load the draft with get_purchase_order first (you must resend every line you want to keep and reuse its VAT rate). Materials and prices from list_supplier_materials (sup = the draft's table_id, loc = its location_id) or list_materials. No stock moves; the supplier is not notified. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_draft_purchase_order_lines"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required uuid of a draft (is_draft = 1) from list_supplier_invoices / create_draft_purchase_order.
edit_mode enum: processing, receiving optional "processing" = edit the ORDER (use this for drafts, as the dashboard does): lines are replaced by the list you send, qty = requested qty, new materials allowed.
items array<object> required The COMPLETE line list of the draft (at least one; 400 "Please send at least one line item to update.").
subtotal number required Header subtotal = Σ line subtotals.
vat number required VAT AMOUNT (not percent) = round4(subtotal × VAT% / 100), VAT% = the draft's stored vat / subtotal × 100 (0 if its vat is 0).
payable number required subtotal + vat.
discountamount number optional Header discount amount; the dashboard resends the draft's current value (normally 0).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_draft_purchase_order_lines",
  "uuid": "example",
  "items": [
    11
  ],
  "subtotal": 1,
  "vat": 1,
  "payable": 1
}'

Example Response

{
  "success": true,
  "message": "Your updates were saved.",
  "invoice": {
    "id": 6187,
    "items_number": 2,
    "subtotal": 32,
    "vat": 1.6,
    "payable": 33.6,
    "discountamount": 0,
    "discountpct": 0,
    "paid": 0,
    "change": 0,
    "note": "",
    "uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
    "created_at": "2026-05-19T16:16:14.000Z",
    "customer_id": 437,
    "location_id": 907,
    "info_name": "Faisal Suppliers",
    "info_address": "Industrial Area 2, Sharjah",
    "info_phone": "+971500000003",
    "info_email": "sales@faisal.example",
    "status": "draft",
    "user": "Nexus Cafe",
    "user_id": 1201,
    "table_id": 881,
    "payment_method": null,
    "order_id": 18,
    "invoice_id": null,
    "custom_price": null,
    "your_order_ref": "",
    "supplier_order_ref": "",
    "published_date": null,
    "is_draft": 1,
    "parent_purchase_uuid": "6f6db90f-2a7d-4910-82e9-3a0dae7e6e70",
    "delivery_date": null,
    "order_request_uuid": null,
    "supplier_invoice_number": null,
    "received_by": null,
    "received_at": null,
    "approved_by": null,
    "approved_at": null,
    "rejected_by": null,
    "rejected_at": null,
    "supplier_invoice_id": 3,
    "delivery_details": null,
    "is_archive": 0
  },
  "items": [
    {
      "id": 14292,
      "name": "SRC-BT-86CBPR-SG3 BB86-BB92",
      "price": 2,
      "qty": 10,
      "subtotal": 20,
      "purchase_uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
      "customer_id": 437,
      "location_id": 907,
      "material_id": 112157,
      "extras": null,
      "custom_price": 2,
      "product_id": null,
      "unit": null,
      "wastage": null,
      "brand_id": null,
      "requested_qty": 10,
      "remark": null
    },
    {
      "id": 14310,
      "name": "Atx 27.5 In Vibrant Blue",
      "price": 3,
      "qty": 4,
      "subtotal": 12,
      "purchase_uuid": "52e14591-f11f-430c-8672-aa9412cc3eb9",
      "customer_id": 437,
      "location_id": 907,
      "material_id": 112295,
      "extras": null,
      "custom_price": 3,
      "product_id": null,
      "unit": null,
      "wastage": null,
      "brand_id": null,
      "requested_qty": 4,
      "remark": null
    }
  ]
}

create_inventory_count

POST /api/inventory-counts/create. Why: Record a physical stock count for one branch as a DRAFT (MCP forces status draft): each line stores counted vs expected quantity and unit cost. Stock is NOT changed — a user must submit and approve the count in the dashboard, which then posts counted − expected as adjustments. Ids/quantities come from list_materials / get_material (material, units, avg_price), get_material_stock or list_materials with loc (expected stock), list_locations (branch). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_inventory_count"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Count name.
type number required 0 = partial (only the materials you list), 1 = full (every stockable material of the branch), 2 = from a count sheet.
start_date string required Count date as display text (stored verbatim).
start_time string required Count time as display text (stored verbatim).
is_partial number required 1 when type = 0, else 0 (what the dashboard sends).
location_id number required ONE branch id (list_locations) — where expected stock is taken and where approval will adjust stock.
materials array<object> required One row per material, each with EXACTLY these 5 keys (any other key → validation error "must enter materials value as an array of objects with material_id, cou…

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_inventory_count",
  "name": "Iced Latte",
  "type": 1,
  "start_date": "example",
  "start_time": "example",
  "is_partial": 1,
  "location_id": 11,
  "materials": [
    11
  ]
}'

Example Response

{
  "status": true,
  "message": "Inventory count created",
  "data": {
    "id": 478,
    "start_date": "Sep 26, 2026",
    "start_time": "03:15 PM",
    "location_id": 907,
    "name": "Default location - Sep 26, 2026 03:15 PM",
    "type": 0,
    "is_partial": 1,
    "status": "draft",
    "created_at": 1790414100,
    "updated_at": 1790414100,
    "created_by_user_id": 4734,
    "materials": [
      {
        "material_id": 119196,
        "counted_quantity": 48.5,
        "expected_quantity": 50,
        "price": 100,
        "name": "Test Material"
      }
    ]
  },
  "_note": "Code-derived (no write calls made)."
}

update_inventory_count_materials

PUT /api/inventory-counts/update-materials. Why: Correct lines already on a DRAFT count — counted, expected, price and name per material (matched by inventory_count_id + material_id). Only updates existing lines: it cannot add (add_inventory_count_item) or remove lines. Use get_inventory_count first; stock only changes when the count is approved in the dashboard. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_inventory_count_materials"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
inventory_count_id number required Count id (list_inventory_counts / get_inventory_count).
materials array<object> required Lines to overwrite (non-empty; no duplicate material_id → "Duplicate material_id in materials array is not allowed").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_inventory_count_materials",
  "inventory_count_id": 11,
  "materials": [
    11
  ]
}'

Example Response

{
  "status": true,
  "message": "Inventory count updated successfully",
  "data": {
    "inventory_count_id": 478,
    "materials_updated": 1,
    "materials": [
      {
        "material_id": 119196,
        "counted_quantity": 47,
        "expected_quantity": 50,
        "price": 100,
        "name": "Test Material"
      }
    ]
  },
  "_note": "Code-derived."
}

update_inventory_count_item

PUT /api/inventory-counts/update-item-count. Why: Change only the counted quantity of one or more count LINES, addressed by line id (get_inventory_count → materials[].id) + material_id. Lighter than update_inventory_count_materials (no expected/price/name needed). Use on draft counts; stock changes only on dashboard approval. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_inventory_count_item"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
materials array<object> required Non-empty array of { id, material_id, counted_quantity }.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_inventory_count_item",
  "materials": [
    11
  ]
}'

Example Response

{
  "status": true,
  "message": "Item count updated successfully",
  "data": [
    {
      "id": 9747,
      "material_id": 119197,
      "counted_quantity": 10.5
    }
  ],
  "_note": "Code-derived."
}

add_inventory_count_item

POST /api/inventory-counts/add-item. Why: Add material lines to an existing DRAFT count (e.g. an item found on the shelf that was not on the list). Material ids / expected stock / cost from list_materials (loc = the count's branch) or get_material + get_material_stock. No stock change until the count is approved in the dashboard. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"add_inventory_count_item"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
inventory_count_id number required Count id (list_inventory_counts / get_inventory_count).
materials array<object> required Non-empty array of new lines { material_id, counted_quantity, expected_quantity, price, name }.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "add_inventory_count_item",
  "inventory_count_id": 11,
  "materials": [
    11
  ]
}'

Example Response

{
  "status": true,
  "message": "Item count saved",
  "data": [
    {
      "material_id": 119197,
      "name": "Test",
      "price": 0.2873,
      "counted_quantity": 3,
      "expected_quantity": 2.5
    }
  ],
  "_note": "Code-derived."
}

create_wastage_draft

POST /api/wastage. Why: Record wasted stock (expired / damaged / spoiled …) for one branch as a DRAFT wastage batch — MCP forces approval_status draft and strips skip_stock_movement, so no stock moves. A user then submits it and an approver approves it in the dashboard, which deducts the stock. Material ids, units and cost from list_materials / get_material; branch from list_locations; brand from list_brands. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_wastage_draft"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
wastage_reason string required Batch reason (one for all lines).
location_id number required ONE branch id (list_locations) where the waste happened; stock-out on approval happens here.
batch_notes string optional Free-text note for the batch.
brand_id number optional Batch brand (list_brands).
items array<object> required Non-empty list of lines (empty → 500 "No items provided").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_wastage_draft",
  "wastage_reason": "example",
  "location_id": 11,
  "items": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Wastage batch created successfully",
  "data": {
    "id": 34,
    "batch_number": "WB-2026-00031",
    "approval_status": "draft"
  },
  "_note": "Code-derived (no write calls made)."
}

update_wastage_draft

PUT /api/wastage/:id. Why: Change a DRAFT wastage batch (reason, branch, notes, brand, lines). NOT a patch: header fields you omit become null and the lines are replaced by exactly the items you send — get_wastage first and resend everything with your change. The API rejects non-draft batches. No stock change. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_wastage_draft"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Wastage batch id (list_wastage / get_wastage) whose approval_status is draft.
wastage_reason string required Batch reason; omitted → null.
location_id number required Branch id; omitted → null → DB error (NOT NULL).
batch_notes string optional Note; omitted → null (resend header.batch_notes to keep it).
brand_id number optional Batch brand; omitted → null (resend header.brand_id when it is not null to keep it).
items array<object> required ALL lines of the batch — FULL REPLACE: existing lines are deleted and these inserted; omitted/[] → the batch ends with 0 lines and total 0.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_wastage_draft",
  "id": 11,
  "wastage_reason": "example",
  "location_id": 11,
  "items": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Wastage batch updated successfully",
  "data": {
    "wastage_reason": "expired",
    "items_count": 1,
    "total_cost": 75,
    "batch_notes": null,
    "location_id": 907,
    "brand_id": null
  },
  "_note": "Code-derived."
}

create_stock_transfer

POST /api/stock-transfer-invoices. Why: Create an inter-branch stock transfer request (materials from one branch to another) in status pending — the same document the dashboard "Add New Transfer" wizard creates. No stock moves: the source approves, dispatches and the destination receives it in the dashboard. Branch ids from list_locations (must be two different branches the key may use); lines from list_materials (qty in storage unit, avg_price as cost); check source stock with list_inventory / get_material_stock. For buying from a supplier use create_draft_purchase_order instead; for a branch asking head office for goods the dashboard uses order requests (create_stock_order_request). Response data is null — find the new transfer with list_stock_transfers (status=pending, newest first, match uuid). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_stock_transfer"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required New random UUID v4 you generate (the dashboard does uuidv4()).
from_location_id number required SOURCE branch id (list_locations) — stock will leave here on dispatch.
to_location_id number required DESTINATION branch id (list_locations) — stock will arrive here on receive.
items array<object> required Lines to transfer (at least one; the server crashes with 500 when items is missing).
subtotal number optional Sum of line subtotals.
vat number optional VAT AMOUNT (not percent): subtotal × branch VAT % when "Include VAT" is ticked (dashboard default ticked, VAT % from the source branch config, 5 if unset), els…
payable number optional subtotal + vat.
note string optional Header note, max 300 chars.
delivery_date string optional Planned delivery date YYYY-MM-DD (dashboard optional date picker).
expected_delivery_date string optional Expected delivery date YYYY-MM-DD.
priority enum: low, normal, high, urgent optional Priority label.
info_name string optional Requester name shown as "User" in the list.
info_email string optional Requester email.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_stock_transfer",
  "uuid": "example",
  "from_location_id": 11,
  "to_location_id": 11,
  "items": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Stock transfer created successfully",
  "data": null
}

update_stock_transfer

PUT /api/stock-transfer-invoices/update/:id. Why: Edit the header of a PENDING (or draft) transfer: note, delivery date, expected delivery date, priority. Lines are edited with update_stock_transfer_items. Get the id and check status with list_stock_transfers / get_stock_transfer first; approved/dispatched/received transfers are rejected by the server. No stock moves. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_stock_transfer"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Numeric transfer id (list_stock_transfers data.data[].id).
note string optional New header note (max 300).
delivery_date string optional New delivery date YYYY-MM-DD.
expected_delivery_date string optional New expected delivery date YYYY-MM-DD.
priority enum: low, normal, high, urgent optional New priority label.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_stock_transfer",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Stock transfer request updated successfully",
  "data": {
    "transfer": {
      "id": 7776,
      "items_number": 2,
      "subtotal": 5841.84,
      "vat": 292.092,
      "payable": 6133.932,
      "discountamount": 0,
      "discountpct": 0,
      "paid": 0,
      "change": 0,
      "note": "Deliver before lunch",
      "uuid": "fe1ecd13-f615-4326-b6a1-482b5353a3c6",
      "created_at": "2026-08-12T10:48:07.000Z",
      "customer_id": 437,
      "from_location_id": 907,
      "info_name": "Nexus Cafe",
      "info_address": null,
      "info_email": "admin@devnexus.co",
      "info_phone": null,
      "status": "pending",
      "user": "Nexus Cafe",
      "table_id": null,
      "payment_method": null,
      "order_id": null,
      "invoice_id": 44,
      "to_location_id": 922,
      "customer_location_id": 907,
      "email": null,
      "delivery_date": "2026-09-28T00:00:00.000Z",
      "user_id": 4734,
      "is_edit": null,
      "username": "Nexus Cafe",
      "transfer_no": "STR-2026-01628",
      "requested_by": 4734,
      "approved_by": null,
      "dispatched_by": null,
      "received_by": null,
      "requested_at": "2026-08-12T10:48:07.000Z",
      "approved_at": null,
      "dispatched_at": null,
      "received_at": null,
      "notes": null,
      "priority": "high",
      "expected_delivery_date": null,
      "updated_at": "2026-08-12T10:48:20.000Z",
      "rejected_by": null,
      "rejected_at": null,
      "parent_transfer_id": null
    }
  }
}

update_stock_transfer_items

PUT /api/stock-transfer-invoices/items/:id. Why: Edit the lines of a PENDING (or draft) transfer: change qty / price / note, add a material, or remove a line — the dashboard transfer page "Save" before approval. Read the lines with get_stock_transfer (items[].id, material_id, qty, price) and send the full list back; materials from list_materials. On approved/dispatched transfers this same route is the dashboard dispatch/receive action, so never call it unless status is pending or draft. No stock moves while pending. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_stock_transfer_items"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Numeric transfer id (list_stock_transfers / get_stock_transfer).
items array<object> required The COMPLETE line list after your change (non-empty).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_stock_transfer_items",
  "id": 11,
  "items": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Transfer items updated successfully",
  "data": {
    "transfer": {
      "id": 7770,
      "items_number": 2,
      "subtotal": 127.256,
      "vat": 6.3628,
      "payable": 133.6188,
      "discountamount": 0,
      "discountpct": 0,
      "paid": 0,
      "change": 0,
      "note": "",
      "uuid": "51ed4a86-e033-45a1-9a34-795bb89eeb2f",
      "created_at": "2026-07-30T07:07:35.000Z",
      "customer_id": 437,
      "from_location_id": 907,
      "info_name": "Nexus Cafe",
      "info_address": "",
      "info_email": "admin@devnexus.co",
      "info_phone": "",
      "status": "pending",
      "user": "Nexus Cafe",
      "table_id": null,
      "payment_method": null,
      "order_id": null,
      "invoice_id": 40,
      "to_location_id": 908,
      "customer_location_id": 907,
      "email": "admin@devnexus.co",
      "delivery_date": null,
      "user_id": 4734,
      "is_edit": null,
      "username": null,
      "transfer_no": "STR-2026-01621",
      "requested_by": 4734,
      "approved_by": null,
      "dispatched_by": null,
      "received_by": null,
      "requested_at": "2026-07-30T07:07:36.000Z",
      "approved_at": null,
      "dispatched_at": null,
      "received_at": null,
      "notes": "{\"is_auto_created\":true,\"source_transfer_id\":7769,\"source_transfer_no\":\"STR-2026-01620\",\"source_transfer_uuid\":\"f6248006-ef35-4098-a3a7-88130bf7a8f0\",\"originating_po_uuids\":[\"1ba0b8cd-8495-4e43-80c9-f74dd58214f9\"],\"created_at\":\"2026-07-30T07:07:35.982Z\"}",
      "priority": null,
      "expected_delivery_date": null,
      "updated_at": "2026-07-30T07:07:35.000Z",
      "rejected_by": null,
      "rejected_at": null,
      "parent_transfer_id": null,
      "from_location": "Default location",
      "to_location": "Johar Town",
      "requested_by_username": "Nexus Cafe",
      "approved_by_username": null,
      "dispatched_by_username": null,
      "received_by_username": null,
      "items": [
        {
          "id": 67280,
          "name": "Gotcha Recipe",
          "price": 0.214,
          "qty": 80,
          "subtotal": 17.12,
          "stock_transfer_uuid": "51ed4a86-e033-45a1-9a34-795bb89eeb2f",
          "location_id": 907,
          "customer_id": 437,
          "material_id": 120802,
          "extras": "{\"inherited_from_transfer_id\":7769,\"inherited_po_uuid\":\"1ba0b8cd-8495-4e43-80c9-f74dd58214f9\",\"inherited_po_number\":\"PO-006919\"}",
          "is_edit": "0",
          "approved_qty": 100,
          "dispatched_qty": null,
          "received_qty": null,
          "variance_qty": null,
          "variance_reason": null,
          "unit": "Kilogram",
          "unit_id": null,
          "item_status": null,
          "notes": "reduced",
          "created_at": "2026-07-30T07:07:35.000Z",
          "updated_at": "2026-07-30T07:07:35.000Z",
          "stock_transfer_id": null,
          "brand_id": null,
          "disposition_type": null,
          "disposition_status": null,
          "resolved_by": null,
          "resolved_at": null,
          "disposition_notes": null
        },
        {
          "id": 67300,
          "name": "Test Material",
          "price": 11.0136,
          "qty": 10,
          "subtotal": 110.136,
          "stock_transfer_uuid": "51ed4a86-e033-45a1-9a34-795bb89eeb2f",
          "location_id": 907,
          "customer_id": 437,
          "material_id": 119196,
          "extras": null,
          "is_edit": "0",
          "approved_qty": null,
          "dispatched_qty": null,
          "received_qty": null,
          "variance_qty": null,
          "variance_reason": null,
          "unit": "Kilogram",
          "unit_id": null,
          "item_status": null,
          "notes": null,
          "created_at": "2026-07-30T07:07:35.000Z",
          "updated_at": "2026-07-30T07:07:35.000Z",
          "stock_transfer_id": null,
          "brand_id": null,
          "disposition_type": null,
          "disposition_status": null,
          "resolved_by": null,
          "resolved_at": null,
          "disposition_notes": null
        }
      ]
    }
  }
}

create_stock_order_request

POST /api/stock-order-request. Why: Create a branch order request (status Pending): a list of materials with quantity, cost and supplier that the branch wants purchased — the dashboard "Create Order Request" form. Nothing is ordered and no stock moves; in the dashboard a manager Declines it or clicks Create Draft PO (one draft PO per supplier). Materials from list_materials, suppliers from list_suppliers / list_supplier_materials, branch from list_locations. To create a purchase order directly use create_draft_purchase_order; to move stock between branches use create_stock_transfer. Response echoes the body (no id); find it with list_stock_order_requests (st=Pending) by uuid. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_stock_order_request"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required New random UUID v4 you generate (dashboard uuidv4()).
from_location_id number required Requesting branch id (list_locations).
to_location_id number required Destination branch — the dashboard sends the SAME id as from_location_id (the branch receives what it requested).
items array<object> required Requested lines (at least one).
subtotal number optional Σ line subtotals (stored as sent; not recomputed).
vat number optional VAT AMOUNT = subtotal × branch VAT % (dashboard "Include VAT" default on), or 0.
payable number optional subtotal + vat (stored as sent).
note string optional Header note (max 300).
delivery_date string optional Requested delivery date-time "YYYY-MM-DD HH:mm:ss" (dashboard date picker, default now).
info_name string optional Requester name (dashboard: logged-in username).
info_email string optional Requester email.
table_id number optional Legacy; dashboard sends 0.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_stock_order_request",
  "uuid": "example",
  "from_location_id": 11,
  "to_location_id": 11,
  "items": [
    11
  ]
}'

Example Response

{
  "uuid": "b8a4c7f2-5d61-4f3e-9c0a-1e2d3f4a5b6c",
  "from_location_id": 907,
  "to_location_id": 907,
  "subtotal": 6000,
  "vat": 300,
  "payable": 6300,
  "note": "",
  "info_name": "Nexus Cafe",
  "delivery_date": "2026-09-30 09:00:00",
  "table_id": 0,
  "items": [
    {
      "material_id": 112157,
      "name": "SRC-BT-86CBPR-SG3",
      "qty": 2000,
      "price": 3,
      "subtotal": 6000,
      "unit_name": "Pieces",
      "supplier_id": 881
    }
  ],
  "status": "Pending",
  "customer_id": 437,
  "location_id": 907
}

update_stock_order_request

PUT /api/stock-order-request/update/:uuid. Why: Edit a PENDING order request — lines (material, qty, price, supplier), totals, note, delivery date — like the dashboard Edit page / Draft PO page "Save changes". Read it first with get_stock_order_request; the line list is replaced entirely. MCP strips status / reason / reject_reason, so the request stays Pending; declining or creating the draft PO stays in the dashboard. No stock moves. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_stock_order_request"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Order request uuid (list_stock_order_requests).
items array<object> required The COMPLETE new line list — all existing lines are deleted and these inserted (a line you omit is removed; a field you omit becomes null).
subtotal number optional New Σ line subtotals.
vat number optional New VAT AMOUNT (subtotal × branch VAT % or 0).
payable number optional New subtotal + vat.
note string optional Header note.
delivery_date string optional "YYYY-MM-DD HH:mm:ss"; null/"" clears; omitted → unchanged.
from_location_id number optional Requesting branch (list_locations).
to_location_id number optional Destination branch (dashboard: same as from_location_id, or the PO destination on the Draft PO page).
info_name string optional Requester name.
info_email string optional Requester email.
table_id number optional Legacy (dashboard sends the existing value or 0).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_stock_order_request",
  "uuid": "example",
  "items": [
    11
  ]
}'

Example Response

{
  "message": "Order updated successfully",
  "data": {
    "uuid": "59c4fb6e-fb85-4ab1-ac0b-4338bd97b70a",
    "subtotal": 4500,
    "payable": 4725,
    "vat": 225,
    "status": "Pending",
    "items": [
      {
        "material_id": 112157,
        "name": "SRC-BT-86CBPR-SG3",
        "qty": 1500,
        "price": 3,
        "subtotal": 4500,
        "unit_name": "Pieces",
        "supplier_id": 881
      }
    ]
  }
}

create_work_order

POST /api/work-orders. Why: Plan a production batch of STOCKABLE recipes at one branch, as a DRAFT (MCP forces status "draft"): what to produce (recipe, output qty) and what it will consume (one material line per ingredient, computed from get_recipe). No stock moves now: the user moves it to In Progress and completes it in the dashboard, which then consumes the material lines and adds the output to the recipes' prepared materials (list_inventory_movements). Each recipes[] line must carry materials[] (one per recipe ingredient, yield-adjusted qty, unit, unit_price, total_price) — the server never derives them, and completion consumes only these lines. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_work_order"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
production_date string required Planned production day YYYY-MM-DD (NOT NULL).
expected_completion string optional Expected completion day YYYY-MM-DD; omitted/null → none.
location_id number required ONE branch id (list_locations) — where materials are consumed and the item produced (NOT NULL; completion refuses an order without it).
notes string optional Free-text note.
net_total number optional Planned cost = Σ recipes[].total_cost (stored as sent; 0/omitted → null).
recipes array<object> required Recipes to produce, at least one (none → 500 "No recipes provided").

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_work_order",
  "production_date": "example",
  "location_id": 11,
  "recipes": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Work order created successfully",
  "data": {
    "id": 1738,
    "work_order_no": "PRD-2026-01660"
  }
}

update_work_order

PUT /api/work-orders/:id. Why: Edit a DRAFT work order: dates, branch, notes and the full set of recipe + material lines (full replace; net_total is recomputed from the lines). MCP strips `status`. Non-draft orders are refused. No stock moves. Start from get_work_order. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_work_order"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Work order id (list_work_orders); must be a draft (get_work_order header.status).
production_date string required Planned production day YYYY-MM-DD (NOT NULL).
expected_completion string optional Expected completion day YYYY-MM-DD; omitted/null → none.
location_id number required ONE branch id (list_locations) — where materials are consumed and the item produced (NOT NULL; completion refuses an order without it).
notes string optional Free-text note.
recipes array<object> required FULL list of lines — replaces all recipe and material lines.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_work_order",
  "id": 11,
  "production_date": "example",
  "location_id": 11,
  "recipes": [
    11
  ]
}'

Example Response

{
  "success": true,
  "message": "Work order updated successfully",
  "data": {
    "production_date": "2026-09-27",
    "expected_completion": "2026-09-28",
    "notes": "Morning batch",
    "net_total": 22.3566,
    "location_id": 907
  }
}

update_user

PUT /api/users/:id. Why: Edit one staff user — what the dashboard user edit form saves: name, email, role (role_id from list_roles), branches (allow_locations + default location_id from list_locations), dashboard modules, app ios_modules, the positional POS switch string `permissions`, max discount, report e-mails, default brand (list_selling_brands) / channel and the named POS switches. Admin-only. It is a FULL-RECORD save: read the user with list_users first and resend every field. For the session user's own POS switches one at a time prefer update_user_permissions; role-only change: assign_user_role. Full replace: omitted role_id, allow_locations, permissions, max_discount_permit, report flags, image, organization_customer_ids → NULL; brand_id / dining_guest_count → 0; ios_modules → tenant default. Resend the list_users row. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_user"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required User id (list_users [].id).
username string required Display name (dashboard "Name").
email string required Login email.
role_id string optional Role id (list_roles).
role enum: admin, staff optional Legacy role text "admin" | "staff" (what non-master dashboards send).
location_id number required Default branch id (list_locations) — one id, not csv.
allow_locations string required CSV of every branch id the user may use, e.g.
modules string required ';'-separated 0/1 dashboard/app module flags by position: 0 Dashboard, 1 POS, 2 KDS, 3 Sales, 4 Reporting, 5 Products, 6 Inventory, 7 Config, 8 Preview Invoice…
ios_modules string required ';'-separated 0/1 app feature flags by position: 0 is_kds_enable, 1 is_daily_sale_report_enable, 2 is_order_hisotry_screen_enable, 3 is_sms_enable, 4 is_cloud_…
permissions string required ';'-separated 0/1 POS switches by position (NOT role permissions): 0 print_receipt, 1 kds_auto_receipt, 2 invoice_auto_receipt, 3 cart_options, 4 print_and_sav…
max_discount_permit number optional Max manual discount % at the POS (0-100).
can_preview_invoice number optional 0 | 1.
is_daily_report number optional 0 | 1.
is_weekly_report number optional 0 | 1.
is_monthly_report number optional 0 | 1.
is_ingredient_disabled number optional 0 | 1.
image string optional Profile image: the file name, or the list_users URL (the server keeps the part after "uploads/").
organization_customer_ids string optional CSV of linked tenant ids (dashboard "Linked Customers"); "" = none.
brand_id number optional Default POS brand (list_selling_brands brand_id; 0 = none).
dining_guest_count number optional 0 | 1.
channel_id number optional Default sales channel (list_selling_channels channel_id; 0 = none).
print_no_of_times number optional Receipt copies (≥ 1).
fraction_digits number optional Quantity fraction digits (not money).
receipt_format number optional Receipt template 0-3; other values → 0.
row_discount number optional 0 | 1.
language string optional App language code, e.g.
zebra_ip string optional Zebra label-printer IP (null → "").
zebra_port number optional Zebra label-printer port (non-numbers → 0).
cloud_target_user_id number optional User id (list_users) whose device receives this user's cloud prints; 0 = none.
cloud_target_device_id string optional Device id that receives this user's cloud prints; null = none.
station_id number optional KDS station id (0 = none).
currency string optional BRANCH setting, not a user field: upserts the config `currency` of the user's location_id for everyone at that branch ("" clears an existing row).
orderhistory_print_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
show_receipt_screen number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_brand_groups number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_extras_enble number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
display_modifier_titles number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
modifier_sort_min_required_desc number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_only_save_kds_orders number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_note_on_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
order_type number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
open_dine_in number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
hide_cart_pay_button number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
tip_amount number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_void_orders number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
has_void_order_permission number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
hide_clear_checkout number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
kds_instruction number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
pay_default_usd number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_business_logo number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_order_type number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_is_modified number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_biller_info number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_total_before_vat number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_vat_info number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_net_total number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_exchange_rate number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_dine_guest_count number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
rpt_paid_at number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
esc_command number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
qr_code_on_print_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
delivery_detail_enable number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_attributed_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
exchange_format number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_channels_enable number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_cds_enable number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
external_order_print_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
text_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
each_category_kds_print number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
user_product_sales_counter number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_change_payment_method_enabled number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
skip_zero_quantity_items number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
skip_zero_amount_items number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
settle_wallet_topup number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_deleted_item_on_receipt number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
block_print_until_order_open number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
draft_order number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
open_price_pincode number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_transfer_enabled number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
sms_invoice number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
offline_order_sync number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_multi_location_allowed number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_qr_payment number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_kds_expeditor number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
unify_brands number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
dinein_serving_role number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
ingredient_stay_standalone number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
reservation_with_dine_in number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
pay_zero_amount number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
receipt_translation number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
product_translation number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
category_translation number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_invoice_translation number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
print_kds_translation number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
splitefire number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
allegy_item number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
one_table_one_order number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_socket_server number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_client number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
web_local_network_printer number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_shift_management number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
shift_transactions number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
eod_shift_report number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
shift_z_report number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
view_estimated_cash number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
external_order_ready number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
acknowledge_dine_table_edit number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
lock_screen number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
is_soldout number optional 0 | 1 (1, "1" or true → 1, anything else → 0).
force_crm_user number optional 0 | 1 (1, "1" or true → 1, anything else → 0).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_user",
  "id": 11,
  "username": "example",
  "email": "example",
  "location_id": 11,
  "allow_locations": "example",
  "modules": "example",
  "ios_modules": "example",
  "permissions": "example"
}'

Example Response

{
  "data": {
    "id": "4789",
    "username": "Sara Cashier",
    "email": "sara.cashier@example.com",
    "role_id": 32,
    "location_id": 907,
    "allow_locations": "907,909,908",
    "modules": "1;1;1;1;1;1;1;1;1;1;1",
    "ios_modules": "1;1;1;1;1;1;1;1;1",
    "permissions": "1;0;1;1;0;0;0;0;1;1;0;0;0;0;0;1;0;0;0;1;0;0;0;0;0;0",
    "max_discount_permit": 30,
    "can_preview_invoice": 1,
    "is_daily_report": 0,
    "is_weekly_report": 0,
    "is_monthly_report": 0,
    "is_ingredient_disabled": 0,
    "image": "",
    "organization_customer_ids": "",
    "brand_id": 0,
    "dining_guest_count": 0,
    "role": "staff",
    "is_admin": 0
  },
  "message": "User updated successfully"
}

assign_user_role

POST /api/users/assign-role. Why: Change only a staff user's role (users.role_id) without resending the whole record — the lighter alternative to update_user. role_id from list_roles, user_id from list_users. The role's permissions decide which dashboard pages the user can open. Unlike update_user it changes role text and is_admin only for roles whose name contains "admin"; moving an admin to a non-admin role leaves is_admin 1 — use update_user with role_id for that. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"assign_user_role"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
user_id string required User id (list_users [].id).
role_id string required Role id (list_roles [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "assign_user_role",
  "user_id": 11,
  "role_id": 11
}'

Example Response

{
  "success": true,
  "message": "User role assigned successfully."
}

create_pos_crm_user

POST /api/v2/crm/users. Why: Create a customer from the POS — the web-pos "Add user" form. Only first_name is required; phone and email must be unique in the tenant. Returns the new id (user_id) to use as crm_user_id on the order. The customer is tenant-wide (visible at every branch and on the dashboard list_crm_users). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_pos_crm_user"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
first_name string required REQUIRED.
last_name string optional Optional.
mobile_number string optional Optional.
email string optional Optional.
dob string optional Optional.
gender enum: male, female optional Optional.
delivery_area string optional Optional.
city string optional Optional.
country string optional Optional.
preferred_currency string optional Optional.
is_push_notification_enable enum: 0, 1 optional Optional.
is_mobile_number_verified enum: 0, 1 optional Optional.
latitude number optional Optional.
longitude number optional Optional.
trn_number string optional Optional.
company_name string optional Optional.
thumbnail string optional Optional.
earn_points number optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_pos_crm_user",
  "first_name": "example"
}'

Example Response

{
  "status": true,
  "message": "User has been created successfully.",
  "user_id": 64900
}

update_pos_crm_user

POST /api/v2/crm/users/:id. Why: Edit a customer from the POS — the web-pos "Edit" form (fix a phone number, add a delivery area before a delivery order). Partial update: only the fields you send with a non-empty value change. Tenant-checked by id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_pos_crm_user"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Customer id (list_pos_crm_users data.users[].id).
first_name string optional Optional.
last_name string optional Optional.
mobile_number string optional Optional.
email string optional Optional.
dob string optional Optional.
gender enum: male, female optional Optional.
delivery_area string optional Optional.
city string optional Optional.
country string optional Optional.
preferred_currency string optional Optional.
is_push_notification_enable enum: 0, 1 optional Optional.
is_mobile_number_verified enum: 0, 1 optional Optional.
latitude number optional Optional.
longitude number optional Optional.
trn_number string optional Optional.
company_name string optional Optional.
thumbnail string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_pos_crm_user",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "User has been updated successfully"
}

create_crm_user

POST /api/crm-users. Why: Create a customer from the dashboard. Tenant-wide (every branch). Rejects a phone or email already used by another customer. Unlike create_pos_crm_user it stores no placeholders (missing dob/gender stay null). Returns user_id = crm_user_id. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_crm_user"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
first_name string required REQUIRED.
last_name string optional Optional.
company_name string optional Optional.
email string optional Optional.
mobile_number string optional Optional.
dob string optional Optional.
delivery_area string optional Optional.
city string optional Optional.
country string optional Optional.
preferred_currency string optional Optional.
latitude number optional Optional.
longitude number optional Optional.
gender enum: male, female optional Optional.
thumbnail string optional Optional.
trn_number string optional Optional.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_crm_user",
  "first_name": "example"
}'

Example Response

{
  "success": true,
  "message": "CRM User created successfully",
  "user_id": 64900
}

update_crm_user

PUT /api/crm-users/:id. Why: Replace a customer's profile from the dashboard. FULL REPLACE: every field below that you omit is set to NULL — read get_crm_user first and send every value you want to keep. Use it to CLEAR a field (update_pos_crm_user cannot). Does not touch loyalty points, wallet, flags or purchase stats. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_crm_user"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Customer id (crm_user_id).
first_name string required REQUIRED.
last_name string optional Last name.
company_name string optional Company name.
email string optional Valid email.
mobile_number string optional Phone (digits, optional leading +).
dob string optional Date of birth after 1960-01-01.
delivery_area string optional Delivery area (letters, digits, spaces, commas, hyphens).
city string optional City.
country string optional Country.
preferred_currency string optional Currency code.
latitude number optional Latitude.
longitude number optional Longitude.
gender enum: male, female optional "male" or "female".
thumbnail string optional Image path as returned by get_crm_user.
trn_number string optional Tax registration number.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_crm_user",
  "id": 11,
  "first_name": "example"
}'

Example Response

{
  "success": true,
  "message": "CRM User updated successfully"
}

create_role

POST /api/roles. Why: Create a role with a set of dashboard permissions — what the Roles & Permissions page's "New role" / Duplicate does. Assign it to users with update_user (role_id) or assign_user_role. Permission ids come from list_roles / get_role. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"create_role"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
name string required Role name, unique in the tenant (409 when taken).
description string optional Free text shown on the Roles page.
permissions array<string> optional Permission ids (list_roles / get_role permissions[].id) — the COMPLETE set the role should grant.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "create_role",
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Role created successfully",
  "data": {
    "id": 190,
    "name": "Shift Supervisor",
    "description": "Brands and users read access",
    "customer_id": 437,
    "is_deleted": 0,
    "created_at": "2026-09-26T09:00:00.000Z",
    "updated_at": "2026-09-26T09:00:00.000Z",
    "permissions": [
      {
        "id": 403,
        "name": "Access",
        "value": "dashboard.access",
        "group": "Dashboard",
        "sub_group": "Dashboard Access",
        "action": null
      },
      {
        "id": 405,
        "name": "Brands",
        "value": "products.brands",
        "group": "Products",
        "sub_group": "Brands",
        "action": null
      },
      {
        "id": 625,
        "name": "List",
        "value": "users.list",
        "group": "Users",
        "sub_group": "List",
        "action": null
      }
    ]
  }
}

update_role

PUT /api/roles/:id. Why: Rename a role or change the dashboard permissions it grants — what Save on the Roles & Permissions page does. Affects every user with this role_id (list_users) immediately. Read it with get_role first. Send name AND description AND the complete permissions list like the dashboard: permissions replace the whole set ([] removes all), and a body with neither name nor description fails with 500. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"update_role"} BEFORE calling this tool. Safety: write. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id string required Role id (list_roles data[].id).
name string required Role name (409 if another role has it).
description string optional Free text.
permissions array<string> optional Permission ids (list_roles / get_role permissions[].id) — the COMPLETE set the role should grant.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "update_role",
  "id": 11,
  "name": "Iced Latte"
}'

Example Response

{
  "success": true,
  "message": "Role updated successfully",
  "data": {
    "id": 182,
    "name": "Manager",
    "description": "Store manager",
    "customer_id": 437,
    "is_deleted": 0,
    "created_at": "2025-12-13T13:24:49.000Z",
    "updated_at": "2025-12-13T13:24:49.000Z",
    "permissions": [
      {
        "id": 139,
        "name": "Attendance",
        "value": "module_attendance",
        "group": "module_access",
        "sub_group": null,
        "action": null
      },
      {
        "id": 405,
        "name": "Brands",
        "value": "products.brands",
        "group": "Products",
        "sub_group": "Brands",
        "action": null
      },
      {
        "id": 406,
        "name": "Add",
        "value": "products.brands.add",
        "group": "Products",
        "sub_group": "Brands",
        "action": "add"
      }
    ]
  }
}

Products (destructive)

delete_product

DELETE /api/products/:id. Why: Soft-delete a product: sets is_deleted = 1 so it disappears from list_products, list_selling_products and the menus at every branch; the row, branch rows, modifier links and history stay (there is no undelete tool). Prefer archive_product when it may come back, or location_prices[].is_available=false (update_product) to hide it at one branch. Get the id from list_products. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_product"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Product id (list_products data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_product",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Product deleted successfully"
}

delete_category

DELETE /api/categories/:id. Why: Permanently delete an unused catalog category (dashboard trash icon / Edit page Delete). Refused while any active product has it in product.category_id (check get_category linked_products_count first; move those products with update_product, or set is_active 0 via update_category instead). Irreversible. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_category"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Category id (list_categories / get_category).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_category",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "category deleted successfully."
}

delete_modifier

DELETE /api/modifiers/:id. Why: Permanently remove a modifier group (dashboard trash icon). Also deletes its product links (product_modifiers) at every branch. Does NOT delete its items (they stay as orphans — delete or re-group them first with delete_modifier_item / update_modifier_item), its modifiers_locations rows or product_modifier_item_settings. Past orders (sync_invoice lines) keep their own copy. Confirm with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_modifier"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier group id to delete (list_modifiers [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_modifier",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "modifier deleted successfully"
}

delete_modifier_item

DELETE /api/modifierItems/:id. Why: Permanently remove one option (dashboard row menu › Delete). Deletes the modifier_Items row and its modifier_item_assignments; its branch rows, kitchen instructions, recipes and per-product settings (product_modifier_item_settings) are left behind. Past orders (sync_invoice lines) keep their own copy. Confirm with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_modifier_item"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Modifier item id to delete (list_modifier_items [].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_modifier_item",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "modifierItem deleted successfully"
}

delete_brand

DELETE /api/brands/:id. Why: Permanently delete a brand row. No safety checks: nothing blocks deleting a brand that is in use, and its groups, categories and products are untouched (groups just lose the brand). Prefer editing with update_brand; confirm the id via get_brand (customer_id must be yours) first. Irreversible. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_brand"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Brand id (list_brands).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_brand",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Brand deleted successfully."
}

delete_group

DELETE /api/groups/:id. Why: Permanently delete a group (hard delete; no undo). Its brand loses those categories on the POS menu. Consumes a group id from list_groups. Catalog management (dashboard) tool. For menu scoping on the POS use list_selling_brands, which already returns each brand's groups with their category ids. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_group"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Group id (list_groups data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_group",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Group deleted successfully"
}

delete_addon

DELETE /api/products/:id. Why: Soft-delete an add-on (is_deleted = 1): it disappears from list_addons, list_selling_addons and the POS, but branch rows, links and the row stay (get_addon still returns it with is_deleted = 1); there is no undelete tool. To hide it reversibly use archive_product (or update_addon with is_archive = 1). Takes an id from list_addons. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_addon"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Add-on id (list_addons data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_addon",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Product deleted successfully"
}

delete_label

DELETE /api/label/:id. Why: Permanently delete a label and its branch links (dashboard Labels → Delete). Refused while an active product uses it: find them with list_products label_id=<id> and move them with update_product first. Archived products, menus and past sale lines (sync_invoice / list_orders_history items[].label_id) keep the dead id. Get the id from list_labels. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_label"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Label id (list_labels data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_label",
  "id": 11
}'

Example Response

{
  "message": "Label deleted successfully"
}

delete_deal

DELETE /api/deals/:id. Why: Permanently remove a deal: hard-deletes the products row, its slots (deal_categories) and channel prices. Id from list_deals. Past orders keep their combo lines (sync_invoice history is untouched). To only hide a deal from the POS use archive_product on the deal id instead. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_deal"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Deal id (list_deals items[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_deal",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Deal deleted successfully"
}

delete_channel

DELETE /api/channels/:id. Why: Hard-delete a channel and every product price for it (product_channel_price). No in-use check: orders keep their channel_id/channel_name, discounts keep the stale id in channel_ids, and channels_locations rows are left orphaned. Linkage: a channel id is referenced by list_selling_products data.products[].price_levels[].channel_id (per-channel price, product_channel_price), by orders (channel_id + channel_name, stored verbatim by sync_invoice — web-pos sends 0/""), and by discount codes channel_ids (list_discounts / get_user_extras data.discount_code[]). Branch availability lives in channels_locations (location_id in these tools); master channels have channels.location_id NULL. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_channel"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Channel id (list_channels).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_channel",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Channels deleted successfully"
}

delete_menu

DELETE /api/menus/:id. Why: Permanently delete a menu and its category, branch and product links (hard delete, no restore). Products and categories themselves are not touched. Prefer update_menu with is_active: 0 to hide it. Menus are dashboard records only: nothing else in SysPOS reads them today. The POS (web-pos) never calls /menus, list_selling_products / list_selling_categories do not look at menus, and the aggregator syncs (Deliverect, Grubtech, Otter, LimeTray) build their menus from categories (sync_* flags, list_categories) and products, not from these menus. Adding or removing a product here does not change what a branch sells; for that use update_product (location_prices) or the category tools. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_menu"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Menu id (list_menus data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_menu",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Menu deleted successfully"
}

delete_coupon

DELETE /api/coupons/:id. Why: Hard-delete a coupon with its branch and product/category links. Past invoices keep their coupon_code. To stop a coupon but keep history, prefer update_coupon is_active: 0. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_coupon"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Coupon id (list_coupons data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_coupon",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Coupon has been successfully deleted.",
  "data": "1001"
}

delete_discount

DELETE /api/discounts/:id. Why: Permanently delete a discount (discount code); it disappears from the POS (get_user_extras) on its next refresh. There is no archive — to stop it temporarily use update_discount with is_active 0. Past orders keep their stored discount amounts. Id from list_discounts. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_discount"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Discount id (list_discounts data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_discount",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Discount has been successfully deleted.",
  "data": "2400"
}

delete_payment_method

DELETE /api/payment-methods/:id. Why: Permanently delete a payment method and all its branch links. NO in-use check (the guard is commented out) — past invoices keep the name string, but no branch can settle with it any more. To stop using it at one branch, update_payment_method with a smaller location_id list; to pause it, set is_enable = 0. Confirm with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_payment_method"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Payment method id (list_payment_methods).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_payment_method",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Payment method \"Wallet\" deleted successfully."
}

delete_order_type

DELETE /api/order-type/:id. Why: Permanently delete a custom order type for the whole tenant (all branches lose it). System defaults (is_system_default = 1) cannot be deleted. There is NO in-use check — past invoices keep the name string, but the type disappears from the POS. Confirm with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_order_type"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Order type id (list_order_types, must have is_system_default = 0).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_order_type",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Order type deleted successfully"
}

delete_location

DELETE /api/locations/:id. Why: Permanently delete a branch that was never used. The server refuses (status false) when ANY row still points at the branch in config, customers, inventory, invoices, invoice_items, orders, order_items, materials, product_materials, products, categories, tables or users — so a branch that sold anything, has staff, tables, or even saved business info / hours config cannot be deleted. On success it hard-deletes the locations row and removes the id from the calling user's allow_locations. Confirm with the user first; there is no undo and no soft delete. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_location"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Location id (list_locations[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_location",
  "id": 11
}'

Example Response

{
  "status": false,
  "message": "You can't delete this location. It's being used."
}

delete_material

DELETE /api/materials/:id. Why: Deactivate a material: is_active = 0 and ALL its branch rows (stock + cost) are deleted, no inventory movement is posted and recipe links stay. Use it only for materials that are no longer bought or used — check get_material_used_in_recipes (and product BOMs) first, and zero stock through counts/wastage/transfers if you need an audit trail. Cannot be undone through the API (update_material cannot reactivate; a new create_material makes a new id). Never use DELETE /materials/all. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_material"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material id (list_materials).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_material",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Material deactivated and location mappings removed"
}

delete_material_category

DELETE /api/inventory/categories/:id. Why: Soft-delete an EMPTY material category (no active materials assigned and no subcategories). Move materials first with update_material (category_id) and delete subcategories first. Taxonomy only; no stock impact. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_material_category"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Material category id (list_material_categories).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_material_category",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Material category deleted successfully",
  "data": {
    "id": 6,
    "deleted": true
  }
}

delete_supplier

DELETE /api/suppliers/:id. Why: Permanently remove a supplier from the directory. Only for suppliers that are no longer used: check list_supplier_invoices sid=<id> (and list_suppliers total_orders / items_in_catalog) first — the API's own "in use" check only looks at legacy tables and will delete a supplier that still has purchase orders or material links. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_supplier"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Supplier id from list_suppliers.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_supplier",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "supplier deleted successfully."
}

delete_recipe

DELETE /api/recipe/:id. Why: Permanently delete a recipe: its lines, cost snapshot and product links, plus its prepared material and that material's rows at every branch (including any stock on hand). No undo. Linked products stop deducting ingredients on sale; other recipes that use it as a sub-recipe keep a dangling line. Check get_recipe (linked_products) and get_material_used_in_recipes on its prepared material first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_recipe"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Recipe id (list_recipes / get_recipe).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_recipe",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Recipe deleted successfully"
}

delete_inventory_count_sheet

DELETE /api/inventory-count-sheets/delete-sheet/:id. Why: Remove a count-sheet template (soft delete: is_deleted = 1). Existing counts made from it are untouched (counts do not store the sheet id) and no stock changes. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_inventory_count_sheet"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Count sheet id (list_inventory_count_sheets → data[].id).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_inventory_count_sheet",
  "id": 11
}'

Example Response

{
  "status": true,
  "message": "Inventory count sheet deleted successfully"
}

delete_draft_purchase_order

DELETE /api/supplier-invoices/:uuid. Why: Permanently delete a DRAFT purchase order and its lines (a receive-shortfall draft or a draft generated from a stock order request). The API refuses non-drafts (403), so live POs cannot be deleted. Check the draft with get_purchase_order and confirm with the user first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_draft_purchase_order"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
uuid string required Draft uuid (list_supplier_invoices st=draft / create_draft_purchase_order → draft.uuid).

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_draft_purchase_order",
  "uuid": "example"
}'

Example Response

{
  "success": true,
  "message": "Draft deleted successfully"
}

delete_wastage_draft

DELETE /api/wastage/:id. Why: Permanently delete a DRAFT wastage batch and its lines (hard delete). The API does NOT check the status — it would also delete pending or approved batches without reversing their stock-out — so confirm approval_status is draft with get_wastage first. Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_wastage_draft"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Wastage batch id (list_wastage / get_wastage) whose approval_status is draft.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_wastage_draft",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Wastage batch deleted successfully",
  "data": {
    "success": true
  }
}

delete_work_order

DELETE /api/work-orders/:id. Why: Permanently delete a DRAFT work order with its recipe and material lines (no stock was moved by a draft). The server does not check the status — never use it on in_progress / completed / cancelled orders (the dashboard cancels those instead, which has no MCP tool). Full guide (every response field, rules, caveats, examples): call describe_tool {"name":"delete_work_order"} BEFORE calling this tool. Safety: destructive. customer_id is taken from the MCP session — do not send it.

Parameters

ParameterTypeDescription
id number required Work order id (list_work_orders); must be a draft — check get_work_order header.status first.

Example Request

curl -X POST https://pos-mcp.syspos.ae/api/call \
  -H "Authorization: Bearer syspos_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "delete_work_order",
  "id": 11
}'

Example Response

{
  "success": true,
  "message": "Work order deleted successfully",
  "data": {
    "success": true
  }
}
SysPOS MCP Server