Errors
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /concepts/errors.md. Index of every page and the rules for every call: /llms.txt. Guides and concepts in one file: /llms-full.txt. OpenAPI: /openapi.json.
The rule
Section titled “The rule”A request failed when either of these is true:
- The HTTP status is
4xxor5xx. - The JSON body has
"success": false.
Check both. A few operations refuse with status 200 and "success": false, and some refuse a change with 500. They’re listed under Known exceptions.
Send Accept: application/json on every request. Matched paths always answer JSON, but a path that doesn’t exist (404) or a method it doesn’t take (405) answers an HTML page without that header. A missing second /v2/ is the usual cause. There’s no request id header to quote: when you ask for help, send the method, path, time and answer instead.
The two error bodies
Section titled “The two error bodies”A failed request with Accept: application/json answers JSON in one of two shapes. Your code must handle both.
1. The standard error body. Most errors look like this:
{ "status": 422, "success": false, "error": { "code": "validation_failed", "message": null, "fields": { "customer.name": ["The customer.name field is required."] } }}| Field | Meaning |
|---|---|
status |
Usually the HTTP status, repeated. On the 200 refusals listed under Known exceptions, it says 500 while the HTTP status is 200. Branch on the HTTP status and success, not on this field. |
success |
false on an error. |
error.code |
A short machine-readable reason, such as validation_failed or user_is_not_authorized. Can be null. |
error.message |
A human-readable explanation. Often null. |
error.fields |
On a validation error, the problems for each field, keyed by the field’s path in your body. |
error.field_reasons |
Sometimes, with fields: a machine-readable reason for each field. |
2. A bare message. A few errors have only message, with no status, success or error:
{ "message": "Insufficient Permission" }You get this shape from:
- A
403when your role lacks a permission checked before the operation runs, such as Manage Settings:"Insufficient Permission". - A
422when the team’s plan doesn’t include a feature:"This feature is not available to your team's plan." - A
500from an unexpected failure:"Server Error". - A few operation-specific refusals, which their reference pages show, such as a product that isn’t found when you add a line.
Without Accept: application/json, a path that doesn’t exist (404) or a method it doesn’t take (405) answers an HTML page instead. A request over 100 MB is refused with 413 before YoPrint reads it, so that answer isn’t JSON either.
So read error.code when error is an object, and fall back to message:
const code = typeof body?.error === 'object' && body.error !== null ? body.error.code : null;const message = body?.error?.message ?? body?.message ?? null;Handle errors in this order:
- HTTP status and successDid it fail?
- error.codeWhy, when it isn’t
null - error.fieldsWhich input to fix
- error.message or messageTo log or show a person
Statuses
Section titled “Statuses”| Status | What it means | What to do |
|---|---|---|
400 |
A value YoPrint checks before anything else isn’t one it knows, such as the event name in Show a sample event. | Fix the value. The operation’s reference page lists the ones it takes. |
401 |
The API key is missing or wrong, or it was regenerated. error.code is unauthenticated. |
Send Authorization: Bearer YOUR_API_KEY with the current key. See Authentication. |
402 |
A payment for your YoPrint subscription failed. | Follow next_action: use_different_card, update_card_then_retry, or refresh_billing when the subscription ended during a retry. |
403 |
Your team’s plan doesn’t include API access (api_access_not_in_plan), your role doesn’t allow this operation, or the call manages the key itself (self_credential). |
For api_access_not_in_plan, use a team on the Pro plan or higher. Otherwise check your role. See Roles and permissions. |
404 |
The team slug is wrong, you’re not on that team, or the record doesn’t exist on this team. error.code is page_not_found or null. |
Check the slug and the id. Ids are the id field, not document numbers. See IDs. |
405 |
The path exists, but not with this method. | Check the method on the operation’s reference page. |
409 |
Not saved: the record changed since you read it, or the id is an older, replaced version of it (some addresses and locations). | Read it again and retry. When there’s a newer version, the answer points at it, such as current_address. |
410 |
Gone for good: a file or an attempt that expired, such as an import’s error file. | Start again. |
413 |
The request is over 100 MB. The web server refuses it before YoPrint reads it, so the body isn’t JSON. | Send a smaller file. |
422 |
The request was understood but refused: a field is missing or invalid, or a business rule stops it. | Read error.fields or error.message, fix the request, and send it again. |
429 |
Your team already has the most data exports running at once. | Wait for one to finish, then start yours. See Limits. |
500 |
Something failed inside YoPrint, or one of the business refusals listed below. | Check the list below. Otherwise read the record back before you retry. See Retries and duplicates. |
503 |
YoPrint is briefly unavailable, or exports are paused (export_creation_paused, with Retry-After). |
Wait and retry, reading back first if it was a change. |
The two kinds of 422
Section titled “The two kinds of 422”validation_failed: a field is wrong. error.fields names each field with its problems, and error.message is null.
validation_error, or another code: a business rule refuses the change. The fields are fine, but the record’s state doesn’t allow it. error.message says why. For example, recording a payment on a draft order answers:
{ "status": 422, "success": false, "error": { "code": "validation_error", "message": "Payment Action Not Allowed - due to order internal state" }}Fix the state first (here, publish the order), then retry.
Error codes
Section titled “Error codes”These are the codes you can get on most operations. Operation-specific codes, such as payment_below_minimum or stale_address, are on each operation’s reference page under Errors.
| Code | Status | What it means | What to do |
|---|---|---|---|
unauthenticated |
401 |
The key is missing or wrong, or it was regenerated. | Send Authorization: Bearer YOUR_API_KEY with your current key. |
api_access_not_in_plan |
403 |
The key works, but the team’s plan doesn’t include API access. Every request to this team answers this. | Use a team on Pro or higher, or upgrade this one. See Authentication. |
self_credential |
403 |
The call reads, creates or resets the key itself. A key can’t manage itself. | Manage the key in Profile. |
user_is_not_authorized |
403 |
Your role doesn’t allow this action on this record. | Ask the team owner to change your role, or use a key whose role allows it. |
unauthorized |
403 |
A role or ownership check refused it. message is null. |
As above. |
No code: { "message": "Insufficient Permission" } |
403 |
Your role lacks a permission, such as Manage Settings. | As above. |
page_not_found |
404 |
The team slug is wrong, you’re not a member of the team, or an id in the path isn’t one of this team’s. | Check the slug against Team ID in Profile, and the id. |
null, with a message starting “No query results for” |
404 |
No record with that id on this team. | Check the id. Ids are the id field, not document numbers. |
validation_failed |
422 |
A field is missing or invalid. error.fields names each one. |
Fix the fields and send the request again. |
validation_error |
422 |
A business rule refused the change. error.message says why. |
Fix the record’s state, then retry. |
bulk_validation_payload_failed |
422 |
One item of a bulk request is invalid. error.bid names it. Nothing was saved. |
Fix that item and send the whole request again. |
amount_out_of_range |
422 |
The save would answer with an amount the older number format can’t carry exactly. Nothing was saved. | Send X-Yoprint-Decimal-Format: exact-v1 and retry. |
upload_not_allowed |
422 |
The file is a web page, a script or a program. | Send another file type, or zip it. |
mockup_type_not_allowed, image_not_allowed |
422 |
A mockup isn’t an image YoPrint can preview, or an image is over a size limit. | Upload it as a production file, or send a smaller image. |
duplicate_report |
422 |
An export of the same kind is already running. | Wait for it to finish. |
connect_timeout |
422 |
Another service YoPrint called, such as Shippo, Shopify or QuickBooks, didn’t answer in time. The change may have happened there. | Read the record back before you retry. |
No code: { "message": "This feature is not available to your team's plan." } |
422 |
The team’s plan doesn’t include this feature. | Upgrade the plan, or leave the feature out. |
validation_error, “You are sending too many emails…” |
422 |
The team went over an email limit. All of the team’s email is now blocked. | Stop sending, and contact YoPrint support to lift the block. See Limits. |
email_feature_not_enabled_in_trial |
422 |
The team is on a trial, and YoPrint hasn’t turned email on for it. Nothing was sent. | Contact YoPrint support, or send once the subscription is paid. |
export_capacity |
429 |
The team already has 3 exports running. | Wait for one to finish, then start yours. |
action_not_allowed |
200 or 500 |
The record’s state doesn’t allow the action. | See Known exceptions. Don’t retry without fixing the state. |
delete_fail |
500 or 422 |
The record can’t be deleted: it’s a default, made by YoPrint, a primary contact, or a customer with an open quote or order. | Change the default or the primary first, or close the open orders. |
export_creation_paused |
503 |
Exports are paused for a few minutes while YoPrint updates. Retry-After says how long. |
Wait that many seconds, then send it again. |
No code: { "message": "Server Error" } |
500 |
Something failed inside YoPrint. | Read the record back before you retry. See Retries and duplicates. |
error.code is sometimes not a short code but a sentence or a number, such as "Insufficient Order Balance" on some payment refusals. Branch on the HTTP status and success first, and treat any code you don’t know as a refusal.
Not found
Section titled “Not found”A 404 for a record means YoPrint found nothing with that id on this team. The usual causes:
- The id is a document number such as
SO1001, or a value from another team. - The record was deleted.
- The slug is wrong, so no record on any path will be found.
The message of a record 404 can name an internal class. Ignore it: it isn’t part of the API.
Known exceptions
Section titled “Known exceptions”These operations don’t follow the rule that a refusal is a 4xx. Your code must check success, and must not retry the 500s below without fixing the cause: they fail the same way every time.
Refused with 200 and "success": false
Section titled “Refused with 200 and "success": false”These answer HTTP 200 with a body like this. They change nothing, except Delete a job, which deletes the job anyway. “Neither a draft nor published” only happens on some older orders, made before YoPrint’s current order system. Orders made today are always one or the other.
{ "status": 500, "success": false, "error": { "code": "action_not_allowed", "message": null } }| Operation | When |
|---|---|
| Publish a sales order or quote | It’s already published. |
| Restore a sales order or quote | It isn’t deleted. |
| Create a job, or add one from a job preset | The order is neither a draft nor published. |
| Reorder an order’s jobs | The order is neither a draft nor published. |
| Update a job | The order is neither a draft nor published. |
| Change a job’s status | The order isn’t published (a draft). |
| Delete a job | The order is neither a draft nor published. The job is deleted anyway, then this refusal comes back. Don’t retry: read the order’s jobs, and the job is gone. |
| Add or list a job’s tasks (work pipeline) | The order is neither a draft nor published. |
Integration data for Square with type create_device_code (a terminal pairing code) |
Square refuses. Here error.code is a number, 422. |
Refused with 200 and "success": true, but nothing done
Section titled “Refused with 200 and "success": true, but nothing done”Publishing a sales order or quote answers 200 with "valid": false when the order isn’t ready. Check data.valid after you publish:
{ "status": 200, "success": true, "data": { "id": "V4keXwwxMQyXw7", "valid": false, "validation": { "has_customer": true, "has_order_items": false, "has_sellable_items": true, "unsellable_items": [] } }}Refused with 500
Section titled “Refused with 500”| Operation | When |
|---|---|
| Delete a customer’s or vendor’s contact | It’s the primary contact (delete_fail). |
| Delete a currency, payment method, payment term, shipping box, shipping method, tax preset or fee preset | It’s the team’s default, or made by YoPrint (delete_fail). |
| Create, delete or sync statuses | The status type isn’t allowed, such as pre_production, or a start or pre_production status is deleted. |
| Delete a sales order approval | The approval is completed. |
| Delete a system message template | Always (delete_fail). |
| Filter shipments | You filtered on ship_due_date, order_external_type or shipment_source. |
| Buy a GoShippo label | Shippo failed or timed out during the purchase. A label may have been bought. Don’t retry. See Ship an order. |
| Get rates, carriers or estimates from GoShippo, or track a package | Shippo refused the request or couldn’t be reached. |
Each operation’s reference page lists its own errors, including these.
