Developers
Connect your website or app to your stock.
A JSON REST API and signed webhooks, on the Pro plan. Read products and live stock, create customers and orders, and hear about new orders, deliveries, low stock, receipts and payments as they happen.
1. Get a token
In the app, open Setup, API tokens, and create a token. Choose read, or read and write to create orders and customers. The token is shown once; we keep only a fingerprint of it. Revoke it there at any time.
curl https://inventory.ovelithub.com/api/v1/products?per_page=50 \ -H "Authorization: Bearer ost_your_token"
2. Endpoints
Base address https://inventory.ovelithub.com/api/v1. Every request needs the token and only ever sees your own business.
| Method | Path | Token | What it does |
|---|---|---|---|
| GET | /products | read | Products, 25 a page (per_page up to 100). Filters: updated_since, sku, barcode, status. |
| GET | /products/{sku} | read | One product with its stock at every location. |
| GET | /stock | read | Stock per product and location: on hand, reserved, available, incoming. Filters: location_id, sku, updated_since. |
| GET | /customers | read | Customers with their baki (balance). Filters: phone, email, updated_since. |
| POST | /customers | write | Create a customer: name (required), phone, email, address, area, type. A phone already on file returns 409. |
| GET | /orders | read | Sales orders with lines. Filters: status, channel, customer_id, updated_since. |
| GET | /orders/{id} | read | One sales order. |
| POST | /orders | write | Create a sales order from SKUs. Status pending (default, waits for you to confirm) or confirmed (reserves stock, checks the credit limit). |
| GET | /purchase-orders | read | Purchase orders with lines and what has arrived. Filters: status, updated_since. |
Lists come back as data, meta (page, per_page, total, last_page) and links (next, prev). Dates are ISO 8601; pass updated_since=2026-10-20T08:00:00+06:00 to fetch only what changed.
3. Create an order
Send an Idempotency-Key header with a value unique to the order. If the request is sent again with the same key, you get the first order back (header Idempotent-Replayed: true) and nothing is created twice.
curl -X POST https://inventory.ovelithub.com/api/v1/orders \
-H "Authorization: Bearer ost_your_token" \
-H "Idempotency-Key: web-10045" \
-H "Content-Type: application/json" \
-d '{
"status": "pending",
"customer": {"name": "Nusrat Jahan", "phone": "01712345678", "address": "House 12, Road 4, Dhanmondi"},
"lines": [{"sku": "P1001", "qty": 2}, {"sku": "P1002", "qty": 1, "unit_price": 450}],
"shipping_fee": 60,
"notes": "Website order 10045"
}'
The customer is matched by phone, then email, or created. Leave out unit_price to use your price list. Orders arrive on the website channel unless you send another channel.
4. Errors and limits
Errors always look the same, with an HTTP status to match:
{"error": {"code": "unknown_sku", "message": "Some SKUs are not in the catalog or are archived.", "details": {"skus": ["P9999"]}}}
- 401 unauthenticated: no token, a wrong token or a revoked one.
- 403 plan_required or forbidden: the plan does not include the API, or the token cannot write.
- 404 not_found. 409 duplicate. 422 validation_failed, unknown_sku, not_confirmed (for example over the credit limit).
- 429 rate_limited: 60 requests a minute per token. Wait for the seconds in
Retry-After.
5. Webhooks
In Setup, Webhooks, add the address that should hear about events and pick them:
order.created: New orderorder.delivered: Order deliveredstock.low: Stock low or outpo.received: Purchase receivedpayment.received: Payment received
Each event is a POST with a JSON body: id, event, created_at, account and data. Answer with any 2xx status within 10 seconds. Anything else is tried again after 1, 5, 30 and 120 minutes, 5 attempts in all. Every send is in the delivery log, and "Send test" posts a ping event.
Check the X-Ovelit-Signature header before trusting a request. It reads t=1761200000,v1=<hex>, where the hex is the HMAC-SHA256 of the timestamp, a dot and the raw body, keyed with the endpoint's signing secret.
// PHP
[$t, $sig] = sscanf($_SERVER['HTTP_X_OVELIT_SIGNATURE'], 't=%d,v1=%s');
$ok = hash_equals(hash_hmac('sha256', $t.'.'.file_get_contents('php://input'), $secret), $sig)
&& abs(time() - $t) < 300;
6. Online stores
On the Growth plan and up, an online store connected under Setup, Integrations is synced every 10 minutes: new store orders come in as website orders to confirm, matched to your products by SKU, and available stock at the location you choose goes back to the store. Items that do not match a SKU are listed in your inbox.