Getting started

Authentication

D2 is Paltrack's message switch service — it receives structured data from authorised vendors and routes it to the relevant parties on the network. This API is the integration point for that exchange.

Every endpoint accepts either a bearer token or an API key — pick whichever suits your integration, no need to send both. The examples on this page show the Authorization: bearer header for brevity, but an x-api-key header works identically on every endpoint in this guide.

Bearer Token

Request a token using the OAuth 2 Resource Owner Password flow. Tokens are valid for 60 minutes; request a new one before expiry.

HTTP
POST https://helpdesk.paltrack.co.za/token
Content-Type: application/x-www-form-urlencoded

grant_type=password&username={your_username}&password={your_password}

The response returns an access_token:

JSON
{
  "access_token": "eyJ0eXAiOiJKV1QiLC...",
  "token_type":   "bearer",
  "expires_in":   3599
}

Pass the token on every subsequent request:

HTTP Header
Authorization: bearer {access_token}

API Key

A simpler alternative to Bearer tokens for server-to-server integrations that don't want to manage token refresh. API keys don't expire on a fixed cycle — they're valid until you revoke them, or until an optional expiry date you set yourself.

HTTP Header
x-api-key: {your_api_key}

Getting a key:

  • Self-service — log in to Paltrack's client portal (PalPortal) and go to API Keys. Click Generate to issue a new key for your company.
  • Via Paltrack support — if you don't have PalPortal access, ask your Paltrack contact to issue one from your company record in Helpdesk.

Each key is tied to a single company (not an individual user), so any of your integrations can share one key. You can view your keys and revoke them any time from the same API Keys screen.

Credential scoping

Both bearer tokens and API keys are scoped to your Paltrack Location Code(s) and Location ID(s) — Depots, Packhouses, and Exporters can only access data linked to their own location codes, and only the data D2's Split rule engine has determined they should receive. This is the same scoping logic D2 Classic (flat files) uses — nothing changes here for vendors migrating from flat files to the API.

Getting started

Testing

A QA environment is available so you can test your integration before going live:

Base URL
https://papiqa.paltrack.co.za

Endpoint paths and request/response formats are identical to production — just point your integration at this host instead. For a QA API key, contact your Paltrack representative — QA keys are issued directly rather than through self-service PalPortal. Credential scoping applies in QA exactly as it does in production.

Rate Limiting & Content Type

All clients are limited to 60 requests per minute per IP address. Exceeding this returns HTTP 429. Implement exponential back-off and retry logic in your client.

All requests and responses use JSON only. Set Content-Type: application/json on every POST request.

Inbound — sending data to D2

Submit Dispatch

The tables below cover the fields most integrations need. Several request bodies have deeply nested objects (e.g. Consignments[].Pallets[], Consignments[].Container) with many more optional fields than are practical to list here — for the complete field list on any endpoint, see the Swagger UI.

POST /v2/SubmitDispatch

Submit a truck dispatch (PO file) to D2 for routing to the destination depot. Returns a trackingId for the submission.

Request body — POFileDTO

Field Type Description
POFileNameREQstringUnique file name for this dispatch
LocationIdREQstringD2 depot ID where pallets were loaded
LocationCodeREQstringD2 depot code where pallets were loaded
BatchNumberREQstringSequence number for this batch file
TruckHeaderREQobjectTruck and load details (see below)
ConsignmentsREQarrayOne or more consignment documents (see below)
CreatedDateTimedatetimeFile creation timestamp — defaults to now
ProviderstringName of your system — defaults to "Paltrack API"
VersionstringVersion of your application
POPreviousFileNamestringPrevious file name — only set on re-submissions

TruckHeader key fields

FieldTypeDescription
PalletQuantityREQint?Number of pallets on the truck
CartonQuantityREQint?Number of cartons on the truck
NextTypeREQstringDP (depot) or CU (customer)
NextCodeREQstringLocation code the truck is going to next
SeasonREQstringSeason identifier
TransactionUserREQstringUser creating/updating the record
TransactionDateTimeREQdatetimeDate/time of the transaction
LoadReferencestringTruck registration or load reference
LoadNamestringLoad name or trip number
TransportstringMode of transport — R (rail) or T (truck)
LoadTypestringLoad type — F (flat-bed) or R (reefer)
Temperaturedecimal?Set temperature
LoadIdint?Internal load ID (optional)

