Errors
Errors are problem details (RFC 9457). Most are served as application/problem+json, a few as application/json: read the body by its shape, not by the content type.
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "Integration.IdempotencyKeyRequired", "status": 400, "detail": "Send an X-Idempotency-Key header (1 to 128 characters) with every write: create, update, cancel and upload.", "code": "Integration.IdempotencyKeyRequired"}codeis the contract. Switch on it. It is stable within v1.detailis for people. Show it or log it, but never parse it: its wording can change, and a few platform-wide refusals (for exampleCompany.Suspended) are worded in the language of the request’sAccept-Languageheader.argsnames what the refusal is about, when there is one thing to name: the field of an update (args.field), the metadata key (args.key), the deal of an awarded request (args.dealId, also at the top asdealId).- A problem can carry more fields than shown here. Ignore the ones you do not know.
The token endpoint is the one exception: it answers OAuth errors ({ "error": "...", "error_description": "..." }), as RFC 6749 asks. See Authentication.
Validation errors
Section titled “Validation errors”When fields fail validation the answer is 422 with an errors object keyed by the field’s path in your body:
{ "title": "Validation Error", "status": 422, "detail": "...", "errors": { "origin.countryCode": ["..."], "transportMode": ["Unknown value 'Truck'. Expected one of: Road, Sea, Air, Rail, Multimodal, HandCarry."] }}Fields are checked by the same rules as a request made in the app. Text values such as transportMode, modePolicy and incoterm are matched by name, in any case; a number is never accepted for them.
Status codes
Section titled “Status codes”| Status | What it means | What to do |
|---|---|---|
400 |
The request is incomplete or breaks a rule: no X-Idempotency-Key (Integration.IdempotencyKeyRequired), no externalReference (Integration.ExternalReferenceRequired), neither or both deal filters (Integration.DealFilterRequired), an update body that is not a JSON object (Integration.InvalidBody), names an unknown or read-only field (Integration.UnknownField, Integration.FieldNotUpdatable) or clears one that cannot be cleared (Integration.FieldNotClearable), a metadata limit (Integration.MetadataInvalid), text with a control character such as U+0000 (Integration.InvalidBody naming the field, Integration.MetadataInvalid naming the key), an upload without a file or a category (Document.FileRequired, Document.InvalidCategory), a category the app does not offer your side at the deal’s stage (Document.CategoryNotOffered, with args.offered), or a business rule refusal. |
Read code, detail and args, fix the request. |
401 |
No valid key or token (Integration.Unauthorized). The reason is never given. |
Check the key, its expiry and your outbound address; get a new token if it expired. Your Owner or Admin sees the call in the call log. |
403 |
Authenticated but not allowed: a missing scope (Integration.Forbidden), the key may not be used right now (Integration.AccessDenied), a suspended company (Company.Suspended), a create by a company that is not a shipper (Company.WrongSegment), or a live key on the sandbox route (Integration.SandboxOnly). |
Check the key’s scopes and mode. If it persists, ask your Owner or Admin. |
404 |
Not found: the record does not exist, your company is not a party to it, or it belongs to the other mode (live or test). A deal’s tracking answers 404 until the deal goes live (Tracking.NotFound). |
Check the id and the key’s mode. |
409 |
A conflict: the externalReference is already used (Integration.DuplicateExternalReference, with requestId), a call with the same idempotency key is still running (Retry-After: 1), the request cannot change in its state (ShipmentRequest.InvalidTransition), it was awarded and is a deal (Integration.RequestAwarded, with dealId), it was opened in the app, not created through the API (Integration.RequestNotCreatedViaApi), the deal was withdrawn and takes no documents (Document.DealNotOpen), a carrier’s transport document before the shipper’s papers were approved (Document.BillOfLadingTooEarly), a sandbox step out of order (Integration.SandboxTransition), a sandbox document’s download (Integration.SandboxFileNotKept), or a document not scanned clean yet (Document.NotClean). |
For a duplicate, use the returned requestId. For a call still running, retry with the same key a second later. For a state, read the request and see Updating and cancelling. For a document, try again later. |
412 |
If-Match does not match the request’s current ETag: it changed after you read it (Integration.PreconditionFailed). Nothing was written. The answer is not kept for replay. |
Read the request again, review what changed, and retry with the new ETag (the same X-Idempotency-Key is fine). |
413 |
The uploaded file is over 25 MB (Document.PayloadTooLarge), or an update’s body is over 64 KB (Integration.BodyTooLarge, with args.limitBytes). |
Send a smaller file or body. |
415 |
The upload’s type is not allowed, or its contents do not match the declared type (Document.UnsupportedMediaType): for example an archive declared as a DOCX or XLSX that is not a Word or Excel package. |
Send a PDF, JPEG, PNG, DOCX or XLSX, declared as what it is. |
422 |
Validation failed: errors lists each field’s reasons. Or the uploaded file failed the malware scan (Document.Infected) and was not stored. Or the X-Idempotency-Key was already used for a different request on this route (Integration.IdempotencyKeyReused); nothing ran. |
Fix the fields and send with a new X-Idempotency-Key. Never retry an infected file. For a reused key, send the new request with a new key. |
429 |
The rate limit was reached, the company’s daily upload allowance (Integration.DailyUploadLimit), or the key’s daily allowance of new idempotency keys (Idempotency.DailyLimit, nothing ran). The answer is not kept for replay. |
Wait the Retry-After seconds when present, else a few seconds, and back off on repeats; the same X-Idempotency-Key is fine. See Rate limits. |
5xx |
A temporary problem on our side, for example the upload’s storage (Document.StorageFailed, 502), the malware scanner (Document.ScanUnavailable, 503) or the carriers’ side of an amendment (ShipmentRequest.AmendmentNotDelivered, 503, nothing applied). The answer is not kept for replay. |
Retry with the same X-Idempotency-Key and externalReference. If the request was created, you get it back, never a second one. |
| Code | Status |
|---|---|
Integration.Unauthorized |
401 |
Integration.Forbidden |
403 |
Integration.AccessDenied |
403 |
Integration.SandboxOnly |
403 |
Company.Suspended |
403 |
Company.WrongSegment |
403 |
Integration.IdempotencyKeyRequired |
400 |
Integration.ExternalReferenceRequired |
400 |
Integration.DealFilterRequired |
400 |
Integration.InvalidBody |
400 |
Integration.UnknownField |
400 |
Integration.FieldNotUpdatable |
400 |
Integration.FieldNotClearable |
400 |
Integration.MetadataInvalid |
400 |
Document.FileRequired |
400 |
Document.InvalidCategory |
400 |
Document.CategoryNotOffered |
400 |
Document.LoadingInstructionShipperOnly |
400 |
ShipmentRequest.NotFound |
404 |
Deal.NotFound |
404 |
Tracking.NotFound |
404 |
Document.NotFound |
404 |
Integration.DuplicateExternalReference |
409 |
Integration.SandboxTransition |
409 |
Document.NotClean |
409 |
ShipmentRequest.InvalidTransition |
409 |
Integration.RequestNotCreatedViaApi |
409 |
Integration.RequestAwarded |
409 |
Document.DealNotOpen |
409 |
Document.BillOfLadingTooEarly |
409 |
Integration.SandboxFileNotKept |
409 |
Integration.PreconditionFailed |
412 |
Document.PayloadTooLarge |
413 |
Integration.BodyTooLarge |
413 |
Document.UnsupportedMediaType |
415 |
Document.Infected |
422 |
Integration.IdempotencyKeyReused |
422 |
Integration.DailyUploadLimit |
429 |
Idempotency.DailyLimit |
429 |
Document.StorageFailed |
502 |
Document.ScanUnavailable |
503 |
ShipmentRequest.AmendmentNotDelivered |
503 |
Other 400 codes come from the platform’s own business rules. New codes can be added within v1, so treat an unknown code by its HTTP status.
Publishing refusals are not errors
Section titled “Publishing refusals are not errors”A create with "publish": true that may not publish still succeeds: the request is created as a draft, and publish.refusalCode and publish.refusalDetail in the 201 answer say why (for example PageAccess.ApprovalRequired). A person can publish it in the app.
