API Docs

Connect your own platform to Tijar Suite.

A REST API for pushing orders in and reading/updating inventory, plus outgoing webhooks so your system hears about order and stock changes in real time. Works with any backend — React, PHP, Node.js, a Shopify custom app, or anything else — no Tijar Suite plugin required.

How it fits together

From API Key to a live integration.

1
Customer generates API Key + SecretFrom Integration Settings → Custom / Other Platform in their Tijar Suite dashboard.
2
Customer shares the credentials with their developerThe API Secret is only ever shown once, at generation time — the customer must copy and hand it over then.
3
Developer reads this documentationAuthentication, Orders, Inventory, Webhooks and error handling are all covered below.
4
Developer writes the integrationUsing cURL, Node.js, PHP, or any HTTP client capable of sending headers and JSON — no Tijar Suite plugin or SDK required.
5
Customer Website / Shopify / Custom AppThe integration runs on the customer’s own platform — React, PHP, a Shopify custom app, or anything else.
6
Tijar Suite APIOrders and stock updates flow in through /api/v1; order and inventory events flow back out through webhooks.
Quick reference

Base URL: https://tijarsuite.tech/api/v1

Connectivity

MethodEndpointDescription
GET/pingConfirm your API Key/Secret work

Orders

MethodEndpointDescription
POST/ordersCreate an order
GET/orders/:idGet an order by id or your externalOrderId
PATCH/orders/:idUpdate an order

Inventory

MethodEndpointDescription
GET/inventoryList inventory items (paginated, filterable)
GET/inventory/:idGet one item by id or SKU
PATCH/inventory/:id/stockSet or adjust stock for an item
Authentication

Every request needs your API Key and Secret.

Generate these once from Integration Settings → Custom / Other Platform. Send them as headers on every request:

HeaderValue
X-Api-KeyYour API Key (safe to log; it identifies the credential, not a secret by itself)
X-Api-SecretYour API Secret — shown once at generation time. Treat it like a password.

A missing or invalid key/secret returns 401 UNAUTHORIZED. A disabled or revoked credential, or an inactive workspace, returns 403 FORBIDDEN. Both cases use the same response envelope documented under Errors, below.

HTTPS is required. Send every request to https://tijarsuite.tech/api/v1 over HTTPS — plain HTTP is not supported.

There is no separate "login" step and no token to refresh — the API Key and Secret are sent on every request, the same way on every call.

Orders

Create, read and update orders.

These endpoints call the exact same order logic the Tijar Suite dashboard itself uses — stock is deducted the same way, the same validation rules apply, and orders created here show up immediately in the dashboard.

Create Order

POST /orders

FieldTypeDescription
externalOrderIdstringoptionalYour own order id. Used for duplicate detection — creating an order with an externalOrderId that already exists returns the existing order instead of a duplicate.
customer.namestringrequiredRecipient name.
customer.phonestringrequiredRecipient phone number.
shipping.address / .city / .zone / .areastringoptionalDelivery address. billing.* is accepted as a fallback if shipping is omitted.
itemsarrayrequiredNon-empty array of order items (see below). Every sku must already exist in your Tijar Suite inventory.
items[].skustringrequiredMust match an existing inventory item’s SKU/code.
items[].quantityintegerrequiredA positive whole number.
items[].unitPricenumberrequiredA non-negative number.
items[].name / .sizestringoptionalDisplay name and variant size.
items[].discountnumberoptionalA fixed amount taken off this line item.
orderTotalnumberrequiredThe order grand total.
shippingChargenumberoptionalDelivery charge, default 0.
paymentMethodstringoptionalFree-text payment method label.
paymentStatus"paid" | "unpaid" | "partial"optionalIf "partial", amountPaid is required and must be a positive number.
amountPaidnumberoptionalRequired when paymentStatus is "partial".
orderStatusstringoptionalOne of PENDING, CONFIRMED, DISPATCHED, DELIVERED, COMPLETED, HOLD, RETURNED, EXCHANGED, CANCELLED. Defaults to PENDING.
orderDatestringoptionalDefaults to the current date.
notesstringoptionalFree-text order notes.

Send an Idempotency-Key header to make retries safe — if a request with the same key and endpoint was already handled for your credential, the exact same response is replayed instead of creating a second order. This is independent from the externalOrderId duplicate check above; use either or both.