Note: SealNumber is not a TruckHeader field — it belongs to Container, a separate nested object under Consignment.

Consignment key fields

FieldTypeDescription
DocumentNumberstringConsignment note / document number
OrganisationstringExporter / organisation code
DocumentTypestringDocument type code
DocumentDatestringDocument date
SeasonstringSeason year (e.g. "2026")
ChannelstringMarketing channel
ToLocationCodestringDestination depot code
CartonQuantityintNumber of cartons
PalletQuantitydecimalNumber of pallets
PalletsarrayIndividual pallet records
Full schema for TruckHeader, Consignment, and their nested Container/Location/Pallet objects: see Swagger.

Response

JSON — 200 OK
{
  "trackingId": "550e8400-e29b-41d4-a716-446655440000",
  "status":     "Received"
}
JSON — 400 / 409 Validation error
{
  "trackingId":  "550e8400-e29b-41d4-a716-446655440000",
  "payloadType": "Dispatch",
  "errors": [
    { "field": "POFileName", "message": "Required field missing" }
  ]
}

Submit Intake

POST /v2/SubmitIntake

Submit a cold-store intake (PI file) to D2. Use this to report pallet arrivals at your depot.

Request body — PIFileDTO

FieldTypeDescription
PIFileNameREQstringUnique file name for this intake
LocationCodeREQstringDepot code receiving the intake
LocationIdstringPaltrack depot ID
BatchNumberstringBatch sequence number
CreateDateTimedatetimeFile creation timestamp
ProviderstringName of your system
VersionstringVersion of your application
DocumentsarrayIntake documents (IntakeDocumentDTO[])

IntakeDocument key fields

FieldTypeDescription
DocumentNumberstringIntake document number
OrganisationstringExporter / organisation code
DocumentTypestringDocument type
DocumentDatestringDocument date
SeasonstringSeason year
ChannelstringMarketing channel
CartonQuantityintNumber of cartons
PalletQuantitydecimalNumber of pallets
FromLocationCodestringOrigin depot code
InspectorstringInspector identifier
InspectionDatedatetime?Inspection date
TemperaturedecimalTemperature reading
IntakeDocumentDTO has many more optional fields than shown above, including nested Pallet and TransferIn objects — see Swagger for the full schema.

Response shape is identical to Submit Dispatch.

Submit Stock

POST /v2/SubmitStock

Submit a full stock snapshot (PS file) to D2. Used to synchronise on-hand pallet records with authorised parties on the network.

Request body — PSFileDTO

FieldTypeDescription
PSFileNameREQstringUnique file name for this stock file
LocationCodeREQstringDepot code
LocationIdstringPaltrack depot ID
BatchNumberstringBatch sequence number
CreateDateTimedatetimeFile creation timestamp
ProviderstringName of your system
VersionstringVersion of your application
StockRecordsarrayPallet lines (StockRecordDTO[])

StockRecord key fields

FieldTypeDescription
PalletIdstringPaltrack pallet identifier
SSCCstringGS1 SSCC barcode
DocumentNumberstringAssociated consignment document
LocationCodestringCurrent depot code
OrganisationCodestringExporter code
CommoditystringCommodity code
VarietystringVariety code
PackstringPack code
GradestringGrade code
SizeCountstringSize/count code
MarkstringBrand/mark code
CartonQuantitystringNumber of cartons on pallet
PalletQuantitystringPallet quantity (usually 1)
IntakeDatestringDate pallet was received
TemperaturestringStorage temperature
PalletStatusstringCurrent pallet status code
StockRecordDTO has many more optional fields than shown above — see Swagger for the full schema.

Response shape is identical to Submit Dispatch.

Submit Mates

POST /v2/SubmitMates

Submit a Mates Receipt (MT file) to D2 — the ship discharge record confirming which pallets came off at each port. Use this once a vessel's cargo has been reconciled against the ship's manifest.

Request body — MTFileDTO

FieldTypeDescription
MTFileNameREQstringUnique file name for this Mates submission
LocationIdstringPaltrack depot ID
LocationCodeREQstringDepot code submitting the Mates file
BatchNumberstringBatch sequence number
CreatedDateTimedatetimeFile creation timestamp
ProviderstringName of your system
VersionstringVersion of your application
ShipHeaderREQobjectShip header details (OL record, see below)

