Docs
Getting started

10-minute quickstart

Product → designer → a first test order, in six calls. Every call on this page is a real route.

Quickstart · 10 minutes

What you need

  1. A PrintWisp organisation with a product that has at least one size and a price (Catalogue).
  2. A Custom Website (API) store that offers the product: add it on the store's Products tab.
  3. A test key, made on the store's Overview → API keys. Copy it when it is shown: it is shown once.
  4. Use the test key: the order you place in step 4 is then a test order. It is kept apart from real orders, is never charged, and goes with Delete test data.
  5. A page of yours served over https at an origin you own, say https://shop.example: the designer may be framed only by the origin the session names.
Put the key in the environmentCopy
export PRINTWISP_KEY=sk_test_…   # the test key you copied, shown once in the portal

1 · Pick a size code

Ask for the store's catalogue. Each size has a code, for example canvas-40x30: that code is what you send as profile. The store's Products tab shows the same codes.

curl "https://printwisp.com/v1/products" \
  -H "Authorization: Bearer $PRINTWISP_KEY"

# 200
{
  "store": { "code": "web-au", "name": "Harbour Prints AU", "currency": "AUD" },
  "products": [
    {
      "code": "canvas",
      "name": "Photo canvas",
      "sizes": [
        {
          "code": "canvas-40x30",
          "name": "40 × 30 cm",
          "price": "89.00",
          "currency": "AUD",
          "packed_kg": 2.431,
          "packed_kg_extra": 2.431
        },
        {
          "code": "canvas-60x40",
          "name": "60 × 40 cm",
          "price": "149.00",
          "currency": "AUD",
          "packed_kg": 4.576,
          "packed_kg_extra": 4.576
        }
      ],
      "options": [
        {
          "code": "edge",
          "name": "Edge",
          "values": [
            { "code": "mirror", "name": "Mirrored", "price_delta": "0.00" },
            { "code": "white", "name": "White", "price_delta": "0.00" }
          ]
        }
      ]
    }
  ]
}
<?php
$ch = curl_init('https://printwisp.com/v1/products');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    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/products', {
  headers: { Authorization: 'Bearer ' + process.env.PRINTWISP_KEY },
});
const answer = await r.json();
import os, requests

r = requests.get(
    "https://printwisp.com/v1/products",
    headers={"Authorization": f"Bearer {os.environ['PRINTWISP_KEY']}"},
)
answer = r.json()

2 · Create a session

From your server, with the key. origin is the scheme and host of the page that will show the designer; the link then lasts 24 hours and may be framed only there. customer_ref is your own customer id, so the same shopper can reopen the design later. metadata is anything you want back with Add to cart, as {{metadata.<key>}} in the store's JSON: a Custom Website passes its own cart id, {"cart": "c_8f3c41"} (see Custom Website: Add to cart). Every parameter →

curl -X POST "https://printwisp.com/v1/sessions" \
  -H "Authorization: Bearer $PRINTWISP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"profile":"canvas-40x30","origin":"https://shop.example","customer_ref":"cust_8812","qty":1}'

# 201
{
  "session": "dss_3f9c1a7e5b2d8c40",
  "token": "7b1e5c0a9d3f2e8b4c6a1d7e9f0b3c5a2e4d6f8a1b3c5e7d",
  "url": "https://printwisp.com/d/7b1e5c0a9d3f2e8b4c6a1d7e9f0b3c5a2e4d6f8a1b3c5e7d",
  "expires_at": "2026-10-06T06:12:00+00:00",
  "origin": "https://shop.example",
  "product": "canvas",
  "profile": "prf_9a21c4d8e0f17b35",
  "design": null
}
<?php
$ch = curl_init('https://printwisp.com/v1/sessions');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode([
      'profile' => 'canvas-40x30',
      'origin' => 'https://shop.example',
      'customer_ref' => 'cust_8812',
      'qty' => 1
    ]),
    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/sessions', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + process.env.PRINTWISP_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ profile: 'canvas-40x30', origin: 'https://shop.example', customer_ref: 'cust_8812', qty: 1 }),
});
const answer = await r.json();
import os, requests

