1. Updates
PostPlus API
  • Authentication, Environments
  • Endpoints Overview
    • Parcels Endpoints
    • Shipments Endpoints
    • Tracking Endpoints
    • Accounts Endpoints
  • Scenarios And Workflows
    • Working with parcels
    • Create or update parcel record
    • Cancel or delete a parcel record
    • Manifesting parcels in bags and shipments
    • Query parcel tracking events
    • Import parcel tracking events
  • Updates
    • [2026-09-15] EU product identifiers
  • All API Endpoints
    • Parcels
      • Create or update single parcel.
      • Create or update up to 200 parcels in a single request.
      • Get parcel details by parcel Id.
      • Get up to 100 parcels details by parcel Ids.
      • Get parcel details by parcel number (tracking number).
      • Get up to 100 parcels details by parcel numbers (tracking numbers).
      • Get parcel details by sender reference number.
      • Get up to 100 parcel details by sender reference numbers.
      • Query up to 500 parcels.
      • Delete parcel by Id.
      • Delete up to 100 parcels by Ids.
      • Get print details and contents by print Id.
      • Get up to 100 parcel print details and contents by print Ids.
      • Get pickup point details by Id.
      • Query pickup points.
      • Group parcels into bags and link to a shipment (update parcel references). When AllowPartialSuccess is true, valid parcels are updated and invalid ones returned in FailedParcels.
      • Get parcel details by manifest reference numbers.
      • Get up to 100 parcel details by manifest reference numbers.
      • Get parcel details by carrier reference numbers.
      • Get up to 100 parcel details by carrier reference numbers.
    • Shipments
      • Cancel erroneous parcels in a shipment.
      • Delete shipment manifest file.
      • Create new shipment.
      • Create new bag level shipment.
      • Update bag level shipment.
      • Link parcels with specified bag numbers to a shipment.
      • Validate and prepare shipment for submitting.
      • Submit and lock shipment and it's parcels.
      • Group parcels into bags and link to a shipment, then prepare and submit shipment in one transaction.
      • Delete shipment.
      • Update shipment.
      • Get shipment details by shipment id.
      • Query up to 500 shipments.
      • Upload shipment manifest file.
      • Upload shipment document.
      • Get shipment details by shipment number.
    • Documents
      • Download manifest file.
      • Download resulting (processed) manifest file.
      • Download shipment documents in archive file.
      • Download resulting (processed) shipment file.
      • Download shipment document file by Id.
      • Download parcel label by Id.
      • Generate and download generic bag labels.
      • Generate and download bag label.
      • Generate and download shipment parcel labels.
      • Generate and download parcel commercial invoices.
    • Tracking
      • Get event codes and other metadata.
      • Fetch up to 500 tracking events in cursor fashion.
      • Get tracking events by the list of parcel numbers, up to 50, comma separated.
      • Get tracking events by the list of sender reference numbers, up to 50, comma separated.
      • Get tracking events by the list of parcel numbers or sender reference numbers, up to 50, comma separated.
      • Get bag tracking events by the list of bag numbers, up to 50, comma separated.
      • Upload tracking events file.
      • Get bag tracking events by the list of bag numbers or shipment numbers, up to 50, comma separated.
    • Accounts
      • Get current account details.
    • Integrations
      • Add up to 500 tracking events to the processing queue. The whole batch either succeeds or fails.
      • Add tracking event with generic payload.
  • Shipments
    • Update a shipment document's effective date. The document's visibility and status are preserved.
      PUT
  • HsCodes
    • Resolve HS codes by list of descriptions.
      POST
  • Tracking
    • Get the parcel journey phases, in journey order - the dictionary for the category that event codes and tracking events carry. Changes only on release, so fetch it once and cache it.
      GET
  • Schemas
    • AccountAddressType
    • AccountCommonSettings
    • AccountSettingsAddressInfo
    • AddressInfo
    • AddTrackingEventsRequest
    • BagEventsResponse
    • BagPrintInfo
    • BagPrintType
    • BagStatus
    • BagViewInfo
    • CancelShipmentParcelsWithErrorsRequest
    • ContainerType
    • CreateBagLevelShipmentRequest
    • CreateIntegrationEventsRequest
    • CreateOrUpdateManyParcelsRequest
    • CreateOrUpdateParcelAdditionalInfo
    • EmaiNotificationsSettings
    • CreateOrUpdateParcelIdentifiers
    • CreateOrUpdateParcelInfo
    • CreateOrUpdateParcelItemInfo
    • IncludePrintSizes
    • CreateOrUpdateParcelReceiverInfo
    • LabelSize
    • CreateOrUpdateParcelReferences
    • CreateOrUpdateParcelRequest
    • CreateOrUpdateParcelSenderInfo
    • CreateOrUpdateSingleParcelRequest
    • CreateShipmentRequest
    • CustomsSettings
    • CreateOrUpdateParcelStatusInfo
    • DeleteParcelRequest
    • TaxType
    • DownloadBagPrintRequest
    • DownloadManifestFileRequest
    • DownloadParcelPrintRequest
    • GenerateBagPrintsRequest
    • DownloadShipmentDocumentRequest
    • DownloadShipmentDocumentsRequest
    • GetAccountDetailsResponse
    • FileFormat
    • FileStatus
    • GetPickupPointDetailsResponse
    • GetShipmentDetailsResponse
    • GenerateParcelCommercialInvoicesRequest
    • GenerateParcelCommercialInvoicesResponse
    • ParcelLinkResultInfo
    • LabelsSettings
    • ParcelValidationSettings
    • GenerateShipmentParcelPrintsResponse
    • LinkBagsToShipmentRequest
    • LinkBagsToShipmentResponse
    • GetCurrentAccountDetailsRequest
    • ManifestAndSubmitShipmentParcelInfo
    • GetEventsBatchResponse
    • ManifestAndSubmitShipmentRequest
    • ManifestAndSubmitShipmentResponse
    • ManifestFileViewInfo
    • GetManyParcelPrintsResponse
    • NewIntegrationEvent
    • ParcelAdditionalInfo
    • EventCategoryInfo
    • GetPickupPointDetailsRequest
    • ParcelEventsResponse
    • ParcelIdentifiers
    • ParcelInfo
    • ParcelItemInfo
    • ParcelNrSequenceInfo
    • ParcelPrintInfo
    • LeanMileSettings
    • MailType
    • ParcelReferences
    • ParcelStatusInfo
    • ManifestFileType
    • UpdateManyParcelReferencesRequest
    • ManyParcelsDetailsResponse
    • PickupPointInfo
    • BagTrackingEventInfo
    • NewTrackingEvent
    • PostalLabelSettings
    • ParcelAndTrackingEventInfo
    • EmaiNotificationsInfo
    • ParcelCommercialInvoiceInfo
    • PrepareShipmentResponse
    • ParcelDetailsResponse
    • ParcelDocument
    • ParcelDocumentType
    • ResendOrRelabelParcelRequest
    • ResolveHsCodesByDescriptionsRequest
    • ParcelNrType
    • SearchParcelEventsResponse
    • TrackingEventInfo
    • ParcelPrintFormat
    • ShipmentArrivalInfo
    • ParcelPrintType
    • ParcelReceiverInfo
    • ShipmentDocumentViewInfo
    • ParcelSenderInfo
    • ParcelStatus
    • ShipmentStatusInfo
    • ParcelTransactionType
    • PickupPointType
    • ShipmentViewInfo
    • SubmitShipmentRequest
    • EventCodeInfo
    • TrackingParcelInfo
    • UpdateBagLevelShipmentRequest
    • UpdateManyParcelReferencesResponse
    • UpdateShipmentDocumentRequest
    • UpdateShipmentRequest
    • UpsertBagRequest
    • PostalSettings
    • PrepareShipmentRequest
    • QstepSettings
    • QueryParcelsResponse
    • QueryPickupPointsResponse
    • QueryShipmentsResponse
    • RateManagementSettings
    • SearchBagEventsResponse
    • ShipmentArrivalViewInfo
    • ShipmentDocumentType
    • ShipmentManifestLevel
    • ShipmentManifestSettings
    • ShipmentStatus
    • ShipmentTerminalInfo
    • ShipmentType
    • TelemetrySettings
    • UploadManifestFileResponse
    • UploadShipmentDocumentResponse
    • UploadTrackingEventsFileResponse
    • X7TradeSettings
  1. Updates

