Backbone connector API

Read a shop's Backbone inventory and, only after the person confirms, change a stock count or receive a delivery.

Overview

Backbone is inventory software for independent shops. This API lets an AI assistant, such as Meta’s Muse, work with one business’s Backbone account, as the person who connected it and with that person’s Backbone permissions.

It can read stock levels, products, low-stock alerts, purchase orders, the last stock count and stock reports. It can make exactly two kinds of change: adjust a stock count and receive a delivery against a purchase order. Both are prepared first and applied only after the person confirms. A stock count change can be undone the same way. There is no shopping or checkout surface.

The same 12 tools are available three ways: over MCP (the primary transport), as tool calls by name, and as REST endpoints.

Connecting: OAuth 2.0 with PKCE

  1. Register your client with Dynamic Client Registration (RFC 7591): POST https://app.backbonesolutions.ai/api/connector/oauth/register. Clients are public (no client secret).
  2. Send the person to authorize: https://app.backbonesolutions.ai/api/connector/oauth/authorize with response_type=code, your client_id and redirect_uri, a state, the scope you need, and a PKCE code_challenge with code_challenge_method=S256 (required).
  3. The person signs in to Backbone, picks exactly one business, reads the permissions in plain English and approves (or denies).
  4. Exchange the code at https://app.backbonesolutions.ai/api/connector/oauth/token with grant_type=authorization_code and your code_verifier.
  5. Call the API with Authorization: Bearer <access_token>. Access tokens last one hour by default. Use grant_type=refresh_token to renew; refresh tokens rotate on every use and last 30 days by default. After a refresh, switch to the new access token: the earlier ones stop working 60 seconds later.

Server metadata (RFC 8414): https://app.backbonesolutions.ai/.well-known/oauth-authorization-server. Protected-resource metadata (RFC 9728): https://app.backbonesolutions.ai/.well-known/oauth-protected-resource. Revoke a token (RFC 7009): POST https://app.backbonesolutions.ai/api/connector/oauth/revoke.

A scope limits what the connection may do. It never widens what the person may do: every call runs as that person under their Backbone role and permissions. If they lose access to the business, the connection ends for good; if they are given access again, they connect again.

Scopes

ScopeWhat the person approvesTools
inventory:readSee your products, stock levels, stock counts and low-stock alertsquery_inventory, lookup_product, list_low_stock_alerts, last_stock_count
purchasing:readSee your purchase orders and what has been receivedlist_purchase_orders, get_purchase_order
reports:readRun your stock reports, such as what to reorder and what is not sellingrun_report
activity:readSee the stock changes made through Muse, and whether any were undonelist_recent_changes
inventory:writeChange stock counts, but only after you confirm each changeadjust_inventory_count, confirm_change, undo_change
purchasing:writeReceive deliveries against your purchase orders, but only after you confirm each onereceive_purchase_order, confirm_change

MCP endpoint

POST https://app.backbonesolutions.ai/api/connector/mcp speaks MCP over Streamable HTTP in stateless mode (protocol 2025-06-18). Send each JSON-RPC message as its own POST with Content-Type: application/json (any other content type is refused with 415) and Accept: application/json, text/event-stream; a batch (a JSON array of messages) is refused with 400. Answers normally come back as plain JSON; Backbone may instead answer a POST as a short event stream (Content-Type: text/event-stream, one message event carrying the same JSON-RPC answer, then the stream closes), as the MCP specification allows, so handle both. There is no session, so GET and DELETE answer 405, and the older HTTP+SSE transport (a GET event stream plus a separate message endpoint) is not offered.

  • Without a valid token the endpoint answers 401 with a WWW-Authenticate header whose resource_metadata points to https://app.backbonesolutions.ai/.well-known/oauth-protected-resource/api/connector/mcp. That is where an MCP client starts OAuth.
  • tools/list returns only the tools this connection’s scopes allow, no change tools while the business owner has turned changes through Muse off, and no change tool the person’s Backbone permissions could never complete (a view-only member sees only the read tools). Backbone still checks every change on its own side.
  • Read tools carry readOnlyHint: true. confirm_change is the one tool marked destructiveHint: true.
  • A failed tools/call comes back as a tool result with isError: true and the same plain-English message and next step as the REST error envelope.