ShipHeader key fields

FieldTypeDescription
ShipNumberREQstringShip number
OrganisationstringOrganisation code
SeasonstringSeason identifier
TransportstringTransport type
LoadTypestringShip type
LoadNamestringShip name
StartDatedatetimeStart loading date
DepartureDatedatetime?Departure date
NextType / NextCodestringNext location type/code the ship is destined for
LoadingLocationREQobjectLoading port details (ON record, TranType = L)
DispatchLocationREQarrayOne or more discharge/destination locations (ON records, TranType = D), each carrying its own pallet records

Both LoadingLocation and each entry in DispatchLocation share the same shape — the file format lists loading and discharge legs as siblings, not a parent/child pair.

DispatchLocation[].PalletRecords[] key fields — MTPalletRecordDTO

FieldTypeDescription
PalletIdREQstringPallet ID (typically placed in the SSCC field)
SSCCREQstringSSCC (18-character) identifier
CommodityREQstringCommodity code
VarietyREQstringVariety code
PackREQstringPack code
GradeREQstringGrade code
FarmREQstringFarm code
CartonQuantityintNumber of cartons — must be greater than zero
PalletQuantitydecimalPallet quantity — must be greater than zero (see part-pallet rule)
OrganisationstringOrganisation code
ConsignmentNumberstringOriginal intake consignment number
DestinationLocationstringDestination location code
MTShipHeaderDetailDTO, MTTransportLocationDTO and MTPalletRecordDTO have many more optional fields than shown above — see Swagger for the full schema.

Response shape is identical to Submit Dispatch.

Submit Day Instruction

POST /v2/SubmitDayInstruction

Submit a Day Instruction (DI file) to D2 — a loading instruction for a single order (one ship or truck load). Unlike Dispatch/Intake/Stock, this is an instruction to be actioned, not a completion record.

Request body — DIFileDTO

FieldTypeDescription
DIFileNameREQstringUnique file name for this Day Instruction submission
LocationIdstringPaltrack depot ID of the sending depot
LocationCodeREQstringDepot code of the sending depot
BatchNumberstringBatch sequence number
CreateDateTimedatetimeFile creation timestamp
ProviderstringName of your system
VersionstringVersion of your application
OrderREQobjectThe loading instruction order (LO record, see below)

Order key fields — DIOrderDTO

FieldTypeDescription
LocationCodestringSending depot code
OrderNumberstringUnique order number for trucks; blank for ships
OrderTypestringV (conventional ship), T (container ship), D (normal truck), Z (container truck)
ShipNumberstringShip number
ShipNamestringShip name
DestinationTypestringPO (ships), DP/CU (depot/customer for trucks)
DestinationCodestringDischarge port (ships) or destination depot/customer code (trucks)
InstructionstringInstruction text
OrderStatusstringBlank for ships, O (open) or C (closed) for trucks
PortstringPort code
OrderDetailsREQarrayOne or more loading instruction line items (LD records, see below)

OrderDetails[] key fields — DIOrderDetailDTO

FieldTypeDescription
LocationCodestringSending depot code
DestinationType / DestinationCodestringDestination port (ships) or depot/customer (trucks)
CommoditystringCommodity code
VarietystringVariety code
PackstringPack code
GradestringGrade code
FarmstringFarm code
TargetMarketstringTarget market code
UnitTypestringP (pallet-level) or C (carton-level) instruction
InstructionQuantityint?Quantity to be shipped
ShippedQuantityint?Quantity already shipped
PalletIdstringPallet SSCC, where the instruction targets a specific pallet
PrioritystringAscending priority order (0–9) in which the order is loaded
DIOrderDTO and DIOrderDetailDTO have more optional fields than shown above — see Swagger for the full schema.
No part-pallet rule on Day Instruction Day Instruction is an instruction to be actioned, not a completion record — the part-pallet rule (sum of PalletQuantity = 1) that applies to Dispatch/Intake/Stock/Mates does not apply here.

Response shape is identical to Submit Dispatch.

Submit Load Instruction

POST /v2/SubmitLoadInstruction

Submit a Loading Instruction (LI file) to D2 — a ship or truck loading instruction. A single submission can cover multiple headers (e.g. multiple ships/trucks or line positions), each with its own destination.

