Skip to content

A connected store, from connect to delivery

A connected store holds two keys and no secret key: its integration key (ik_live_…), for its server, and its publishable key (pk_live_…), for its pages, where the designer runs. This guide follows one customer’s item through such a store, from the store connecting to the order arriving, and names the key each call takes. Every call below that the store’s server makes takes its integration key, which reaches the store’s own designs, prints, orders and share links, and the workspace’s templates, blanks and printshops (Connecting a store).

Step Who calls Key
The customer approves a proof The store’s page, or a hosted page Publishable key, or the hosted page’s link
Commit, re-reference, read, delete a design The store’s server Integration key
Approve a design, hand out a proof page The store’s server Integration key
Print and order The store’s server Integration key
Hear what happened Pixonaut, to the store’s endpoint Signed with the endpoint’s secret
Catch up on missed events The store’s server Integration key

1. Connect

The merchant clicks connect in the store, approves in the studio, and the store exchanges the code it is sent back with for its integration key, its publishable key and its webhook endpoint (Connecting a store). Keep the integration key and the endpoint’s secret on the store’s server, and hand only the publishable key to its pages.

2. The customer approves a proof

On the product page, the designer runs with the publishable key, and the customer approves what they made: approve() renders a proof and answers a proofToken and a proofUrl. A store that sends its customer to the hosted designer instead gets the proofToken back on its return address (Sending the customer back to the store). Either way, nothing is stored yet: the token is the customer’s approved picture, waiting for the store to commit it.

The token reached the store through the customer’s browser. Keep it with the cart line on the store’s server once the line is priced, and commit that copy (Cart lifetimes and proof expiry).

3. Commit the design with the store’s reference

The store’s server commits the proof with its integration key, with the cart line as its own reference, externalRef, and any order data its printshop needs:

POST /v1/designs
Authorization: Bearer ik_live_...
Idempotency-Key: cart-10482-1:commit
Content-Type: application/json
{
  "proofToken": "eyJhdWQiOiJwcm9vZiIsImlkIjoicHJmXzljMWUyZDNmNGE1YjZjN2Q4ZTlmMGExYiIsInQiOiJhY21lIiwiZXhwIjoxNzkyNDkxMjQ3MDAwfQ.EXAMPLE_signature_not_a_real_one_0000000000",
  "externalRef": "cart-10482-1",
  "fulfilmentPayload": { "variantSku": "TEE-BLACK-M" }
}
{
  "designId": "6f1c8a2b-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "printFloorDpi": 150,
  "lowResolution": []
}

externalRef, fulfilmentPayload and printshopId are a server’s fields: the store’s page, with its publishable key, is refused them secret_key_required. A proof token commits once; committing it again answers 409 proof_already_committed with the design it became (Placing an order safely).

Before taking payment, read the design back with GET /v1/designs/{id} and check its print size, template and product against what the line was priced as (Cart lifetimes and proof expiry).

When the cart becomes an order, point the design at the order line with PUT /v1/designs/{id}/external-ref:

{ "externalRef": "WEB-10482-1" }

GET /v1/designs?externalRef=WEB-10482-1 then finds it.

Once the customer has paid, the store’s server approves the design with POST /v1/designs/{id}/approve, and says how the customer agreed, so the design keeps it: by adding it to the cart, by ticking a box, or by approving it again on the store after correcting it (edit). ref is the store’s own reference for where it was given:

{
  "consent": { "kind": "checkbox", "at": 1790000050000, "ref": "WEB-10482" },
  "expectedRevision": 1
}
{ "status": "approved" }

design.approved carries the consent and the revision it approved:

{
  "id": "1790000060000-5d7e1f2a-3b4c-4d5e-8f60-718293a4b5c6",
  "type": "design.approved",
  "createdAt": 1790000060000,
  "payload": {
    "designId": "6f1c8a2b-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
    "approvedVia": "explicit",
    "revision": 1,
    "consent": { "kind": "checkbox", "at": 1790000050000, "ref": "WEB-10482" }
  }
}

afterDays approves later instead, and expectedRevision refuses the approval revision_changed when the design was corrected since the store read it (Design to order).

5. Or let the customer decide on a proof page

A store that wants the customer to approve the finished proof, rather than approving it on their behalf, hands out a deciding proof page with POST /v1/share-links and sends the customer its url, by email or from their account page:

{
  "kind": "proof-page",
  "subjectId": "6f1c8a2b-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "mayDecide": true,
  "locale": "sv"
}
{
  "id": "0b1c2d3e-4f50-4a6b-8c7d-000000000010",
  "kind": "proof-page",
  "subjectId": "6f1c8a2b-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
  "mayDecide": true,
  "returnUrl": null,
  "name": null,
  "brandId": null,
  "designLimit": null,
  "printAreaFrame": false,
  "placementFrame": false,
  "locale": "sv",
  "createdAt": 1790000050000,
  "expiresAt": 1792592050000,
  "token": "EXAMPLE_share_link_token_not_a_real_one_000",
  "url": "https://app.pixonaut.example/h/proof/EXAMPLE_share_link_token_not_a_real_one_000?lang=sv"
}

The customer approves, rejects or asks for changes there. An approval on the page keeps a proof_page consent naming the link, and emits design.approved as above; a request for changes emits design.changes_requested, and with a returnUrl the page sends the customer back to the store to correct the design (Sending the customer back to the store). A link the store’s key handed out stops working when the store disconnects.

6. Print and order

POST /v1/prints makes the approved design’s print files, answering 202 while it renders; once GET /v1/prints/{id} reads completed, or print.completed arrives, POST /v1/prints/{id}/orders sends the completed print to the printshop, with the order line as externalRef and who it ships to:

POST /v1/prints/c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f/orders
Authorization: Bearer ik_live_...
Idempotency-Key: WEB-10482-1:order
Content-Type: application/json
{
  "quantity": 1,
  "externalRef": "WEB-10482-1",
  "recipient": { "name": "Alexandra Lind", "email": "[email protected]" },
  "shippingAddress": {
    "line1": "12 High Street",
    "postalCode": "SW1A 1AA",
    "city": "London",
    "country": "GB"
  }
}

An item printed on several placements is ordered as one with POST /v1/orders (Front and back on one item). Send an Idempotency-Key with each, so a retried request makes nothing twice (Placing an order safely). What each answers, and what the order goes through from there, is in Design to order. The integration key also cancels an order (POST /v1/orders/{id}/cancel), retries a failed one (POST /v1/orders/{id}/retry), and settles one whose outcome is unknown (PATCH /v1/orders/{id}).

7. Hear what happened, and catch up

The store’s endpoint receives the events about the store’s own work, and about the workspace’s templates, blanks and printshops, signed with the endpoint’s secret (Receive webhooks): the design’s design.committed and design.approved, the print’s print.completed, and the order’s order.submitted and each order.status_changed up to shipped. Narrow them to what the store handles with PATCH /v1/webhooks/{id} on its own endpoint (Connecting a store).

A store that was down misses nothing for good. Deliveries are retried (Webhooks gives the schedule), and the store’s server pulls the events in the order they were recorded with GET /v1/events, narrowed with types to the ones it subscribes to. Keep the pull’s own checkpoint, the id of the last event a pull answered, and pass it as after; leave after out on the first pull. Never move the checkpoint on because a webhook arrived: deliveries can arrive out of order, and an event that failed while a later one was delivered would then be skipped.

GET /v1/events?after=1790000060000-5d7e1f2a-3b4c-4d5e-8f60-718293a4b5c6&types=order.status_changed
Authorization: Bearer ik_live_...
{
  "events": [
    {
      "id": "1790086400000-0f1e2d3c-4b5a-4968-8776-655443322110",
      "type": "order.status_changed",
      "createdAt": 1790086400000,
      "payload": {
        "orderId": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
        "printId": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
        "status": "shipped",
        "previousStatus": "printed"
      }
    }
  ],
  "nextCursor": null
}

Each event comes in the envelope a delivery carries, with the same id, so the store drops one it already handled by webhook. Follow nextCursor as the next after until it is null. The pull answers the events the endpoint receives: never one about another store’s work.

8. Clean up a cart nobody bought

A design committed for a cart the customer abandons is deleted with DELETE /v1/designs/{id}, which answers 204 and emits design.deleted. A design that was approved or printed stays (Placing an order safely).

9. Erase a customer’s personal data

When a customer asks to be erased, the store’s server erases the delivery a design holds with DELETE /v1/designs/{id}/delivery, and the recipient and address of each order once it has shipped, failed or been cancelled, with POST /v1/orders/{id}/erase-personal-data. It finds the orders by its own reference, GET /v1/orders?externalRef= (Erasing personal data).

When the store goes away

Before the store is removed, or when the merchant disconnects it, the store’s server calls DELETE /v1/connect with its integration key. Its integration key, its publishable key, its webhook endpoint and every share link it handed out stop working at once. Its designs, prints and orders stay with the workspace, and the same site connecting again keeps working with them (Connecting a store).

Questions? Write to [email protected].

Status · Terms of service · Privacy policy · Subprocessors