REST and tool calls

Base URL: https://app.backbonesolutions.ai/api/connector/v1. Every request needs the bearer token. Responses are JSON and never cached (Cache-Control: no-store).

  • Tool call: POST /tools/{name} with the tool input as the JSON body, for any tool below.
  • REST: the same tools at the paths below. GET inputs go in the query string; POST inputs in the JSON body; path parts are named after the input field they fill.
MethodPathWhat it doesTool
GET/manifestThe tools this connection may use
GET/inventoryCheck stock levelsquery_inventory
GET/products/lookupFind a productlookup_product
GET/alertsSee low-stock alertslist_low_stock_alerts
GET/purchase-ordersList purchase orderslist_purchase_orders
GET/purchase-orders/{purchase_order_id}Open a purchase orderget_purchase_order
GET/stock-counts/latestSee the last stock countlast_stock_count
GET/reports/{report}Run a stock reportrun_report
GET/changesSee recent changeslist_recent_changes
POST/inventory/adjustments/previewPrepare a stock count changeadjust_inventory_count
POST/purchase-orders/{purchase_order_id}/receipts/previewPrepare receiving a deliveryreceive_purchase_order
POST/changes/confirmConfirm a prepared changeconfirm_change
POST/changes/{action_id}/undoPrepare undoing a stock changeundo_change

Changing stock: preview, then confirm

  1. Prepare. Call adjust_inventory_count, receive_purchase_order or undo_change. Nothing changes. You get a one-sentence summary and a preview_token.
  2. Ask. Show the person the summary.
  3. Confirm. Only if they clearly say yes, call confirm_change with the preview_token and an Idempotency-Key.
Backbone enforces this on its own side; it never relies on the assistant. A preview token is signed, tied to this connection, person and business, works once, and expires after 10 minutes (preview_expired). If the stock changed since the preview, confirm refuses with stale_preview and changes nothing. The person’s Backbone permission (“Edit inventory” or “Receive POs”) is checked again at confirm, as is the business owner’s switch for changes through Muse.

1. Prepare

POST /api/connector/v1/tools/adjust_inventory_count
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "sku": "CHR-2IN",
  "location_id": "5f1d2c3b-4a59-4e68-8d7c-6b5a49382716",
  "counted_qty": 9,
  "reason": "damaged"
}
{
  "preview_token": "bbp1.eyJ2IjoxLCJhIjoi…",
  "action_id": "9b2f6c1e-2d7a-4f0e-9a51-3c8d1e7f4a20",
  "summary": "Set 'Chrome Emblem 2in' at Front Rack from 12 to 9 (-3). Reason: damaged.",
  "before": {
    "item": "Chrome Emblem 2in",
    "sku": "CHR-2IN",
    "location": "Front Rack",
    "qty_available": 12
  },
  "after": {
    "qty_available": 9,
    "qty_delta": -3
  },
  "expires_at": "2026-09-27T18:10:00.000Z"
}

3. Confirm

POST /api/connector/v1/changes/confirm
Authorization: Bearer <access_token>
Idempotency-Key: 3c9e1f7a-adjust-chrome-2in
Content-Type: application/json

{
  "preview_token": "bbp1.eyJ2IjoxLCJhIjoi…"
}
{
  "action_id": "9b2f6c1e-2d7a-4f0e-9a51-3c8d1e7f4a20",
  "action": "adjust_inventory_count",
  "status": "applied",
  "summary": "Set 'Chrome Emblem 2in' at Front Rack from 12 to 9 (-3). Reason: damaged.",
  "after": {
    "qty_available": 9,
    "qty_delta": -3
  },
  "applied_at": "2026-09-27T18:01:12.000Z",
  "result": {
    "inventory_event_ids": [
      "5e0c…"
    ],
    "qty_delta": -3
  },
  "reversible": true,
  "request_id": "7f3c2a9e-1b4d-4c55-9e61-0a8b2d3c4e5f"
}

