API docs
Guide: inventory sync for a channel manager
This guide is for systems like Flxpoint that publish stock to sales channels (a BigCommerce store, Amazon, Etsy, eBay, Walmart) and route orders. It shows how to keep an accurate picture of a shop's on-hand inventory, with Black Beach POS as the source of truth for what's on the shelf.
Permissions to ask the owner for:
products: readinventory: readlocations: readinventory: write, only if you'll push adjustments backpurchase_orders: write, if you'll place and receive replenishment ordersorders: write, if you'll create orders (see Marketplace orders)
Ask for a webhook endpoint subscribed to inventory.changed, product.created, product.updated and product.deleted.
1. Map the locations
curl https://app.blackbeachpos.com/api/v1/locations -H "Authorization: Bearer $KEY"
Each location is one warehouse or source on your side. bigcommerce_location_id tells you which BigCommerce inventory location it feeds, if any.
2. Initial load
Page through products with their inventory, 200 at a time:
curl "https://app.blackbeachpos.com/api/v1/products?limit=200&include=inventory" -H "Authorization: Bearer $KEY"
# then repeat with &cursor=<meta.next_cursor> while meta.has_more is true
Match products on sku, upc, vendor_sku, or the bigcommerce.product_id and bigcommerce.variant_id pair. Publish qty_available per location.
- Lot-tracked products (
tracks_lots: true): the level is the sum of the product's lots at that location. If your channel needs lot or best-by data, readGET /v1/inventory?include=lotsorGET /v1/inventory/lots. Consider holding back lots withstatus: "expired";GET /v1/inventory/lots?status=expiredlists them. - Serialized products:
qty_on_handis the number of units in stock.GET /v1/inventory/serialized?product_id=…&status=in_stocklists the serial numbers.
Record the time before you started the load. That's your first updated_since.
3. Stay current with webhooks
Every stock change fires inventory.changed with the new level:
{ "type": "inventory.changed", "data": { "object": { "product_id": 617, "location_id": 18, "qty_on_hand": 20, "qty_available": 20, "qty_delta": -2, "reason": "sale", "sku": "CDL-SSD-08", "lot": null, "updated_at": "2026-09-25T01:48:03Z" } } }
Set your number to qty_available; don't add qty_delta to it. Deliveries can repeat or arrive out of order, and an absolute value is always safe to apply. Skip an event if its updated_at is older than what you already have. For lot-tracked products, lot is the lot that moved, with its remaining quantity.
4. Reconcile on a schedule
Webhooks can be missed, for example while your receiver is down. Every 15–60 minutes, pull what changed:
curl "https://app.blackbeachpos.com/api/v1/inventory?updated_since=2026-09-25T01:00:00Z&limit=200" -H "Authorization: Bearer $KEY"
curl "https://app.blackbeachpos.com/api/v1/products?updated_since=2026-09-25T01:00:00Z&limit=200" -H "Authorization: Bearer $KEY"
On Established, this takes a few requests an hour out of 10,000 a day.
5. Writing stock back (optional)
When stock arrives through your flow (a dropship return landing in the store, say), adjust by the difference and always send an Idempotency-Key:
curl -X POST https://app.blackbeachpos.com/api/v1/inventory/adjust \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: flx-receipt-55120-line-3" \
-d '{ "sku": "CDL-SSD-08", "location_id": 18, "qty_delta": 24, "reason": "receive", "note": "Flxpoint receipt 55120" }'
Or set an absolute count with POST /v1/inventory/set.
Limits:
- Lot-tracked products can't be adjusted or counted through the API (
409 LOT_TRACKED). Receive them against a purchase order with their lot number (next section), or leave counts to the store. - Serialized products can't be adjusted through the API (
409 SERIALIZED). The store receives them, entering each serial number. - Mind the rate limit. Throttle bulk writes to stay under 60 per minute on Established. Watch
X-RateLimit-Remainingand honorRetry-After.
6. Purchase orders to suppliers
When your platform places a replenishment order with a supplier for the store, create it as a purchase order so the store sees what's on the way. Put your own id in flxpoint_order_id (it's unique per shop, so a retry can't create a second one) and send an Idempotency-Key:
curl -X POST https://app.blackbeachpos.com/api/v1/purchase-orders \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: po-FXP-448120" \
-d '{ "vendor_id": 34, "location_id": 18, "flxpoint_order_id": "FXP-448120", "submit": true,
"lines": [ { "sku": "CDL-SSD-08", "qty": 24, "unit_cost_cents": 650 }, { "sku": "SOAP-LAV", "qty": 60 } ] }'
When the shipment is delivered, receive against it. Stock goes up and receiving.posted and inventory.changed fire. A lot-tracked line needs the lot number printed on the delivery:
curl -X POST https://app.blackbeachpos.com/api/v1/purchase-orders/77/receive \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: rcv-FXP-448120-1" \
-d '{ "reference": "UPS 1Z999AA10123456784",
"lines": [ { "purchase_order_line_id": 511, "qty": 24 },
{ "purchase_order_line_id": 512, "qty": 60, "lot_number": "LAV-2610-C", "expires_on": "2027-10-01" } ] }'
all_outstanding: true receives everything left in one call, except lot-tracked lines (they need a lot number) and serialized lines (the store enters serials); those come back in skipped. Receive through the purchase order rather than inventory/adjust, so the store's purchase orders stay in step with its stock. See Purchase orders for every rule.
7. Prices and product data
product.updated covers price, sale-price and detail changes. effective_price_cents is what the counter charges right now. map_price_cents is the manufacturer's advertised minimum, when there is one.
If your system is the pricing authority, send PATCH /v1/products/{id} with price_cents or a sale window (products: write).
