Orders
Orders with their totals, status and payment state. Compact by default; expand items and the customer when you need them.
Paste a key and every snippet on this page switches from the placeholder to your key — and the Try it panel under each endpoint is ready to send. It is stored in this browser only and goes nowhere except, if you press Send, straight to the API host you pick there.
List orders
/ext/v1/ordersorders:readCompact by default. Add ?expand=customer,items for the full object. updated_after is the sync filter — it catches orders placed before your last poll but changed since.
Query parameters
| Name | Type | Description |
|---|---|---|
limit | numberdefault 25max 100 | Page size. |
starting_after | string | Return the page AFTER this object id (the previous page's next_cursor). |
ending_before | string | Return the page BEFORE this object id. |
sort | stringdefault -created_at | Newest first by default. Only created_at is sortable — a cursor over a mutable key (like updated_at) cannot page reliably. Use updated_after to sync changes.created_at-created_at |
store_id | string | Restrict to one store. Required breadth control for company-scoped keys; on a store-scoped key it must match the key's own store. |
status | string | Filter to one of the values below.pendingfollow_upconfirmedprocessingready_to_shipin_transitdeliveredreturnedfailed_deliverycancelledholdspam |
payment_status | string | Filter to one of the values below.unpaidpartially_paidpaidrefunded |
fulfillment_status | string | Filter to one of the values below.unfulfilledpartially_fulfilledfulfilledreturned |
channel | string | Sales channel SLUG (the stable per-store key), not its id. |
created_after | date-time | ISO-8601 timestamp. Inclusive lower bound. |
created_before | date-time | ISO-8601 timestamp. Exclusive upper bound. |
updated_after | date-time | Only orders changed since this instant. THE sync filter: an order placed last month and confirmed this morning is exactly the row a created_after poll would miss. |
search | string | Match on order number, customer name or customer phone. |
expand | string[] | Include related objects. Allowed: customer, items. Comma-separated, one level. Omitted by default so a sync walking pages stays cheap. |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS "https://api.salafems.com/ext/v1/orders?limit=25&updated_after=2026-08-01T00%3A00%3A00Z&expand=customer%2Citems" \
-H "Authorization: Bearer $SALAF_API_KEY"const url = new URL("https://api.salafems.com/ext/v1/orders");
Object.entries({
limit: "25",
updated_after: "2026-08-01T00:00:00Z",
expand: "customer,items",
}).forEach(([key, value]) => url.searchParams.set(key, value));
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const { data, meta } = await response.json();import os
import requests
response = requests.get(
"https://api.salafems.com/ext/v1/orders",
params={
"limit": "25",
"updated_after": "2026-08-01T00:00:00Z",
"expand": "customer,items",
},
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
page = response.json()
rows, meta = page["data"], page["meta"]<?php
$key = getenv('SALAF_API_KEY');
$ch = curl_init('https://api.salafems.com/ext/v1/orders?limit=25&updated_after=2026-08-01T00%3A00%3A00Z&expand=customer%2Citems');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key],
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}
$rows = $response['data'];
$meta = $response['meta'];https://api.salafems.com/ext/v1/orders?channel=facebookStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Query parameters
Blank fields are left out of the request.
Paste a key above to enable Send.
Responses
200A cursor page of orders.401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"data": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z"
}
],
"meta": {
"has_more": true,
"next_cursor": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001"
}
}Create an order
/ext/v1/ordersorders:writeRuns the SAME order-creation core as the dashboard and storefront: availability-checked stock reservation, coupon engine, totals math and plan-quota metering are all inherited, never re-implemented. Lines reference variants by id or SKU; the customer is an existing id or a find-or-create by phone. Price overrides are honored — this is a trusted-merchant surface. Company-scoped keys must send store_id.
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Keyrequired | string | REQUIRED. A unique value per logical request (a UUID is ideal); reuse it verbatim when retrying. A retry replays the original response with Idempotent-Replayed: true instead of running twice. |
Body parameters
| Name | Type | Description |
|---|---|---|
store_id | uuid | Target store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store. |
channel | string | A sales-channel SLUG for attribution. Defaults to the store's manual channel (Decision Q11 — tagging is optional, never forced). |
location_id | uuid | Location (warehouse) to allocate stock from. Defaults to the company's default location — the admin-desk rule. |
customer_id | uuid | An existing customer. Provide this OR customer. |
customer | CreateOrderCustomerDto | Find-or-create by phone. Provide this OR customer_id. |
itemsrequired | CreateOrderLineDto[] | — |
shipping_address | object | Free-form shipping address object, stored verbatim. |
shipping_charge | number | Delivery charge as YOUR system computed it (trusted surface). A free-shipping coupon still zeroes it. Default 0. |
tax_amount | number | — |
coupon_code | string | Priced and redeemed by the same engine as every other surface; an unusable code fails the order rather than mispricing it. |
note | string | — |
payment | CreateOrderPaymentDto | An upfront settled payment (like the admin desk's "paid amount"). Omit for an unpaid order; record later payments via POST /v1/orders/{id}/payments. |
customer fields
| Name | Type | Description |
|---|---|---|
phonerequired | string | Bangladeshi mobile, any accepted spelling — stored canonical. THE find-or-create match key. |
full_name | string | Required when the phone matches no existing customer (a new record needs a name); ignored when one exists — a sale never silently renames a customer. |
email | string | — |
items[] fields
| Name | Type | Description |
|---|---|---|
variant_id | uuid | The variant to sell. Provide this OR sku. |
sku | string | Resolve the variant by SKU instead of id. |
quantityrequired | number | — |
unit_price | number | Override the regular unit price (trusted surface). Omitted = the variant's current catalog price — the admin-desk rule, verbatim. |
discount_price | number | Override the discounted unit price. Only honored when it is a REAL discount (above 0, below the regular price) — same rule as the desk. |
payment fields
| Name | Type | Description |
|---|---|---|
payment_method_id | uuid | One of the store's payment methods (must be active). |
amountrequired | number | — |
transaction_id | string | — |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS -X POST "https://api.salafems.com/ext/v1/orders" \
-H "Authorization: Bearer $SALAF_API_KEY" \
-H "Idempotency-Key: e7a1…-your-uuid" \
-H "Content-Type: application/json" \
-d '{
"channel": "facebook",
"customer": {
"phone": "01712345678",
"full_name": "Rafiqul Islam"
},
"items": [
{
"sku": "TSHIRT-RED-M",
"quantity": 2
}
],
"shipping_charge": 120,
"note": "Deliver after 6pm."
}'const url = new URL("https://api.salafems.com/ext/v1/orders");
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
"Idempotency-Key": "e7a1…-your-uuid",
"Content-Type": "application/json",
},
body: JSON.stringify({
"channel": "facebook",
"customer": {
"phone": "01712345678",
"full_name": "Rafiqul Islam"
},
"items": [
{
"sku": "TSHIRT-RED-M",
"quantity": 2
}
],
"shipping_charge": 120,
"note": "Deliver after 6pm."
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const object = await response.json();import os
import requests
payload = {
"channel": "facebook",
"customer": {
"phone": "01712345678",
"full_name": "Rafiqul Islam",
},
"items": [
{
"sku": "TSHIRT-RED-M",
"quantity": 2,
},
],
"shipping_charge": 120,
"note": "Deliver after 6pm.",
}
response = requests.post(
"https://api.salafems.com/ext/v1/orders",
json=payload,
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
"Idempotency-Key": "e7a1…-your-uuid",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
obj = response.json()<?php
$key = getenv('SALAF_API_KEY');
$payload = json_encode([
'channel' => 'facebook',
'customer' => [
'phone' => '01712345678',
'full_name' => 'Rafiqul Islam',
],
'items' => [
[
'sku' => 'TSHIRT-RED-M',
'quantity' => 2,
],
],
'shipping_charge' => 120,
'note' => 'Deliver after 6pm.',
]);
$ch = curl_init('https://api.salafems.com/ext/v1/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key, 'Idempotency-Key: e7a1…-your-uuid', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}https://api.salafems.com/ext/v1/ordersStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
generating…A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.
Paste a key above to enable Send.
Responses
201The created order, in the same shape asGET /v1/orders/{id}with customer and items expanded.401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403Missing scope, or the plan order quota is exhausted (plan_limit_reached).409A request with this idempotency key is still in flight (IDEMPOTENCY_IN_FLIGHT).422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z",
"items": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"variant_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_name": "Classic Cotton T-Shirt",
"variant_name": "Red / M",
"sku": "TSHIRT-RED-M",
"quantity": 2,
"unit_price": "1250.00",
"discount_price": "1250.00",
"subtotal": "1250.00"
}
],
"customer": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"full_name": "Rafiqul Islam",
"email": null,
"phone": "01712345678",
"type": "manual",
"status": "active"
}
}Get an order by id
/ext/v1/orders/{id}orders:readAdd ?expand=customer,items for the related objects.
Path parameters
| Name | Type | Description |
|---|---|---|
idrequired | uuid | The order id. |
Query parameters
| Name | Type | Description |
|---|---|---|
expand | string[] | — |
store_id | string | Restrict the lookup to one store (company-scoped keys). |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS "https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID?expand=customer%2Citems" \
-H "Authorization: Bearer $SALAF_API_KEY"const url = new URL("https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID");
Object.entries({
expand: "customer,items",
}).forEach(([key, value]) => url.searchParams.set(key, value));
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const object = await response.json();import os
import requests
response = requests.get(
"https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID",
params={
"expand": "customer,items",
},
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
obj = response.json()<?php
$key = getenv('SALAF_API_KEY');
$ch = curl_init('https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID?expand=customer%2Citems');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key],
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}https://api.salafems.com/ext/v1/orders/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001Stored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Path parameters
Query parameters
Blank fields are left out of the request.
Paste a key above to enable Send.
Responses
200The order.401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.404Order not found.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z",
"items": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"variant_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_name": "Classic Cotton T-Shirt",
"variant_name": "Red / M",
"sku": "TSHIRT-RED-M",
"quantity": 2,
"unit_price": "1250.00",
"discount_price": "1250.00",
"subtotal": "1250.00"
}
],
"customer": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"full_name": "Rafiqul Islam",
"email": null,
"phone": "01712345678",
"type": "manual",
"status": "active"
}
}Cancel an order
/ext/v1/orders/{id}/cancelorders:writeSugar for the cancelled transition: same map, same stock unwind (release an open reservation, reverse a committed sale).
Path parameters
| Name | Type | Description |
|---|---|---|
idrequired | uuid | The order id. |
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | Optional, but honored: send it to make retries of this request safe. |
Body parameters
| Name | Type | Description |
|---|---|---|
store_id | uuid | Target store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store. |
note | string | Recorded on the order's timeline. |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS -X POST "https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/cancel" \
-H "Authorization: Bearer $SALAF_API_KEY" \
-H "Idempotency-Key: e7a1…-your-uuid" \
-H "Content-Type: application/json" \
-d '{
"note": "Customer changed their mind."
}'const url = new URL("https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/cancel");
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
"Idempotency-Key": "e7a1…-your-uuid",
"Content-Type": "application/json",
},
body: JSON.stringify({
"note": "Customer changed their mind."
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const object = await response.json();import os
import requests
payload = {
"note": "Customer changed their mind.",
}
response = requests.post(
"https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/cancel",
json=payload,
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
"Idempotency-Key": "e7a1…-your-uuid",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
obj = response.json()<?php
$key = getenv('SALAF_API_KEY');
$payload = json_encode([
'note' => 'Customer changed their mind.',
]);
$ch = curl_init('https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/cancel');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key, 'Idempotency-Key: e7a1…-your-uuid', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}https://api.salafems.com/ext/v1/orders/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/cancelStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Path parameters
generating…A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.
Paste a key above to enable Send.
Responses
200The cancelled order (customer and items expanded).400The order cannot be cancelled from its current status (INVALID_STATUS_TRANSITION).401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.404Order not found.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z",
"items": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"variant_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_name": "Classic Cotton T-Shirt",
"variant_name": "Red / M",
"sku": "TSHIRT-RED-M",
"quantity": 2,
"unit_price": "1250.00",
"discount_price": "1250.00",
"subtotal": "1250.00"
}
],
"customer": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"full_name": "Rafiqul Islam",
"email": null,
"phone": "01712345678",
"type": "manual",
"status": "active"
}
}List an order's payments
/ext/v1/orders/{id}/paymentspayments:readEvery settled, pending and refunded row against the order, oldest first. Refunds are negative amounts with status refunded.
Path parameters
| Name | Type | Description |
|---|---|---|
idrequired | uuid | The order id. |
Query parameters
| Name | Type | Description |
|---|---|---|
store_id | uuid | Restrict the lookup to one store. Breadth control for company-scoped keys; on a store-scoped key it must match the key's own store. |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS "https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments" \
-H "Authorization: Bearer $SALAF_API_KEY"const url = new URL("https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments");
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
},
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const { data } = await response.json();import os
import requests
response = requests.get(
"https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments",
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
page = response.json()
rows = page["data"]<?php
$key = getenv('SALAF_API_KEY');
$ch = curl_init('https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key],
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}
$rows = $response['data'];https://api.salafems.com/ext/v1/orders/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/paymentsStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Path parameters
Query parameters
Blank fields are left out of the request.
Paste a key above to enable Send.
Responses
200The order's payment rows.401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.404Order not found.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"data": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"order_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"amount": "1250.00",
"status": "paid",
"transaction_id": "TRX9F3K2",
"payment_method": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "bKash",
"type": "mobile_banking"
},
"paid_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z"
}
]
}Record a payment against an order
/ext/v1/orders/{id}/paymentspayments:writeThe staff path verbatim: the amount is capped at the order's NET due (refunds counted), the paid/due rollup recomputes under a row lock, and the paid-in-full events fire exactly once. A provider transaction id is unique per store — recording the same TrxID twice is a 409.
Path parameters
| Name | Type | Description |
|---|---|---|
idrequired | uuid | The order id. |
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Keyrequired | string | REQUIRED. A unique value per logical request (a UUID is ideal); reuse it verbatim when retrying. A retry replays the original response with Idempotent-Replayed: true instead of running twice. |
Body parameters
| Name | Type | Description |
|---|---|---|
store_id | uuid | Target store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store. |
amountrequired | number | — |
payment_method_id | uuid | One of the store's payment methods (must be active). |
transaction_id | string | Provider transaction id. Unique per store — recording the same TrxID twice is a 409. |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS -X POST "https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments" \
-H "Authorization: Bearer $SALAF_API_KEY" \
-H "Idempotency-Key: e7a1…-your-uuid" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"transaction_id": "TRX9F3K2"
}'const url = new URL("https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments");
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
"Idempotency-Key": "e7a1…-your-uuid",
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": 500,
"transaction_id": "TRX9F3K2"
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const object = await response.json();import os
import requests
payload = {
"amount": 500,
"transaction_id": "TRX9F3K2",
}
response = requests.post(
"https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments",
json=payload,
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
"Idempotency-Key": "e7a1…-your-uuid",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
obj = response.json()<?php
$key = getenv('SALAF_API_KEY');
$payload = json_encode([
'amount' => 500,
'transaction_id' => 'TRX9F3K2',
]);
$ch = curl_init('https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key, 'Idempotency-Key: e7a1…-your-uuid', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}https://api.salafems.com/ext/v1/orders/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/paymentsStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Path parameters
generating…A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.
Paste a key above to enable Send.
Responses
201The updated order (customer and items expanded).401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.404Order not found.409Duplicate transaction id, or an idempotency key still in flight.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z",
"items": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"variant_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_name": "Classic Cotton T-Shirt",
"variant_name": "Red / M",
"sku": "TSHIRT-RED-M",
"quantity": 2,
"unit_price": "1250.00",
"discount_price": "1250.00",
"subtotal": "1250.00"
}
],
"customer": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"full_name": "Rafiqul Islam",
"email": null,
"phone": "01712345678",
"type": "manual",
"status": "active"
}
}Change an order status
/ext/v1/orders/{id}/statusorders:writeValidated against the SAME strict transition map staff use — an illegal move is refused with INVALID_STATUS_TRANSITION. Stock side effects (commit on delivery, release/reverse on cancel/return) are the staff path's, inherited.
Path parameters
| Name | Type | Description |
|---|---|---|
idrequired | uuid | The order id. |
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | Optional, but honored: send it to make retries of this request safe. |
Body parameters
| Name | Type | Description |
|---|---|---|
store_id | uuid | Target store. REQUIRED with a company-scoped key; with a store-scoped key it may only repeat the key's own store. |
statusrequired | string | —pendingfollow_upconfirmedprocessingready_to_shipin_transitdeliveredreturnedfailed_deliverycancelledholdspam |
note | string | Recorded on the order's timeline. |
Request
export SALAF_API_KEY="salaf_sk_YOUR_API_KEY"
curl -sS -X PATCH "https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/status" \
-H "Authorization: Bearer $SALAF_API_KEY" \
-H "Idempotency-Key: e7a1…-your-uuid" \
-H "Content-Type: application/json" \
-d '{
"status": "confirmed",
"note": "Confirmed over the phone."
}'const url = new URL("https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/status");
const response = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.SALAF_API_KEY}`,
"Idempotency-Key": "e7a1…-your-uuid",
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "confirmed",
"note": "Confirmed over the phone."
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message} [${error.request_id}]`);
}
const object = await response.json();import os
import requests
payload = {
"status": "confirmed",
"note": "Confirmed over the phone.",
}
response = requests.patch(
"https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/status",
json=payload,
headers={
"Authorization": f"Bearer {os.environ['SALAF_API_KEY']}",
"Idempotency-Key": "e7a1…-your-uuid",
},
timeout=30,
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
obj = response.json()<?php
$key = getenv('SALAF_API_KEY');
$payload = json_encode([
'status' => 'confirmed',
'note' => 'Confirmed over the phone.',
]);
$ch = curl_init('https://api.salafems.com/ext/v1/orders/REPLACE_WITH_ID/status');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key, 'Idempotency-Key: e7a1…-your-uuid', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$response = json_decode($body, true);
if ($status >= 400) {
throw new RuntimeException("{$response['error']['code']}: {$response['error']['message']}");
}https://api.salafems.com/ext/v1/orders/5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001/statusStored in this browser only and never sent anywhere except to the API host you picked above — these pages are static files with no server behind them.
Path parameters
generating…A new value is generated whenever you edit this request, and kept while you do not — so sending twice without changing anything is a real retry and comes back Idempotent-Replayed: true instead of writing again.
Paste a key above to enable Send.
Responses
200The updated order (customer and items expanded).400The transition map forbids this move (INVALID_STATUS_TRANSITION).401Missing/invalid API key, revoked or expired key, plan without API access, or a dead store.403The key's scopes do not cover this endpoint.404Order not found.422Validation failed —error.fieldsmaps each offending field to its messages.429Rate limit exceeded for this key. HonorRetry-Afterand theX-RateLimit-*headers.
Every failure uses the one error envelope — Errors lists each code and what to do with it.
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"store_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"number": "ORD-20260811-1042",
"source": "api",
"channel": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"name": "Facebook",
"slug": "facebook",
"type": "facebook"
},
"customer_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"location_id": null,
"status": "pending",
"payment_status": "unpaid",
"fulfillment_status": "unfulfilled",
"subtotal": "1250.00",
"discount_amount": "1250.00",
"shipping_charge": "1250.00",
"tax_amount": "1250.00",
"total_amount": "1250.00",
"paid_amount": "1250.00",
"due_amount": "1250.00",
"refunded_amount": "1250.00",
"shipping_address": null,
"coupon": null,
"custom_fields": null,
"note": null,
"delivered_at": "2026-08-11T10:00:00.000Z",
"created_at": "2026-08-11T10:00:00.000Z",
"updated_at": "2026-08-11T10:00:00.000Z",
"items": [
{
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"variant_id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"product_name": "Classic Cotton T-Shirt",
"variant_name": "Red / M",
"sku": "TSHIRT-RED-M",
"quantity": 2,
"unit_price": "1250.00",
"discount_price": "1250.00",
"subtotal": "1250.00"
}
],
"customer": {
"id": "5f7d2f60-0d1c-4b3a-9a68-6f4d21f6a001",
"full_name": "Rafiqul Islam",
"email": null,
"phone": "01712345678",
"type": "manual",
"status": "active"
}
}