Undo: undo_change with the action_id of an applied stock count change prepares the reversing change; confirm_change applies it. Received deliveries cannot be undone through Muse (not_reversible); a manager records a correction in Backbone instead. A change can be undone once. list_recent_changes shows what was changed and what can still be undone.

Retries and Idempotency-Key

  • Send Idempotency-Key as a header on REST, or idempotency_key in the input (the only way on MCP and tool calls). If you send both, they must match.
  • Any value of 1 to 255 printable ASCII characters. Keys are scoped to the connection and kept at least 24 hours.
  • Same key, same preview: you get the first response again, byte for byte, including its request_id. Nothing is applied twice.
  • Same key, different preview: 409 idempotency_conflict, nothing applied.
  • A confirm that failed stores nothing under its key.

Rate limits

WhatLimitCounted per
All REST, tool-call and MCP requests120 per minuteconnection
Changes: preview, confirm, undo (in addition)20 per minuteconnection
OAuth sign-in and consent (authorize)60 per 15 minutesIP address
Client registration300 per 15 minutesIP address
Token and revoke calls600 per 15 minutesIP address
Token and revoke calls with the same refresh token, code or token10 per 15 minutescredential

Over a connector limit you get 429 rate_limited with retry_after (seconds) and a Retry-After header. Registration, token and revoke calls answer 429 in the OAuth error shape (temporarily_unavailable) with a Retry-After header: wait and retry; the refresh token is still good. List tools return at most 50 rows per call.

Errors

Every connector error has the same shape. The message is written for the shop owner and what_to_do is one next step; neither ever contains database, vendor or stack text.

{
  "error": "stale_preview",
  "message": "The numbers changed after this change was prepared, so Backbone did not apply it.",
  "what_to_do": "Ask Muse to prepare the change again with the latest numbers.",
  "request_id": "7f3c2a9e-1b4d-4c55-9e61-0a8b2d3c4e5f",
  "retryable": false
}
CodeHTTPRetry as-is?Default message
invalid_token401noBackbone did not recognise this connection.
token_expired401noYour Backbone connection in Muse has expired.
link_revoked401noThis Backbone connection was turned off, or you are no longer a member of this business.
insufficient_scope403noThis connection was not given permission to do that.
forbidden_capability403noYour Backbone role does not allow this.
writes_disabled_for_business403noChanges through Muse are turned off for this business.
feature_not_available403noThis business's Backbone plan does not include this.
not_found404noBackbone could not find that.
invalid_input400noSome of the details in this request are not valid.
invalid_preview400noThat confirmation does not match a change Backbone prepared.
preview_expired410noThat prepared change expired before it was confirmed, so nothing was changed.
stale_preview409noThe numbers changed after this change was prepared, so Backbone did not apply it.
already_applied409noThat change was already applied.
not_reversible409noThat change cannot be undone through Muse.
idempotency_conflict409noThis retry key was already used for a different change, so nothing was applied.
rate_limited429yesToo many requests reached Backbone in a short time.
upstream_unavailable503yesBackbone is having trouble right now. Nothing was changed.
internal_error500noSomething went wrong on Backbone's side.

Tools

Check stock levels query_inventory

Read-only

Read-only. This cannot change anything in Backbone. Shows how many of an item you have on hand, how many are held for orders, and how many are on the way, at each of your store locations. Use it for questions like "how many AA batteries do we have?" or "what is low at the Front Rack?". Search by part of the name, the exact SKU, or the exact barcode, and optionally one location or only items at or below their reorder point. Never shows cost or price.

Tool call
POST /api/connector/v1/tools/query_inventory or MCP tools/call with name: "query_inventory"
REST
GET /api/connector/v1/inventory
Scope
inventory:read
Backbone permission
Any member of the business, within what they can see in Backbone

Input

FieldTypeRequiredMeaning
variant_querystring, up to 120 charactersnoPart of the product name, for example "AA batteries" or "2in emblem".
skustring, up to 120 charactersnoThe exact SKU.
barcodestring, up to 120 charactersnoThe exact barcode (UPC or EAN) as scanned.
location_idBackbone id (UUID)noOnly this store location. Leave out for every location.
low_stock_onlybooleannoOnly items at or below their reorder point.
limitinteger, 1 to 50noHow many rows to return, 1 to 50. Default 20.