[2026-09-15] EU product identifiers

From 1 November 2026, every item on a B2C parcel up to 150 EUR imported into the EU must carry customs product identifiers, under Commission Delegated Regulation (EU) 2026/1022. They are collected per item, so a parcel with three different products carries three sets. An item line that stands for several products lists one code per product (see Several products on one item line below).
This page tells you what to change in an existing integration, then specifies each field.

Migrating an existing integration#

If you already call create-or-update or create-or-update-many, the whole change is inside parcel.items[]:
1.
Add nsCode, spCode and spCodeNotApplicable to each item. Nothing else in the request structure changes.
2.
Keep sending skuCode as you do today — it is the Merchant PID, unchanged.
3.
Decide per item what spCodeNotApplicable should be. This is the only part that needs a rule rather than a mapping.
4.
Optionally add originCountryCode if your items differ in origin.
5.
Check hasErrors in the response, which echoes every item as we stored it.
The shipment calls need no change — they carry no item-level data. And there is no cutover to coordinate: all four fields stay optional until 1 November, so you can deploy the day you are ready.

Before and after#

The one decision: spCodeNotApplicable#

Does the product carry a manufacturer barcode (GTIN/EAN/UPC/ISBN)?
├── Yes → spCode = the barcode    + spCodeNotApplicable = false
├── No  → spCode = "" (or omit)   + spCodeNotApplicable = true
└── Don't know yet → omit both fields entirely
Never send false while a product is left without a barcode, and never send true with a barcode for every product. Both are contradictions and both are returned as parcel errors. Omitting spCodeNotApplicable is never a contradiction: it records "not stated", not false. From 1 November, though, an in-scope item with neither spCode nor spCodeNotApplicable is reported as missing, and the parcel is not handled until you send one.

