Developer API Overview
The Brother POS Developer API lets external systems read and write data from your store using simple REST endpoints. Use it to build custom dashboards, sync inventory with other platforms, automate loyalty workflows, or receive event notifications via webhooks.
What You Can Do
| Capability | Description |
|---|---|
| Read products | Fetch your catalog with prices, stock levels, categories, and images |
| Read customers | Access customer profiles, loyalty points, and visit counts |
| Read sales | Pull transactions with line items and the payment method |
| Read inventory | Check stock levels, and read back the full stock adjustment ledger |
| Read purchasing | Pull purchase orders and receiving — what was ordered, invoiced, and what actually arrived |
| Read cost | Landed cost, average cost and wholesale price, behind their own scope |
| Write customers | Create and update customer records from external systems |
| Write products | Create products, bulk upsert a catalogue by SKU, bulk-update prices and stock |
| Adjust inventory | Push stock corrections from warehouse or external systems |
| Receive webhooks | Get notified when sales change status, products or customers change, inventory moves, or a delivery is received |
How It Works
- Create an API key from Settings → Developer API (the button is shown to admins once the Developer API is enabled for your store).
- Send requests to
/api/v1/*endpoints with your key in theX-API-Keyheader. - Receive JSON responses wrapped in a standard envelope with pagination and metadata.
- Subscribe to webhooks to get push notifications instead of polling.
curl -H "X-API-Key: bpos_your_key_here" \
https://yourstore.brotherpos.ca/api/v1/products
Base URL
All API requests go to your store's subdomain:
https://{your-store}.brotherpos.ca/api/v1/
The store is determined by the API key, not the subdomain.
Response Format
Successful (2xx) JSON responses to API-key requests are wrapped in a standard envelope:
{
"data": [ ... ],
"pagination": {
"current_page": 1,
"per_page": 25,
"total_count": 142,
"total_pages": 6
},
"meta": {
"request_id": "a1b2c3d4-...",
"timestamp": "2026-03-22T12:00:00Z"
}
}
How data is built:
- If the endpoint's response has a single top-level key (for example
productsorcustomer),datais that key's value — an array for lists, an object for a single resource. - If the response has several top-level keys,
datais the whole object (for exampleGET /api/v1/webhook_subscriptions/:idreturnsdata.webhook_subscriptionanddata.recent_deliveries). paginationis present only on paginated endpoints.
Error responses (4xx/5xx) are not wrapped — see Error Handling.
Rate Limits
Each API key has a rate limit in requests per minute (default: 60), counted in fixed one-minute windows. The key's own limit takes effect once it has completed a request within the last five minutes; before that, the default of 60 applies.
When the limit is exceeded, the API returns 429 Too Many Requests with these headers:
| Header | Value |
|---|---|
X-RateLimit-Limit | Requests allowed per minute |
X-RateLimit-Remaining | Always 0 on a 429 |
X-RateLimit-Reset | When the current window ends, as a Unix timestamp in seconds (e.g. 1774180860) |
Retry-After | Seconds left until the current window ends |
Rate-limit headers are only sent on 429 responses.
Recommended Sync Architecture
Do not choose between webhooks and polling — run both. They fail in different ways, and together they cover each other.
1. Webhooks for latency
Subscribe to the events you care about so your system reacts soon after something happens in the store, instead of waiting for the next poll. See Webhooks.
Webhook delivery can repeat. A retried delivery carries the same id / X-BrotherPOS-Delivery value as the first attempt, so record the IDs you have processed and skip repeats. Upserting the record by resource_id keeps your handler safe even if a duplicate slips through.
2. A scheduled reconciliation pull for correctness
Webhooks can be missed. Several event types do not fire in every case you might expect (see Available Events), and after 5 failed deliveries (each one having used up its retries) the subscription is automatically disabled — at which point you stop receiving events entirely and, without a second channel, silently drift out of sync.
So run a scheduled pull as well — hourly or nightly is usually enough — using the incremental cursors:
# Store the timestamp you last completed a successful pull, then:
curl -H "X-API-Key: bpos_..." \
"https://yourstore.brotherpos.ca/api/v1/products?updated_since=2026-08-19T02:00:00Z&per_page=100"
Endpoints supporting an incremental cursor:
| Endpoint | Cursor parameter | Invalid value |
|---|---|---|
GET /api/v1/products | updated_since | Silently ignored (returns unfiltered results) |
GET /api/v1/customers | updated_since | Silently ignored (returns unfiltered results) |
GET /api/v1/purchase_orders | updated_since | 422 Unprocessable Entity |
GET /api/v1/stock_adjustments | since and until (on creation time) | 422 Unprocessable Entity |
Set your cursor slightly behind the last run — for example, last run minus five minutes — rather than exactly at it. Records written while the previous pull was in flight are otherwise easy to skip. Because your write path is already idempotent for webhooks, reprocessing the overlap costs nothing.
3. Reconcile, do not just re-import
The pull's job is to correct the webhook stream, so treat it as a comparison: for each changed record, upsert on your side and log where your stored copy disagreed with the API. Persistent disagreement is your signal that a subscription got disabled or a handler is dropping events.
What's Next?
- Authentication — Set up API keys and understand scopes
- Pagination & Filtering — Control result sets
- Webhooks — Receive event notifications
- Error Handling — Understand error responses
- Endpoints — Full endpoint reference