Picsha Fulfillment API (1.0.0-draft)

Download OpenAPI specification:

Pete Traversy — Picsha, a Graphx company: petert@graphx.com License: LicenseRef-Proprietary

Receive print-ready orders from every Picsha storefront, and (roadmap) submit work to Picsha.

Draft v1.0, revised October 15, 2026 — published for partner technical review. Endpoint names, fields, and payloads are stable in intent but open to feedback before the spec is frozen. First published July 28, 2026 for Picsha Retouch and Picsha Books; this revision covers all six Picsha storefronts and the six-state production status every storefront shows its customers.

Picsha is a family of white-label storefronts for photo retailers — Retouch (AI photo restoration), Prints, Books (AI-designed photo books), Cards and photo calendars, Posters (AI vintage travel posters) and Gifts (photos on mugs, mousepads, puzzles and more) — with one landing page per store at yourstore.picsha.com (Picsha Hub) and the same storefronts on an in-store kiosk. Every storefront already delivers print-ready files to the retailer in its admin.

Stores that run Photogize Lab need no API

A retailer whose production runs through Photogize Lab connects each storefront from store admin and every paid order lands in the Lab's harvest queue — Prints as one job per photo, Retouch and Posters as one job per print, Cards as one job per printed side, calendars as thirteen collated jobs, Books as the cover and every page. Live today for every storefront.

Two integration surfaces for every other platform

  • Part B — Fulfillment Feed (Picsha → partner). The v1 surface. Orders placed on a retailer's Picsha storefronts are pushed to your platform via signed webhooks (see the Webhooks section), with time-limited download URLs for the print-ready files. A pull queue covers reconciliation and polling-only integrations; an optional round-trip moves the order's production status so the retailer and customer see it in one place. The order, line and output shapes are what the storefronts record today.
  • Part A — Order Submission (partner → Picsha). Roadmap. Your platform submits photos and product options; Picsha performs the AI work and returns finished, print-ready output. Documented so partners can plan for it; not open in the sandbox yet.

Conventions

  • Every order is scoped to a store (a retailer), identified by storeId — the same slug as the retailer's storefront subdomains, unchanged when the store serves them from its own domain.
  • All processing is asynchronous: submit, then receive a webhook or poll.
  • Finished output is delivered as presigned URLs valid for 60 minutes; re-fetch the order at any time for a fresh URL. Feed outputs are available 30 days after payment (book PDFs are kept for reprints); Part A outputs 90 days, source assets 30 days.
  • lines carry product facts (what was bought and what the finished thing is); outputs carry file facts (what is in each file). Where an order has several files, line (1-based position in lines) or side ties a file to what it is for.
  • Dimensions are inches, to three decimals where a millimetre product (cards, gifts) is converted. Months are one-based.
  • Lines fulfilled by Picsha directly (customer downloads) or by Picsha's own partners (lab-printed books, outsourced gifts) are not pushed to the feed.
  • Rate limits: 60 requests/minute per key for order submission, 600/minute for reads. Higher limits on request.

Idempotency

POST /orders accepts an Idempotency-Key header; retrying with the same key returns the original result rather than creating a duplicate order.

Fulfillment feed

Part B — the v1 surface. Receive Picsha storefront orders into your production workflow, and report production progress back. Webhook payloads are documented under Webhooks; the pull queue returns the same shape.

List storefront orders by production status

Pull-based access to the fulfillment feed, in the same shape as the order.ready webhook. status filters on the fulfillment status (production) and defaults to received — orders paid and ready to make that nobody has started. Use for reconciliation, recovery after webhook downtime, or as a simple polling integration if your platform cannot receive webhooks.

Authorizations:
bearerAuth
path Parameters
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$
Examples: theprintrefineryky

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

query Parameters
status
string (FulfillmentStatus)
Default: "received"
Enum: "received" "in_production" "ready" "shipped" "delivered" "cancelled"

Fulfillment status — production, the status the retailer and the customer see. received is paid with nothing done yet; ready is ready for pickup (pickup orders); shipped carries a carrier and tracking number (shipping orders); delivered closes either. The store moves it by hand from admin, a connected lab moves it as it works, and a partner moves it through the status round-trip.

product
string (Product)
Enum: "retouch" "prints" "book" "card" "calendar" "poster" "gift"

Only orders from one storefront.

Responses

Response samples

Content type
application/json
{
  • "orders": [
    ]
}

Report production progress (status round-trip)

Optional in v1; strongly encouraged. Moves the order's fulfillment status so the retailer and the customer see production progress in one place. The customer is emailed by the store at ready (ready for pickup) and shipped, exactly as when the store sets them by hand; shipped takes a carrier and tracking number, linked on the customer's order page.