The identifiers#

FieldCustoms elementWhat to sendMax length
skuCodeC127 — Merchant PIDYour own product code70 per code
nsCodeC128 — Non-standardised manufacturer PIDThe manufacturer's model or part number70 per code
spCodeC129 — Standardised PIDThe barcode: GTIN-8/12/13/14, EAN, UPC or ISBN70 per code
spCodeNotApplicableY081 — exception codetrue only when the product genuinely carries no barcode—
Each item also takes an optional originCountryCode (ISO 3166-1 alpha-2, e.g. US). It is not one of the identifiers and is not part of the mandate — origin is a separate element we otherwise resolve per shipment/account. Send it when your items differ in origin; omit it otherwise.
📌 Two different fields share the name originCountryCode. The one here is per item, inside parcel.items[]. The shipment request has its own arrivalInfo.originCountryCode — the origin of the transport, unrelated. Setting one does not affect the other.

Timeline#

PeriodBehaviour
Now → 31 Oct 2026All four fields optional. The Commission has stated no sanction applies to missing or incorrect PIDs during this window.
From 1 Nov 2026Mandatory per item on B2C parcels up to 150 EUR imported into the EU. Above 150 EUR the fields stay optional. A declaration missing a mandatory PID is incomplete and will not be accepted for release. A missing identifier comes back as a parcel error, and the parcel is not handled until you send the value.
There is no soft-enforcement period afterwards. Treat the date as hard.