Returns

count (integer), items (list of objects), note (string)

Find a product lookup_product

Read-only

Read-only. This cannot change anything in Backbone. Finds items in your catalog by exact SKU, exact barcode, or part of the name, brand or code, and shows the item's name, codes, selling price, reorder point and case size. Unit cost appears only for people allowed to see financials. Age-restriction flags are shown for information only. For stock on hand, use query_inventory.

Tool call
POST /api/connector/v1/tools/lookup_product or MCP tools/call with name: "lookup_product"
REST
GET /api/connector/v1/products/lookup
Scope
inventory:read
Backbone permission
Any member of the business; cost and value figures only for people with “View financials”

Input

FieldTypeRequiredMeaning
skustring, up to 120 charactersnoThe exact SKU.
barcodestring, up to 120 charactersnoThe exact barcode (UPC or EAN) as scanned.
querystring, up to 120 charactersnoPart of the product name, brand or code. Used only when no SKU or barcode is given.
limitinteger, 1 to 50noHow many matches to return, 1 to 50. Default 10.

Returns

match (one of: sku, barcode, name), count (integer), items (list of objects), note (string)

See low-stock alerts list_low_stock_alerts

Read-only

Read-only. This cannot change anything in Backbone. Lists the low-stock and out-of-stock alerts Backbone has raised for your items, newest first, with the item, the location and the level that triggered each one. The list is as fresh as Backbone's last stock check; for a live count use query_inventory. Open alerts only unless you ask to include ones already marked as seen.

Tool call
POST /api/connector/v1/tools/list_low_stock_alerts or MCP tools/call with name: "list_low_stock_alerts"
REST
GET /api/connector/v1/alerts
Scope
inventory:read
Backbone permission
Any member of the business, within what they can see in Backbone

Input

FieldTypeRequiredMeaning
typeone of: low_stock, out_of_stocknoOnly "low_stock" or only "out_of_stock". Leave out for both.
location_idBackbone id (UUID)noOnly this store location. Leave out for every location.
include_acknowledgedbooleannoAlso include alerts someone has already marked as seen. Default false (open alerts only).
limitinteger, 1 to 50noHow many alerts to return, 1 to 50. Default 20.

Returns

count (integer), alerts (list of objects), note (string)

List purchase orders list_purchase_orders

Read-only

Read-only. This cannot change anything in Backbone. Lists your purchase orders, newest first, with the supplier, the status, and how many units have arrived and how many are still to come. Use status "open" for orders still waiting on a delivery. Order totals appear only for people allowed to see financials.

Tool call
POST /api/connector/v1/tools/list_purchase_orders or MCP tools/call with name: "list_purchase_orders"
REST
GET /api/connector/v1/purchase-orders
Scope
purchasing:read
Backbone permission
Any member of the business; cost and value figures only for people with “View financials”

Input

FieldTypeRequiredMeaning
statusone of: open, draft, pending_approval, sent, partially_received, received, cancelled, closedno"open" = ordered and still waiting for some or all of the delivery. Or one exact status. Leave out for every status.
vendorstring, up to 120 charactersnoPart of the supplier name.
location_idBackbone id (UUID)noOnly orders shipping to this store location.
limitinteger, 1 to 50noHow many orders to return, 1 to 50. Default 20.

Returns

count (integer), purchase_orders (list of objects), note (string)

Open a purchase order get_purchase_order

Read-only

Read-only. This cannot change anything in Backbone. Shows one purchase order, by id or PO number, line by line: what was ordered, what has arrived so far, and what is still to come. Use it before receiving a delivery. Costs appear only for people allowed to see financials.

Tool call
POST /api/connector/v1/tools/get_purchase_order or MCP tools/call with name: "get_purchase_order"
REST
GET /api/connector/v1/purchase-orders/{purchase_order_id}
Scope
purchasing:read
Backbone permission
Any member of the business; cost and value figures only for people with “View financials”

Input

