One rulebook
The same rules as the scan station: on-hand never goes negative, orders draw only from released, unexpired, unreserved lots first to expire first, and every change is one permanent ledger entry.
Safe to retry
Every write carries an
Idempotency-Key. A retry posts once.Authentication
Create a key in the app under Inventory → Integrations. A key belongs to your pharmacy and carriesinventory:read and/or inventory:write. Send it as Authorization: Bearer vr_live_....
- Live keys can read and write.
- Test keys (
vr_test_...) can read but never change stock: there is no sandbox for inventory. - Keys can be revoked at any time and are rate limited per key;
429 rate_limitedincludes aRetry-Afterheader.
Posting a movement
type is receive, fulfill, dispense or return. A fulfill or dispense without a lot_number draws first-expiry-first-out and can return several transactions. Adjustments, waste, counts and transfers are done in the app by a person, because some need a witness.
Rule failures
Failures return{ "error": { "code", "message" } } with the reason in plain English.
Webhooks
Subscribe in the app or withPOST /inventory/webhooks. The URL must be public https. Each request is signed:
X-Webhook-Signature: sha256=<hex>: HMAC-SHA256 of the exact raw body, keyed with your endpoint secret (shown once)X-Webhook-Event,X-Webhook-Delivery(unique per event, use it to deduplicate) andX-Webhook-Timestamp
Storefronts should show availability from
stock.changed (available is on hand minus reserved, released and unexpired only) rather than polling.

