Skip to main content

Integration API

This API lets an online store connector read the Nadigit IMS catalog and stock, reserve stock for a cart and record orders. In return, Nadigit IMS notifies the connector with signed webhooks. It is the API used by the Bagisto connector, and the one to build a connector for another platform.

This page documents the Integration API only: the other Nadigit IMS APIs are reserved for the web console.

Before you start​

  • Enterprise plan.
  • An integration declared and provisioned in E-commerce Integrations. Provisioning supplies the connector configuration. See E-commerce integration.
Configuration fieldUse
backendUrlBase address of the API: the paths below are appended to it
keycloakTokenUrlToken endpoint
keycloakClientId, keycloakClientSecretConnector credentials
webhookSecretSecret used to verify webhooks

Authentication​

The connector obtains an access token with the OAuth 2.0 client credentials flow, then sends it in the Authorization header of every call.

curl -s -X POST "$KEYCLOAK_TOKEN_URL" \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET"
GET /api/ecommerce/catalog HTTP/1.1
Authorization: Bearer <access_token>
  • The token is short-lived (expires_in, in seconds): keep it and renew it just before it expires, rather than on every call.
  • Scope. The connector can only reach the Integration API, and only for the warehouse and shop bound to the integration. The warehouse is never a request parameter: it is fixed by the connector's identity.

Conventions​

  • JSON exchanges, UTF-8 encoding.
  • The SKU is the item's reference in Nadigit IMS, unique within the warehouse.
  • Quantities are expressed in the item's selling unit, with decimals for items sold by weight or measure (fractional).
  • Amounts are in the organization's currency.

Endpoints​

MethodPathRole
GET/api/ecommerce/catalogPublishable catalog, paged
GET/api/ecommerce/catalog/{sku}One item
GET/api/ecommerce/stock?skus=…Stock for up to 200 items
PUT/api/ecommerce/reservations/{cartId}Reserve stock for a cart
POST/api/ecommerce/ordersRecord an order
POST/api/ecommerce/orders/{reference}/cancelCancel an order

Catalog​

GET /api/ecommerce/catalog?page=0&size=100
GET /api/ecommerce/catalog/ACC-POIG-BL

The catalog contains the active, physical items of the bound warehouse; services are excluded. The list is paged (page from 0, size from 1 to 200, 100 by default) and sorted by SKU. An unknown, inactive or other-warehouse SKU returns 404.

{
"sku": "ACC-POIG-BL",
"name": "Poignée de fenêtre à crémone – blanc",
"description": "…",
"category": "Accessoires",
"categoryPath": ["Accessoires"],
"price": 45.0,
"taxRate": 20.0,
"taxClass": "…",
"taxInclusive": false,
"allocatable": 146.0,
"measureUnit": "Unité",
"fractional": false,
"quantityPrecision": 0,
"primaryImageUrl": "https://…",
"imageUrls": ["https://…"],
"barcodes": ["…"]
}

The list returns these objects in a page (content, totalElements, totalPages, number, size). The catalog has no "modified since" filter yet: synchronize it in full, then rely on webhooks for changes.

Stock​

GET /api/ecommerce/stock?skus=ACC-POIG-BL,SIL-NEU-280,UNKNOWN
[
{ "sku": "ACC-POIG-BL", "found": true, "sellable": 148.0, "reservedByOthers": 2.0, "allocatable": 146.0 },
{ "sku": "UNKNOWN", "found": false, "sellable": null, "reservedByOthers": null, "allocatable": null }
]
FieldMeaning
sellableSellable stock in the warehouse
reservedByOthersQuantity reserved by other carts or ongoing sales
allocatableWhat can still be sold: the quantity to display
foundfalse for an unknown SKU: take it off sale

Up to 200 SKUs per call, comma-separated.

Reservations​

Reserving stock for a cart avoids selling online an item that is already gone. Call this endpoint on every cart change and just before payment.

PUT /api/ecommerce/reservations/cart-8841
Content-Type: application/json

{ "lines": [ { "sku": "ACC-POIG-BL", "quantity": 2 } ] }
  • 204: the cart's reservation is replaced by these lines.
  • 409: payment must be blocked.
{
"code": "insufficient_stock",
"message": "…",
"lines": [ { "sku": "ACC-POIG-BL", "requested": 2, "allocatable": 1 } ]
}

code is insufficient_stock (not enough stock, line by line) or reservations_disabled (soft reservations are disabled in Nadigit IMS). A reservation expires by itself: an abandoned cart releases its stock without any connector action.

