TypeScript SDK
A typed client for Node.js, Bun, Deno and edge runtimes, with retries, paging and webhook checks built in.
The SDK wraps the API with types for every request and response. It has no dependencies and needs Node.js 20 or later (or Bun, Deno, or an edge runtime).
npm install @getfom/sdkKeep your key on the server; never use the SDK with a key in a browser or app.
Make requests
import { FomClient } from "@getfom/sdk"
const fom = new FomClient({ apiKey: process.env.FOM_API_KEY! })
const { shop, api_key } = await fom.shop.get()
console.log(`${shop.name}, as ${api_key.acts_for}`)
const order = await fom.orders.create({
customer: { name: "Ko Ko", phone: "09123456789" },
items: [{ variant_id: "var_123", qty: 2 }],
})
await fom.orders.changeStatus(order.id, {
status: "confirmed",
expected_revision: order.revision,
})
await fom.products.adjustStock("var_123", { delta: -2, reason: "manual_adjust" })| Area | Methods |
|---|---|
| Shop | shop.get() |
| Orders | orders.list, listAll, get, create, changeStatus |
| Customers | customers.list, listAll, get, create, update |
| Products | products.list, listAll, get, create, update, adjustStock |
| Deliveries | deliveries.list, listAll, get, update |
Lists and syncing
list() returns one page with data, nextCursor and hasMore. listAll() goes through every page for you:
let since = await loadCheckpoint() // e.g. "2026-11-01T00:00:00.000Z"
for await (const product of fom.products.listAll({ updated_since: since })) {
await save(product)
since = product.updated_at
}
await saveCheckpoint(since)Retries and errors
The SDK retries 429s, 5xx answers and network failures twice, waiting for Retry-After when FOM sends one. Every write gets an Idempotency-Key that stays the same on each retry; pass your own with { idempotencyKey: "sale-42" }.
Errors are classes you can check:
import { FomConflictError, FomValidationError } from "@getfom/sdk"
try {
await fom.orders.changeStatus(order.id, { status: "delivered", expected_revision: 3 })
} catch (error) {
if (error instanceof FomConflictError) {
// Someone changed the order; read it again.
} else if (error instanceof FomValidationError) {
console.log(error.details)
} else {
throw error
}
}| Class | Status |
|---|---|
FomAuthenticationError | 401 |
FomPermissionError | 403 |
FomNotFoundError | 404 |
FomConflictError | 409 |
FomValidationError | 422 |
FomRateLimitError | 429 |
FomConnectionError | No answer: network failure or timeout |
All of them have status, code, message and requestId.
Webhooks
constructWebhookEvent checks the FOM-Signature header and returns a typed event, or throws if it doesn't match:
import express from "express"
import { constructWebhookEvent } from "@getfom/sdk"
app.post("/fom-webhooks", express.raw({ type: "application/json" }), async (req, res) => {
let event
try {
event = await constructWebhookEvent(req.body, req.get("FOM-Signature"), process.env.FOM_WEBHOOK_SECRET!)
} catch {
return res.sendStatus(400)
}
if (event.type === "order.created") {
console.log("New order", event.data.order.order_no)
}
res.sendStatus(200)
})Options
new FomClient({
apiKey: "fom_live_…",
maxRetries: 2, // retries for 429, 5xx and network failures
timeoutMs: 30_000, // per try
maxRetryDelayMs: 60_000, // a longer Retry-After throws instead of waiting
fetch: myFetch, // e.g. through a proxy
})