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.yourdomain.com/api/v1/ (or your store root)

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.
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. Inventory Adjustment

Direct stock updates for barcode scanners and warehouse integrations:

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

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
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.

Verifying Webhook Signatures

Every webhook request includes an X-Joomni-Hmac-Sha256 header containing a Base64-encoded HMAC-SHA256 signature calculated with your webhook’s secret key.

Always verify webhook signatures

Verifying the signature ensures the request was not forged and was not tampered with in transit.
// Node.js Express / Next.js Webhook Verification
import crypto from 'crypto';

export function verifyWebhook(rawBody, secret, signature) {
  const hash = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('base64');
  return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(signature));
}
Need a hand?