Docs
Endpoints · Orders

POST/v1/orders

Record a paid order; its print files are queued.

What it does

Every line must name a design this organisation issued: a handoff, or a design with its sig. Nothing is recorded when any line is wrong. Each design is frozen as bought and its print file queued; the order.created event fires once. Posting the same order_ref again answers 200 with the existing order and "created": false: a retry never prints twice. Where an order came from is PrintWisp's to say: an order posted here is always recorded with source api, whatever the body says. A test key (sk_test_…) records a test order (ord_t…, oln_t…): kept apart from real orders, never charged, removed with Delete test data. A live key records a real order only once the organisation is live and, for a store key, only while the store is Open.

Send the key: Authorization: Bearer sk_test_… or sk_live_…. Any method but GET or POST answers 405 method_not_allowed: “GET or POST.”

curl -X POST "https://printwisp.com/v1/orders" \
  -H "Authorization: Bearer $PRINTWISP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"order_ref":"WEB-10021","placed_at":"2026-10-05T04:31:00Z","currency":"AUD","customer":{"name":"Jane Citizen","email":"jane@example.com"},"lines":[{"line_ref":"1","qty":1,"amount":"89.00","design":"dsn_5c2a9e01b7d4f638","sig":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}]}'

# 201
{
  "order": "ord_7d1e4b9a2c6f0583",
  "order_ref": "WEB-10021",
  "created": true,
  "lines": [
    {
      "line": "oln_41aa8c3e9d2b7f16",
      "line_ref": "1",
      "design": "dsn_5c2a9e01b7d4f638",
      "qty": 1,
      "print_status": "queued",
      "components": [],
      "fields": []
    }
  ]
}
<?php
$ch = curl_init('https://printwisp.com/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode([
      'order_ref' => 'WEB-10021',
      'placed_at' => '2026-10-05T04:31:00Z',
      'currency' => 'AUD',
      'customer' => ['name' => 'Jane Citizen', 'email' => 'jane@example.com'],
      'lines' => [
        [
          'line_ref' => '1',
          'qty' => 1,
          'amount' => '89.00',
          'design' => 'dsn_5c2a9e01b7d4f638',
          'sig' => 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
        ]
      ]
    ]),
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . getenv('PRINTWISP_KEY'), 'Content-Type: application/json'],
]);
$answer = json_decode(curl_exec($ch), true);
// Node (your server). The key never reaches a browser.
const r = await fetch('https://printwisp.com/v1/orders', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + process.env.PRINTWISP_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    order_ref: 'WEB-10021',
    placed_at: '2026-10-05T04:31:00Z',
    currency: 'AUD',
    customer: { name: 'Jane Citizen', email: 'jane@example.com' },
    lines: [
      {
        line_ref: '1',
        qty: 1,
        amount: '89.00',
        design: 'dsn_5c2a9e01b7d4f638',
        sig: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
      }
    ]
  }),
});
const answer = await r.json();
import os, requests

r = requests.post(
    "https://printwisp.com/v1/orders",
    json={
      "order_ref": "WEB-10021",
      "placed_at": "2026-10-05T04:31:00Z",
      "currency": "AUD",
      "customer": { "name": "Jane Citizen", "email": "jane@example.com" },
      "lines": [
        {
          "line_ref": "1",
          "qty": 1,
          "amount": "89.00",
          "design": "dsn_5c2a9e01b7d4f638",
          "sig": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
        }
      ]
    },
    headers={"Authorization": f"Bearer {os.environ['PRINTWISP_KEY']}"},
)
answer = r.json()

Parameters

ParameterTypeDescription
order_ref requiredstring ≤ 100Your own order number. Unique within a store: each store numbers its own orders, and the orders that belong to no store share one numbering.
lines[] requiredarray 1–200Each with handoff, design, sig, line_ref, qty, amount, currency, extras, sku, variant, unit_price: see below.
placed_atdate-timeWhen the shopper paid; now when missing.
testbooleantrue records a test order (no fee, never counted) even with a live key -- for an order your platform marks as a test. It never turns a test key's order into a real one.
customerobject{name, email}; other keys are dropped.
currencystringThe default for every line's amount.
fxobjectThe exchange rate your platform used for amounts in a currency other than your catalogue's: {base, currency, rate, stale} -- how many currency one base (your catalogue's currency) is; stale: true for a last known rate rather than the day's. PrintWisp converts those lines at it for its fee when it is believable (within 20% of the store's own rate), else at the store's own rate. Optional: an order is never refused for it.
connectionstringFor an account key: a connected store's id (int_…). A store's own key needs none.
lines[].handoffstringA hand-off (hnd_…) from a cart PrintWisp filled. Give handoff, or design + sig.
lines[].designstringThe design (dsn_…), with its sig, when your own cart held it.
lines[].sigstringThe design's signature, as the designer handed it to your page.
lines[].line_refstring ≤ 100Your id for the line; l1, l2 … when missing.
lines[].qtyinteger 1–100000
lines[].amountnumber | stringWhat the shopper paid for the line, a number 0 or more: the base of PrintWisp's per-order fee.
lines[].currencystring3 letters; the order's currency when missing.
lines[].extrasarrayReady lines: the codes of the extras ticked (a product's extras, e.g. ["gift-wrap"]); each adds its price when PrintWisp prices the line.
lines[].skustring ≤ 160Your SKU, for a design + sig line. On its own (no handoff, no design): a READY product's variant SKU -- a line with nothing to print, priced by PrintWisp from the store unless you send unit_price.
lines[].variantstring ≤ 100A ready product's variant code, instead of sku.
lines[].unit_pricenumber | stringReady lines only: your own price per item, when your shop priced it.

Response

201 application/json. Created.

{
  "order": "ord_7d1e4b9a2c6f0583",
  "order_ref": "WEB-10021",
  "created": true,
  "lines": [
    {
      "line": "oln_41aa8c3e9d2b7f16",
      "line_ref": "1",
      "design": "dsn_5c2a9e01b7d4f638",
      "qty": 1,
      "print_status": "queued",
      "components": [],
      "fields": []
    }
  ]
}

Errors

HTTPerrormessage
401unauthorizedSend Authorization: Bearer <secret key>. Keys are made in PrintWisp, on your store's Overview → API keys.
405method_not_allowedGET or POST.
409organisation_not_liveYour organisation is not live yet: record orders with a test key (sk_test_...) until it is.
409store_not_openThis store is not Open: record its orders with a test key (sk_test_...) until it is.
422invalid_orderorder_ref is required: the shop's order number.
422invalid_orderAn order needs at least one line.
422invalid_orderAt most 200 lines in one order.
422invalid_orderLine 1: qty must be a positive whole number.
422invalid_orderLine 1: amount must be a number, 0 or more.
422invalid_orderLine 1: no such hand-off hnd_6e4b1a9c3d7f2058.
422invalid_orderLine 1: hand-off hnd_6e4b1a9c3d7f2058 never reached a cart.
422invalid_orderLine 1: design dsn_5c2a9e01b7d4f638 is not one this shop issued, or its signature is wrong.
422invalid_orderLine 1: name a handoff, or a design with its sig.
422invalid_orderNo such connection: int_2b7c9e0d4a1f6835.
{ "error": "invalid_order", "message": "order_ref is required: the shop's order number." }

← Every routeQuickstart →