Skip to content

Catalog and availability ​

Summary: GET /catalog returns the products your account's default clinic is set up to order, with the price, the states each can ship to, and flags for any that are held or backordered right now. Read it instead of keeping your own list, subscribe to product.availability_changed to hear quickly when a product runs out or comes back, and still re-read the catalog regularly: it is the record, the webhook is a nudge. For a specific clinic, POST /orders/check gives the exact answer.

Reading the catalog ​

GET /api/inbound/{slug}/catalog returns { "ok": true, "catalog": [...], "retired": [...] }, sorted by SKU.

FieldMeaning
skuThe code to put in items[].sku.
aliasesOther codes that also resolve to this product for you.
name, strength, formFactorHow to show it. formFactor is free text such as Vial, Pen, Cartridge or Nasal Spray; do not rely on a fixed list or letter case.
imageUrlA generic product image you may show, or null.
clinicPriceCentsYour price per unit, in cents.
pricingSourceWhich price rule produced it. Informational.
availableStatesThe two-letter states an order for this SKU can ship to today.
backordered, expectedBackAtOut of stock, and when we expect it back if known.
unavailableUntilA future date before which it cannot be ordered, or null.

Only products that have a price for you are listed. The list and the prices are worked out for your account's default clinic, and the ship-to states at account level. Another clinic can have its own pricing, its own catalog and its own routing rules, so POST /orders/check with that clinic's clinic.external_id is the exact answer for it.

Ship-to states ​

A product can only be shipped to a state where a pharmacy that fills it is licensed, and coverage differs by product. availableStates applies the licensing and state rules order create applies, at account level, so it is the right list to offer. It does not include routing rules set for one particular clinic, which can narrow or widen what that clinic can order, so POST /orders/check is the final word for a given clinic and state (a refusal is 422 ship_state_not_licensed, product_unavailable_in_state or no_pharmacy_can_fill). Coverage changes as licensing changes, so read it rather than hard-coding it.

Availability changes ​

A product can stop being orderable for three independent reasons: it is switched off, it is held until a launch date, or it is out of stock. Subscribe to product.availability_changed to hear about these within minutes, and keep re-reading the catalog on a schedule: a delivery can be missed, and the catalog is the record.

json
{
  "event": "product.availability_changed",
  "partner": "acme-health",
  "environment": "production",
  "product": { "sku": "EXAMPLE-SKU-1", "revision": 7, "orderable": false, "reasons": ["backordered"], "unavailableUntil": null, "expectedBackAt": "2026-11-03T05:00:00.000Z" },
  "previous": { "orderable": true, "reasons": [], "unavailableUntil": null, "expectedBackAt": null },
  "observedAt": "2026-10-25T18:00:00.000Z"
}
  • product.orderable is the answer to act on. reasons can hold inactive, held and backordered at once, so a product coming back in stock is not necessarily orderable.
  • revision increases with each change for a SKU. Keep the highest you have seen and ignore a lower one that arrives late.
  • You only receive events for SKUs in your catalog.

An order for a product that is not orderable is refused: 422 product_backordered (with expectedBackAt), 422 product_not_yet_available (with availableAt), or, for a product that has been switched off, 422 unknown_product with reason: product_inactive.

Retired products ​

When we retire a product for good, it leaves catalog and appears in retired as { "sku": "...", "retiredAt": "..." } for 90 days, so you can remove it from your own lists. Nothing in retired can be ordered (422 product_retired), and no replacement is chosen for you.