Field-by-field rules#

skuCode — Merchant PID (C127)#

Your own product code, e.g. CGN-02333. Must be unique per product variant: same brand, size, colour and packaging is one code; two sizes are two codes. Nothing changes in what you already send — only that from 1 November it must be on every item of a B2C parcel up to 150 EUR imported into the EU.

nsCode — Non-standardised manufacturer PID (C128)#

The manufacturer's own model or part number, e.g. NOW-0674. Mandatory on the same parcels as skuCode, with no exception code — there is no nsCodeNotApplicable, and Y081 covers a missing spCode only.
Where the manufacturer assigned no separate reference, DG TAXUD guidance permits repeating the spCode value here — but send it explicitly; we never copy it across for you. The declaration is yours; a value we filled in would be our assertion, not yours.

spCode — Standardised PID (C129)#

The manufacturer's barcode — GTIN-8/12/13/14, EAN, UPC or ISBN — exactly as printed on the product. On receipt:
Grouping spaces are stripped, and so are invisible characters such as zero-width spaces. 590 1234 123457 is stored as 5901234123457; those spaces are typography, not data.
Hyphens are kept. In an ISBN they are part of how the code is printed, and C129 is transmitted as printed.
Never padded, and the check digit is never recomputed or validated. A GTIN-12 stays 12 digits.
Upper-cased, which matters only for an ISBN-10 X check digit.
skuCode and nsCode are free-form references, so they are not upper-cased. Spaces, tabs and line breaks are trimmed from both ends, and each run inside becomes one space. Invisible characters (zero-width, text-direction marks) are removed. Nothing else changes, apart from placeholders (below). We never shorten skuCode, nsCode or spCode for you. If your own skuCode is over 70 characters, switch to a shorter code that still names exactly one variant. Never shorten the manufacturer's nsCode or spCode.
Placeholders are stored empty. 0, -, --, ., N/A, NA, NONE, NULL, EMPTY or CLEAR (case-insensitive) in skuCode, nsCode or spCode is not a code, so we store it empty and never send it to a carrier or to customs.

spCodeNotApplicable — exception code Y081#

Send true only when the product genuinely carries no standardised barcode. Customs is then given exception code Y081 in place of C129.
The field has three states, recorded exactly as you send them:
ValueMeaningRequires
omitted / nullNot stated — you have said nothing either waynothing
trueNo barcode; claims the Y081 exemptionspCode empty, or empty at the position of each product without one
falseA barcode existsspCode non-empty, for every product on the line
true and false are assertions, so each must agree with spCode. null asserts nothing and is never a contradiction — silence is allowed, a false statement is not.
If you always populate spCode, always sending false is correct. If you cannot guarantee that, omit the field on the items you are unsure about rather than defaulting to false.

originCountryCode — optional, ISO 3166-1 alpha-2#

Two-letter code, e.g. US; case is normalised, so us is accepted. Optional, and it stays optional after 1 November. Omit it (or send null) when you do not hold it — an empty string is stored as "not stated", never as a country.

Several products on one item line#

If you declare goods per HS code rather than per product, one item line can stand for up to 33 products. List one code per product, separated by , or ;, in the same order in all three fields: position 1 in each field is the first product, position 2 the second, and so on.
"skuCode": "CGN-02333,CGN-02334,CGN-02335",
"nsCode": "NOW-0674,NOW-0675,NOW-0676",
"spCode": "733739006745,,733739006769",
"spCodeNotApplicable": true
Here the second product has no barcode: its spCode position is empty, and spCodeNotApplicable is true.
Rule
Separator, or ;, also full-width , ;. Spaces around it are ignored. Stored and returned with ,
Limits70 characters per code, 33 codes per field
One skuCode or nsCodeCovers every product on the line, e.g. one skuCode for all variants of a listing
One spCodeIs one product's barcode, never all of them: list one per product
Empty position in skuCode or nsCodeThat product's code is missing
Empty position in spCodeNeeds spCodeNotApplicable: true (Y081 for that product)
PlaceholderA placeholder (N/A, 0, … see spCode above) in a position counts as an empty position
spCodeNotApplicableOne per line. true: the products at the empty spCode positions have no barcode. false: every product has one
A code that contains a separator is split in two, so replace that character in your own codes. A carrier that takes one product code per item gets the first skuCode.

