Skip to main content

Usage Guide

This guide explains every shipper EDI endpoint: what it is for, when to use it, and how it connects to the other endpoints.

Step 1: Authentication​

Shippers can generate an API key in either way:

  1. Cargors Portal — portal.cargors.com → NEW RFQ → Integrations → Cargors API
  2. Key management API — POST /api-keys with HTTP Basic Auth

Full walkthrough: Shipper Generate API key.

Send these headers with every request:

  • x-api-key: <API_KEY_VALUE>
  • Content-Type: application/json

Use the same API base URL for testing and live traffic: https://app.cargors.com/api/edi.

  • With a Development API key, create endpoints validate your payload and return a sample response — no real RFQ or shipment is created.
  • With a Production API key, those endpoints create real RFQs / shipments. Production keys require Go live access; after testing, email operations@cargors.com to request it.

POST /quotes returns real live quote estimates in both modes.
GET /rfqs/{rfqId}/quotes returns only quotes already stored for that RFQ (empty array if none yet).

See Development vs Production API keys.

Rate limits and shipment-status polling​

The EDI API does not currently impose a fixed application-level request quota per API key. Quote requests are calculation-intensive, so call them on demand rather than polling aggressively.

Shipment webhooks are not currently available. For status synchronization, poll GET /shipments every five minutes, follow its pagination metadata, and use pickup or delivery date filters to limit the result set. Retry temporary failures with exponential backoff.


Step 2: Choose your integration flow​

Pick one booking flow. Do not mix accept endpoints across flows.

FlowWhen to useEndpoints
A — Instant shipmentBook immediately; Cargors picks the carrierPOST /shipments
B — Quote-first (live)Compare live prices, then book without saving an RFQ firstPOST /quotes → POST /accept-quote
C — RFQ-first (stored)Save an RFQ, read stored quotes later, accept with quoteId onlyPOST /rfqs → GET /rfqs/{rfqId}/quotes → POST /rfqs/{rfqId}/accept-quote

Endpoint purpose cheat sheet​

EndpointPurposeCreates RFQ?Creates shipment?
POST /shipmentsInstant bookingYes (internal)Yes
POST /quotesLive quotes for a payload (nothing saved)NoNo
POST /accept-quoteAccept a live /quotes result (carrierId + full payload)YesYes
POST /rfqsSave open RFQ onlyYesNo
GET /rfqs/{rfqId}/quotesRead stored quotes for that RFQ (shipper prices only)NoNo
POST /rfqs/{rfqId}/accept-quoteAccept stored quote (quoteId only)Already existsYes
GET /shipmentsList shipmentsNoNo
GET /shipments/{shipmentId}Get one shipmentNoNo
POST /shipments/{shipmentId}/labelDownload labelNoNo

Which accept API?​

  • After POST /quotes → use POST /accept-quote (carrierId + full shipment payload).
  • After POST /rfqs → use POST /rfqs/{rfqId}/accept-quote (quoteId only).

Continue with Section A, Sections B–C, or Sections D–F below.


Optional: Assign a booking to a sub-shipper​

Use your parent shipper's API key and send subShipperEmail, the sub-shipper's existing portal login email. Create the sub-shipper in the portal first. The API does not create users.

EndpointWhere to send subShipperEmailResult
POST /shipmentsInside each shipments itemLinks that item's RFQ and shipment to the sub-shipper
POST /quotesTop level of the request bodyValidates the identity; saves nothing
POST /accept-quoteTop level, alongside carrierId and shipment detailsLinks the new RFQ and shipment to the sub-shipper
POST /rfqsTop level, alongside shipment detailsLinks the new RFQ to the sub-shipper
POST /rfqs/{rfqId}/accept-quoteTop level, alongside quoteId (optional)Assigns the resulting shipment to this sub-shipper; omission inherits the RFQ owner

For instant booking, add the field to the full shipment payload shown in Section A:

{
"shipments": [
{
"subShipperEmail": "logistics@example.com",
"pickupDetails": { "...": "full pickup details from Section A" },
"deliveryDetails": { "...": "full delivery details from Section A" },
"loadType": "LTL",
"loadInformation": [{ "...": "full load details from Section A" }]
}
]
}

This is a placement example; replace the abbreviated objects with full shipment data. You can assign different sub-shippers within one batch or omit the field on individual items to book as the parent.

For an RFQ-first booking, send the email when creating the RFQ. Accepting its quote with only quoteId preserves that owner:

{ "quoteId": "6650ab2d9f1f2b4a6c789099" }

To explicitly assign the resulting shipment when accepting a stored quote:

{
"quoteId": "6650ab2d9f1f2b4a6c789099",
"subShipperEmail": "logistics@example.com"
}

For live quotes, resend subShipperEmail in POST /accept-quote; POST /quotes saves no ownership information.

The email is trimmed and matched case-insensitively. It must identify an active, unblocked sub-shipper under your own account. Unknown, removed, blocked, or other company's sub-shippers return HTTP 400 with error code badRequest. An invalid identity never silently falls back to the parent. All identities in an instant-booking batch are checked before any records are created, including with Development keys.

Omitting the field or sending null, "", or whitespace keeps existing booking behavior without error. No sub-shipper lookup is performed for these values. Billing, prices, and the API key stay with the parent shipper. Ownership uses the same link as portal-created records, so the sub-shipper can see and manage them according to their portal rights; this field does not grant additional rights. An explicit owner at RFQ acceptance applies to the new shipment and leaves the original RFQ's creator unchanged.


Section A: Create an instant shipment​

Endpoint​

Purpose​

Create one or multiple shipments immediately (up to 10 in one request).
Cargors chooses the carrier and returns shipment details. No quote comparison step.

Duplicate reference validation​

For POST /shipments, each pickupDetails.reference is checked against existing pickup references, and each deliveryDetails.reference against existing destination references, in both RFQs and shipments. Matches are exact and case-sensitive, across all shippers and statuses. A match on either reference rejects the request.

Pickup references must also be distinct within the batch, as must delivery references. A duplicate rejects the entire batch with HTTP 400 before any RFQ or shipment is created. This applies to both Development and Production API keys.

The response uses error code badRequest and one of these messages:

  • Pickup reference or delivery reference already exists
  • Pickup reference or delivery reference is duplicated in this request

For example, an existing database reference returns:

{
"errors": [
{
"code": "badRequest",
"message": "Pickup reference or delivery reference already exists"
}
]
}

The checks do not guarantee duplicate prevention for simultaneous requests. If a creation response is lost, check the shipment list before retrying.

Minimum required shipment fields​

  • pickupDetails
  • deliveryDetails
  • loadType
  • loadInformation

Example request​

{
"shipments": [
{
"pickupDetails": {
"streetName": "Science Park",
"streetNo": "608",
"postCode": "1098 XH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "12-02-2026",
"fromTime": "19:00",
"toTime": "20:00",
"reference": "NA",
"person": "Najeeb",
"company": "Cargors B.V.",
"email": "najeeb@cargors.com",
"phone": "0655772164"
},
"deliveryDetails": {
"streetName": "Contactweg",
"streetNo": "22",
"postCode": "1014 BH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "13-02-2026",
"fromTime": "08:30",
"toTime": "17:00",
"reference": "NA",
"person": "Inara",
"company": "Cargors",
"email": "operations@cargors.com",
"phone": "0031655772164"
},
"vehicleType": "No preference",
"transportType": "Standard - dry",
"equipment": ["No truck equipment needed"],
"loadType": "LTL",
"loadInformation": [
{
"quantity": 2,
"width": 80,
"height": 200,
"length": 120,
"weight": 200,
"typegoods": "Euro pallets"
}
]
}
]
}

Example success response​

{
"shipments": [
{
"shipmentId": "SH12A",
"shipmentStatus": "UPCOMING",
"pickupData": { "date": "12-02-2026", "fromTime": "19:00", "toTime": "20:00" },
"deliveryData": { "date": "13-02-2026", "fromTime": "08:30", "toTime": "17:00" }
}
]
}

