All guide topics

Guide Settings

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.

  1. Go to Settings → API Keys.
  2. 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.
  3. 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 by search (name or SKU), sku (exact match on the product's SKU or one of its variants' SKUs), category_id, and is_active.
  • Look up — GET /admin/products/{id} returns the product with its variants and images.
  • Every product includes a stock block: status (in_stock, out_of_stock, untracked, digital, or build_to_order), in_stock, tracked, and qty_available. qty_available is the count the storefront shows as "In Stock (N available)"; it is null for products that don't track inventory, digital products, and build-to-order products.
  • Every product also includes image_url (the main photo, absolute URL or null), and the single product includes images, each with a url.

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/products with name and price (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 shape GET returns.
  • Update — PUT /admin/products/{id} with any of the same fields. Fields you leave out are not changed.
  • weight (pounds) and length / width / height (inches) are the shipping size, the same units as the admin form. Each must be 0 or more; send null to clear one.
  • sku is the product's SKU (stored as the part number the admin shows).
  • category_id / category_ids put 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_id is its main category and is always included. Send only category_id to put the product in just that one category; send only category_ids and the first becomes the main one. category_id: null takes the product out of every category. Leave both out and the categories don't change.
  • product_type is physical, digital, build_to_order, or bundle. A new product without one is physical, as on the admin form. The older is_digital flag still works (true = digital, false = physical), but product_type wins when you send both.
  • track_inventory turns on the stock count. A physical product with track_inventory=true and a stock_quantity reads back as in_stock or out_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. PUT then leaves the stock as it is and ignores stock_quantity; the response says so in ignored.stock_quantity. POST still 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_active publishes the product; new products are active unless you send false. visibility is public (everyone) or private (only the customers and groups picked on the admin form).
  • Photos — POST /admin/products/{id}/images as multipart/form-data with image (one file) or images[] (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 send is_primary=true; the others go into the gallery. An optional alt_text is saved on gallery photos. The response is the product with its image_url and images.
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 by status, fulfillment_status, customer_email (the order's billing email), date_from, date_to) and GET /admin/orders/{id}. Each order's user is the buyer's id, name and email only. The single order includes its items, customer, shipment_boxes (label and per-box tracking), and a refund_gate block: refundable_amount, already_refunded, is_shipped, has_label, boxes, can_cancel, and can_refund (orders:read).
  • Status — PUT /admin/orders/{id}/status with status (pending, processing, shipped, delivered, cancelled) changes the order status only. Tracking sent with it is not saved: the reply lists those fields under ignored_fields with a warnings message. Use Shipments or Fulfillment below for tracking (orders:write).
  • Fulfillment / tracking — PUT /admin/orders/{id}/fulfillment with fulfillment_status (pending, processing, partially_shipped, labeled, shipped, delivered) and optional tracking_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. Marking shipped stamps shipped_at, advances build-to-order lines, and registers the number with Shippo and PayPal. The customer is emailed only when notify_customer=true (orders:manage).
  • Shipments from outside Kaniva — POST /admin/orders/{id}/shipments with carrier, tracking_number (one per call), optional tracking_url, optional items ([{"order_item_id": 12, "quantity": 1}]; leave it out to ship everything not yet shipped) and notify_customer (default false). 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 (200 instead of 201). Refused with 422 for 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. Omit amount for the full refundable balance or pass a partial amount; add a reason. Any paid order can be refunded (processing through shipped or completed, and a paid order cancelled without a refund), and never more than refund_gate.refundable_amount. The money is returned at Stripe or PayPal first; if that fails nothing is recorded and you get a 502 (pass force=true to record a refund you already issued in the provider dashboard). Cash orders are recorded with manual_provider_refund_required: true. The customer is emailed unless notify_customer=false. Pass restock_lines ({order item id: quantity}) to put items back in stock; the response's restocked says how many went back (orders:manage).
  • Cancel — POST /admin/orders/{id}/cancel with an optional reason. 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 (refunded and provider_refund_id in the response); pass refund=false to cancel and keep the money. If the provider turns the refund down, nothing is cancelled (422). Revokes downloads and emails the customer unless notify_customer=false. restock=true puts every item back in stock (or restock_lines for some), reported as restocked (orders:manage). PUT /admin/orders/{id}/status won't set cancelled on 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 (shippo or fallback) 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, a payments block (Stripe Connect, your own Stripe keys, PayPal keys, modes), a shipping block, per-integration configured flags (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 by status (draft, in_progress, completed, cancelled, or active), priority, overdue, work_center, assigned_to, due date, and search (manufacturing:read).
  • Look up — GET /admin/work-orders/{id}, or scan a number with GET /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 .../pause and .../resume track active labour time without changing the work order's status. Each work order reports is_running, is_paused, and active_minutes (worked time, excluding pauses) (manufacturing:write).
  • Operations — POST .../operations/{id}/start|complete|skip to drive routing steps in sequence (manufacturing:write).
  • Production logging — POST .../production records 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 count
  • order.updated: a paid order's status, payment status, shipping status or tracking number changed; changed lists which
  • order.fulfilled: the order shipped (sent once, not again on delivery)
  • order.refunded: money went back, with amount, total_refunded and is_full_refund
  • order.cancelled: the order was cancelled, with previous_status

Each carries id, order_number, status, payment_status and total.

  • Create a webhook through the API, with a key that has the webhooks:write scope. 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 need webhooks:write), so save it then. Reading a webhook returns secret_hint, its first 8 characters, never the secret itself
  • POST /admin/webhooks/{id}/test reports 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.

This is the same manual that ships inside Kaniva. Start a free trial and follow along in your own store.