Orders​

POST /api/ecommerce/orders
Content-Type: application/json

{
"reference": "WEB-1000231",
"cartId": "cart-8841",
"orderDate": "2026-09-24T18:40:00",
"customer": { "email": "customer@example.ma", "firstName": "Salma", "lastName": "Idrissi", "phone": "+212600000000" },
"lines": [ { "sku": "ACC-POIG-BL", "quantity": 2, "unitPrice": 45.0 } ],
"declaredTotal": 108.0,
"paymentMethod": "card",
"paymentReference": "ch_3Q…"
}
FieldRequiredNote
referenceYesOrder reference on the store, 64 characters at most. Used as the uniqueness key
cartIdNoThe cart whose reservation is consumed
customerNoCustomer found by e-mail, or created
linesYesSKUs and quantities. unitPrice is advisory
shipping, declaredTotalNoAdvisory, compared with the recalculated total
paymentMethodNoSee below
paymentReferenceNoTransaction reference, kept on the payment

Behaviour:

  • Nadigit IMS recalculates prices and taxes. If declaredTotal differs, the order is accepted and priceMismatch is true.
  • The order takes stock out of the warehouse and is attached to the bound shop.
  • Idempotency: sending the same reference again returns the existing order (alreadyExisted: true) without creating a duplicate. A connector can safely replay an order.
  • paymentMethod: card, wallet, stripe, paypal, online, transfer or cash record a payment: the order is paid. cod (cash on delivery), an empty or unknown value record nothing: the order stays unpaid.
{
"reference": "WEB-1000231",
"status": "Processing",
"paymentStatus": "PAID",
"totalAmount": 108.0,
"dutyFreeAmount": 90.0,
"taxAmount": 18.0,
"alreadyExisted": false,
"priceMismatch": false,
"declaredTotal": 108.0
}

Cancellation​

POST /api/ecommerce/orders/WEB-1000231/cancel

Cancels the order and puts its stock back in the warehouse. An order already canceled returns success with alreadyExisted: true; a reference unknown in the warehouse returns 404. Any refund is not automatic.

Webhooks​

Nadigit IMS calls the connector at Storefront URL + Webhook path declared in the integration, with POST, after each recorded change.

Events​

product.updated: an item changed (stock, price, state).

{ "event": "product.updated", "action": "upsert", "sku": "ACC-POIG-BL", "occurredAt": "2026-09-24T18:41:07Z" }
  • action: "upsert": read the item again with GET /api/ecommerce/catalog/{sku} and apply it.
  • action: "unpublish": take the item off sale (deactivated, sold out or no longer in the warehouse).

order.status: a web order changed status.

{ "event": "order.status", "reference": "WEB-1000231", "status": "Delivered", "paymentStatus": "PAID", "occurredAt": "2026-09-25T10:02:13Z" }
statusMeaning
ProcessingBeing prepared (status on intake)
Delivered, CompletedDelivered, completed
CanceledCanceled
Return_Pending, Returned, Partial_ReturnReturn pending, returned, partially returned

paymentStatus is UNPAID, PARTIALLY_PAID or PAID.

Verify the signature​

Every webhook carries two headers:

  • X-Timestamp: the sending time, in seconds since 1 January 1970;
  • X-Signature: HMAC-SHA256(webhookSecret, X-Timestamp + "." + body), in lowercase hexadecimal.

Recompute the signature on the raw body received, compare in constant time, and reject a timestamp that is too old (for example more than five minutes).

$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $body, $webhookSecret);
if (!hash_equals($expected, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(401);
exit;
}
const crypto = require('crypto');
function isValid(rawBody, ts, sig, secret) {
const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
return sig.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
&& Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}

Response and retries​

Answer 2xx quickly, then process the webhook. On failure or no answer, Nadigit IMS retries several times with increasing delays. Every attempt is logged on the Nadigit IMS side. Process webhooks idempotently: the same event can arrive twice.

Error codes​

CodeCause
400Invalid request (missing or malformed field); the body details the fields
401Token missing, expired or invalid: renew the token
403Access denied, or feature not included in the installation's plan
404SKU or order unknown in the bound warehouse
409Insufficient stock or reservations disabled (see Reservations)
503Installation without an active licence

Errors other than 409 return a body of this form:

{ "timestamp": "…", "status": 403, "error": "Forbidden", "message": "…", "path": "/api/ecommerce/catalog", "errorCode": "…" }

See also​