Skip to content

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"
}
  • code is the contract. Switch on it. It is stable within v1.
  • detail is for people. Show it or log it, but never parse it: its wording can change, and a few platform-wide refusals (for example Company.Suspended) are worded in the language of the request’s Accept-Language header.
  • args names 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 as dealId).
  • 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.

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 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.

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.