FieldTypeRequiredMeaning
purchase_order_idBackbone id (UUID)noThe purchase order id (from list_purchase_orders).
po_numberstring, up to 40 charactersnoThe PO number as printed, for example "PO-00001234". Used when no id is given.
line_limitinteger, 1 to 50noHow many lines to return, 1 to 50. Default 50.
line_offsetinteger, at least 0noSkip this many lines first, to page through a long order. Default 0.

Returns

purchase_order (object), lines (list of objects), lines_returned (integer), line_offset (integer), more_lines (boolean)

See the last stock count last_stock_count

Read-only

Read-only. This cannot change anything in Backbone. Shows the most recent finished stock count at each of your locations: when it was done, how many items were counted, how many units were missing or extra, and the biggest differences. Use it for questions like "when did we last count the back room, and what was off?".

Tool call
POST /api/connector/v1/tools/last_stock_count or MCP tools/call with name: "last_stock_count"
REST
GET /api/connector/v1/stock-counts/latest
Scope
inventory:read
Backbone permission
Any member of the business, within what they can see in Backbone

Input

FieldTypeRequiredMeaning
location_idBackbone id (UUID)noOnly this store location. Leave out for the latest count at each location.
include_in_progressbooleannoAlso consider counts that are not finished yet. Default false (finished counts only).
limitinteger, 1 to 50noHow many locations to return, 1 to 50. Default 5.

Returns

count (integer), stock_counts (list of objects), note (string)

Run a stock report run_report

Read-only

Read-only. This cannot change anything in Backbone. Runs one of your stock reports: "reorder" (what is at or below its reorder point and needs ordering), "dead_stock" (what has stock but has not sold in 90 days or more) or "velocity" (your best sellers, by units sold per day). Quantities and dates for everyone; cost and value figures only for people allowed to see financials.

Tool call
POST /api/connector/v1/tools/run_report or MCP tools/call with name: "run_report"
REST
GET /api/connector/v1/reports/{report}
Scope
reports:read
Backbone permission
Any member of the business; cost and value figures only for people with “View financials”

Input

FieldTypeRequiredMeaning
reportone of: reorder, dead_stock, velocityyes"reorder" = what is at or below its reorder point and needs ordering; "dead_stock" = what has stock but has not sold in 90 days or more; "velocity" = the best sellers, by units sold per day.
location_idBackbone id (UUID)noOnly this store location. Leave out for every location.
daysinteger, at least 90nodead_stock only: at least this many days without a sale (90 or more).
limitinteger, 1 to 50noHow many rows to return, 1 to 50. Default 20.

Returns

report (one of: reorder, dead_stock, velocity), count (integer), items (list of objects), min_days_without_sale (integer), note (string), note_financials (string)

See recent changes list_recent_changes

Read-only

Read-only. This cannot change anything in Backbone. Lists the recent stock changes made through Muse for this business, newest first: what changed, whether you or someone else made it, and whether it was undone. Use it for questions like "what did you change today?". Each stock count change shows whether undo_change can still undo it.

Tool call
POST /api/connector/v1/tools/list_recent_changes or MCP tools/call with name: "list_recent_changes"
REST
GET /api/connector/v1/changes
Scope
activity:read
Backbone permission
Any member of the business, within what they can see in Backbone

Input

FieldTypeRequiredMeaning
limitinteger, 1 to 50noHow many changes to return, 1 to 50. Default 20.

Returns

count (integer), changes (list of objects)

Prepare a stock count change adjust_inventory_count

Prepares a change (preview)

Prepares a change to the stock count of one item at one location, for example after a recount or when items are damaged. This step changes NOTHING: it returns a plain summary such as "Set Chrome Emblem 2in at Front Rack from 12 to 9 (-3). Reason: damaged." and a preview_token. Show the summary to the person, and only if they say yes, call confirm_change with the preview_token. The preview expires after 10 minutes.

Tool call
POST /api/connector/v1/tools/adjust_inventory_count or MCP tools/call with name: "adjust_inventory_count"
REST
POST /api/connector/v1/inventory/adjustments/preview
Scope
inventory:write
Backbone permission
“Edit inventory”
Applied by
confirm_change, after the person says yes

Input

