Orders from a Custom Marketplace
Send orders from your website or marketplace to the order address of its Custom Marketplace connection.
Overview
A Custom Marketplace connection has its own order address. Your website or marketplace sends each order there, signed with the connection's secret, and O2M saves it like any other order.
Address
Each Custom Marketplace connection has its own address, shown on its Orders tab in O2M. It needs no API key: the connection's signing secret (or its order key) proves the request is yours.
POST https://api.o2m.in/api/custom-marketplace/inbound/<connection>/ordersSign each request
X-O2M-Timestamp: 1790000000
X-O2M-Signature: 5f2c9a…- 1Take the time in Unix seconds as the timestamp.
- 2Join the timestamp, a full stop and the raw body you are about to send:
timestamp.body. - 3Compute the HMAC-SHA256 of that text with the connection's signing secret, in hexadecimal. That is the signature.
- 4Send both headers. O2M refuses a request whose timestamp is more than 5 minutes from its own clock.
import crypto from "node:crypto";
const body = JSON.stringify(order);
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHmac("sha256", process.env.O2M_SIGNING_SECRET).update(`${timestamp}.${body}`).digest("hex");
await fetch(process.env.O2M_ORDER_URL, {
method: "POST",
headers: { "Content-Type": "application/json", "X-O2M-Timestamp": timestamp, "X-O2M-Signature": signature },
body,
});Good to know If the connection is set to check orders by key instead, send the key in an X-O2M-Key header and no signature.
Body
In O2M's order format, the body is the same as Create an order, without channel: the connection is the channel. If the connection reads your own format, send your order as it is; the connection's mapping finds each value.
{
"order_id": "W-1001",
"ordered_at": "2026-11-04T10:00:00+05:30",
"payment_method": "prepaid",
"customer": {
"name": "Ravi",
"email": "ravi@example.com"
},
"shipping_address": {
"name": "Ravi",
"phone": "9800000000",
"address1": "1 MG Road",
"city": "Pune",
"state": "Maharashtra",
"pincode": "411001"
},
"lines": [
{
"sku": "TEE-S",
"quantity": 1,
"unit_price": 499
}
]
}To cancel an order you sent before, send it again with "status": "cancelled" (in your own format, with a status value the connection lists as cancelled). It is cancelled only before it ships.
Answers
| Status | Body | Meaning |
|---|---|---|
201 | { "created": true, "order": { … } } | The order is saved and its stock reserved. |
200 | { "created": false, "order": { … } } | An order with this order_id was already there; nothing changed. |
200 | { "duplicate": true } | This exact body was handled before; nothing changed. |
200 | { "cancelled": true, "order": { … } } | The order was cancelled. |
Errors
| Status | Code | Message | What to do |
|---|---|---|---|
401 | UNAUTHORISED | Not authorised | Check the signing secret, the timestamp and that you signed the raw body exactly as sent. |
409 | NOT_ACTIVE | <name> is not turned on in O2M | Turn the connection on in O2M. |
403 | LOCKED | Custom Marketplace is not enabled for this company | Ask your O2M account manager. |
400 | BAD_JSON | The body is not JSON | Send a JSON body. |
422 | UNKNOWN_SKU | No item has SKU … | Use SKUs that exist in O2M. A refused order can be sent again once fixed. |
422 | order_id is required: your own number for this order | Every order needs your own order number. | |
429 | Too many orders at once. Slow down and retry. | At most 120 requests a minute per connection; wait and retry. |