Request body — LIFileDTO

FieldTypeDescription
LIFileNameREQstringUnique file name for this Loading Instruction submission
LocationIdstringPaltrack depot ID where the instructions originate
LocationCodeREQstringDepot code where the instructions originate
BatchNumberstringBatch sequence number
CreateDateTimedatetimeFile creation timestamp
ProviderstringName of your system
VersionstringVersion of your application
InstructionHeadersREQarrayOne or more instruction headers (LH records, see below)

InstructionHeaders[] key fields — LIInstructionHeaderDTO

FieldTypeDescription
LocationCodestringSending depot code
OrderTypestringV (conventional ship), T (container ship), D (normal truck), Z (container truck)
OrderNumberstringBlank for ships, unique identifier for trucks
ShipNumber / ShipNamestringShip identifiers
DestinationType / DestinationCodestringDischarge port (ships) or destination depot/customer (trucks)
OrderStatusstringO (open) or C (closed); blank for ships
ChannelstringL (local) or E (export)
Commodity / Variety / Pack / GradestringProduct classification codes
InstructionQuantityint?Quantity of units to be shipped
InstructionDetailsREQarrayOne or more instruction line items (LD records, see below) — at least one header must carry at least one detail line

InstructionHeaders[].InstructionDetails[] key fields — LIInstructionDetailDTO

FieldTypeDescription
OrderNumber / ShipNumberstringMatches the parent header
LocationCodestringSending depot code
DestinationType / DestinationCodestringDestination port/depot/customer
Commodity / Variety / Pack / GradestringProduct classification codes
InstructionQuantityint?Quantity of units to be shipped
InstructionShippedint?Quantity already shipped
PoolstringPool code
WarehousestringWarehouse code
GtinstringGTIN (Global Trade Item Number)
AgreementCodestringeCert agreement code
LIInstructionHeaderDTO and LIInstructionDetailDTO have more optional fields than shown above — see Swagger for the full schema.
No part-pallet rule on Load Instruction Loading Instruction is an instruction to be actioned, and its line item (LD record) carries no per-pallet SSCC field — the part-pallet rule does not apply here.

Response shape is identical to Submit Dispatch.

Outbound — receiving data from D2

Find Payloads

GET /v1/FindPayloads

Returns all payloads queued for your depot(s) in D2. No request body or query parameters.

Response

JSON — 200 OK
[
  {
    "fileType":          "PO",
    "fileName":          "PO_PACKHOUSE_20260630_001.txt",
    "sourceDepot":       "PACKHOUSE",
    "destinationDepot":  "COLDSTORE",
    "exporterLocnId":    "P01",
    "documentNumber":    "DOC001",
    "season":            "2026",
    "statsDetailId":     12345
  }
]
FieldDescription
fileTypePayload type: PO, PI, PS, MT, DI, LI, etc.
fileNameOriginal file name of the transmission
sourceDepotDepot the payload originated from
destinationDepotIntended recipient depot
exporterLocnIdPaltrack exporter location ID
documentNumberDocument number(s) covered by this payload
seasonSeason year
statsDetailIdKey used in GetPayload and PayloadReceived
Results are scoped to the depots your account is authorised for in D2. An account with multiple depot permissions will see payloads for all of them.
Mates/Day Instruction/Load Instruction queue semantics For MT, one row is queued per discharge port on the ship; destinationDepot is that port and is informational only (Mates distribution is driven by Organisation Code, not destination), and documentNumber is left blank (a single Mates file can cover thousands of pallets sharing one original intake consignment number). For DI, one row is queued per file; documentNumber is the order number (the closest equivalent to a document ID). For LI, one row is queued per instruction header; destinationDepot/documentNumber reflect that header's own destination/order number.

Get Payload

POST /v1/GetPayload

Downloads the full data payload for a specific D2 transmission. Pass the PayLoadInfo object returned by FindPayloads as the request body.

Request body — PayLoadInfo

JSON
{
  "fileType":          "PO",
  "fileName":          "PO_PACKHOUSE_20260630_001.txt",
  "sourceDepot":       "PACKHOUSE",
  "destinationDepot":  "COLDSTORE",
  "exporterLocnId":    "P01",
  "documentNumber":    "DOC001",
  "season":            "2026",
  "statsDetailId":     12345
}

