Complete API documentation for bPOS integration
https://your-domain.com/api/v1
60 requests per minute per API key
All responses are in JSON format with the following structure:
{
"success": true,
"message": "Success message",
"data": { ... },
"meta": { ... } // For paginated responses
}
All API requests require authentication using an API key. Include your API key in the request header:
X-API-Key: your-api-key-here
Alternative format:
Authorization: Bearer your-api-key-here
Important: Keep your API key secure and never share it publicly.
/products
List all products with pagination and optional filters
| Parameter | Type | Required | Description |
|---|---|---|---|
| per_page | integer | No | Items per page (default: 20, max: 100) |
| page | integer | No | Page number (default: 1) |
| category | string/integer | No | Filter by category name or ID |
| brand | string/integer | No | Filter by brand name or ID |
| search | string | No | Search by product name or code |
| featured | boolean | No | Filter featured products (true/false) |
| include_stock | boolean | No | Include stock information (true/false) |
| branch_id | integer | No | Branch ID for stock (required if include_stock=true) |
GET /api/v1/products?per_page=10&include_stock=true&branch_id=1
{
"success": true,
"message": "Products retrieved successfully",
"data": [
{
"code": "PROD-001",
"parent_code": null,
"name": "iPhone 15 Pro",
"sinhala_name": null,
"description": "Latest iPhone model",
"category": {
"id": 1,
"name": "Mobile Phones"
},
"brand": {
"id": 5,
"name": "Apple"
},
"price": {
"amount": 125000.00,
"currency": "LKR",
"formatted": "125,000.00 LKR"
},
"marked_price": null,
"images": [
"https://your-domain.com/images/iphone15.jpg",
"https://your-domain.com/images/iphone15-2.jpg"
],
"custom_fields": null,
"is_featured": true,
"plu_number": "PLU-001",
"weight": 221,
"weight_unit": "g",
"stock_quantity": 15
}
],
"meta": {
"current_page": 1,
"per_page": 10,
"total": 150
}
}
/products/search
Search products (autocomplete)
| Parameter | Type | Required | Description |
|---|---|---|---|
| term | string | Yes | Search term |
| limit | integer | No | Max results (default: 10, max: 50) |
GET /api/v1/products/search?term=iphone&limit=10
{
"success": true,
"message": "Search results retrieved successfully",
"data": [
{
"code": "PROD-001",
"name": "iPhone 15 Pro",
"image": "https://your-domain.com/images/iphone15.jpg"
}
]
}
/products/{code}
Get single product details by product code
| Parameter | Type | Required | Description |
|---|---|---|---|
| include_stock | boolean | No | Include stock information (true/false) |
| branch_id | integer | No | Branch ID for stock (required if include_stock=true) |
GET /api/v1/products/PROD-001?include_stock=true&branch_id=1
{
"success": true,
"message": "Product retrieved successfully",
"data": {
"code": "PROD-001",
"parent_code": null,
"name": "iPhone 15 Pro",
"sinhala_name": null,
"description": "Latest iPhone model",
"category": {
"id": 1,
"name": "Mobile Phones"
},
"brand": {
"id": 5,
"name": "Apple"
},
"price": {
"amount": 125000.00,
"currency": "LKR",
"formatted": "125,000.00 LKR"
},
"marked_price": null,
"images": [
"https://your-domain.com/images/iphone15.jpg",
"https://your-domain.com/images/iphone15-2.jpg"
],
"custom_fields": null,
"is_featured": true,
"plu_number": "PLU-001",
"weight": 221,
"weight_unit": "g",
"stock_quantity": 15
}
}
/categories
List all categories
{
"success": true,
"message": "Categories retrieved successfully",
"data": [
{
"id": 1,
"name": "Electronics",
"other_name": null,
"slug": "electronics",
"parent_id": null,
"order": 1,
"is_service": false,
"main": 1,
"subcategories": [
{
"id": 10,
"name": "Mobile Phones",
"other_name": null,
"slug": "mobile-phones",
"parent_id": 1,
"order": null,
"subcategories": [],
"product_count": null
}
],
"product_count": null
}
]
}
/categories/{id}
Get a single category with its subcategories
{
"success": true,
"message": "Category retrieved successfully",
"data": {
"id": 1,
"name": "Electronics",
"other_name": null,
"slug": "electronics",
"parent_id": null,
"order": 1,
"is_service": false,
"main": 1,
"subcategories": [
{
"id": 10,
"name": "Mobile Phones",
"other_name": null,
"slug": "mobile-phones",
"parent_id": 1,
"order": null,
"subcategories": [],
"product_count": null
}
],
"product_count": null
}
}
/categories/{id}/subcategories
Get subcategories of a specific category
{
"success": true,
"message": "Subcategories retrieved successfully",
"data": [
{
"id": 10,
"name": "Mobile Phones",
"other_name": null,
"slug": "mobile-phones",
"parent_id": 1,
"order": 2,
"subcategories": [],
"product_count": null
},
{
"id": 11,
"name": "Laptops",
"other_name": null,
"slug": "laptops",
"parent_id": 1,
"order": 3,
"subcategories": [],
"product_count": null
}
]
}
/brands
List all brands
{
"success": true,
"message": "Brands retrieved successfully",
"data": [
{
"id": 1,
"name": "Apple",
"slug": "apple",
"logo": "https://your-domain.com/logos/apple.png"
}
]
}
/orders
List all orders with pagination and filters
| Parameter | Type | Required | Description |
|---|---|---|---|
| per_page | integer | No | Items per page (default: 20, max: 100) |
| page | integer | No | Page number (default: 1) |
| status | string | No | Filter by order status |
| customer_id | integer | No | Filter by customer ID |
| start_date | date (Y-m-d) | No | Filter orders from this date |
| end_date | date (Y-m-d) | No | Filter orders until this date |
GET /api/v1/orders?per_page=20&status=pending&start_date=2026-01-01&end_date=2026-01-31
{
"success": true,
"message": "Orders retrieved successfully",
"data": [
{
"id": 12345,
"date": "2026-01-22",
"status": "pending",
"customer": {
"id": 123,
"name": "John Doe"
},
"total": 5400.00,
"items_count": 3
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 150
}
}
/orders
Create a new order
| Parameter | Type | Required | Description |
|---|---|---|---|
| customer_id | integer | Yes | Customer ID |
| items | array | Yes | Array of order items (min: 1) |
| items.*.product_code | string | Yes | Product code |
| items.*.quantity | integer | Yes | Quantity (min: 1) |
| items.*.price | numeric | No | Custom price (optional) |
| delivery_address_id | integer | No | Delivery address ID |
| delivery_date | date | No | Delivery date |
| payment_type | integer | No | Payment type (1=COD, 2=Card, 3=Bank) |
| notes | string | No | Order notes |
| discount | numeric | No | Manual discount (min: 0) |
{
"customer_id": 123,
"items": [
{
"product_code": "PROD-001",
"quantity": 2,
"price": 2500.00
},
{
"product_code": "PROD-002",
"quantity": 1
}
],
"delivery_address_id": 45,
"delivery_date": "2026-01-25",
"payment_type": 1,
"notes": "Please deliver before 5 PM",
"discount": 100.00
}
{
"success": true,
"message": "Order created successfully",
"data": {
"id": 12345,
"date": "2026-01-22",
"status": "pending",
"customer": {
"id": 123,
"name": "John Doe"
},
"total": 5400.00,
"shipping": 400.00
}
}
/customers
List all customers with pagination and search
| Parameter | Type | Required | Description |
|---|---|---|---|
| per_page | integer | No | Items per page (default: 20, max: 100) |
| page | integer | No | Page number (default: 1) |
| search | string | No | Search by name, email, or phone |
| grade | integer | No | Filter by customer grade ID (see GET /customer-grades) |
GET /api/v1/customers?per_page=20&search=john&grade=1
{
"success": true,
"message": "Customers retrieved successfully",
"data": [
{
"id": 456,
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+94771234567",
"created_at": "2026-01-15T10:30:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total": 85
}
}
/customers
Create a new customer
| Parameter | Type | Required | Description |
|---|---|---|---|
| f_name | string | Yes | First name (max: 255) |
| l_name | string | No | Last name (max: 255) |
| Yes | Email address (must be unique) | ||
| phone | string | Yes | Phone number |
| address | string | No | Address |
| city_id | numeric | No | City ID |
| lat | numeric | No | Latitude |
| lng | numeric | No | Longitude |
| custom_fields | string | No | Custom fields data |
{
"f_name": "John",
"l_name": "Doe",
"email": "john.doe@example.com",
"phone": "+94771234567",
"address": "123 Main Street, Colombo",
"city_id": 1,
"lat": 6.9271,
"lng": 79.8612,
"custom_fields": "VIP Customer"
}
{
"success": true,
"message": "Customer created successfully",
"data": {
"id": 456,
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+94771234567",
"customer_id": 789,
"created_at": "2026-01-22T19:05:00Z"
}
}
/customer-grades
List all active customer grades
{
"success": true,
"message": "Customer grades retrieved successfully",
"data": [
{
"id": 1,
"name": "A"
}
]
}
/customers/{id}/addresses
Get all addresses for a specific customer
{
"success": true,
"data": [
{
"id": 1,
"address": "123 Main Street",
"address_line_1": "Apartment 4B",
"address_line_2": "Near Central Park",
"city": "Colombo",
"province": "Western",
"country": "Sri Lanka",
"postal_code": "00100",
"contact_person": "John Doe",
"company_name": "ABC Company",
"contact": {
"id": 5,
"number": "+94771234567"
},
"lat": 6.9271,
"lng": 79.8612
}
]
}
/customers/{id}/addresses
Create a new address for a customer
| Parameter | Type | Required | Description |
|---|---|---|---|
| address | string | Yes | Main address |
| contact | string | Yes | Contact phone number |
| address_line_1 | string | No | Address line 1 |
| address_line_2 | string | No | Address line 2 |
| city | string | No | City |
| province | string | No | Province/State |
| country | string | No | Country |
| postal_code | string | No | Postal/ZIP code (max: 10) |
| contact_person | string | No | Contact person name |
| company_name | string | No | Company name |
| lat | numeric | No | Latitude |
| lng | numeric | No | Longitude |
{
"address": "456 Park Avenue",
"address_line_1": "Floor 3",
"address_line_2": "Suite 301",
"contact": "+94771234567",
"contact_person": "Jane Smith",
"company_name": "XYZ Corp",
"city": "Kandy",
"province": "Central",
"country": "Sri Lanka",
"postal_code": "20000",
"lat": 7.2906,
"lng": 80.6337
}
{
"success": true,
"message": "Address created successfully",
"data": {
"id": 2,
"address": "456 Park Avenue",
"city": "Kandy",
"contact_person": "Jane Smith"
}
}
/customers/{customerId}/addresses/{addressId}
Update an existing customer address
Same parameters as POST /customers/{id}/addresses (all optional for PATCH)
{
"address": "456 Park Avenue Updated",
"postal_code": "20001",
"contact_person": "Jane Smith Updated"
}
{
"success": true,
"message": "Address updated successfully",
"data": {
"id": 2,
"address": "456 Park Avenue Updated",
"postal_code": "20001",
"contact_person": "Jane Smith Updated"
}
}
/reports/sales
Get sales report with date range and filters
| Parameter | Type | Required | Description |
|---|---|---|---|
| start_date | date (Y-m-d) | Yes | Report start date |
| end_date | date (Y-m-d) | Yes | Report end date (must be >= start_date) |
| branch_id | integer | No | Filter by branch |
| customer_id | integer | No | Filter by customer |
| product_code | string | No | Filter by product code |
| category | string | No | Filter by category |
| brand | string | No | Filter by brand |
| agent_id | integer | No | Filter by sales agent |
| page | integer | No | Page number (min: 1, default: 1) |
| per_page | integer | No | Items per page (min: 1, max: 100, default: 15) |
GET /api/v1/reports/sales?start_date=2026-01-01&end_date=2026-01-31&branch_id=1&page=1&per_page=20
{
"success": true,
"message": "Sales report retrieved successfully",
"data": [
{
"date": "2026-01-22",
"product_name": "iPhone 15 Pro",
"sales_quantity": 5,
"sales_total": 625000.00,
"profit": 125000.00
}
],
"meta": {
"summary": {
"total_sales": 625000.00,
"total_profit": 125000.00
}
}
}
/invoices
Get invoice summaries for a customer. Returns header-level invoice data without line items.
| Parameter | Type | Required | Description |
|---|---|---|---|
| customer_id | integer | Yes | Customer ID to filter invoices by |
| per_page | integer | Yes | Items per page (max: 200) |
| page | integer | Yes | Page number (min: 1) |
| date_from | date (Y-m-d) | No | Filter invoices from this date |
| date_to | date (Y-m-d) | No | Filter invoices up to this date (must be >= date_from) |
| sort | string | No | Sort order by date: ASC or DESC (default: DESC) |
GET /api/v1/invoices?customer_id=4&per_page=20&page=1&date_from=2026-01-01&date_to=2026-06-30&sort=DESC
| Field | Type | Description |
|---|---|---|
| id | integer | Invoice ID |
| date | date | Invoice date |
| due_date | date | Payment due date |
| addedOn | datetime | Record creation timestamp |
| customer_id | integer | Customer ID |
| customer_name | string | Customer full name |
| total | decimal | Invoice total amount |
| discount | decimal | Discount applied |
| delivery_cost | decimal | Delivery charge |
| vat | decimal | VAT amount |
| ret_total | decimal | Return total amount |
| ret_discount | decimal | Return discount |
| status | integer | Invoice status |
| job_status | integer | Job card status |
| paying_terms | string | Payment terms |
| tracking_id | string | Tracking reference |
| warehouse_id | integer | Branch/warehouse ID |
| is_return | boolean | Whether invoice has a return |
| is_exchanged | boolean | Whether invoice was exchanged |
| is_delivery_free | boolean | Free delivery flag |
| cashier | string | Cashier username |
| agent | string | Sales agent username |
{
"success": true,
"message": "Invoices retrieved successfully",
"data": [
{
"id": 1042,
"date": "2026-06-01",
"due_date": "2026-06-15",
"addedOn": "2026-06-01 09:32:10",
"customer_id": 4,
"customer_name": "John Doe",
"total": 15000.00,
"discount": 500.00,
"delivery_cost": 300.00,
"vat": 214.29,
"ret_total": 0.00,
"ret_discount": 0.00,
"status": 1,
"job_status": 0,
"paying_terms": "cash",
"tracking_id": null,
"warehouse_id": 1,
"is_return": 0,
"is_exchanged": 0,
"is_delivery_free": 0,
"cashier": "admin",
"agent": "agent01"
}
],
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"per_page": 20,
"to": 20,
"total": 58
}
}
Official WordPress plugin to sync your bPOS ERP with WooCommerce. Automatically syncs products, categories, brands, stock levels, customers, and orders.
bpos-v1.0.0.zip file| Setting | Description |
|---|---|
| API URL | Your bPOS API base URL (e.g., https://your-domain.com) |
| API Key | Your X-API-Key for authentication |
| Branch ID | Branch ID for stock sync (matches branch_id parameter in API) |
Every bPOS deployment ships a
Model Context Protocol (MCP) server at /mcp/.
It lets MCP clients such as Claude Code and Claude Desktop read your
products, customers, sales orders, job cards and BChat messages — and, with a write-enabled key,
create products and sales orders or send chat messages. It is a separate channel from the REST API
above, with its own keys.
https://your-domain.com/mcp/?key=your-mcp-key
Use the ?key= form. The server
also accepts an Authorization: Bearer header, but Apache does not forward that header to
PHP on a standard deployment, so the query-parameter form is the supported way to connect. The
transport is Streamable HTTP.
Run this once from your project folder:
claude mcp add --transport http brylix-erp \ "https://your-domain.com/mcp/?key=your-mcp-key" # check it connected claude mcp list
Add -s user
to make it available in every project instead of just the current one. Once connected, the tools appear as
brylix-erp tools and you simply ask in plain language —
“list the products in the Beverages category” or “show me today's open job
cards”. There is nothing else to configure.
Add the entry to your MCP config
(.mcp.json in the project, or the client's own settings file):
{
"mcpServers": {
"brylix-erp": {
"type": "http",
"url": "https://your-domain.com/mcp/?key=your-mcp-key"
}
}
}
curl -s -X POST "https://your-domain.com/mcp/?key=your-mcp-key" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
| Result | Meaning |
|---|---|
| 11 tools | Read-only key working |
| 16 tools | Write-enabled key working |
| HTTP 403 | Key rejected — wrong value, or the key was deactivated |
| HTTP 401 | No key reached the server — check the ?key= parameter is present |
A read-only key exposes 11 tools; a write-enabled key exposes 16. Write tools are hidden entirely from read-only keys.
| Tool | Purpose |
|---|---|
| list_jobs | Job cards with date, status, technician and section filters |
| get_job | One job card with custom fields, sections and notes |
| list_products | Products with category, brand, unit and current marked prices |
| list_categories | Product categories with parent hierarchy and product counts |
| list_brands | Product brands with product counts |
| list_units | Measurement units |
| list_customers | Customers with contact number, credit limit and price type |
| list_sales_orders | Sales orders with totals, due amounts and line items |
| list_chat_users | Active users that can be messaged, with any existing DM conversation id |
| list_chat_conversations | The AI user's conversations, with unread counts and last message |
| read_chat_messages | Messages in one conversation (does not mark them read) |
| Tool | Purpose |
|---|---|
| create_product | Create a product (does not set prices) |
| update_product | Partial update of a product (never touches prices, images or the code) |
| create_sales_order | Create an unpaid sales order with line items |
| send_chat_message | Send a text BChat message, opening the DM if needed |
| mark_chat_read | Clear a conversation's unread count for the AI user |
These limits are intentional. Anything financial stays in the ERP UI, where it is reviewed by a person.
MCP keys are separate from REST API keys and are generated on the server, from the ERP root, so they use that deployment's database connection.
# read-only key (recommended first) php mcp/generate_key.php "Claude Code - Production (read only)" # write-enabled key: name, user_id, company_id, branch_id, can_write php mcp/generate_key.php "Claude Code - Production" 1 1 4 1
The key is printed once — copy it straight into your client config. A write key requires a valid, active ERP user and a non-zero company id; the script refuses to create one otherwise. Bind it to a dedicated integration user rather than a real person's account: every MCP write is logged under that user, and the key can be disabled without disturbing anyone's login.
UPDATE mcp_api_keys SET is_active=0 WHERE id=?;
Important: the key sits in the connection URL, so treat that whole URL as a secret. Never paste it into a shared config, a screenshot or a ticket.