Public REST API & Webhooks

Connect external ERPs, logistics couriers, warehouse barcode scanners, and custom headless apps to your Joomni store using scoped secret keys and real-time HMAC-signed event webhooks.

In the dashboard:Settings › API & WebhooksPlan:Business and above (and during free trial)

Overview

Joomni exposes a versioned, enterprise-grade REST API at /api/v1/ and an event-driven Webhook Delivery Engine. Designed around modern commerce patterns (similar to Shopify), it gives external systems full, programmatic control over your catalog, orders, and stock levels without compromising security.

Authentication & API Keys

Every API call requires a secret API key generated in your dashboard under Settings › API & Webhooks. Keys are prefixed with jk_live_ and granted only the specific permission scopes you select.

FieldWhat it does
Header Option 1 (Standard)Authorization: Bearer jk_live_your_key_here
Header Option 2 (Custom)X-Joomni-Access-Token: jk_live_your_key_here
Base URLhttps://api.joomni.com/api/v1/

Permission Scopes

ScopeWhat it allows
orders:readView orders, order items, customer addresses, and financial/fulfillment statuses.
orders:writeCreate orders, fulfill items with tracking details, and cancel orders with restock.
products:readBrowse the catalog, search items, inspect variants, images, and prices.
products:writeCreate, update, and remove catalog items and variants.
inventory:readRead inventory levels and audit history.
inventory:writeAdjust or set stock levels with automatic logging and multi-variant synchronization.
customers:read / writeRead and manage customer records, tags, and contact profiles.
leads:read / writeRead and manage inbound contact inquiries, consultation requests, RFP data, and CRM tags.
bookings:read / writeAccess appointment schedules, booking requests, calendar slots, and staff assignments.
store:readInspect store metadata, currency, timezone, and active vertical settings.

Core Endpoints (/api/v1)

1. Products

Read and update catalog items:

  • GET /api/v1/products — List products with pagination (?page=1&limit=20&search=shirt).
  • GET /api/v1/products/:id — Retrieve single product details with all variants.
  • POST /api/v1/products — Create a new product.
  • PUT /api/v1/products/:id — Update product details.
  • DELETE /api/v1/products/:id — Safely delete a product (unlinks order snapshots).

2. Orders & Fulfillment

Automate orders and book courier shipments:

  • GET /api/v1/orders — List store orders (filter by ?status=PENDING&fulfillmentStatus=UNFULFILLED).
  • GET /api/v1/orders/:id — Full order breakdown including line item snapshots and shipping address.
  • POST /api/v1/orders/:id/fulfill — Mark an order fulfilled and attach tracking company / tracking number.
  • POST /api/v1/orders/:id/cancel — Cancel an order with optional automatic stock restoration.

3. Inquiries & Leads (B2B / CRM)

Sync inbound contact form submissions with external CRMs (HubSpot, Salesforce, Zoho):

  • GET /api/v1/leads — List incoming inquiries with pagination (?page=1&limit=20&status=NEW).
  • GET /api/v1/leads/:id — Retrieve specific inquiry with AI intent and qualification score.
  • POST /api/v1/leads — Create a new lead or import from an external marketing channel.
  • PUT /api/v1/leads/:id — Update lead status (CONTACTED, QUALIFIED, CONVERTED).

4. Inventory Adjustment

Direct stock updates for barcode scanners and warehouse integrations:

POST /api/v1/inventory/adjusthttp
POST /api/v1/inventory/adjust
{
"productId": "64a...",
"adjustment": -1,
"reason": "MANUAL_ADJUSTMENT"
}

Live API Sandbox Console

Test requests interactively against your store’s live endpoints or examine sample response payloads and headers:

Live REST API Sandbox Console

v1 REST

Test live requests, inspect JSON schemas, and verify response formats directly from your browser.

Manage API Keys

Leave blank to test using your current logged-in store session, or paste an API key created under Settings › API & Webhooks.

Real-Time Webhooks

Instead of polling the API every minute, configure Webhooks to receive real-time HTTP POST notifications the millisecond an event occurs in your store.

TopicSent when
leads/createA visitor submits a contact form, consultation inquiry, or quote request.
leads/updatedA lead status, tag, or AI qualification score changes.
bookings/createA client books an appointment, consultation, or service.
bookings/cancelledAn appointment is cancelled or rescheduled.
orders/createA customer or staff member places an order.
orders/updatedAn order’s financial or delivery status changes.
orders/fulfilledAn order is fulfilled or dispatched with a courier.
orders/cancelledAn order is cancelled or refunded.
products/create / update / deleteCatalog items are created, modified, or removed.
inventory/level_updateStock is decremented by sales or adjusted by staff.
customers/createA new customer profile is registered.

Webhook Headers

HeaderValue
X-Joomni-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
X-Joomni-TimestampThe same Unix timestamp (seconds) that was signed.
X-Joomni-Event-IdUnique per delivery attempt, e.g. evt_3f9a… Use it to ignore repeats.
X-Joomni-TopicThe event topic, e.g. orders/create.
X-Joomni-Store-IdThe store the event belongs to.
X-Joomni-Webhook-IdThe subscription that matched.
X-Joomni-Hmac-Sha256Deprecated. Base64 HMAC of the body only — it cannot detect replays. Verify X-Joomni-Signature instead.

Webhook addresses must use https:// and resolve to a public host. Private, loopback and link-local addresses are refused, and redirects are not followed. Your endpoint should reply with a 2xx status within 10 seconds.

Verifying Webhook Signatures

Compute an HMAC-SHA256 of `${t}.${rawBody}` with your webhook’s signing secret and compare it to the v1 value in X-Joomni-Signature. Then reject the request if t is more than 5 minutes old, so a captured request cannot be replayed later.

Sign the raw body, not parsed JSON

Use the exact bytes you received. Parsing the JSON and serialising it again can change key order or whitespace, and the signature will no longer match. In Express, read the body with express.raw({ type: 'application/json' }) on your webhook route.

Find the signing secret under Settings › API & Webhooks › Subscriptions (Reveal or Copy).

Node.js webhook verificationjavascript
1import crypto from 'crypto';
2
3const TOLERANCE_SECONDS = * ;
4
5// rawBody: Buffer or string exactly as received; header: X-Joomni-Signature
6export function verifyJoomniWebhook(rawBody, secret, header) {
7 const parts = Object.fromEntries(
8 String(header || '').split(',').map((p) => p.trim().split('=')),
9 );
10 const timestamp = Number(parts.t);
11 if (!timestamp || !parts.v1) return false;
12 if (Math.abs(Date.now() / - timestamp) > TOLERANCE_SECONDS) return false;
13
14 const expected = crypto
15 .createHmac('sha256', secret)
16 .update(`${timestamp}.${rawBody}`, 'utf8')
17 .digest('hex');
18
19 const a = Buffer.from(expected, 'hex');
20 const b = Buffer.from(parts.v1, 'hex');
21 // timingSafeEqual throws on different lengths, so check first.
22 return a.length === b.length && crypto.timingSafeEqual(a, b);
23}
Python webhook verificationpython
import hashlib, hmac, time
TOLERANCE_SECONDS = 5 * 60
def verify_joomni_webhook(raw_body: bytes, secret: str, header: str) -> bool:
parts = dict(p.strip().split("=", 1) for p in (header or "").split(",") if "=" in p)
try:
timestamp = int(parts["t"])
received = parts["v1"]
except (KeyError, ValueError):
return False
if abs(time.time() - timestamp) > TOLERANCE_SECONDS:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received)
Need a hand?