On success, responds 201 Created with the order (see response shape under Get Order, below). If externalOrderId already existed, responds 200 OK with the existing order and "duplicate": true.

Get Order

GET /orders/:id

:id may be either the Tijar Suite order id or the externalOrderId you created it with.

Response shape (data):

FieldTypeDescription
idstringoptionalTijar Suite order id.
externalOrderIdstring | nulloptionalYour own order id, if provided.
statusstringoptionalCurrent order status.
orderDatestringoptional
customerobjectoptional{ name, phone, email }
shippingobjectoptional{ address, city, zone, area }
itemsarrayoptional[{ sku, name, quantity, unitPrice, discount }]
subtotal, discountType, discountValue, shippingCharge, orderTotalnumber/stringoptionalOrder totals.
paymentMethod, amountPaid, amountDuestring/numberoptionalPayment summary.
notesstring | nulloptional
createdAt, updatedAtstringoptionalISO timestamps.

Update Order

PATCH /orders/:id

Accepts the same fields as Create Order, all optional — only fields you include are changed. externalOrderId and the order type cannot be changed once set. Changing orderStatus to a different value triggers an order.status_changed webhook (see Webhooks, below) in addition to order.updated.

Inventory

Read your catalog and update stock.

Stock updates go through the same stock-movement system the dashboard uses, so every change is recorded in your stock history — nothing is silently overwritten.

Get Inventory (list)

GET /inventory

Pagination: ?page= (default 1) and ?limit= (default 50, max 200).

Filters: ?sku=, ?id=, ?category=, ?locationId=, ?updatedSince= (ISO date/time — returns items updated at or after this time). Archived items are excluded.

Response: { items: [...], total, page, pages }

Get Product

GET /inventory/:id

:id may be either the internal item id or the product SKU/code.

Item shape (list items use the same shape):

FieldTypeDescription
idstringoptionalInventory item id.
skustringoptionalProduct code.
namestring | nulloptional
categorystringoptional
locationobject | nulloptional{ id, name }
type"Single" | "Variable"optional
currentStocknumberoptionalTotal across all variants for a Variable item.
variantsarrayoptional[{ size, stock }] — empty for a Single-type item.
stockStatus"in_stock" | "out_of_stock"optionalNo finer-grained low-stock threshold exists.
pricenumberoptionalThe item’s MRP. Cost price is never exposed by this API.
updatedAtstringoptionalISO timestamp.

Update Stock

PATCH /inventory/:id/stock

FieldTypeDescription
mode"set" | "adjust"required"adjust" applies quantity as a +/- delta. "set" treats quantity as the new absolute stock level.
quantityintegerrequiredA whole number. Cannot be negative when mode is "set".
sizestringoptionalRequired if the item has variants (Variable type); ignored for Single-type items.
reasonstringoptionalFreeform note, recorded on the stock movement.

Response is the updated item (as above) plus movement: { previousStock, newStock, quantity, reason, actionType, timestamp } (or null if the requested change resulted in no change).

Webhooks

Hear about changes as they happen.

Configure a webhook URL from Integration Settings → Custom / Other Platform. Tijar Suite will POST an event to it whenever one of the events below occurs — the same Webhook Secret you already have (shown alongside your API Key) is used to sign every delivery, no separate secret needed.

Available events

EventFired when
order.createdA new order is created through this API or the Tijar Suite dashboard.
order.updatedAny field on an order changes.
order.status_changedAn order’s status specifically changes (also fires alongside order.updated).
inventory.stock_changedAn item’s stock quantity changes, for any reason (order, return, manual adjustment via this API).

The "Send Test Webhook" button in Integration Settings sends a webhook.test event with a fixed message payload — useful for confirming your endpoint and signature verification work before going live.

Payload

Every delivery is a JSON POST with this envelope:

Envelope{
  "id": "evt_cjk3x8f2a0001qs08a1b2c3d4",
  "type": "order.created",
  "createdAt": "2026-10-01T09:12:00.000Z",
  "data": { ... }
}

order.created / order.updated

data{
  "id": "order_id",
  "externalOrderId": "your-order-123",
  "status": "PENDING",
  "orderTotal": 1250,
  "recipientName": "Jane Doe",
  "recipientPhone": "01700000000",
  "itemCount": 2,
  "updatedAt": "2026-10-01T09:12:00.000Z"
}