r = requests.post(
    "https://printwisp.com/v1/sessions",
    json={
      "profile": "canvas-40x30",
      "origin": "https://shop.example",
      "customer_ref": "cust_8812",
      "qty": 1
    },
    headers={"Authorization": f"Bearer {os.environ['PRINTWISP_KEY']}"},
)
answer = r.json()

3 · Open the designer on your page

Load embed.js and open the url. The overlay shows a spinner until the designer says ready. Make something and press Add to cart: the designer saves, then sends your page the add-to-cart message with design, sig, qty and options (the built-in payload, when the store has no Add-to-cart connector; with one, see Messages).

Before you go live: keep the designer through a refresh, so a shopper who reloads the page comes back to their work.

Your pageCopy
<!doctype html>
<meta charset="utf-8">
<title>Design yours</title>
<script src="https://printwisp.com/studio/js/embed.js"></script>
<button id="design">Design yours</button>
<pre id="out"></pre>
<script>
document.getElementById('design').onclick = async function () {
  // your server made the session with the key (step 2) and hands the page its url
  const s = await (await fetch('/printwisp/session')).json();
  const d = StudioEmbed.open({ url: s.url, title: 'Photo canvas 40 × 30 cm' });
  d.on('add-to-cart', function (p) {
    document.getElementById('out').textContent = JSON.stringify(p, null, 2);   // design, sig, qty, options
    d.close();
  });
};
</script>

4 · Record the order

Pretend the shopper paid: post the order once with your own number as order_ref and the line's design + sig from step 3, with amount, what the shopper paid for the line. You get the order id and a line id (oln_t… for a test order) with print_status: "queued". Post the same order_ref again and you get 200 with "created": false: nothing is printed twice. An order_ref is unique within the store.

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()

5 · Fetch the print file

Ask GET /v1/print?line= until print_status is ready (a canvas takes seconds; a book longer), then download the url within 10 minutes. In production you would not poll: the design.print_ready webhook brings the same link. Webhooks →

curl "https://printwisp.com/v1/print?line=oln_41aa8c3e9d2b7f16" \
  -H "Authorization: Bearer $PRINTWISP_KEY"

# 200
{
  "line": "oln_41aa8c3e9d2b7f16",
  "order_ref": "WEB-10021",
  "design": "dsn_5c2a9e01b7d4f638",
  "print_status": "ready",
  "components": [],
  "fields": [],
  "url": "https://printwisp.com/v1/file?l=oln_41aa8c3e9d2b7f16&e=1759640400&s=9f2c4e1a7b3d8f05c6e2a9d1b4f7c3e8a5d2b9f6c1e4a7d3b8f5c2e9a6d1b4f7",
  "expires_at": "2026-10-05T04:51:00+00:00"
}

curl "https://printwisp.com/v1/file?l=oln_41aa8c3e9d2b7f16&e=1759640400&s=9f2c4e1a7b3d8f05c6e2a9d1b4f7c3e8a5d2b9f6c1e4a7d3b8f5c2e9a6d1b4f7" \
  -o WEB-10021-1.pdf
<?php
$ch = curl_init('https://printwisp.com/v1/print?line=oln_41aa8c3e9d2b7f16');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    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/print?line=oln_41aa8c3e9d2b7f16', {
  headers: { Authorization: 'Bearer ' + process.env.PRINTWISP_KEY },
});
const answer = await r.json();
import os, requests

r = requests.get(
    "https://printwisp.com/v1/print?line=oln_41aa8c3e9d2b7f16",
    headers={"Authorization": f"Bearer {os.environ['PRINTWISP_KEY']}"},
)
answer = r.json()

6 · See it in the portal

Turn the Test mode switch on: the order is under the store's Orders, with its print file. Delete test data removes it. From here: add a webhook for order.created and design.print_ready under Organisation → API keys & webhooks, move the five steps into your real product page, and use a live key when the store is Open. Endpoint reference →