The Karatry API lets your website, your spreadsheet or the tools you had built read your workspace and send work into it. Webhooks tell your systems the moment an order is confirmed, an invoice goes out or stock changes.
The API works with the records you already keep in Karatry, under the same rules as the app.
Read styles, stock items and numbered pieces, with what is on hand and what is held for orders, so your site shows what you can really sell.
Create customers, draft quotes, draft sales orders and draft purchase orders from your own order form or buying sheet. Nobody retypes them.
Webhooks send a signed message to your server when an order is confirmed, an invoice is issued, a payment is received, a memo is settled, a piece is made or sold, or stock changes.
A key can ask to confirm, ship or invoice an order, record a payment, receive a purchase order, settle a memo line or adjust stock. Each request waits for an admin to approve it in Karatry.
Cost, margin and vendor details stay out of a key's answers unless you tick them when you make it. A key for your website never needs to see what a piece cost you.
Each record a key makes or changes is written to your audit log with the key's name. Revoke a key and it stops on its next request.
The API is part of every Karatry workspace, on the free trial and on the paid plan. There is nothing to install and nothing to request.
Send the key as a bearer token. This asks for five stock items:
curl "https://app.karatry.com/api/v1/stock-items?limit=5" \
-H "Authorization: Bearer kty_your_key"
The answer is JSON. A list comes back as data with a next_cursor for the next page:
{
"data": [
{
"id": "…",
"sku": "RING-1001",
"name": "Solitaire ring, 14K yellow",
"unit": "EACH",
"on_hand": 12,
"reserved": 3,
"retail_price": "1450.00"
}
],
"next_cursor": null
}
The example shows a few of the fields. The reference lists every field of every record.
A change needs a key with Can make changes and an Idempotency-Key header, a name you choose for this one request. If your script times out and sends the same request again under the same name, Karatry answers with the first result and makes nothing twice.
curl "https://app.karatry.com/api/v1/sales-orders" \
-H "Authorization: Bearer kty_your_key" \
-H "Idempotency-Key: web-order-20418" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "…",
"customer_po_number": "PO-7731",
"lines": [
{
"description": "Solitaire ring, made to order",
"quantity": 1,
"unit_price": "1450.00"
}
]
}'
The order arrives as a draft, with its number, tax and totals worked out the way the app works them out. A person confirms it in Karatry.
Every endpoint, field and error is described in one OpenAPI 3.1 document, served by Karatry itself, so it always matches the API you are calling. Most API tools and code generators read it directly.
https://app.karatry.com/api/v1/openapi.jsonOne address, https://app.karatry.com/api/v1, and one key in the Authorization header.
Styles with their bill of materials and costing, stock items, numbered pieces, locations, customers, vendors, quotes, sales orders, invoices, payments, memos, purchase orders, metal prices and your workspace's own details.
A change runs the same checks as the screen it stands in for, so the API never saves a record the app would refuse.
Anything that moves money or goods is a request, not a change. A key can ask to:
Each request appears in Karatry as a card that shows exactly what it would do. An admin approves or dismisses it within 7 days, and your script can read how its request stands. If the record changed after the request was filed, the card says so and cannot be approved.
Three kinds of figure are kept from a key unless you grant them: cost, margin and vendors. A field the key may not see is left out of its answers, and a key can only set what it can see.
limit. Pass next_cursor back as cursor for the next page.code and a sentence that says what to fix, under the matching HTTP status.X-RateLimit headers; over the limit the answer is 429 with Retry-After.Give Karatry an https address and it sends your server a message when something happens, so you never have to keep asking.
In Settings → Developers, under Webhooks, an admin enters a name, an https address and the events to send. Karatry shows the webhook's signing secret once. A workspace can have 5 webhooks.
sales_order.confirmed: a sales order was confirmed.invoice.issued: an invoice was issued.payment.received: a customer's payment was recorded.memo.settled: a memo line was sold or returned.piece.created: a numbered piece was made.piece.sold: a numbered piece was sold.stock.changed: a stock item's count on hand or held for orders changed.A message is small: the event's id, type and created_at, and data with the record's id, number, status and totals. It never carries a cost, a margin or a vendor. When your server needs more, it reads the record with its key.
Every message carries a Karatry-Signature header with a time t and a signature v1. The signature is the HMAC-SHA256 of the time, a dot and the exact body you received, keyed with the webhook's signing secret. Work it out yourself and compare, and refuse a message whose time is more than five minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";
// header: the Karatry-Signature value. body: the request body, exactly as received.
function fromKaratry(secret, header, body) {
const parts = new Map(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.get("t"));
const v1 = parts.get("v1") ?? "";
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret).update(t + "." + body).digest("hex");
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}
id is the same every time, so keep the ids you have handled and skip a repeat.The API follows the same rules as the rest of Karatry. More on Security & your data.
Start free, make a key in Settings → Developers, and call the API the same day.
Start free — no card →