⚙️ Custom site (API)

Server-side REST reference for hand-built sites: resolve store SKUs to 3D products, read saved configurations back, and create / track orders from your backend.

Looking for the front-end part (embed snippet, choice screen, PwaiEmbed.onAddToCart(fn) payload)? That is covered in the Custom site — full integration guide. This page documents the HTTP endpoints behind it.

Base URL & conventions

https://printwearai.com/api

Keys & authentication

Your workspace (Integration page) exposes two keys:

KeyPrefixUsage
Public embed keyfk_…Identifies your workspace. Safe to expose in HTML. Sent as the X-Embed-Key header (recommended) or the embed_key query parameter.
API secretsk_…Confidential — keep it server-side only, never ship it to a browser. Rotate it anytime from your workspace settings. It is not required by the endpoints below, which authenticate with the embed key and end-customer session tokens.
Prefer the X-Embed-Key header over the query string: keys in query strings leak into Referer headers, proxy logs and browser history. When you use the query parameter, the API adds an X-Embed-Key-Warning response header to remind you.

End-customer sessions

The /configs and /customer/orders endpoints act on behalf of a logged-in end customer of your workspace. A session is created by:

Both set a pwai_session cookie. From a server, keep the cookie jar, or replay the session token as a bearer header:

Authorization: Bearer <session-token>   # the pwai_session cookie value

GET /api/resolve — SKU → 3D product

Maps an external identifier (your store's SKU, product id or product page URL) to the linked 3D product. This is the endpoint embed.js and the SDK call during auto-detection; you can call it from your server too.

Query parameterRole
embed_keyRequired. Your fk_… key
external_skuExternal SKU / reference
external_idExternal product id
external_urlProduct page URL (fallback, matched on the URL path)

Lookup order: imported external products by SKU → by external id → the 3D product's own SKU / reference field → external product URL. Provide at least one identifier.

curl "https://printwearai.com/api/resolve?embed_key=fk_YOUR_KEY&external_sku=JERSEY-001"
{
  "product_slug": "home-jersey",
  "product_name": "Home jersey",
  "glb_path":     "/models/home-jersey.glb",
  "thumbnail":    "https://…/thumb.jpg",
  "price_base":   29.9,
  "zones_config": { … },
  "sizes":        ["S","M","L","XL"],
  "external_sku": "JERSEY-001",
  "external_id":  "123456"
}

Errors: 400 missing embed_key · 403 invalid key · 404 no linked product found.

/api/configs — saved configurations

Auth: end-customer session. A config_id received in the widget's add_to_cart payload refers to one of these records.

EndpointRole
GET /api/configsList the customer's configs (most recent first), each with product_name and product_slug. Heavy fields (config_json, patron_svg) are omitted from the list.
GET /api/configs/{id}Full record, including the decoded config_json.
POST /api/configsCreate. Body: product_id + config_json (required), optional name, patron_svg, thumbnail_b64. Returns { "id": … } and fires the config.saved webhook.
PUT /api/configs/{id}Update any of name, config_json, patron_svg, thumbnail_b64. Returns { "ok": true }.
DELETE /api/configs/{id}Delete. Returns { "ok": true }.
curl -X POST "https://printwearai.com/api/configs" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "product_id": 12, "name": "Home kit v2",
        "config_json": { "zones": { "front": { "color": "#0044AA" } } } }'
# → { "id": 341 }

/api/customer/orders — orders

Auth: end-customer session plus tenant context (X-Embed-Key header).

POST /api/customer/orders — create an order

Body fieldRole
itemsRequired, non-empty array. Each item: product_id (required), quantity, and optional config_id, sizes_breakdown (e.g. {"M":3,"L":2}), patron_svg, config_snapshot
shipping_addrObject (free-form address)
billing_addrObject — defaults to shipping_addr
notesFree text

Prices are computed server-side from the product's base price and quantity tiers — any client-sent price is ignored. Totals (total_ht, total_ttc) use the workspace VAT rate. The response is the full order row; the order.created webhook fires for your backend.

Initial status depends on the merchant's payment setup:

Errors: 400 no items · 402 monthly order quota of the workspace plan reached.

curl -X POST "https://printwearai.com/api/customer/orders" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "X-Embed-Key: fk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "product_id": 12, "quantity": 14, "config_id": 341,
        "sizes_breakdown": { "S": 2, "M": 6, "L": 4, "XL": 2 } }
    ],
    "shipping_addr": { "name": "FC Example", "line1": "1 Stadium Rd", "city": "Lyon", "zip": "69000", "country": "FR" },
    "notes": "Deliver before the 15th"
  }'
# → full order row; includes "checkout_url" when status is "pending_payment"

GET /api/customer/orders — list the customer's orders

Returns the customer's orders, most recent first, each with an nb_items count.

curl "https://printwearai.com/api/customer/orders" \
  -H "Authorization: Bearer $SESSION_TOKEN"

POST /api/customer/orders/{id}/checkout — (re)start payment

Creates a Stripe Checkout session on the merchant's connected account for an unpaid order and returns { "url": "https://checkout.stripe.com/…" }. Errors: 404 order not found · 400 order already paid / cancelled / refunded, or empty · 503 the merchant has not enabled online payments.

Typical server-side flow

  1. Your product page embeds the widget (front-end guide); your PwaiEmbed.onAddToCart(fn) handler stores m.config_id on your cart line.
  2. At your checkout, your backend calls POST /api/customer/orders with the customer's session, passing config_id per item — that is the thread linking the design to production.
  3. You track progress via the order.created / config.saved webhooks, and the order appears in your workspace → Orders with the production-ready pattern export.

See also: Custom site — full guide (front-end) · JavaScript SDK · Webhooks