Developers
API and webhooks
Connect an ERP, a website or a warehouse system to an Okount shop. The API is built and opens soon; until then every call answers api_off.
Keys
The shop’s Admin makes a key in Okount: Settings, API and webhooks. A shop has at most 10 live keys. Each key can Read, Change, or both. The key starts okk_ and is shown once; a lost key is turned off and a new one made.
A key acts as the Admin who made it, so every rule the app keeps (stock checks, the activity log) holds for calls too, and the key stops working if that person is no longer the shop’s Admin. Send it on every call:
Authorization: Bearer okk_...Base address: https://okount-api.gouravofficial007.workers.dev/v1
Reading
| Call | Answers |
|---|---|
GET /v1/products[/<id>] | Products, newest first: id, name, code, barcode, unit, sell_price, mrp, category, brand, active, stock, created_at. |
GET /v1/customers[/<id>] | Customers that are not archived: id, name, phone, email, code, balance, loyalty_points, tags, created_at. |
GET /v1/orders[/<id>] | Bills with their lines: id, order_no, invoice_no, channel, status, payment_status, payment_mode, customer, subtotal, discount, tax, total, currency_code, placed_at, created_at, items. |
Lists take limit (1 to 200, default 50) and after (the next value from the page before). A page with no next is the last. Costs, margins and staff details are never part of an answer.
{ "data": [ ... ], "next": "2026-11-04T10:00:00.000000Z_<id>" }Writing
Keys that can Change may send up to 100 items per call, at most 256 KB, as {"items": [ ... ]}.
| Call | Each item |
|---|---|
POST /v1/products | Matched by id or code. A new one needs name and may set code; then any of name, sell_price, mrp, barcode, unit, category, brand, gst_rate, hsn_code, active. Only the fields sent change. |
POST /v1/stock | product_id or code, optional warehouse_id, and either on_hand (the new count) or change (the difference), plus an optional note. Stock never goes below zero. Logged as a recount by the key. |
POST /v1/customers | Matched by id, code or phone. A new one needs name; then any of name, phone, email. |
Each item succeeds or fails on its own:
{ "data": { "ok": 2, "failed": 1, "results": [
{ "index": 0, "status": "ok", "id": "...", "created": true },
{ "index": 1, "status": "ok", "product_id": "...", "was": 12, "now": 25 },
{ "index": 2, "status": "error", "error": "No product has that id or code" } ] } }Send an Idempotency-Key header (1 to 80 of A-Z a-z 0-9 . _ : -) to make a retry safe: the same key within a day returns the first answer with "replayed": true and changes nothing again.
Errors
| Status | error | Meaning |
|---|---|---|
| 400 | bad_json, bad_body, bad_idempotency_key, bad_cursor | The body or a parameter is not one the API takes. |
| 401 | bad_key | No key, a wrong key, or a turned-off one. |
| 403 | scope | The key may not read (GET) or change (POST). |
| 403 | key_maker_not_admin | The person who made the key is no longer the shop's Admin. |
| 404 | not_found, unknown_resource, unknown_version | The answer lists what there is. |
| 405 | method_not_allowed | Only GET and POST, and POST only to a list. |
| 409 | in_progress | The first call with this Idempotency-Key is still running; try again in a moment. |
| 413 | too_large | The body is over 256 KB. |
| 429 | rate_limit | Over 600 calls a minute (bursts to 1,200). Each written item counts as one call. |
| 503 | api_off, unavailable | The API is not open, or Okount is down. |
Webhooks
The Admin adds an https:// address (no port, no login in it; at most 10) and picks the events: order.created, order.updated (status or payment changed) and customer.created. Each is sent as a POST within about a minute:
Okount-Event: order.created
Okount-Delivery: 12345
Okount-Signature: t=1700000000,v1=<hex>
{ "event": "order.created", "shop_id": "...", "at": "...", "data": { ... } }To check it came from Okount, compute HMAC-SHA256 of "<t>.<raw body>" with the webhook’s signing secret (shown once, starts whs_) and compare with v1; refuse a t more than 5 minutes old.
Answer with any 2xx. Anything else, or no answer in 10 seconds, is tried again after 1, 4, 9, 16… minutes, up to 8 times. An address that has failed for 3 days is switched off and shown as failing in Okount.
Versions
A change that could break your code comes as /v2/...; /v1/ keeps answering as it does now. New fields may be added to a v1 answer, never taken away or renamed. Security questions go on the Trust center.