Section B: Get quotes for shipment data (Flow B, step 1)​

Endpoint​

Purpose​

Return live quote options for a shipment payload. Nothing is saved (no RFQ, no shipment).

Use this only for Flow B. For saved RFQs, use GET /rfqs/{rfqId}/quotes instead.

Important note​

Save the carrierId from the response. You must pass it to POST /accept-quote with the same shipment payload.

Example request​

Use the same shipment structure as the quote acceptance endpoint:

{
"pickupDetails": {
"streetName": "Science Park",
"streetNo": "608",
"postCode": "1098 XH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "12-02-2026",
"fromTime": "19:00",
"toTime": "20:00",
"reference": "NA",
"person": "Najeeb",
"company": "Cargors B.V.",
"email": "najeeb@cargors.com",
"phone": "0655772164"
},
"deliveryDetails": {
"streetName": "Contactweg",
"streetNo": "22",
"postCode": "1014 BH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "13-02-2026",
"fromTime": "08:30",
"toTime": "17:00",
"reference": "NA",
"person": "Inara",
"company": "Cargors",
"email": "operations@cargors.com",
"phone": "0031655772164"
},
"vehicleType": "No preference",
"transportType": "Standard - dry",
"equipment": ["No truck equipment needed"],
"loadType": "LTL",
"loadInformation": [
{
"quantity": 2,
"width": 80,
"height": 200,
"length": 120,
"weight": 200,
"typegoods": "Euro pallets"
}
]
}

Example success response​

{
"quotes": [
{
"carrierId": "6650ab2d9f1f2b4a6c789012",
"companyName": "Carrier One",
"totalPrice": "122.44"
}
]
}

Section C: Accept selected quote and create shipment (Flow B, step 2)​

Endpoint​

Purpose​

Accept a live quote from POST /quotes, create the RFQ internally, and create the shipment.

Requires carrierId and the full shipment payload.
If you already have an RFQ + quoteId from Flow C, use POST /rfqs/{rfqId}/accept-quote instead.

Required fields​

  • carrierId from /quotes response
  • Full shipment payload (same shape as /quotes)

Example request​

{
"carrierId": "6650ab2d9f1f2b4a6c789012",
"pickupDetails": {
"streetName": "Science Park",
"streetNo": "608",
"postCode": "1098 XH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "12-02-2026",
"fromTime": "19:00",
"toTime": "20:00",
"reference": "NA",
"person": "Najeeb",
"company": "Cargors B.V.",
"email": "najeeb@cargors.com",
"phone": "0655772164"
},
"deliveryDetails": {
"streetName": "Contactweg",
"streetNo": "22",
"postCode": "1014 BH",
"city": "Amsterdam",
"country": "Netherlands",
"countryCode": "NL",
"date": "13-02-2026",
"fromTime": "08:30",
"toTime": "17:00",
"reference": "NA",
"person": "Inara",
"company": "Cargors",
"email": "operations@cargors.com",
"phone": "0031655772164"
},
"vehicleType": "No preference",
"transportType": "Standard - dry",
"equipment": ["No truck equipment needed"],
"loadType": "LTL",
"loadInformation": [
{
"quantity": 2,
"width": 80,
"height": 200,
"length": 120,
"weight": 200,
"typegoods": "Euro pallets"
}
]
}

Example success response​

{
"shipmentId": "SH1234",
"shipmentStatus": "UPCOMING",
"shipmentLabel": "https://signed-url/shipment_labels/SH1234.pdf",
"rfqId": "6650ce14fb1d6f4a7ef12345"
}

Section D: Create an RFQ (Flow C, step 1)​

Endpoint​

  • POST https://app.cargors.com/api/edi/rfqs
  • Reference: Create an RFQ

Purpose​

Create an open RFQ from a shipment payload. No shipment is created.

Next: poll GET /rfqs/{rfqId}/quotes, then accept with POST /rfqs/{rfqId}/accept-quote.

