Authentication
The Developer API uses API keys for authentication. Each key belongs to a single store and has scopes that control which endpoints it can access.
Creating an API Key
Only users with the Admin role can manage API keys.
- Log in to the admin and open Settings.
- Click Developer API. (The button only appears once the Developer API is enabled for your store.)
- Click Create API Key.
- Fill in the form:
| Field | Description |
|---|---|
| Key Name | A descriptive label (e.g., "Website Sync", "BI Dashboard") |
| Permissions | The scopes the key should have (see below) |
| Rate Limit (requests/min) | Default: 60. The form caps it at 1,000. |
| Expires At (optional) | Leave empty for no expiration. An expired key is rejected with 401. |
| Test Mode | Makes the key read-only — see below |
- Click Create API Key.
- Copy the key immediately. It is displayed only once and cannot be retrieved later. Keys start with
bpos_.
The full API key is shown only once when you create it. If you lose it, you must revoke the key and create a new one. Store it securely — treat it like a password.
A Test Mode key can make GET and HEAD requests with its read scopes. Any other request is rejected with 403 Forbidden and the message This API key is in Test Mode, which is read-only. Use a key without Test Mode for write operations. — whatever write scopes the key holds.
There is no way to change a key's scopes, rate limit or expiry after creation. To change them, create a new key and revoke the old one.
Using Your API Key
Include the key in the X-API-Key header on every request:
curl -H "X-API-Key: bpos_abc123..." \
https://yourstore.brotherpos.ca/api/v1/products
The key is only read from the X-API-Key header. A request without that header is treated as a staff session request (JWT or browser session) and returns 401 Unauthorized if it has neither.
Changes made through an API key are recorded in the audit trail against the key and the admin who created it.
Scopes
Scopes control what your API key can access. Select only the scopes you need.
Read Scopes
| Scope | Grants Access To |
|---|---|
products:read | Products, categories, store settings |
customers:read | Customer profiles, loyalty info |
sales:read | Sales and their line items |
inventory:read | The stock adjustment ledger |
purchasing:read | Purchase orders, receiving, and variance |
costs:read | Product cost, average cost, and wholesale price fields |
loyalty:read | Loyalty tiers and rewards |
gift_cards:read | Gift card balances |
store_credits:read | Store credit balances |
storefront:read | Storefront config, schema, and change history |
Write Scopes
| Scope | Grants Access To |
|---|---|
products:write | Create products, bulk upsert, bulk-update prices and stock |
customers:write | Create and update customer records |
inventory:write | Create stock adjustments and inventory reconciliations |
webhooks:manage | List, create, read, update, delete and test webhook subscriptions (required for GET requests too) |
storefront:write | Update storefront sections, publish drafts, revert AI changes |
Scopes are checked per HTTP verb. GET/HEAD requests require the endpoint's read scope; every other verb requires its write scope. A key holding only customers:write can create and update customers but gets 403 Forbidden on GET /api/v1/customers. If your integration reads and writes the same resource, grant both scopes.
Some endpoints define a read scope only — sales, purchase orders, gift cards, store credits, loyalty, categories and store settings. Any non-GET request to those endpoints is rejected with 403 Forbidden and the message Write operations are not available for this endpoint via API key, regardless of which scopes the key holds.
Other 403 responses you may see:
| Message | Meaning |
|---|---|
This endpoint is not available via API key authentication | The endpoint is internal to the POS and cannot be called with a key at all |
Insufficient scope. Required: <scope> | The key is missing the named scope |
This API key is in Test Mode, which is read-only. Use a key without Test Mode for write operations. | A Test Mode key sent a request other than GET/HEAD |
Loyalty point manipulation is not available via API keys | Earning, redeeming or adjusting loyalty points is blocked for keys |
Why Cost Is a Separate Scope
costs:read is deliberately not bundled into products:read.
A catalogue read is a low-sensitivity operation — a menu board, a website, a price-comparison feed all need it. Landed cost, average cost and wholesale price are a different class of data: together with the price they publish your margin on every line you sell.
Separating them means you can hand a marketing agency or a reporting vendor a products:read key and share the catalogue without also handing over what you paid for it. Only integrations that genuinely need margin — a cost-of-goods report, an accounting sync — get costs:read.
Concretely, when a key does not hold costs:read, the cost, average_cost and wholesale_price fields are absent from product responses entirely — not present-and-null, so an absent field can never be misread as a cost of $0. See Products for the exact fields.
Cost visibility for a staff session (the POS and admin screens) follows the user's role permission for financial data, not this scope. costs:read applies to API keys.
Revoking a Key
If a key is compromised or no longer needed:
- Go to Settings → Developer API.
- Click on the key name.
- Click Revoke Key.
Revocation is immediate. All requests using that key receive 401 Unauthorized with Invalid or expired API key.
Webhook subscriptions created with a revoked or expired key stop receiving deliveries, including retries already scheduled. They still show as active, and they still count toward the store's limit of 25 subscriptions. Subscriptions can only be deleted through the API with the key that created them, so if you want them gone, call DELETE /api/v1/webhook_subscriptions/:id for each one before you revoke the key. See Webhooks.
Error Responses
| Status | Meaning |
|---|---|
401 Unauthorized | Invalid, expired, or revoked API key (Invalid or expired API key) |
402 Payment Required | The store has no active POS subscription or has not finished onboarding |
403 Forbidden | Valid key, but a missing scope, a read-only endpoint, an internal endpoint, or a write attempted with a Test Mode key |
429 Too Many Requests | Rate limit exceeded — see Rate Limits |
What's Next?
- Pagination & Filtering — Control result sets
- Webhooks — Subscribe to events
- Endpoints — Full endpoint reference