# API error reference

Source: https://portal.printedsimply.com/docs/errors

Every error code the PrintedSimply API can return, with its HTTP status and an actionable fix. Errors share a structured body — `code`, `message`, and a `request_id`. An error's `doc_url` links straight to its entry below (`not_available`'s, to the API reference).

## Authentication & authorization

[`missing_api_key`](https://portal.printedsimply.com/docs/errors#missing_api_key) 401

API key is required. Pass it in the x-api-key header or as an Authorization: Bearer credential.

Suggestion: Include your API key in the x-api-key header, or send it as `Authorization: Bearer <key>`. Get one at printedsimply.com/dashboard/api-keys.

[`invalid_api_key`](https://portal.printedsimply.com/docs/errors#invalid_api_key) 401

The API key provided is invalid or has been revoked.

Suggestion: Check that you're using the correct key. Test keys start with ps_test_, live keys with ps_live_ (keys created before the PrintedSimply rename start with mf_test_ or mf_live_ and keep working). If you sent the docs' placeholder (ps_test_YOUR_KEY), replace it with your own key from printedsimply.com/dashboard/api-keys.

[`key_expired`](https://portal.printedsimply.com/docs/errors#key_expired) 401

This API key has expired.

Suggestion: Generate a new key at printedsimply.com/dashboard/api-keys and update your integration.

[`insufficient_scope`](https://portal.printedsimply.com/docs/errors#insufficient_scope) 403

The API key does not have the scope required for this endpoint.

Suggestion: Create a key with the required scope, or use a key with full access (no explicit scopes).

[`ip_not_allowed`](https://portal.printedsimply.com/docs/errors#ip_not_allowed) 403

This API key is restricted to specific IP addresses.

Suggestion: Call from an allowed IP, or update the key's IP allowlist in the dashboard.

[`sandbox_only`](https://portal.printedsimply.com/docs/errors#sandbox_only) 403

This operation is only available with a sandbox test key.

Suggestion: Use a ps_test_ key for this endpoint.

[`test_mode_only`](https://portal.printedsimply.com/docs/errors#test_mode_only) 400

This operation only applies to test-mode resources.

Suggestion: Sandbox-only helpers (like order simulation) work on orders created with a test key.

[`live_key_required`](https://portal.printedsimply.com/docs/errors#live_key_required) 403

This change reaches live traffic, which a test key cannot.

Suggestion: A test key subscribes only to test traffic and manages only test-mode subscriptions. Use mode "test", or make the change with a live key (ps_live_*).

[`auth_unavailable`](https://portal.printedsimply.com/docs/errors#auth_unavailable) 503

Your credentials could not be verified right now.

Suggestion: Nothing is wrong with your API key — the credential store could not be read. Retry after the number of seconds in the Retry-After header; if it persists, quote the request_id to support.

## Rate limits

[`rate_limit_exceeded`](https://portal.printedsimply.com/docs/errors#rate_limit_exceeded) 429

Rate limit exceeded. Try again later.

Suggestion: The limit is 60 requests per 60 seconds. Check the X-RateLimit-Reset header for when you can retry.

[`daily_quota_exceeded`](https://portal.printedsimply.com/docs/errors#daily_quota_exceeded) 429

Daily request quota exceeded for your plan.

Suggestion: You've reached your plan's daily request limit. It resets at 00:00 UTC. Retrying before then will not clear it.

## Validation

[`validation_error`](https://portal.printedsimply.com/docs/errors#validation_error) 400

Request body failed validation.

Suggestion: Check the details field for specific validation failures. Refer to the endpoint reference for required fields.

[`invalid_json`](https://portal.printedsimply.com/docs/errors#invalid_json) 400

The request body is not valid JSON.

Suggestion: Send a JSON body with Content-Type: application/json and check for syntax errors.

[`cursor_source_changed`](https://portal.printedsimply.com/docs/errors#cursor_source_changed) 409

The catalog this cursor walks is now served from another source.

Suggestion: A GET /v1/products cursor names a place in the catalog as it was served when the cursor was returned: live, from the last-good snapshot, from the local synced copy, or from our price book (`details.issued_under`). This page is served from another (`details.source`, with `X-Catalog-Stale` and `X-Catalog-Degraded` saying why), which is another list, so continuing would skip or repeat rows. Leave `cursor` out and walk the catalog again from the first page. `details.issued_under` is null for a cursor returned before cursors named their source; it is refused the same way. Retrying the same cursor succeeds only once the catalog is served from the source it names again.

[`payload_too_large`](https://portal.printedsimply.com/docs/errors#payload_too_large) 413

Request body exceeds the size limit for this endpoint.

Suggestion: Most endpoints accept up to 256 KB. Asset uploads accept up to 70 MB of base64 in JSON; bulk orders have their own limit. Send artwork through POST /v1/assets and reference it by id rather than inlining it elsewhere.

[`not_found`](https://portal.printedsimply.com/docs/errors#not_found) 404

The requested resource was not found.

Suggestion: Check the ID in the path. A resource another account owns answers the same 404 as one that does not exist. For a product, GET /v1/products lists every SKU you can order.

[`method_not_allowed`](https://portal.printedsimply.com/docs/errors#method_not_allowed) 405

This path does not take the request's method.

Suggestion: The Allow header, and details.allow, list the methods this path takes; check the method against the API reference. Every path that takes GET also answers HEAD, and every path answers OPTIONS.

[`method_not_implemented`](https://portal.printedsimply.com/docs/errors#method_not_implemented) 501

This API does not implement the request's method on any path.

Suggestion: Send GET, HEAD, OPTIONS, POST, PUT, PATCH or DELETE: no other method is implemented anywhere. Where the path exists, the Allow header and details.allow list the methods it takes.

## Orders

[`order_not_found`](https://portal.printedsimply.com/docs/errors#order_not_found) 404

Order not found.

Suggestion: Check the order ID and ensure you're using the correct API key mode (test vs live).

[`already_cancelled`](https://portal.printedsimply.com/docs/errors#already_cancelled) 400

Order has already been cancelled.

Suggestion: This order was previously cancelled. Use GET /order/{id} to check current status.

[`cancellation_failed`](https://portal.printedsimply.com/docs/errors#cancellation_failed) 502

Could not cancel order in production system.

Suggestion: The order may have already entered production. Contact support if this persists.

[`not_cancellable`](https://portal.printedsimply.com/docs/errors#not_cancellable) 409

The order can no longer be cancelled.

Suggestion: An order can be cancelled only before it goes into production (pending_payment, paid or accepted). Once production has started it is made and shipped as placed, and nothing is refunded by this call; for anything else about the order, contact support at /contact.

[`order_not_modifiable`](https://portal.printedsimply.com/docs/errors#order_not_modifiable) 409

The order can no longer be modified.

Suggestion: Address, shipping and line changes are taken only before the order goes into production (pending_payment, paid or accepted). Past that the order is made and shipped as placed; `actions` on GET /v1/order/{id} says what an order still takes, and support (/contact) answers anything else.

[`duplicate_reference`](https://portal.printedsimply.com/docs/errors#duplicate_reference) 409

An order with this reference was already placed. Nothing was charged.

Suggestion: details.order_id names the order that already carries this reference (placed in the last 30 days and not cancelled, with a key of the same mode): read it with GET /v1/order/{id} instead of placing it again. details.in_flight means another request with this reference is being placed right now; wait a moment and look it up with GET /v1/orders?reference=. Cancelling an order frees its reference. To place a second order under the same reference on purpose, send allow_duplicate_reference: true.

[`reference_check_unavailable`](https://portal.printedsimply.com/docs/errors#reference_check_unavailable) 503

Whether this order's reference is already in use could not be checked, so the order was not placed. Nothing was charged.

Suggestion: Retry the same request after the number of seconds in the Retry-After header; the Idempotency-Key is free, because nothing was charged or placed. If it persists, quote the request_id to support.

[`address_invalid`](https://portal.printedsimply.com/docs/errors#address_invalid) 422

The shipping address cannot be delivered as written. Nothing was charged.

Suggestion: details.findings names each problem with its field: zip_format (send five digits, or ZIP+4 as 12345-6789), state_unknown (send a USPS state, territory or military code such as TX, PR or AE), zip_state_mismatch (the ZIP belongs to a state that does not border the one named, so one of the two is wrong), or delivery_point_not_found (USPS has no delivery point at the street address: check the house number and street; a live order only, where USPS verification is configured). details.address_check.normalized is the address in USPS style. Check an address before ordering with POST /v1/addresses/validate. Nothing was charged and the Idempotency-Key is free for the corrected order.

[`shopper_paid_order`](https://portal.printedsimply.com/docs/errors#shopper_paid_order) 409

A shopper paid for this order, so it takes no charge on your card.

Suggestion: A shopper paid for this order, so its refunds go back to the shopper. Place a separate order for anything you want to add.

[`letter_report_refused`](https://portal.printedsimply.com/docs/errors#letter_report_refused) 409

This order cannot be reported as a missing letter.

Suggestion: details.reason says why: not_a_letter (it shipped as a parcel; use its tracking), not_shipped (it has not left the plant), not_yet_due (wait until details.expected_by), already_reshipped (one free replacement per letter; details.replacement_order_id names it) or is_a_reship (a replacement is not reshipped again; contact support).

[`order_error`](https://portal.printedsimply.com/docs/errors#order_error) 500

The order could not be processed.

Suggestion: Retry the request; if the problem persists, contact support with the request id.

[`settlement_recording_failed`](https://portal.printedsimply.com/docs/errors#settlement_recording_failed) 500

The payment was processed but the order record could not be updated. This request will not be retried automatically.

Suggestion: The money movement already completed — do NOT retry with a new Idempotency-Key, which would charge or refund a second time. Fetch the order to see its current state and contact support with the request id if it looks wrong.

[`invalid_state`](https://portal.printedsimply.com/docs/errors#invalid_state) 409

The resource is not in a state that allows this operation.

Suggestion: Fetch the resource to confirm its current state before retrying.

[`already_terminal`](https://portal.printedsimply.com/docs/errors#already_terminal) 400

The resource is already in a terminal state.

Suggestion: No further transitions are possible for this resource.

[`idempotency_conflict`](https://portal.printedsimply.com/docs/errors#idempotency_conflict) 409

Another request with this Idempotency-Key is still in flight.

Suggestion: Wait for the original request to finish, then retry with the same key to get its result.

[`idempotency_key_reuse`](https://portal.printedsimply.com/docs/errors#idempotency_key_reuse) 422

This Idempotency-Key was already used for a request with a different payload.

Suggestion: An Idempotency-Key identifies one specific request. Reuse it only to retry that exact payload; generate a new key for a new request.

[`idempotency_unavailable`](https://portal.printedsimply.com/docs/errors#idempotency_unavailable) 503

The Idempotency-Key could not be checked right now, so the request was not run. Nothing was charged.

Suggestion: Retry the same request, with the same Idempotency-Key, after the number of seconds in the Retry-After header — nothing ran, so the retry cannot duplicate anything. If it persists, quote the request_id to support.

[`concurrent_modification`](https://portal.printedsimply.com/docs/errors#concurrent_modification) 409

Another request is already modifying this resource.

Suggestion: A competing write is in flight for the same resource. Retry shortly — this is a lock conflict, not a rejection, and the retry usually succeeds.

[`variable_data_invalid`](https://portal.printedsimply.com/docs/errors#variable_data_invalid) 422

Variable data on one or more lines failed the preflight.

Suggestion: details.problems names each problem by item, unit and slot, with a code: not_own_plant (the SKU is not flat goods made at our own plant, or the order names another provider), count_mismatch (units needs exactly one entry per unit of quantity), unknown_slot, slot_conflict, missing_value, overflow (a value longer than its slot's max_length, or a QR payload over its ceiling) or too_many_units. Nothing was priced or charged; fix the lines and send the request again.

[`variable_data_unavailable`](https://portal.printedsimply.com/docs/errors#variable_data_unavailable) 422

Variable data is not yet available for production.

Suggestion: Live orders and quotes carrying items[].variable_data are refused, before anything is charged, until production is switched on. A test key (ps_test_*) accepts them end to end (quote, order, variant proofs and the sandbox lifecycle), so integrate there now.

[`artwork_not_printable`](https://portal.printedsimply.com/docs/errors#artwork_not_printable) 422

The artwork on one or more own-plant lines cannot be printed as it is.

Suggestion: details.problems names each line by item_index with a code: unreachable (the artwork URL answered 404 or 410), not_an_image (it returned a page or other non-image bytes) or low_resolution (too few pixels for the print size; the message says how many it needs). Nothing was charged and the Idempotency-Key is free: fix the files and send the order again. A test key (ps_test_*) is never refused for artwork; its response carries the same verdict in artwork_check.would_refuse.

[`artwork_required`](https://portal.printedsimply.com/docs/errors#artwork_required) 422

One or more lines our own plant makes carry no artwork.

Suggestion: details.items names each line by item_index and sku (and order_index on POST /v1/orders/bulk). Send the line's file as artwork_url, or as print_file: a URL, or an asset id from POST /v1/assets. Nothing was charged and the Idempotency-Key is free. A test key (ps_test_*) is never refused for it; its response lists the lines in artwork_check.missing_artwork and says artwork_check.would_refuse.

## Items

[`item_not_found`](https://portal.printedsimply.com/docs/errors#item_not_found) 404

Item not found.

Suggestion: Check the item ID. Use GET /order/{id} to see all items in the order.

## Payment

[`spend_cap_exceeded`](https://portal.printedsimply.com/docs/errors#spend_cap_exceeded) 402

This order exceeds a spend cap set on the API key.

Suggestion: The API key has a per-order or daily spend cap. Raise the cap in the dashboard, or use a different key. A cap bounds what one key can spend, whoever holds it.

[`no_payment_method`](https://portal.printedsimply.com/docs/errors#no_payment_method) 402

No payment method on file. Add one at printedsimply.com/dashboard/billing.

Suggestion: Add a credit card at printedsimply.com/dashboard/billing before placing live orders.

[`payment_failed`](https://portal.printedsimply.com/docs/errors#payment_failed) 402

Card was declined.

Suggestion: Update your payment method at printedsimply.com/dashboard/billing.

[`authentication_required`](https://portal.printedsimply.com/docs/errors#authentication_required) 402

This payment requires 3DS authentication.

Suggestion: The card's bank wants the cardholder to approve this payment. On POST /order, details carries payment_intent and client_secret: approve it with Stripe.js handleNextAction, then send the same order again with payment_intent and a new Idempotency-Key. A caller with no browser (an MCP client, a server) hands the order to the person instead: they place the same order on the dashboard's order page, /dashboard/order, which runs the bank's check in their browser. Nothing is charged until that order is placed.

[`refund_failed`](https://portal.printedsimply.com/docs/errors#refund_failed) 500

Could not process refund.

Suggestion: The refund could not be processed automatically. Contact support with your order ID and request ID.

[`fraud_review`](https://portal.printedsimply.com/docs/errors#fraud_review) 402

The order was held for fraud review.

Suggestion: Our team reviews held orders quickly. Contact support to expedite.

## Pricing & quotes

[`pricing_error`](https://portal.printedsimply.com/docs/errors#pricing_error) 400

Could not calculate price. Check SKUs are valid.

Suggestion: Verify that the SKUs exist by calling GET /products. Common SKUs: STICKER-DIE-CUT-3X3, STICKER-KISS-CUT-3X3.

[`mixed_facilities`](https://portal.printedsimply.com/docs/errors#mixed_facilities) 400

These items require different production facilities.

Suggestion: Split the cart into one order per production method and place them separately.

[`invalid_quote`](https://portal.printedsimply.com/docs/errors#invalid_quote) 400

The quote token is invalid or has expired.

Suggestion: Request a fresh quote and place the order before it expires.

[`quote_mismatch`](https://portal.printedsimply.com/docs/errors#quote_mismatch) 400

The order does not match the quoted items or destination.

Suggestion: Place the order with exactly the items and address that were quoted, or get a new quote.

[`unsupported_currency`](https://portal.printedsimply.com/docs/errors#unsupported_currency) 400

We do not hold an exchange rate for that presentment currency.

Suggestion: Quote in one of the supported currencies listed in the error details, or omit `currency` to settle in USD.

[`letter_mail_unavailable`](https://portal.printedsimply.com/docs/errors#letter_mail_unavailable) 400

This order cannot ship as an untracked letter.

Suggestion: details.reason says why: not_offered (letter mail is not a service on this platform), a rule the items broke (only stickers and small vinyl decals from our own plant, a few units, under the value ceiling), or destination_not_us (our own plant ships to US addresses only). Choose a parcel method on offer.

[`shipping_method_unavailable`](https://portal.printedsimply.com/docs/errors#shipping_method_unavailable) 400

This shipping method is not offered on this platform.

Suggestion: details.offered lists the methods you can choose (standard always; priority and express once they are priced). POST /v1/shipping/estimate lists them for your items.

[`destination_unavailable`](https://portal.printedsimply.com/docs/errors#destination_unavailable) 422

Our own facilities do not ship to this destination.

Suggestion: details.reason is destination_not_us and details.country names the destination. Our own plant ships to US addresses only, so send a US address. An order already placed cannot be re-pointed abroad (PUT /v1/order/{id}/address).

[`below_cost_floor`](https://portal.printedsimply.com/docs/errors#below_cost_floor) 422

This order would cost more to make and ship than it is charged.

Suggestion: Nothing was charged. details.skus names the items and details.contribution_cents how far below zero the order falls at worst-case postage. Add units, combine it with another order, or choose another shipping method on offer.

[`unknown_sku`](https://portal.printedsimply.com/docs/errors#unknown_sku) 400

One or more SKUs are not in the catalog.

Suggestion: Check each SKU against GET /v1/products/{sku}: only a product the catalog can make can be ordered, quoted or sold. `details.skus` names the ones it does not have.

## System

[`production_reconciliation_required`](https://portal.printedsimply.com/docs/errors#production_reconciliation_required) 409

Production submission needs reconciliation.

Suggestion: Do not retry with a new key or refund the payment. Contact support with the request ID; production may already exist.

[`fulfillment_error`](https://portal.printedsimply.com/docs/errors#fulfillment_error) 502

Could not complete operation in the production system.

Suggestion: This is a temporary issue with the production system. Retry with an Idempotency-Key header. Contact support if it persists.

[`provider_unavailable`](https://portal.printedsimply.com/docs/errors#provider_unavailable) 503

The chosen production facility is not accepting orders right now.

Suggestion: Every order here is made at our own plant, so there is no other facility to send it to, and naming a `provider` will not change that. Nothing stands charged. Retry in a few minutes. A 503 sent before anything was charged frees the Idempotency-Key, so retry with the same one; a 503 sent after a charge was refunded says so in its message and needs a new key. If it persists, quote the request_id to support.

[`upstream_error`](https://portal.printedsimply.com/docs/errors#upstream_error) 502

Could not fetch data from upstream service.

Suggestion: This is usually a temporary issue: retry the request shortly. If the X-Catalog-Degraded header reads not_configured, the catalog is not set up on this deployment and a retry will not help; quote the request_id to support.

[`stripe_unconfigured`](https://portal.printedsimply.com/docs/errors#stripe_unconfigured) 503

Payments are not configured on this deployment.

Suggestion: The Stripe signing secret is set but the Stripe secret key is not, so this event's handler cannot reach Stripe. Set STRIPE_SECRET_KEY; the event redelivers on Stripe's own retry schedule and is processed then.

[`internal_error`](https://portal.printedsimply.com/docs/errors#internal_error) 500

The request could not be completed because of an unexpected error on our side.

Suggestion: This is our fault, not the request's — the same request may well succeed on retry. If it does not, quote the `request_id` from this response to support; it identifies the captured error.

## Network

[`not_supported_by_provider`](https://portal.printedsimply.com/docs/errors#not_supported_by_provider) 501

The shop that produced this order does not support this operation.

Suggestion: This operation is unavailable at the producing facility. Contact support to confirm the existing production order before retrying an amendment or replacing it.

## Availability

[`not_available`](https://portal.printedsimply.com/docs/errors#not_available) 404

This operation is not available on this portal.

Suggestion: The API reference lists every operation this portal serves: orders, quotes, products, pricing and shipping estimates, artwork validation, asset uploads, webhooks and events.

[`sku_not_available`](https://portal.printedsimply.com/docs/errors#sku_not_available) 422

This product isn't available from PrintedSimply.

Suggestion: details.skus names each one, and details.did_you_mean the products it most likely meant, when one is close. Only products our own plant makes can be ordered here — stickers, decals, magnets and posters; GET /v1/products lists them. Nothing was priced or charged; remove or replace those lines and send the request again.

[`provider_not_available`](https://portal.printedsimply.com/docs/errors#provider_not_available) 422

Orders here are made by PrintedSimply.

Suggestion: Omit `provider`, or send "printedsimply". Nothing was priced or charged.

[`event_not_available`](https://portal.printedsimply.com/docs/errors#event_not_available) 400

This webhook event isn't raised on PrintedSimply.

Suggestion: details.events names each one, and details.available lists every event a subscription can name here — an order's lifecycle, the catalog's and your endpoints' own — with "*" for all of them. Nothing was saved; drop those names and send the request again.

Need help debugging? [Troubleshooting guide](https://portal.printedsimply.com/docs/troubleshooting) · [Full API docs](https://portal.printedsimply.com/docs)

## Problem details

Every error on this page comes back in the envelope by default. Send `Accept: application/problem+json` and the same error comes back as an RFC 9457 problem document, with `Content-Type: application/problem+json`: the shape HTTP clients and API tooling read without knowing ours.

Request:

```bash
curl https://portal.printedsimply.com/api/v1/order/ord_missing \
  -H "x-api-key: ps_test_YOUR_KEY" \
  -H "Accept: application/problem+json"
```

Response:

```json
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
Vary: Accept
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000

{
  "type": "https://portal.printedsimply.com/docs/errors#order_not_found",
  "title": "Order not found",
  "status": 404,
  "detail": "Order not found.",
  "instance": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
  "code": "order_not_found",
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "doc_url": "https://portal.printedsimply.com/docs/errors#order_not_found",
  "suggestion": "Check the order ID and ensure you're using the correct API key mode (test vs live)."
}
```

| Member | What it carries |
| --- | --- |
| `type` | Where the code is documented, as an absolute URI: its entry on this page, the same link as `doc_url`. |
| `title` | The code's short title, the same on every occurrence: “Order not found” for `order_not_found`. |
| `status` | The HTTP status, always the response's own. |
| `detail` | What went wrong this time: the envelope's message. |
| `instance` | This occurrence, as `urn:uuid:` and the request id: the id `X-Request-Id` and `request_id` carry. An idempotent replay is the original answer, so it repeats the original's `request_id` and `instance` under a new `X-Request-Id`. |
| `code, request_id, doc_url, suggestion, details` | The envelope's own members, unchanged, as extension members. Keep branching on code. |

### Which one you get

- Problem details when `Accept` names `application/problem+json` with a higher weight than `application/json`: `application/problem+json` alone, `application/problem+json, application/json;q=0.9`, or `application/problem+json, */*`.
- The envelope, byte for byte, with no `Accept`, or with `*/*`, `application/*` or `application/json` — and when `application/json` and `application/problem+json` are listed at the same weight.
- A success never changes. Every response answered in the envelope says `Vary: Accept`, its headers (`X-Request-Id`, the rate-limit fields, `Retry-After`) are the same either way, and an idempotent replay of a stored error is converted like the original.

### Unknown paths and methods

A path no operation is at answers `404` [`not_found`](https://portal.printedsimply.com/docs/errors#not_found), and a method a path does not take answers `405` [`method_not_allowed`](https://portal.printedsimply.com/docs/errors#method_not_allowed) with an `Allow` header that names the methods it takes (and `details.allow`), both with a request id, in either shape. A method no path takes at all, such as `PROPFIND`, answers `501` [`method_not_implemented`](https://portal.printedsimply.com/docs/errors#method_not_implemented) on any path. Every path that takes `GET` answers `HEAD` too, and every path answers `OPTIONS`.
