@chemicalluck/cin7-core-api-node
v1.1.0
Published
Cin7 Core (DEAR) API v2 Node.js Client
Maintainers
Readme
cin7-core-api-node
A fully typed, dependency-free Node.js client for the Cin7 Core (DEAR) API v2.
Cin7 Core, not Omni. This targets Cin7 Core (formerly DEAR,
inventory.dearsystems.com). If you're on Cin7 Omni (api.cin7.com), use@chemicalluck/cin7-omni-api-nodeinstead.
- Runtime-validated — every model is a zod schema. Responses are validated (non-throwing) and request bodies are validated before sending (throwing), so you can trust the data in both directions.
- Type-safe — types are inferred from the schemas (
z.infer), generated from the official API Blueprint. - One dependency — just
zod; HTTP uses the nativefetchin Node 20+. - Exhaustive — all ~100 documented v2 resource groups (products, sales, purchases, advanced purchases, production, stock, CRM, reference data, and more).
- Batteries included — two-header auth, automatic retries (429/503/5xx, honoring
Retry-After), and transparent envelope-based pagination. - Dual ESM + CJS builds with bundled type declarations.
Installation
npm install @chemicalluck/cin7-core-api-nodeRequires Node.js 20 or newer.
Usage
Create a client with your Cin7 Core Account ID and Application Key (from the API setup page in Cin7 Core → these become the api-auth-accountid / api-auth-applicationkey headers):
import { Cin7Core } from "@chemicalluck/cin7-core-api-node";
const core = new Cin7Core("your-account-id", "your-application-key");
// List — pagination is followed automatically and returns Product[].
const products = await core.product.list({ Limit: 500, Name: "Widget" });
// Single-record read — Core reads by `?ID=` query param, typed as Sale.
const sale = await core.sale.get("2c9f8a3e-...");
// Create / update take a single record object.
const created = await core.product.create({
SKU: "ABC",
Name: "Widget",
UOM: "Item"
});Lists vs. single records
Cin7 Core splits many resources into a list endpoint and a detail endpoint:
core.saleList.list()→GET /saleList(paginated envelope).core.sale.get(id)→GET /sale?ID=(full single object).
Endpoints whose GET returns an envelope expose list(params?); endpoints whose GET returns a bare object expose get(id). To fetch one record from an enveloped resource (e.g. Product), filter the list: await core.product.list({ ID }).
List parameters & pagination
list() methods accept Cin7CoreListParams: Page, Limit (default 100, max 1000), plus any endpoint-specific filters (e.g. Search, Name, Sku, UpdatedSince). Pagination is walked automatically until the response's Total is reached; the full array is returned.
Validation
Every model is a zod schema, and the client validates in both directions with deliberately asymmetric behavior:
- Request bodies (inputs) throw. Before a
create/updateis sent, the body is validated against its input schema; on failure aCin7CoreValidationErroris thrown and nothing is sent. A bad input is your bug — fail fast, don't mutate remote state. - Responses (outputs) warn. Responses are validated non-throwing: on drift,
onValidationErrorruns (defaultconsole.warn) and the raw data is still returned, so an upstream field change never breaks your app. List items are validated one-by-one.
Configure via the client options:
const core = new Cin7Core("account-id", "application-key", {
validateInput: true, // default — throw on bad request bodies
validateOutput: true, // default — validate responses
onValidationError: ({ context, issues, raw }) =>
myLogger.warn(context, issues)
});Schemas are exported too, if you want to validate elsewhere: import { Product, ProductInput } from "@chemicalluck/cin7-core-api-node" (each is both a zod schema value and its inferred type).
Error handling
Non-2xx responses throw Cin7CoreHTTPResponseError (with status + parsed body); invalid request bodies throw Cin7CoreValidationError (with issues). Rate-limit responses (429/503) and other 5xx are retried automatically (up to 3 times, honoring Retry-After).
import { Cin7CoreHTTPResponseError } from "@chemicalluck/cin7-core-api-node";
try {
await core.sale.get("missing");
} catch (error) {
if (error instanceof Cin7CoreHTTPResponseError) {
console.error(error.status, error.body);
}
}Resources
All resources are exposed flat on the client (core.<resource>). The full set (102):
advancedPurchase, advancedPurchaseCreditNote, advancedPurchaseInvoice,
advancedPurchaseManualJournals, advancedPurchasePayments, advancedPurchasePutAway,
advancedPurchaseStockReceived, attributeSet, bankAccounts, bankTransfer, brand, carrier,
chartOfAccounts, customPrices, customer, customerCredits, customerDefaultTemplate, deals,
disassembly, disassemblyList, disassemblyOrder, factoryCalendar, finishedGoods,
finishedGoodsList, finishedGoodsOrder, finishedGoodsPick, fixedAssetType, inventoryWriteOff,
inventoryWriteOffList, journal, lead, location, markupPrices, me, meAddress, meContact,
moneyOperation, moneyTaskList, opportunity, paymentTerm, priceTiers, product,
productAttachments, productAvailability, productCategory, productDiscounts, productFamily,
productFamilyAttachments, productFamilyProductionBOM, productProductionBOM, productSuppliers,
productionOrder, productionOrderList, productionRun, purchase, purchaseAttachments,
purchaseCreditNote, purchaseCreditNoteList, purchaseInvoice, purchaseList,
purchaseManualJournals, purchaseOrder, purchasePayments, purchaseStockReceived, resource,
resourceList, sale, saleAttachments, saleCreditNote, saleCreditNoteList, saleFulfilment,
saleFulfilmentPack, saleFulfilmentPick, saleFulfilmentShip, saleInvoice, saleList,
saleManualJournals, saleOrder, salePayments, saleQuote, shipZones, shipZonesEnabled,
startAWorkflow, stockAdjustment, stockAdjustmentList, stockTake, stockTakeList, stockTransfer,
stockTransferList, stockTransferOrder, supplier, supplierDeposits, suspendReason, task,
taskCategory, tax, templates, transactions, unitOfMeasure, webhooks, workCenters, workflowEach exposes the subset of list / get / create / update / delete that the API documents for it.
Rate limits
Cin7 Core throttles per API Application (per-minute and per-day). Throttled requests return 429/503; the client waits (honoring Retry-After, else 60s) and retries up to 3 times. See the official docs.
Development
npm install
npm run lint
npm run typecheck
npm run build
npm test