With a Development API key, the payload is validated and a sample rfqId is returned — no RFQ is persisted.

Duplicate reference validation​

Same rules as POST /shipments: pickup and delivery references must be unique across existing RFQs and shipments.

Example request​

Use the same shipment structure as POST /quotes.

Example success response​

{
"rfqId": "RFQNL123456",
"rfqStatus": "OPEN",
"pickupData": { "date": "12-02-2026", "fromTime": "19:00", "toTime": "20:00" },
"deliveryData": { "date": "13-02-2026", "fromTime": "08:30", "toTime": "17:00" }
}

Section E: Get quotes for an RFQ (Flow C, step 2)​

Endpoint​

Purpose​

Return stored quotes for an RFQ created with POST /rfqs.

Only shipper-facing prices are returned (totalPrice, transportPrice, etc.). Carrier-net prices such as baseTotalPrice are never included.

If no quotes are stored yet, quotes is [] — poll again later.
Do not confuse with POST /quotes (live quotes, no RFQ).

Save quoteId from the response for Section F.

Example success response​

{
"rfqId": "RFQNL123456",
"rfqStatus": "OPEN",
"quotes": [
{
"quoteId": "6650ab2d9f1f2b4a6c789099",
"carrierId": "6650ab2d9f1f2b4a6c789012",
"companyName": "Carrier One",
"totalPrice": 122.44,
"transportPrice": 118,
"fuelSurchargePrice": 4.44
}
]
}

Section F: Accept an RFQ quote and create shipment (Flow C, step 3)​

Endpoint​

Purpose​

Accept a stored RFQ quote and create the shipment.

Unlike POST /accept-quote (Flow B), you only send quoteId — no full shipment payload and no carrierId.

Required fields​

  • Path: rfqId from POST /rfqs
  • Body: quoteId from GET /rfqs/{rfqId}/quotes

Example request​

{
"quoteId": "6650ab2d9f1f2b4a6c789099"
}

Example success response​

{
"shipmentId": "SH1234",
"shipmentStatus": "UPCOMING",
"shipmentLabel": "https://signed-url/shipment_labels/SH1234.pdf",
"rfqId": "RFQNL123456",
"quoteId": "6650ab2d9f1f2b4a6c789099",
"pickupData": { "date": "12-02-2026", "fromTime": "19:00", "toTime": "20:00" },
"deliveryData": { "date": "13-02-2026", "fromTime": "08:30", "toTime": "17:00" }
}

Section G: List shipments and synchronize statuses​

Endpoint​

Purpose​

List the authenticated shipper's shipments (all statuses), newest first. Use for status sync when webhooks are not available.

You can limit the result set with pickupDateFrom, pickupDateTo, deliveryDateFrom, and deliveryDateTo. Dates use DD-MM-YYYY format.

Use page and limit for pagination. page defaults to 1; limit defaults to 50 and accepts values from 1 to 100. Continue requesting pages while pagination.hasNextPage is true.

Example success response​

{
"shipments": [
{
"shipmentId": "SH12A",
"shipmentStatus": "INVOICE_CREATED",
"createdAt": "2026-08-26T10:30:00.000Z"
}
],
"pagination": {
"currentPage": 1,
"pageSize": 50,
"totalItems": 64,
"totalPages": 2,
"hasNextPage": true,
"hasPreviousPage": false
}
}

Section H: Get shipment details by shipment ID​

Endpoint​

Purpose​

Get details and current status for one shipment by shipmentId.

Example success response​

{
"shipmentId": "SH1234",
"shipmentStatus": "UPCOMING",
"pickupData": { "date": "12-02-2026", "fromTime": "19:00", "toTime": "20:00" },
"deliveryData": { "date": "13-02-2026", "fromTime": "08:30", "toTime": "17:00" }
}

Section I: Download shipment label​

Endpoint​

Purpose​

Download the shipment label PDF for a specific shipmentId (after booking via Flow A, B, or C).

Example success response​

{
"blob": "base64 encoded blob",
"contentType": "application/pdf",
"filename": "label.pdf"
}