Developer API
Kaniva has a REST API so you can connect your store to other software — sync products, pull orders, or power an external calendar/booking widget.
Public documentation
Browse the full, always-current reference (no login needed) at https://kaniva.io/developers. The same reference is in your admin: API Documentation on Settings → API Keys. It lists every endpoint with a copy-paste curl example, plus authentication, scopes, rate limits, and error codes.
Creating an API key
Only the store owner and admins can create, change or revoke API keys.
- Go to Settings → API Keys.
- Click Create API Key, give it a Key Name, and choose its Permissions (Scopes): only what the integration needs, for example the Calendar group for calendar and booking access. You can also set a rate limit and an expiration.
- Click Create, then copy the key (
kn_live_…) right away. It's shown only once.
Editing a key
Open a key from Settings → API Keys and click Edit to change its name, scopes, rate limit or expiration. The token stays the same, so integrations using it keep working, and the new permissions apply immediately. Use this to add a scope (for example Manufacturing) to an existing key.
On the key's page you can also:
- Regenerate Token: issue a new token when the old one leaked. The old token stops working immediately
- Revoke Key (and Reactivate): switch the key off without deleting it
- Delete the key
Using a key
Send it as a Bearer token on every request. The store is identified by the key, so you don't put your store name in the URL. The base URL is shown on the API Documentation page:
curl https://kaniva.io/api/v1/admin/calendar/events \
-H "Authorization: Bearer kn_live_…" \
-H "Accept: application/json"
Product stock endpoints
Requires the products:read scope. Use these to pull inventory into another system instead of reading the storefront page:
- List —
GET /admin/products, filterable bysearch(name or SKU),sku(exact match on the product's SKU or one of its variants' SKUs),category_id, andis_active. - Look up —
GET /admin/products/{id}returns the product with its variants and images. - Every product includes a
stockblock:status(in_stock,out_of_stock,untracked,digital, orbuild_to_order),in_stock,tracked, andqty_available.qty_availableis the count the storefront shows as "In Stock (N available)"; it isnullfor products that don't track inventory, digital products, and build-to-order products. - Every product also includes
image_url(the main photo, absolute URL ornull), and the single product includesimages, each with aurl.
Creating and updating products
Requires the products:write scope. A key without it gets 403 with required_scopes. These calls set everything the admin product form needs to make a product sellable, so an ops tool can list a product without opening the admin:
- Create —
POST /admin/productswithnameandprice(required), plus any of:sku,product_type,track_inventory,stock_quantity,is_active,visibility,category_id,description,short_description,compare_at_price,weight,length,width,height, the SEO fields, and the identifiers (gtin,brand,mpn,asin,condition). The response is the product in the same shapeGETreturns. - Update —
PUT /admin/products/{id}with any of the same fields. Fields you leave out are not changed. weight(pounds) andlength/width/height(inches) are the shipping size, the same units as the admin form. Each must be 0 or more; sendnullto clear one.skuis the product's SKU (stored as the part number the admin shows).category_id/category_idsput the product in categories, the same way the admin form does, so it shows on the storefront's category pages and menu counts.category_ids(a list) replaces the product's categories;category_idis its main category and is always included. Send onlycategory_idto put the product in just that one category; send onlycategory_idsand the first becomes the main one.category_id: nulltakes the product out of every category. Leave both out and the categories don't change.product_typeisphysical,digital,build_to_order, orbundle. A new product without one is physical, as on the admin form. The olderis_digitalflag still works (true= digital,false= physical), butproduct_typewins when you send both.track_inventoryturns on the stock count. A physical product withtrack_inventory=trueand astock_quantityreads back asin_stockorout_of_stock.- Stock in bins. When the store uses Advanced Inventory, a product's stock lives in its inventory item's bins and changes by scanning or with an adjustment.
PUTthen leaves the stock as it is and ignoresstock_quantity; the response says so inignored.stock_quantity.POSTstill sets the starting stock of a new product Every stock number sent on an update, used or ignored, is logged with the API key's name. is_activepublishes the product; new products are active unless you sendfalse.visibilityispublic(everyone) orprivate(only the customers and groups picked on the admin form).- Photos —
POST /admin/products/{id}/imagesasmultipart/form-datawithimage(one file) orimages[](several), up to 10 MB each. Photos are resized and stored exactly as the admin form stores them. The first file becomes the main photo when the product has none yet, or when you sendis_primary=true; the others go into the gallery. An optionalalt_textis saved on gallery photos. The response is the product with itsimage_urlandimages.
curl https://kaniva.io/api/v1/admin/products/194/images \
-H "Authorization: Bearer kn_live_…" \
-H "Accept: application/json" \
-F "image=@front.jpg" -F "is_primary=true"
Order endpoints
Requires the Orders scope group. orders:read reads, orders:write changes the order status, and orders:manage runs the actions below — the same actions, with the same rules, as the admin order page.
- List / look up —
GET /admin/orders(filter bystatus,fulfillment_status,customer_email(the order's billing email),date_from,date_to) andGET /admin/orders/{id}. Each order'suseris the buyer'sid,nameandemailonly. The single order includes its items, customer,shipment_boxes(label and per-box tracking), and arefund_gateblock:refundable_amount,already_refunded,is_shipped,has_label,boxes,can_cancel, andcan_refund(orders:read). - Status —
PUT /admin/orders/{id}/statuswithstatus(pending,processing,shipped,delivered,cancelled) changes the order status only. Tracking sent with it is not saved: the reply lists those fields underignored_fieldswith awarningsmessage. Use Shipments or Fulfillment below for tracking (orders:write). - Fulfillment / tracking —
PUT /admin/orders/{id}/fulfillmentwithfulfillment_status(pending,processing,partially_shipped,labeled,shipped,delivered) and optionaltracking_carrier,tracking_number,tracking_url. A new tracking number is appended to any existing ones; tracking already on the order is never replaced, and its carrier and link are only filled in when empty. Markingshippedstampsshipped_at, advances build-to-order lines, and registers the number with Shippo and PayPal. The customer is emailed only whennotify_customer=true(orders:manage). - Shipments from outside Kaniva —
POST /admin/orders/{id}/shipmentswithcarrier,tracking_number(one per call), optionaltracking_url, optionalitems([{"order_item_id": 12, "quantity": 1}]; leave it out to ship everything not yet shipped) andnotify_customer(defaultfalse). Each call adds a shipment to the order, the same as a box shipped from Shipping Station, with its own carrier and link; nothing already on the order changes. The order is partially shipped until every item is in a shipment, then shipped. Items in a shipment come off the shelf, and on an eBay order only those lines are marked shipped on eBay. Sending the same tracking number again returns the shipment already recorded (200instead of201). Refused with422for cancelled, refunded or local-pickup orders, drop-ship lines and more than is left to ship. eBay buyers are never emailed (orders:manage). - Refund —
POST /admin/orders/{id}/refund. Omitamountfor the full refundable balance or pass a partial amount; add areason. Any paid order can be refunded (processing through shipped or completed, and a paid order cancelled without a refund), and never more thanrefund_gate.refundable_amount. The money is returned at Stripe or PayPal first; if that fails nothing is recorded and you get a 502 (passforce=trueto record a refund you already issued in the provider dashboard). Cash orders are recorded withmanual_provider_refund_required: true. The customer is emailed unlessnotify_customer=false. Passrestock_lines({order item id: quantity}) to put items back in stock; the response'srestockedsays how many went back (orders:manage). - Cancel —
POST /admin/orders/{id}/cancelwith an optionalreason. Pending or processing orders only, and not while a dropship supplier has an item (refund instead). A paid order is refunded in full at Stripe or PayPal as it's cancelled (refundedandprovider_refund_idin the response); passrefund=falseto cancel and keep the money. If the provider turns the refund down, nothing is cancelled (422). Revokes downloads and emails the customer unlessnotify_customer=false.restock=trueputs every item back in stock (orrestock_linesfor some), reported asrestocked(orders:manage).PUT /admin/orders/{id}/statuswon't setcancelledon a paid order.
Shipping settings & integration health
Requires the settings:read scope (group Settings). Both endpoints report whether a key is set — never the key itself.
GET /admin/settings/shipping— ship-from address and whether it is complete, rate source (shippoorfallback) with the fallback formula, allowed countries, local pickup and delivery radii, PrintNode and label format, and whether Google Maps address validation is configured.GET /admin/integrations/health—checkout_ready, apaymentsblock (Stripe Connect, your own Stripe keys, PayPal keys, modes), ashippingblock, per-integrationconfiguredflags (Shippo, PrintNode, Google Maps, Stripe webhook secret), and the go-live checklist progress.
Calendar & booking endpoints
- Events — list/create/update/delete calendar events (
calendar:read/calendar:write). - Availability — read or set your weekly hours and blocked dates (
calendar:read/calendar:write). - Bookings — list bookings and confirm/cancel them (
bookings:read/bookings:write). - Storefront booking — fetch open time slots for a date and create a booking from an external site (
bookings:read/bookings:write).
The storefront cart and checkout endpoints aren't available yet: they answer 501 Not Implemented. Send shoppers to your store's own checkout. The storefront product and category endpoints list only public products, and leave out password-protected categories and products that are only in them.
Work order (manufacturing) endpoints
Requires the Manufacturing scope group (manufacturing:read / manufacturing:write) and the manufacturing module enabled for your store. Lets a shop-floor kiosk or external system run production against Kaniva:
- List / pick-list —
GET /admin/work-orders, filterable bystatus(draft,in_progress,completed,cancelled, oractive),priority,overdue,work_center,assigned_to, due date, andsearch(manufacturing:read). - Look up —
GET /admin/work-orders/{id}, or scan a number withGET /admin/work-orders/{WO-number}?by=number. Returns operations, components, jobs, and a production reconciliation block (manufacturing:read). - Lifecycle —
POST .../start,.../complete,.../cancel(manufacturing:write). Start checks component stock; complete consumes components, produces finished goods, and advances any linked order's build stage. - Pause / resume —
POST .../pauseand.../resumetrack active labour time without changing the work order's status. Each work order reportsis_running,is_paused, andactive_minutes(worked time, excluding pauses) (manufacturing:write). - Operations —
POST .../operations/{id}/start|complete|skipto drive routing steps in sequence (manufacturing:write). - Production logging —
POST .../productionrecords produced good and scrap quantities (with an optional reason and operator name) (manufacturing:write).
Set a Work Center on a work order (or per step on the BOM operation editor) so operators can pull only the jobs for their machine.
Customer endpoints
GET /admin/customers and GET /admin/customers/{id} list and read your store's customers (customers:read). POST /admin/customers adds one and PUT /admin/customers/{id} changes a customer's name (customers:write). A customer's email is their login on every Kaniva store they use, so it can't be changed through the API: the customer changes it from their account. Owners, admins and staff aren't customers and can't be changed here.
Rate limits
Each key has a per-minute request limit (default 60, adjustable when you create the key). Responses include X-RateLimit-Limit and X-RateLimit-Remaining; exceeding the limit returns 429 Too Many Requests with a Retry-After header.
Product add-ons: GET /storefront/products/{slug} includes addon_groups (each with id, name, one for pick-one, and addons with id, name, kind (product or label), price or percent, once, in_stock). Order lines from the admin API include parent_order_item_id, addon_id and addon_kind for add-ons; an add-on's line follows its item's. Products marked Sold only as an add-on are left out of the storefront endpoints.
Webhooks
Webhooks notify your own systems when something happens in your store (for example order.created). You don't need them to run the store.
Order events:
order.created: the order is paid, or its deposit is. Kaniva makes the order a moment earlier, while the customer is still paying; that doesn't countorder.updated: a paid order's status, payment status, shipping status or tracking number changed;changedlists whichorder.fulfilled: the order shipped (sent once, not again on delivery)order.refunded: money went back, withamount,total_refundedandis_full_refundorder.cancelled: the order was cancelled, withprevious_status
Each carries id, order_number, status, payment_status and total.
- Create a webhook through the API, with a key that has the
webhooks:writescope. The request shape and event list are in the API documentation - The webhook URL must be a public internet address. Addresses on a private network, this server, or a cloud metadata address are refused when you save the webhook and again before each delivery, and redirects aren't followed
- Each delivery is signed with the webhook's secret. The secret is shown only when you create the webhook and when you call
POST /admin/webhooks/{id}/regenerate-secret(both needwebhooks:write), so save it then. Reading a webhook returnssecret_hint, its first 8 characters, never the secret itself POST /admin/webhooks/{id}/testreports the HTTP status your endpoint answered with, not the body of its reply- Settings → Webhooks lists your webhooks with their URL and status
- Payloads are signed with HMAC-SHA256 so you can check they came from Kaniva
- A failed delivery is retried up to 3 times. After 10 failures in a row the webhook is switched off; you can turn it back on
Payment webhooks (Stripe, PayPal) are not here: see Payment Settings.