POST/v1/sessions
A design link for one shopper, to open on your page.
What it does
Your server asks, with the key, when a shopper presses Design yours; the browser only ever sees the answer. The answer is an ordinary design link that names one size and at most one design (the shopper can reach nothing else), may be shown in a frame by origin and by no other site, and lasts hours: 24 by default when framed, 30 days when not framed (then it is a link to email or open on its own, and may not be framed at all). The store is the key's store; for a key with no store, the connected store named in connection, else the Your website store whose site address has the host of origin. A store key opens only the products and sizes its store offers. A Paused store opens no designers; a Draft store opens them with a test key, or with its preview_key.
Send the key: Authorization: Bearer sk_test_… or sk_live_…. Any method but POST answers 405 method_not_allowed: “POST only.”
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()Parameters
| Parameter | Type | Description |
|---|---|---|
profile | string | The size to design on: its code (canvas-40x30) or its id (prf_…). Give profile or product; when both are given they must agree. |
product | string | The product's code (or id). Enough on its own when the product has one size or a default size; otherwise the answer is 422 invalid_session listing the sizes to choose from. |
origin | string | The web address of the page that shows the designer: scheme and host, no path (https://shop.example). The link may be framed there only, for 24 hours by default. Without it the link cannot be framed and lasts 30 days. |
customer_ref | string ≤ 190 | Your own id for the shopper. Lets the same shopper reopen the design later with design. A signed-in customer's designs are listed under it (GET /v1/projects). |
signed_in | boolean | Is customer_ref a signed-in customer (true) or a guest (false)? Read from the ref when not sent: wp-user-, shopify-customer- are signed in. A guest gets the designer's ✕ "Keep this design?" sheet; a signed-in shopper is never asked (it is in My designs). |
customer_email | string | A signed-in customer's VERIFIED email. With link: answers link_email_is_customers, and when it is the link's address the design joins their account (proof by mailbox). |
link | string | An emailed link's token (64 hex), which your page read from #pw_link= (embed.js link()): opens the design that link names. A browser that did not make it is offered "Make my own copy"; a signed-in customer under another address, "Add to My designs" (a copy). 404 link_not_found, or 410 link_gone once it was replaced, ordered or deleted. |
page | string | Your product page showing the designer (on origin). A link emailed from this design then opens there, with #pw_link=<token>, rather than on PrintWisp's own designer page. |
customer_name | string ≤ 190 | The shopper's name, kept with the session. |
qty | integer 1–100000 | The quantity the designer starts with. |
hours | integer 1–720 | How long the link lasts. Default 24 when framed, 30 days when not. |
design | string | Reopen a design (dsn_…): it must have been made under the same customer_ref, on the same size, and not yet ordered (an ordered design is frozen as printed). Or a Design's code from GET /v1/designs: the designer opens with every page already set, as this store offers that Design on the product; a code it does not offer is ignored. |
connection | string | For a key with no store: the connected store (int_…) the designer's Add to cart should go to. A store's own key needs none. |
price | number | string | The unit price your page shows, a number like 89.00. When the store's prices come from both your page and PrintWisp and must agree, the designer refuses Add to cart while PrintWisp's price differs from it. |
preview_key | string | While the store is a Draft: the store's preview key, for a live key (a test key needs none). A Paused store opens no designers. |
metadata | object | Anything your server wants back with Add to cart: up to 20 keys (letters, digits, _; up to 40 characters), each a string (up to 500 characters), number, true/false or null. Kept with the session and sent in the Add-to-cart JSON as {{metadata.<key>}}. A Custom Website passes its own cart id here ({"cart": "c_8f3c41"}), so PrintWisp's server-to-server Add to cart knows whose cart it is. |
{"product":"canvas"} // one-size product, a link to email, 30 days
{"profile":"canvas-40x30","origin":"https://shop.example"} // framed on your page, 24 hours
{"profile":"canvas-40x30","origin":"https://shop.example","customer_ref":"cust_8812","design":"dsn_5c2a9e01b7d4f638"} // the same shopper reopens a design
{"link":"9f2c…64 hex…e01b","origin":"https://shop.example","customer_ref":"wp-user-12","signed_in":true,"customer_email":"sarah.t@gmail.com"} // an emailed link, opened on your page (answers link_email_is_customers)Response
201 application/json. The link and what it names. Give your page the url; keep session to tie the design to your own records. profile is the size's id and product the product's code; design is the reopened design or null.
{
"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
}Errors
| HTTP | error | message |
|---|---|---|
| 401 | unauthorized | Send Authorization: Bearer <secret key>. Keys are made in PrintWisp, on your store's Overview → API keys. |
| 403 | store_draft | This store is a Draft: send its preview_key to open designers until it is Open. |
| 404 | product_not_found | No product with that code. |
| 404 | profile_not_found | No size with that id. |
| 404 | design_not_found | No design with that id. |
| 404 | design_not_found | No design with that id for this customer. |
| 404 | link_not_found | No such link here. |
| 405 | method_not_allowed | POST only. |
| 410 | link_gone | A newer link was sent for this design. |
| 409 | store_paused | This store is paused: it opens no designers until it is set to Open or Draft. |
| 409 | account_paused | This PrintWisp account is paused: the free trial ended. Add a card under Organisation → Plan & billing to switch it back on. Nothing was deleted. |
| 422 | invalid_session | That size is not one of that product's. |
| 422 | invalid_session | This product comes in 2 sizes. Say which with profile: prf_9a21c4d8e0f17b35, prf_0b3e7f2a9c4d5618. |
| 422 | invalid_session | This product has no size to design on yet. |
| 422 | invalid_session | Say which product (its code) or which size (profile). |
| 422 | invalid_session | origin must be the web address of the page that shows the designer, like https://shop.example -- scheme and host, no path. |
| 422 | invalid_session | qty must be a whole number, 1 or more. |
| 422 | invalid_session | hours must be a whole number from 1 to 720. |
| 422 | invalid_session | That design was made on another size. |
| 422 | invalid_session | That design has been ordered and cannot be changed. Start a new one. |
| 422 | invalid_session | price is the unit price your page shows, a number like 89.00. |
| 422 | invalid_session | No such connection: int_2b7c9e0d4a1f6835. |
| 422 | product_not_offered | The store web-au does not offer that product. Add it on the Products tab of that store. |
| 422 | size_not_offered | The store web-au does not offer that size. Offer it on the Products tab of that store. |
| 422 | ready_product | That product is ready to ship: it has no designer. Quote it with POST /v1/quote {sku, qty} and order it with lines [{sku, qty}]. |