Validation and error handling#

Two layers, reporting differently. Both count items from 0, like the JSON array: Items[0] is the first item. Parcel errors used to count from 1; if you match on them, update the position.
Layer 1 — request boundary, HTTP 400. The whole request is refused. Each entry in errorDetails names the field in property, prefixed with Parcel. (or Parcels[n].Parcel. on create-or-update-many):
{ "property": "Parcel.Items[0].SpCode", "detail": "max 2343 characters", "code": "MaximumLengthValidator" }
An over-length skuCode, nsCode, spCode or originCountryCode is refused here. For the identifiers this checks the whole field, 2343 characters (33 codes of 70 with their separators); a code over 70 or a 34th code is a Layer 2 error. Match on code, not on the detail text. Every length limit is also in the Swagger schema, as maxLength.
Layer 2 — parcel errors, HTTP 200 with hasErrors: true. The parcel is stored but flagged, and handling is skipped until you fix it. Your value is kept so you can see what you sent. Two exceptions are stored empty: an identifier sent as a placeholder such as 0 (see Field-by-field rules), and an over-long identifier (a code over 70 characters, or over 33 codes). Paths have no prefix:
ErrorCause
Items[0].SpCode: required when SpCodeNotApplicable is falseAsserted a barcode exists but sent none
Items[0].SpCode: one per product when SpCodeNotApplicable is falseListed products, but left one without a barcode
Items[0].SpCode: must be empty when SpCodeNotApplicable is trueClaimed the Y081 exemption but also sent a barcode
Items[0].SpCode: needs an empty position when SpCodeNotApplicable is trueListed products with a barcode for every one, so none is exempt
Items[0].SkuCode: max 70 characters per code, 33 codesA code over 70 characters, or more than 33 codes. The field is not stored
Items[0].OriginCountryCode: not validNot a recognised two-letter country code
Items[0,2].NsCode: required for EU customs (C128)From 1 November: the first and third items have no nsCode
A contradiction is reported rather than silently corrected: clearing your spCode because you also sent spCodeNotApplicable: true would make PostPlus file a Y081 exemption you never intended.
Spreadsheet uploads skip the Layer 1 check, so an over-long identifier comes back as the Layer 2 parcel error Items[0].SpCode: max 70 characters per code, 33 codes. The code is not stored. Correct it and upload again.
From 1 November, completeness is checked on B2C parcels up to 150 EUR imported into the EU and reported like the Layer 2 errors above: in errorDetails (hasErrors: true), with handling skipped until you send the missing value. The 150 EUR limit is tested on the parcelValue you send; if it is 0 or omitted, on the sum of quantity × valuePerItem over all items. Other currencies are converted at the current ECB rate. If no rate is available, the parcel is treated as up to 150 EUR.
Placeholders count as missing (N/A, 0, … see spCode above), whether sent by API or spreadsheet.
One message per field, naming every item that lacks it — the first 10, then .... skuCode is C127 and nsCode is C128.
spCode (C129) is reported when it is missing or a placeholder, unless spCodeNotApplicable is true: Items[0].SpCode: required for EU customs (C129) unless SpCodeNotApplicable is true. false with an empty or placeholder spCode is the contradiction error above instead.
On a line listing several products, each product needs its codes: one skuCode or nsCode covers them all, otherwise every position must hold one; spCode always needs one per product (an empty spCode position only with spCodeNotApplicable: true). A product without one is reported with the same messages, ending in for every product: Items[0].NsCode: required for EU customs (C128) for every product.