order.status_changed

data{
  ...same fields as above,
  "previousStatus": "PENDING",
  "newStatus": "CONFIRMED"
}
inventory.stock_changed — data{
  "id": "inventory_item_id",
  "sku": "TS-SHIRT-01",
  "size": "M",
  "previousStock": 20,
  "newStock": 15,
  "quantityChanged": -5,
  "reason": "Order #1234"
}

HMAC signature & verification

Each request carries these headers:

HeaderValue
X-Webhook-IdSame as the envelope’s id — stable across retries of the same event.
X-Webhook-TimestampUnix timestamp (seconds) of this delivery attempt.
X-Webhook-SignatureHex-encoded HMAC-SHA256 signature.

The signature is computed as HMAC-SHA256(webhookSecret, "{timestamp}.{raw JSON body}"). To verify a delivery: read the raw request body (don’t re-serialize it — use the exact bytes received), concatenate the timestamp header, a period, and the raw body, then HMAC it with your Webhook Secret and compare the result to X-Webhook-Signature using a constant-time comparison. See the Node.js and PHP examples below.

Retry behavior

A delivery is considered successful on any 2xx response, with a 10-second timeout per attempt. A non-2xx response or timeout schedules a retry — up to 5 attempts total, spaced 1 minute, 5 minutes, 15 minutes, 1 hour, then 6 hours apart. After the 5th failed attempt the delivery is marked exhausted and is not retried again.

Duplicate protection: X-Webhook-Id (and the envelope’s id) stays the same across every retry of the same event — store ids you’ve already processed and skip repeats, in case a retry arrives after your first response was lost in transit rather than genuinely failing.

Delivery status, HTTP response status and attempt count for every event are visible in Integration Settings → Custom / Other Platform → Recent Deliveries.

Errors

One envelope, every time.

Every error response has this shape:

Error response{
  "success": false,
  "error": {
    "code": "INVALID_SKU",
    "message": "Unknown SKU(s): TS-DOES-NOT-EXIST",
    "requestId": "req_a1b2c3d4e5f6"
  }
}

Every response — success or error — also carries the request id as an X-Request-Id header. Quote it when contacting support about a specific call.

StatusCode(s)Meaning
400MALFORMED_REQUEST / MISSING_FIELDThe request body is not valid JSON, or a required field is missing.
401UNAUTHORIZEDThe X-Api-Key / X-Api-Secret headers are missing, or the credentials are invalid.
403FORBIDDENThe credential has been disabled, or the workspace is not active.
404NOT_FOUNDThe order or inventory item does not exist.
409—Reserved for future use. No endpoint currently returns 409 — creating an order with an externalOrderId that already exists returns the existing order with 200 instead (see Idempotency, below).
422INVALID_SKU / INVALID_QUANTITY / INVALID_PRICE / INVALID_STATUSA field is present but has an invalid value (e.g. an unknown SKU, a negative quantity).
429RATE_LIMITEDToo many requests. The limit is 300 requests per 15 minutes per API credential.
500INTERNAL_ERRORAn unexpected server error. Safe to retry; contact support if it persists.
Code examples

Working examples in cURL, Node.js and PHP.

Test your connection

cURLcurl https://tijarsuite.tech/api/v1/ping \
  -H "X-Api-Key: tsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "X-Api-Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Node.js (fetch)const res = await fetch('https://tijarsuite.tech/api/v1/ping', {
  headers: {
    'X-Api-Key': process.env.TIJAR_API_KEY,
    'X-Api-Secret': process.env.TIJAR_API_SECRET,
  },
});
const json = await res.json();
console.log(json); // { success: true, data: { message: 'Connection successful', requestId: '...' } }
PHP (cURL)$ch = curl_init('https://tijarsuite.tech/api/v1/ping');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: ' . getenv('TIJAR_API_KEY'),
        'X-Api-Secret: ' . getenv('TIJAR_API_SECRET'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response);

Create an order