problem does not move the customer's timeline: it flags the order for the retailer in admin with your note and emails the store. Use it for anything a person has to decide. Going backwards is allowed and recorded; every change appends to the order's history with who made it.

Authorizations:
bearerAuth
path Parameters
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
Examples: ord_6f39eb74
Request Body schema: application/json
required
status
required
string
Enum: "in_production" "ready" "shipped" "delivered" "problem"

ready is pickup-only (ready for pickup); shipped is ship-only and takes a shipment.

object (Shipment)
note
string <= 500 characters

Free text for the retailer. Required with problem; optional otherwise.

Responses

Request samples

Content type
application/json
Example
{
  • "status": "in_production"
}

Response samples

Content type
application/json
{
  • "error": {
    }
}

Assets

Part A (roadmap): upload customer photos before referencing them in orders.

Get a presigned upload URL for a photo (roadmap)

Returns a presigned URL to PUT the file bytes to directly. Accepted: JPEG, PNG, TIFF, HEIC, up to 30 MB. The asset is usable in orders once the PUT completes.

Authorizations:
bearerAuth
Request Body schema: application/json
required
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

filename
required
string
contentType
required
string
Enum: "image/jpeg" "image/png" "image/tiff" "image/heic"

Responses

Request samples

Content type
application/json
{
  • "storeId": "theprintrefineryky",
  • "filename": "scan-001.jpg",
  • "contentType": "image/jpeg"
}

Response samples

Content type
application/json
{
  • "assetId": "ast_9f1c2ab4",
  • "uploadUrl": "http://example.com",
  • "expiresAt": "2019-08-24T14:15:22Z"
}

Ingest a photo from a URL (roadmap)

Picsha fetches the file from sourceUrl at submission time. The URL must be HTTPS and reachable by Picsha; expiring signed URLs are fine.

Authorizations:
bearerAuth
Request Body schema: application/json
required
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

sourceUrl
required
string <uri>

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "assetId": "ast_9f1c2ab4"
}

Orders

Part A (roadmap): submit orders to Picsha and retrieve status and output.

Submit a fulfillment order (roadmap)

Submits an order for Picsha's AI work. Returns 202 Accepted immediately; processing is asynchronous. Typical restoration time is about a minute per photo.

product: retouch is the first submission product; book (AI-designed photo books from 20–200 photos) and poster (one photo as a 2:3 vintage travel poster) are documented so partners can plan for them.

Authorizations:
bearerAuth
header Parameters
Idempotency-Key
string <= 64 characters

Retrying with the same key returns the original result.

Request Body schema: application/json
required
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

product
required
string
Enum: "retouch" "book" "poster"

retouch first; book and poster are roadmap.

externalRef
string <= 128 characters

Your platform's own order/line identifier; echoed on every status response and webhook.

assets
required
Array of strings (AssetId) [ 1 .. 200 ] items [ items^ast_[a-z0-9]+$ ]
object

Product options. For retouch, addons. For book, sizeId and style. For poster, style, title, subtitle and palette.

object (Customer)

Included only where the retailer's production workflow requires it.

webhookUrl
string <uri>

Overrides your default webhook endpoint for this order.

Responses

Request samples

Content type
application/json
{
  • "storeId": "theprintrefineryky",
  • "product": "retouch",
  • "externalRef": "string",
  • "assets": [
    ],
  • "options": {
    },
  • "customer": {
    },
  • "webhookUrl": "http://example.com"
}

Response samples

Content type
application/json
{
  • "orderId": "ord_6f39eb74",
  • "status": "received"
}

Get order status and output (roadmap)

Returns the current state of a submitted order. When status is ready, outputs contains presigned download URLs valid for 60 minutes; re-fetch for a fresh URL. Polling is supported, but webhooks are strongly recommended.

Authorizations:
bearerAuth
path Parameters
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
Examples: ord_6f39eb74

Responses

Response samples

Content type
application/json
{
  • "orderId": "ord_6f39eb74",
  • "externalRef": "string",
  • "storeId": "theprintrefineryky",
  • "product": "retouch",
  • "status": "received",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "completedAt": "2019-08-24T14:15:22Z",
  • "failureReason": "string",
  • "outputs": [
    ]
}

Storefront order is paid and its print-ready files are available Webhook

The feed event. Sent when payment and AI processing are complete. The payload is one envelope for every storefront: product says which storefront the order came from, lines say what was bought, outputs are the files. outputs[].downloadUrl is a presigned URL valid for 60 minutes; fetch the order or the fulfillment queue for a fresh URL at any time.

All webhook deliveries carry X-Picsha-Signature: t=<unix-ts>,v1=<hex> — an HMAC-SHA256 of "<t>.<raw body>" using your webhook secret. Reject deliveries with a bad signature or a timestamp older than 5 minutes. Deliveries are retried with exponential backoff for up to 24 hours; respond 2xx to acknowledge.