Customs, after filing#

There is no real-time EU database check of PID values — no central product register. Accuracy is assessed on risk analysis: plausibility against the description, HS code and value.
If customs raises a discrepancy, we email your operational contacts with the parcel number and reason and flag the parcel in tracking; you return corrected item data; we amend or re-lodge, and the parcel is released on acceptance. Each parcel travels under its own declaration, so a hold attaches to that parcel only — it does not block the consolidated manifest or the MAWB. Unresolved parcels stay in temporary storage for at most 90 days, then must be re-exported, returned or destroyed at the shipper's cost, though cases normally resolve well before that.
There is no EU-level PID fine schedule; sanctions for incomplete declarations are national law. Until 31 October the no-sanction safe harbour applies; from 1 November the real risk is non-clearance — holds and delays — rather than a defined fine.

Reading back what we stored#

The create/update response echoes the items as stored, so it is where you verify what we hold, including normalisation such as a stripped spCode space, a placeholder stored empty, or an upper-cased originCountryCode.
The read endpoints return them only when you ask for items: pass IncludeItems=true on GET /api/v1/parcels/{id}, the …-nrs lookups or /query. Without it the response gives parcelItems as a count and an empty items array — the default for every item field, not something specific to the identifiers.
spCodeNotApplicable comes back as null when never stated, and as true/false when it was; the three states survive the round trip.

Catalogue readiness#

The API change is small — sourcing the values is the real work.
SituationWhat to do
Barcode knownspCode = barcode, spCodeNotApplicable = false
Product genuinely has no barcode (unbranded, bulk, custom-made)spCode empty, spCodeNotApplicable = true
Barcode exists but you do not hold itA data-sourcing task, not an exemption. Do not send true — that is a false Y081 claim. Omit both fields until you have the value. From 1 November, on a B2C parcel up to 150 EUR imported into the EU, this is a parcel error and the parcel is not handled until you do.
No manufacturer part numberAsk the supplier to assign an internal reference. nsCode has no exemption, so after 1 November it cannot be left empty on a B2C parcel up to 150 EUR imported into the EU.
Prioritise supplier outreach for the part of your catalogue with neither a barcode nor a manufacturer reference — the only group that needs someone else to act before you can comply.

Manifest file uploads#

If you manifest by uploading a spreadsheet, the identifiers are available as columns NsCode, SpCode, SpCodeNotApplicable and OriginCountryCode, next to the existing SkuCode. In all five, a cell holding -, NULL, EMPTY or CLEAR (case-insensitive) counts as blank, and in SkuCode, NsCode and SpCode so does any other placeholder (0, N/A, … see spCode above): an identifier is stored empty and never sent to customs as a code, and an origin is "not stated". A cell can list several products, as in the API. In a CSV file, quote a cell whose list uses the file's own separator, or separate the codes with the other character.
SpCodeNotApplicable accepts true, yes or 1 (claims the exemption) and false, no or 0, case-insensitive. A blank or absent column parses to "not stated", not to false — consistent with the API. Any other value, such as n/a or y, is a parcel error and is read as "not stated": File parser: SpCodeNotApplicable: not valid, use true or false. When a parcel spans several rows, one per item, an error in the item columns of any of them is a parcel error. If every row has an error, the whole file is refused.

Testing#

The fields are live on test — verify against the test Swagger before you build:
Test environment Swagger
Worth testing explicitly:
an item with a barcode (spCodeNotApplicable: false)
an item without one (true, empty spCode)
an item with neither field set — confirm it is still accepted
a deliberate contradiction — confirm you can read the error out of hasErrors
if you list several products on one line: the response echoes the lists comma-separated, inner empty positions kept (S1,,S3), trailing ones dropped
Because all four fields stay optional until 1 November, you can begin populating them in production as soon as your side is ready.
Previous
Import parcel tracking events
Next
Create or update single parcel.
Built with