cURLcurl -X POST https://tijarsuite.tech/api/v1/orders \
  -H "X-Api-Key: tsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "X-Api-Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-123-attempt-1" \
  -d '{
    "externalOrderId": "order-123",
    "customer": { "name": "Jane Doe", "phone": "01700000000" },
    "shipping": { "address": "123 Road", "city": "Dhaka", "zone": "Gulshan", "area": "Gulshan 1" },
    "items": [
      { "sku": "TS-SHIRT-01", "quantity": 2, "unitPrice": 850 }
    ],
    "orderTotal": 1700,
    "paymentStatus": "unpaid"
  }'
Node.js (fetch)const res = await fetch('https://tijarsuite.tech/api/v1/orders', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.TIJAR_API_KEY,
    'X-Api-Secret': process.env.TIJAR_API_SECRET,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-123-attempt-1',
  },
  body: JSON.stringify({
    externalOrderId: 'order-123',
    customer: { name: 'Jane Doe', phone: '01700000000' },
    shipping: { address: '123 Road', city: 'Dhaka', zone: 'Gulshan', area: 'Gulshan 1' },
    items: [{ sku: 'TS-SHIRT-01', quantity: 2, unitPrice: 850 }],
    orderTotal: 1700,
    paymentStatus: 'unpaid',
  }),
});
const order = await res.json();
PHP (cURL)$body = [
    'externalOrderId' => 'order-123',
    'customer' => ['name' => 'Jane Doe', 'phone' => '01700000000'],
    'shipping' => ['address' => '123 Road', 'city' => 'Dhaka', 'zone' => 'Gulshan', 'area' => 'Gulshan 1'],
    'items' => [['sku' => 'TS-SHIRT-01', 'quantity' => 2, 'unitPrice' => 850]],
    'orderTotal' => 1700,
    'paymentStatus' => 'unpaid',
];

$ch = curl_init('https://tijarsuite.tech/api/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($body),
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: ' . getenv('TIJAR_API_KEY'),
        'X-Api-Secret: ' . getenv('TIJAR_API_SECRET'),
        'Content-Type: application/json',
        'Idempotency-Key: order-123-attempt-1',
    ],
]);
$order = json_decode(curl_exec($ch), true);
curl_close($ch);

List inventory

cURLcurl "https://tijarsuite.tech/api/v1/inventory?page=1&limit=50&category=Shirts" \
  -H "X-Api-Key: tsk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "X-Api-Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Node.js (fetch)const url = new URL('https://tijarsuite.tech/api/v1/inventory');
url.searchParams.set('page', '1');
url.searchParams.set('limit', '50');

const res = await fetch(url, {
  headers: {
    'X-Api-Key': process.env.TIJAR_API_KEY,
    'X-Api-Secret': process.env.TIJAR_API_SECRET,
  },
});
const { data } = await res.json();
console.log(data.items, data.total, data.pages);
PHP (cURL)$ch = curl_init('https://tijarsuite.tech/api/v1/inventory?page=1&limit=50');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: ' . getenv('TIJAR_API_KEY'),
        'X-Api-Secret: ' . getenv('TIJAR_API_SECRET'),
    ],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($result['data']['items'] as $item) {
    echo $item['sku'] . ': ' . $item['currentStock'] . "\n";
}

Verifying a webhook signature

This runs on your own server, in the handler that receives Tijar Suite’s webhook POSTs — there’s no outbound API call here, just a signature check against the raw request body.

Node.js (Express)const crypto = require('crypto');

app.post('/webhooks/tijar-suite', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.headers['x-webhook-timestamp'];
  const signature = req.headers['x-webhook-signature'];
  const rawBody = req.body.toString('utf8'); // raw bytes, not re-serialized JSON

  const expected = crypto
    .createHmac('sha256', process.env.TIJAR_WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const valid = signature &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) return res.status(401).send('Invalid signature');

  const event = JSON.parse(rawBody);
  console.log('Received event:', event.id, event.type);
  // ...handle event.type / event.data, then:
  res.status(200).send('ok');
});
PHP$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input'); // raw bytes, not re-encoded JSON

$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, getenv('TIJAR_WEBHOOK_SECRET'));

if (!$signature || !hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($rawBody, true);
// ...handle $event['type'] / $event['data'], then:
http_response_code(200);
echo 'ok';

Need help with your integration?

Reach out and we'll help you get set up.

Contact us

Tijar Suite

This page requires JavaScript to run. Please enable JavaScript in your browser and reload the page.