API Documentation
Public API · version 1
Overview
The public API connects your own website, online store, ERP, warehouse app or point of sale to InventoryLink, both ways:
- Your system reports its sales (or any stock change) to the API, and InventoryLink applies the change to your other channels, like Shopify or WooCommerce.
- InventoryLink tells your system when stock changes on another channel, with a signed webhook to your URL. Or your system reads the counts from the API.
Base URL: https://inventorylink.app/api/v1
Before you start
- The API is included in every plan, and in the free trial.
- In Connections, add your own channel: your system, as a channel next to Shopify or WooCommerce. Give it a webhook URL to receive stock changes, or leave it empty and read them from the API. Changes your system reports are then never sent back to it.
- Create a key under Developer API. The full key is shown only once, so store it somewhere safe. You can revoke it there at any time.
- On SKU Mappings, import your products from a CSV with a
skucolumn (and optionallyquantityandname), then run Auto-Match to link them with the same SKUs on your other channels.
Authentication
Send your key in the Authorization header of every request:
Authorization: Bearer il_live_your_keyKeep keys on your server. Never put them in a website's front end, a mobile app or anything else a customer can download.
Endpoints
GET /api/v1/ping
Checks that a key works. It doesn't change anything.
curl https://inventorylink.app/api/v1/ping \
-H "Authorization: Bearer il_live_your_key"GET /api/v1/inventory
Every linked SKU with its stock on each channel, in SKU order. quantity is the count for your own channel when you have one, otherwise the latest count InventoryLink has. Use limit (1–500, default 100) and pass next_cursor as after to get the next page.
curl "https://inventorylink.app/api/v1/inventory?limit=100" \
-H "Authorization: Bearer il_live_your_key"{
"data": [
{
"sku": "MUG-12OZ-BLK",
"quantity": 41,
"channels": [
{ "type": "shopify", "name": "Harbor Coffee Co.", "quantity": 41 },
{ "type": "custom", "name": "My website", "quantity": 41 }
]
}
],
"has_more": false,
"next_cursor": null
}GET /api/v1/inventory/{sku}
The same for one SKU. Answers 404 if the SKU isn't linked on any channel.
PUT /api/v1/inventory/{sku}
Reports the stock your system has now for one SKU. InventoryLink compares it with the last count your system had and applies the difference to your other channels, on top of what each has, so a sale made there in the meantime still counts. The first count for a SKU, and any count when you have no own channel, is copied to the other channels as is. To make every channel match yours, use Line up stock in Diagnostics.
| Field | Type | Description |
|---|---|---|
| quantity | integer, required | The new count, from 0 to 1,000,000. |
| source | string, optional | A label saved with the change, such as "website". |
curl -X PUT https://inventorylink.app/api/v1/inventory/MUG-12OZ-BLK \
-H "Authorization: Bearer il_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "quantity": 38, "source": "website" }'HTTP/1.1 202 Accepted
{
"ok": true,
"syncJobId": "8f2c…",
"sku": "MUG-12OZ-BLK",
"newQuantity": 38,
"propagatedTo": 1,
"result": { "successCount": 1, "skippedCount": 0, "failureCount": 0 }
}POST /api/v1/adjustments
Reports a change in stock instead of a count. InventoryLink applies delta to your other channels, on top of what each has, and to your own channel's count (newQuantity in the response, never below 0).
| Field | Type | Description |
|---|---|---|
| sku | string, required | The SKU as linked in InventoryLink. Not case-sensitive. |
| delta | number, required | How much the stock changed: -3 for a sale of three units, 10 for a delivery. |
| source | string, optional | A label saved with the change, such as "warehouse-erp". |
curl -X POST https://inventorylink.app/api/v1/adjustments \
-H "Authorization: Bearer il_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "sku": "MUG-12OZ-BLK", "delta": -3, "source": "warehouse-erp" }'For both PUT and POST: if the SKU isn't linked on any channel, nothing changes and the response is 200 with "untracked": true. A SKU linked elsewhere but new to your own channel is added to it automatically.
Webhooks to your own channel
When stock changes on another channel, InventoryLink sends a POST with the new count to your own channel's webhook URL. It must be a public https:// address.
POST /your-webhook-path HTTP/1.1
Content-Type: application/json
InventoryLink-Event: inventory.updated
InventoryLink-Signature: t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
{
"id": "evt_3f9c2a7b1d8e4c6a9b0d5e2f7a1c4b8d",
"type": "inventory.updated",
"created_at": "2026-10-07T16:05:00.000Z",
"data": { "sku": "MUG-12OZ-BLK", "quantity": 40 }
}Check the signature before trusting a webhook. Your signing secret (il_whsec_…) is in Connections, under your channel. Compute an HMAC-SHA256 of t, a dot and the raw request body, with the secret as the key, and compare it to v1. Reject requests whose t is more than 5 minutes old.
import crypto from "node:crypto";
function isFromInventoryLink(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}- Answer with any
2xxstatus within 10 seconds. Redirects count as failures. - If your endpoint doesn't accept a change, InventoryLink retries it automatically after 1, 5 and 15 minutes, then 1, 3, 6, 12 and 24 hours: about two days in all. Only the newest count for each SKU is retried, and a retry keeps the event's
id, so you can skip one you've already processed. - Once retries run out, the change shows as failed in Sync Logs. After fixing your endpoint, use Retry now in Connections, or read
GET /api/v1/inventoryto catch up. - Send test event in Connections sends a signed
testevent, so you can check your endpoint before real changes arrive. - If your system saves the count and reports it back with
PUT, InventoryLink recognizes its own update and doesn't send it on again.
Status codes
Errors come back as JSON with an error message.
| Code | Meaning |
|---|---|
| 200 | OK. For a stock change: the SKU isn't linked, so nothing was changed. |
| 202 | The change was processed. Check result for what each channel did. |
| 400 | A required field is missing or has the wrong type. |
| 401 | The Authorization header is missing, or the key is unknown or revoked. |
| 403 | The key is valid, but the account has no plan (the trial ended or the plan was canceled). |
| 404 | GET /inventory/{sku}: the SKU isn't linked on any channel. |
| 500 | Something went wrong processing the change. It's safe to retry. |
| 503 | The key couldn't be checked right now. Retry in a moment. |
Sync limits
Each channel updated counts as one sync toward your plan's monthly limit, just like changes that come from Shopify or WooCommerce. Once the limit is reached, requests still return 202, but nothing is written: result.skippedCount shows the channels that were skipped, and the Sync Logs page shows them as paused. Reading stock never counts. See pricing for each plan's limit.
Help
Questions about the API? Email support@inventorylink.app.