verification
use it
# add to stunt.yaml
services:
printful:
adapter: embedded:printful-style
# then
$ stunt upThe embedded: source extracts this adapter from the binary — no git clone, fully offline.
how to mock the Printful API locally
Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of Printful API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real Printful API v2 surface — the data is fake, the behavior is real.
backing stores
productsorders
api surface (13)
generated from the adapter manifest — exactly what boots, nothing more · covers 9% of the real API's 55 routes (derived from spec Printful OpenAPI @ 1.0)
13 routes
GET/v2/store/products
POST/v2/store/products
GET/v2/store/products/{product_id}
POST/orders
GET/orders/{order_id}
GET/v2/store/orders
POST/v2/store/orders
POST/v2/store/orders/{order_id}
POST/v2/shipping/rates
GET/webhooks
POST/webhooks
PUT/webhooks
DELETE/webhooks
VM-verified behaviors (22)
- ✓a missing or non-Bearer Authorization is the 401 error envelope
- ✓any non-empty bearer key is accepted — the dev-key model
- ✓a sync product mints a sequential id; variants read back verbatim
- ✓unknown products get the 404 error envelope
- ✓an unpaged list is a bare data array with no paging object
- ✓limit/offset pages walk the paging envelope without repeats
- ✓a malformed offset cursor is the 400 error envelope
- ✓v1 create is result-wrapped with an integer id and draft status
- ✓v1 GET round-trips the result; unknown ids are 404
- ✓the v2 list serves one canonical order doc per order, from both create surfaces
- ✓the status csv filter applies before paging
- ✓a status update flips the canonical doc both surfaces read
- ✓order timestamps are sequence-derived, not wall-clock (documented mock choice)
- ✓STANDARD and EXPRESS quotes scale with the item count
- ✓an unset configuration reads as an empty result
- ✓POST sets and PUT replaces the single config; the secret stays masked
- ✓DELETE returns success, empties the read, and silences delivery
- ✓a config without a url is the 400 error envelope
- ✓deliveries are HMAC-SHA256-signed over the exact delivered bytes
- ✓the types filter delivers only subscribed topics
- ✓an empty types list subscribes to every event type
- ✓a v1-created order emits the v1 result shape as the event data
Not implemented (5)
- No package_shipped, package_returned, product_synced, or order_hold events
- No product update or delete (PUT/DELETE /v2/store/products/{product_id})
- No order confirm or cancel (POST /orders/{id}/confirm, DELETE /orders/{id})
- No catalog endpoints (catalog variants, print files, mockup generator)
- No reference endpoints (countries, taxes, store info)
Differs from the real API (3)
- Webhook set without a secret signs with built-in mock secret stunt_mock_pful_webhook_secret
- v2 responses are bare data/resources; real Printful wraps every response in {code, result}
- Timestamps are sequence-derived synthetic values, never wall-clock
want more of the Printful API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.