Response is the full JSON payload for the requested file. After processing it successfully, call PayloadReceived to mark it as consumed.

Payload Received

POST /v1/PayloadReceived

Acknowledges successful receipt and processing of a D2 payload. Marks the transmission as complete in D2 and removes it from the FindPayloads queue.

Request body is the same PayLoadInfo object used in GetPayload. At minimum, statsDetailId must be set.

JSON — 200 OK
{ "result": true }
Call this once per payload — only after your system has successfully processed the data. Failure to acknowledge will cause the payload to reappear in FindPayloads on subsequent polls.
Reference

Validation

Submitted payloads (Submit Dispatch / Submit Intake / Submit Stock / Submit Mates / Submit Day Instruction / Submit Load Instruction) go through two validation layers, in order. Which layer rejects your payload determines the shape of the error response.

Layer 1 — required fields

Fields marked REQ in the endpoint tables above (e.g. POFileName, TruckHeader, Consignments) are checked before your payload is looked at further. If one is missing, you get back a field-level error shape rather than the errors[] shape used elsewhere in this guide:

JSON — 400 Bad Request
{
  "model.POFileName": [ "The POFileName field is required." ],
  "model.TruckHeader": [ "The TruckHeader field is required." ]
}

This guide's tables cover the common fields only — nested objects (Consignment, TruckHeader, Pallet, etc.) have their own required fields too. The Swagger UI schema reflects all of these, since it's generated directly from the same model.

Layer 2 — business-rule validation

Once required fields pass, Paltrack runs payload-specific business rules and returns errors in the errors[] shape shown earlier in this guide.

CodeMeaning
INVALID_JSONPayload body isn't valid JSON
EMPTY_FIELDPayload body was empty
INVALID_VALUEA value failed validation — e.g. Consignments/Documents/StockRecords array is missing or empty, or payloadType is unsupported
MISSING_FIELDA field required by business logic (not caught by Layer 1) is missing, e.g. PalletQuantity on a pallet row
PART_PALLETSee below — sum of PalletQuantity for a pallet doesn't equal 1
DUPLICATE_PAYLOADReserved for future use — not yet triggered by any current validation rule
MISSING_DEPENDENCYReserved for future use — not yet triggered by any current validation rule
No part pallets For every unique SSCC/PalletId, the sum of PalletQuantity across all its rows must equal exactly 1 (0.001 tolerance). This applies to Consignments[].Pallets[] (Dispatch), Documents[].Pallets[] (Intake), StockRecords[] (Stock), and ShipHeader.DispatchLocation[].PalletRecords[] (Mates) alike. Submitting a pallet split across partial quantities will be rejected. Day Instruction and Load Instruction are instructions to be actioned rather than completion records, so this rule does not apply to either.
Paltrack's payload validation is under active development — expect the specific business rules enforced here to expand over time. The two-layer structure and error-code taxonomy above are stable; treat the exact set of rules as current as of July 2026, not permanent.
Reference

Integration Flow

Outbound — receiving data from D2
1
GET /v1/FindPayloads — list pending payloads for your depot(s)
2
POST /v1/GetPayload — download the payload data
3
Process the data in your system
4
POST /v1/PayloadReceived — acknowledge receipt; payload removed from queue
Inbound — sending data to D2
1
POST /v2/SubmitDispatch — send a truck dispatch
2
POST /v2/SubmitIntake — send a cold-store intake
3
POST /v2/SubmitStock — send a stock snapshot
4
POST /v2/SubmitMates — send a Mates Receipt (ship discharge)
5
POST /v2/SubmitDayInstruction — send a Day Instruction (order-level loading instruction)
6
POST /v2/SubmitLoadInstruction — send a Loading Instruction (ship/truck loading instruction)
Retain the trackingId returned by each Submit call. Include it when contacting Paltrack support about a specific submission.

Error Codes

Status Meaning What to do
200 Success
400 Validation failed Layer 1 or Layer 2 — see Validation
401 Unauthorised Bearer token or API key is missing, expired, or invalid — re-authenticate
409 Reserved Reserved for a future duplicate-submission check — not currently returned
429 Rate limit exceeded Back off and retry — max 60 req/min per IP
500 Server error Contact Paltrack support with your trackingId