Errors
Every error uses the same envelope with a stable code, a message, optional validation and repair details, and the request id.
Envelope
{
"error": {
"type": "validation_error",
"code": "validation_failed",
"message": "Request validation failed.",
"param": "type",
"errors": [{ "path": "type", "message": "Use types instead." }],
"suggestedAction": "replace_param",
"repairCategory": "unknown_param",
"documentationPath": "/api/v1/openapi.json",
"doNotRetrySameRequest": true,
"requestId": "request-id"
}
}
Keep the requestId for debugging.
Repair metadata
v1.21 errors can include machine-readable repair fields, including write-readiness and impact details on supported product and Cost Book writes. Treat them as the next-request plan, not as hints that relax the schema.
| Field | Use it for |
|---|---|
repairCategory | Classify the failure, such as unknown_param, invalid_enum, unsupported_sync_filter, standalone_status, invalid_window, or rate_limited. |
suggestedAction | Choose the next action, such as add_required_filter, replace_param, choose_valid_value, pair_date_window, narrow_filter, wait_and_retry, or stop_and_fix_request. |
validValues | Use only accepted values for the named parameter. |
acceptedFilters | Pick one of the endpoint's supported narrowing filters instead of inventing a broad sync request. |
requiredCombination | Send the fields together, such as paired date-window parameters. |
maxWindowDays | Split changed-record discovery windows that are too wide. |
documentationPath | Open /api/v1/openapi.json or /llms.txt for the exact contract guidance. |
example | Shape the corrected request with an endpoint-specific example. |
doNotRetrySameRequest | Change the request before retrying. Do not loop the identical request. |
writeReadiness | Review machine-readable readiness details when a write request fails validation or policy checks. |
impactAnalysis | Inspect product or product-family impact details before retrying a constrained catalog write. |
For example, if /search rejects type, replace it with types. If /contacts rejects a broad list request, provide parentType and parentId. If /orders or /purchase-orders rejects standalone status, pair it with an exact identifier or a bounded date window.
Common codes
| Code | Caller action |
|---|---|
invalid_api_key | Check the environment value and bearer header format. |
api_key_revoked | Stop and request a new key. |
api_key_expired | Stop and request a new key. |
api_actor_inactive | Stop and issue a key to an active user. |
missing_scope | Use a key with the required scope. |
missing_permission | Use an acting user with the required ShelfCycle permission. |
rate_limited | Wait for Retry-After or retryAfterSeconds. Retry POSTs with the same idempotency key. |
validation_failed | Fix the field named in param or errors[]. |
finder_filter_required | Add a supported narrowing predicate before retrying a root finder. |
unsupported_sync_filter | Remove sync-style filters such as updatedSince. Root finders are not exports. Reference directory and child-list routes support cursor paging only in v1.21. For sales orders and purchase orders, use paired updatedAtFrom/updatedAtTo only for bounded changed-record discovery. |
not_found | Re-search and confirm the id. Cross-org, unsupported, archived, or invisible records can also return not_found. |
idempotency_key_required | Send a stable Idempotency-Key for POST requests. |
idempotency_key_reused | Retry with the original body or choose a new key for a new source event. |
duplicate_customer | Use the existing customer or change the source data before retrying. |
duplicate_supplier | Use the existing supplier or change the source data before retrying. |
duplicate_product | Use the existing product or change product setup references before retrying. |
duplicate_family | Use the existing product family or change the family identity before retrying. |
duplicate_contact | Use the existing contact or change the source data before retrying. |
duplicate_customer_location, duplicate_customer_address, duplicate_supplier_ship_from_location | Use the existing child record or change source identity/address data before retrying. |
duplicate_document | Use the existing attached document or change the source URL before retrying. |
stale_record | Refresh context before retrying. |
archived_record | Do not write to archived records. |
unsupported_linked_record | Remove unsupported records. Orders and purchase orders are context, not note subjects or links. |
not_api_created | Do not patch that note through the public API. |
primary_change_unsupported, address_role_required | Remove primary switching from customer location requests, or keep customer addresses marked billing or shipping. |
unsupported_product_field, unsupported_family_field, invalid_variable_qty, reference_visibility_blocked | Keep product setup inside the public contract, use quantity 1 for variable packaging, and verify referenced records are visible. |
document_url_invalid, document_url_cross_org, unsupported_document_field | Send only fileName and an HTTPS URL that belongs to the authenticated org when it points at a ShelfCycle bucket. |
blocked_order_sensitive_update | Change only allowed metadata fields, or leave order-sensitive product identity, packaging, and supplier fields unchanged. |
On dryRun=true, HTTP 200 can still return data.status: "blocked". Inspect blocked checks such as duplicate_candidate_found, idempotency_target_missing, idempotency_key_required, idempotency_key_reused, stale_record, archived_record, target_not_found_or_not_visible, unsupported_profile_shape, primary_change_unsupported, address_role_required, product_family_reference_active, product_packaging_reference_active, supplier_reference_active, reference_visibility_blocked, order_sensitive_update_blocked, unsupported_linked_record, not_api_created, or validation_failed before execution.
Rate limits
Default private v1 limits are 60 read requests per minute per key and endpoint, and 20 write requests per minute per key and endpoint.
On 429 rate_limited, read Retry-After and wait before retrying. Successful authenticated requests and rate-limited requests include X-RateLimit-Remaining when the rate-limit check runs.
Stop on stale, duplicate, missing access, unsupported sync, revoked, expired, archived, or rate-limit errors. Do not keep retrying without new information.