FOMDocs

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/sdk

Keep 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" })
AreaMethods
Shopshop.get()
Ordersorders.list, listAll, get, create, changeStatus
Customerscustomers.list, listAll, get, create, update
Productsproducts.list, listAll, get, create, update, adjustStock
Deliveriesdeliveries.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
  }
}
ClassStatus
FomAuthenticationError401
FomPermissionError403
FomNotFoundError404
FomConflictError409
FomValidationError422
FomRateLimitError429
FomConnectionErrorNo 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
})

On this page