FieldTypeRequiredMeaning
variant_idBackbone id (UUID)noThe item to adjust. Or give its sku or barcode instead.
skustring, up to 120 charactersnoThe exact SKU of the item to adjust.
barcodestring, up to 120 charactersnoThe exact barcode of the item to adjust.
location_idBackbone id (UUID)yesThe store location whose count changes.
counted_qtyinteger, 0 to 1000000noThe new count, as counted on the shelf. Give this OR qty_delta.
qty_deltainteger, -1000000 to 1000000noUnits to add (positive) or remove (negative), never 0. Give this OR counted_qty.
reasonstring, up to 500 charactersyesWhy the count is changing, for example "damaged" or "recount".

Returns

preview_token (string), action_id (string), summary (string), before (object), after (object), expires_at (string)

Prepare receiving a delivery receive_purchase_order

Prepares a change (preview)

Prepares receiving a delivery against one of your purchase orders: everything still outstanding, or line by line. This step changes NOTHING: it returns a plain summary of what would be added to stock and a preview_token. Show the summary to the person, and only if they say yes, call confirm_change with the preview_token. The preview expires after 10 minutes.

Tool call
POST /api/connector/v1/tools/receive_purchase_order or MCP tools/call with name: "receive_purchase_order"
REST
POST /api/connector/v1/purchase-orders/{purchase_order_id}/receipts/preview
Scope
purchasing:write
Backbone permission
“Receive POs”
Applied by
confirm_change, after the person says yes

Input

FieldTypeRequiredMeaning
purchase_order_idBackbone id (UUID)noThe purchase order the delivery is for. Or give po_number.
po_numberstring, up to 40 charactersnoThe PO number as printed, for example "PO-00001234".
receive_allbooleannoReceive everything still outstanding on the order.
receiptslist of objects, up to 200 itemsnoWhat arrived, line by line. Leave out when receive_all is true.
receipts[].line_idBackbone id (UUID)noThe PO line (from get_purchase_order). Or give sku.
receipts[].skustring, up to 120 charactersno
receipts[].quantityinteger, 1 to 1000000yesSingle units that arrived.
receipts[].notesstring, up to 500 charactersno

Returns

preview_token (string), action_id (string), summary (string), before (object), after (object), expires_at (string)

Confirm a prepared change confirm_change

Applies a prepared change

Confirms and applies ONE change that was prepared by adjust_inventory_count, receive_purchase_order or undo_change, using its preview_token. Call this ONLY after showing the person the preview summary and hearing them say yes. It fails safely, changing nothing, if the preview expired, was already used, or the numbers changed since it was prepared. Send an idempotency_key so a retry can never apply the change twice.

Tool call
POST /api/connector/v1/tools/confirm_change or MCP tools/call with name: "confirm_change"
REST
POST /api/connector/v1/changes/confirm
Scope
inventory:write or purchasing:write
Backbone permission
“Edit inventory” or “Receive POs”

Input

FieldTypeRequiredMeaning
preview_tokenstring, up to 4096 charactersyesThe preview_token from the preview the person agreed to.
idempotency_keystring, up to 255 charactersnoAny retry key of your choice. Sending the same key again returns the first result and never applies the change twice.

Returns

action_id (string), action (one of: adjust_inventory_count, receive_purchase_order, reverse_inventory_adjustment), status (one of: applied), summary (string), before (object), after (object), applied_at (string), result (object), reversible (boolean), request_id (string)

Prepare undoing a stock change undo_change

Prepares an undo (preview)

Prepares undoing one stock count change that was made through Muse. This step changes NOTHING: it returns a summary of the reversing change and a preview_token. Show the summary to the person, and only if they say yes, call confirm_change with the preview_token. Received deliveries cannot be undone through Muse.

Tool call
POST /api/connector/v1/tools/undo_change or MCP tools/call with name: "undo_change"
REST
POST /api/connector/v1/changes/{action_id}/undo
Scope
inventory:write
Backbone permission
“Edit inventory”
Applied by
confirm_change, after the person says yes

Input

FieldTypeRequiredMeaning
action_idBackbone id (UUID)yesThe applied stock adjustment to undo (from confirm_change or list_recent_changes).

Returns

preview_token (string), action_id (string), summary (string), before (object), after (object), expires_at (string)