Authorizations:
bearerAuth
Request Body schema: application/json
event
required
string
Value: "order.ready"
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

source
required
string
Value: "storefront"
product
required
string (Product)
Enum: "retouch" "prints" "book" "card" "calendar" "poster" "gift"

The storefront an order came from (one order comes from one storefront). Part A submission accepts retouch first; book and poster are roadmap. prints, card, calendar and gift are storefront-origin only (the feed).

createdAt
required
string <date-time>
fulfillmentStatus
string (FulfillmentStatus)
Enum: "received" "in_production" "ready" "shipped" "delivered" "cancelled"

Fulfillment status — production, the status the retailer and the customer see. received is paid with nothing done yet; ready is ready for pickup (pickup orders); shipped carries a carrier and tracking number (shipping orders); delivered closes either. The store moves it by hand from admin, a connected lab moves it as it works, and a partner moves it through the status round-trip.

object (Customer)

Included only where the retailer's production workflow requires it.

delivery
string
Enum: "pickup" "ship"

How the customer chose to receive the order. Optional and additive.

object (ShipTo)

The address collected at checkout, flattened to strings, plus the shipping rate the customer chose as method. Present only when an address was actually captured; a pickup order has none.

required
Array of objects (Line)
required
Array of objects (Output)

Responses

Request samples

Content type
application/json
Example
{
  • "event": "order.ready",
  • "orderId": "ord_7a20c1e9",
  • "storeId": "theprintrefineryky",
  • "source": "storefront",
  • "product": "prints",
  • "createdAt": "2026-10-15T15:12:40Z",
  • "customer": {
    },
  • "delivery": "pickup",
  • "lines": [
    ],
  • "outputs": []
}

A paid storefront order was cancelled after it was pushed Webhook

Sent when the store or Picsha cancels a paid order that has already been delivered to the feed, so you can pull it from production. An order you have already started should be reported back with problem on the status round-trip.

Authorizations:
bearerAuth
Request Body schema: application/json
event
required
string
Enum: "order.processing" "order.ready" "order.failed" "order.cancelled"
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
externalRef
string
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

product
required
string (Product)
Enum: "retouch" "prints" "book" "card" "calendar" "poster" "gift"

The storefront an order came from (one order comes from one storefront). Part A submission accepts retouch first; book and poster are roadmap. prints, card, calendar and gift are storefront-origin only (the feed).

createdAt
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "event": "order.processing",
  • "orderId": "ord_6f39eb74",
  • "externalRef": "string",
  • "storeId": "theprintrefineryky",
  • "product": "retouch",
  • "createdAt": "2019-08-24T14:15:22Z"
}

Submitted order has started processing (Part A) Webhook

Authorizations:
bearerAuth
Request Body schema: application/json
event
required
string
Enum: "order.processing" "order.ready" "order.failed" "order.cancelled"
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
externalRef
string
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

product
required
string (Product)
Enum: "retouch" "prints" "book" "card" "calendar" "poster" "gift"

The storefront an order came from (one order comes from one storefront). Part A submission accepts retouch first; book and poster are roadmap. prints, card, calendar and gift are storefront-origin only (the feed).

createdAt
string <date-time>

Responses

Request samples

Content type
application/json
{
  • "event": "order.processing",
  • "orderId": "ord_6f39eb74",
  • "externalRef": "string",
  • "storeId": "theprintrefineryky",
  • "product": "retouch",
  • "createdAt": "2019-08-24T14:15:22Z"
}

Submitted order failed (Part A) Webhook

Authorizations:
bearerAuth
Request Body schema: application/json
event
required
string
Enum: "order.processing" "order.ready" "order.failed" "order.cancelled"
orderId
required
string (OrderId) ^ord_[a-z0-9]+$
externalRef
string
storeId
required
string (StoreId) ^[a-z0-9-]{2,30}$

The retailer's Picsha store identifier — the same slug as every one of their storefront subdomains (theprintrefineryky.picsha.com, theprintrefineryky.prints.picsha.com, …).

product
required
string (Product)
Enum: "retouch" "prints" "book" "card" "calendar" "poster" "gift"

The storefront an order came from (one order comes from one storefront). Part A submission accepts retouch first; book and poster are roadmap. prints, card, calendar and gift are storefront-origin only (the feed).

createdAt
string <date-time>
failureReason
required
string

Responses

Request samples

Content type
application/json
{
  • "event": "order.processing",
  • "orderId": "ord_6f39eb74",
  • "externalRef": "string",
  • "storeId": "theprintrefineryky",
  • "product": "retouch",
  • "createdAt": "2019-08-24T14:15:22Z",
  • "failureReason": "asset_unreadable"
}