Placing an order
An account order is your business buying stock from Merchable at production cost — the same thing as ordering from the Order tab of a design in the web app. The API does not process consumer (storefront) payments.
Request
POST /v1/orders
Authorization: Bearer mchp_…
Content-Type: application/json
Idempotency-Key: po-2026-0042
{
"external_reference": "PO-2026-0042",
"customer": { "first_name": "Sam", "last_name": "Lee", "email": "sam@acme.example",
"phone": "+61 400 000 000", "company": "Acme Climbing" },
"items": [
{ "design_id": "d8Xk2mQz", "variant_id": "7c9e6679-…", "quantity": 12 },
{ "design_id": "d8Xk2mQz", "colour": "Black", "size": "L", "quantity": 4 },
{ "design_id": "d8Xk2mQz", "colour": "Black", "size": "L", "quantity": 1,
"personalisation": [ { "zone_id": "4d1c8f2a-…", "text": "SMITH" } ] }
],
"delivery": {
"type": "delivery",
"shipping_method": "standard",
"address": {
"company": "Acme Climbing",
"line1": "1 Example St", "line2": null,
"city": "Melbourne", "state": "VIC", "postcode": "3000", "country": "AU"
}
},
"payment": { "method": "default_card" },
"notifications": { "suppress_business_emails": true },
"metadata": { "cost_centre": "events" }
}
| Field | Notes |
|---|---|
customer |
Optional. Who the order is for: first_name, last_name, email (required), phone, company. Matched by email within your account — an existing customer is reused, otherwise one is created. Returned on reads and in webhook payloads |
items[].design_id |
Public design id from GET /v1/designs |
items[].variant_id |
Variant UUID from GET /v1/designs/{id}; or identify the variant with colour and/or size |
items[].quantity |
Positive integer. Minimum order quantities per product are enforced |
items[].personalisation |
Optional. Text for the design's personalisation zones, one { "zone_id", "text" } per zone — see Personalisation |
delivery.type |
delivery (default, needs address) or pickup |
delivery.shipping_method |
standard (default) or express |
payment.method |
default_card (default and only method) — charges the account's default saved card after any account credit |
notifications |
Per-order email suppression — see Notification suppression |
webhook_url |
Where to POST every lifecycle event for this order — see Webhooks |
webhook_secret |
Optional HMAC secret for those deliveries; omit to sign with the account signing secret |
metadata |
Up to 8 KB of your own JSON, echoed back on reads and in webhooks |
Response
201 Created with the order. data.id is the short order reference
(A1B2C3D4-E5F6) used everywhere else in the API; data.uuid is the full id.
Quotes
POST /v1/orders/quote accepts the same body and returns the priced lines and
totals (products, shipping, payment processing fee, tax) without placing anything.
Quotes also list any minimum-order-quantity shortfalls.
Idempotency
POST /v1/orders requires an Idempotency-Key header (1–255 characters, unique
per order attempt — a purchase-order number works well). For 24 hours:
- the same key with the same body replays the original response
(
Idempotent-Replayed: true) without placing another order; - the same key with a different body returns
409.
Retry safely on network errors and 5xx responses by resending with the same key.
Payment
- The API charges the default saved card in Billing → Payment methods.
Account credit is applied first. Without a saved card the request fails with
422asking you to add a card; bank transfer is only available in the web app. - Payment failures return
422with the reason indetail. Nothing is created.
After placement
Orders start pending and are reviewed by Merchable before production. Follow
progress with GET /v1/orders/{id}, GET /v1/orders/{id}/events, and
GET /v1/orders/{id}/shipments, or set webhook_url when placing the order to
have every event pushed to you — see Webhooks.
POST /v1/orders/{id}/cancel cancels an order that has not yet entered production
(pending, pre_order or processing); later cancellations return 409.
Personalisation
Designs with personalisation zones (customer text such as a name on the back)
list them under personalisation_zones on GET /v1/designs/{id}. Fill a zone
on an order line with personalisation:
{ "design_id": "d8Xk2mQz", "colour": "Black", "size": "L", "quantity": 1,
"personalisation": [ { "zone_id": "4d1c8f2a-…", "text": "SMITH" } ] }
A personalised line is always its own order line (one line per distinct text),
every required zone must be filled, and text must be within the zone's limits;
failures are 422s naming the field. A line without personalisation orders
the design with its zones empty. Order items echo the text as personalisation.
The full rules and error list are in Designs → Personalisation zones.
Notification suppression
By default Merchable emails the account (and any customer) at each milestone. If
your integration handles communication, set notifications.suppress_business_emails
and/or suppress_customer_emails on the order. Suppression applies to every later
milestone of that order (shipped, delivered, completed), not just placement.
Merchable's internal operations notifications are never suppressed.
Finding variants, prices and stock
GET /v1/designs/{id} returns everything an order line needs — the variant
id to use as variant_id (or the colour/size names), each variant's
purchase price and stock status, the design's purchase_breakdown and the
product's quantity_tiers. See Designs. Quotes apply quantity tiers to the
order's summed quantity for you.
Estimating shipping
POST /v1/shipping/estimate prices shipping for a set of items to a destination
exactly as an order would, without creating anything:
{
"items": [{ "design_id": "d8Xk2mQz", "colour": "Black", "size": "L", "quantity": 12 }],
"destination": { "country": "AU", "state": "VIC", "postcode": "3000" },
"shipping_method": "express"
}
{
"data": {
"currency": "AUD",
"origin_country": "AU",
"destination": { "country": "AU", "state": "VIC", "postcode": "3000", "city": null },
"international": false,
"requires_shipping": true,
"items": [{ "design_id": "d8Xk2mQz", "variant_id": "7c9e…", "quantity": 12, "requires_shipping": true }],
"methods": [
{ "method": "express",
"amount_ex_tax": { "amount": "18.18", "currency": "AUD" },
"tax": { "amount": "1.82", "currency": "AUD" },
"total": { "amount": "20.00", "currency": "AUD" },
"delivery_estimate": "approx 4-7 business days" }
]
}
}
Omit shipping_method to get every method available to the destination. Only
destination.country is required; include state and postcode when you have them.
Items with included shipping or virtual products (gift cards) are priced accordingly.