Print Shop API

Updated 9/17/2026

Print Shop API

If your account owns an approved print shop, these endpoints let you receive and fulfill orders routed to you, and manage your shop's pricing and printer capacity. This is a separate surface from the customer-facing order API — shop endpoints are authorized by shop ownership, not by whether you created the order.
All shop endpoints require an API key or session belonging to the shop's owner, and take a shopId identifying which of your shops the call applies to.

Applying to become a shop

POST /api/v1/shop/apply
json
{
"name": "Camden Print Co",
"addressLine1": "12 Camden High Street",
"addressLine2": "",
"city": "London",
"postcode": "NW1 7JE",
"phone": "+44 20 7946 0958",
"acceptsPickup": true,
"acceptsPost": true
}
Creates a shop application (201 Created). Shops must be approved before they can receive orders — see GET /api/v1/shops, which only lists approved shops.

Listing orders routed to your shop

GET /api/v1/shop/orders?shopId={shopId}
Returns orders currently assigned to your shop, scoped to the current mode (test or live, based on your key or dashboard mode toggle):
json
{ "orders": [ /* PrintOrder[] */ ] }

Getting a single order

GET /api/v1/shop/orders/{id}
Same PrintOrder shape as the customer-facing GET /api/v1/orders/{id}. Returns 403 Forbidden if the order isn't assigned to one of your shops, or if the order's isTest flag doesn't match your key's mode.

Updating order status

POST /api/v1/shop/orders/{id}/status
json
{ "toStatus": "printing", "note": "Queued on printer 2" }
FieldTypeRequiredNotes
toStatus"printing" | "printed" | "ready_for_pickup" | "mailing" | "posted" | "completed"yesMust be a valid transition from the order's current status (see below).
notestringnoOptional internal note, stored with the status change.
trackingCarrier"royal_mail" | "dpd" | "evri" | "parcelforce" | "other"nopost orders only. Send together with trackingNumber.
trackingNumberstringno4-40 letters, numbers or dashes; spaces are removed and letters upper-cased. Send together with trackingCarrier.
Tracking is optional, because not every postage class is tracked. When both fields are sent, the customer sees the carrier, the number and a link to the carrier's tracking page (not for other). Sending only one of them returns 400 Bad Request, as does sending tracking for a pickup order. The tracking link is built for you; it cannot be supplied.
json
{ "toStatus": "posted", "trackingCarrier": "royal_mail", "trackingNumber": "AB123456789GB" }

Valid transitions

A shop can move an order forward to any later stage of its flow (so a batch that is already printed and posted can skip straight ahead). Moving backwards, or to a stage that isn't part of the order's flow, returns 400 Bad Request.
Order typeStages, in order
Pickupsent_to_shop → printing → printed → ready_for_pickup
Postsent_to_shop → printing → printed → mailing → posted → completed
A pickup order's final step, completed (shown as "Picked up"), happens only when the customer's pickup code is verified in the shop dashboard. It can't be set through this endpoint.
paid → sent_to_shop happens automatically when LetterAPI routes the order to your shop — it's not something you trigger.
Moving to printing or posted fires the corresponding webhook (order.printing, order.posted) to the customer's webhook endpoint — see Webhooks.

Managing pricing

GET /api/v1/shop/price-rules?shopId={shopId}
PUT /api/v1/shop/price-rules?shopId={shopId}
json
// PUT body
{
"paperSize": "A4",
"colorMode": "bw",
"fulfillmentType": "post",
"postageClass": "second",
"pricePerPagePence": 12,
"costPerPagePence": 4,
"envelopeCostPence": 30,
"doubleSidedSupported": true,
"finishingOptions": ["none", "stapled", "hole_punched"],
"finishingPricePence": { "stapled": 5, "hole_punched": 5 }
}
One rule per (paperSize, colorMode, fulfillmentType, postageClass) combination — PUT upserts.
FieldNotes
postageClass"none" | "first" | "first_signed" | "second" | "second_signed". Post rules only; none means "any class" and is used when a requested class has no rule. Pickup rules are always none.
costPerPagePence, envelopeCostPenceOptional. Your own costs, stored privately so you can see your margin. They never affect customer prices.
ruleIdOptional. Pass the ID of an existing rule to edit it, including changing its paper size, colour, category or postage class. The rule moves to the new combination; it returns 400 if another rule already uses it.

Managing printer capacity

GET /api/v1/shop/printers?shopId={shopId}
PUT /api/v1/shop/printers?shopId={shopId}
json
// PUT body
{
"colorMode": "bw",
"count": 2,
"ratedPagesPerHour": 3000,
"isActive": true
}
Used by LetterAPI's order-routing logic to avoid over-queuing a shop.

Errors

StatusMeaning
400 Bad RequestInvalid body, or an invalid status transition.
401 UnauthorizedMissing/invalid API key or session.
403 ForbiddenshopId isn't owned by the caller, or an order's mode doesn't match the caller's key mode.
404 Not FoundOrder doesn't exist.