API reference

Air manifests

File the inbound air manifest for a consignment you forward to us — the same document you would otherwise email, sent as JSON so it reaches customs without anyone re-keying it.

A manifest tells us which packages travel on which air waybill, and in which bag. Until it arrives we cannot file the consignment with Azerbaijani customs at all: the customs declaration manifest (the depesh) is filed per air waybill, and nothing else in the system says which waybill a package is on.

Send the manifest as soon as the consignment is built, and send it again whenever it changes. Each filing replaces the last, so the picture we hold stays the picture you hold.

Why the ETD matters more than the ETA

We file your consignment with customs automatically when it reaches the etd you send — the moment the count is both complete and still correctable. After departure a declaration can no longer be amended, so a package added or pulled after that point cannot be reflected. Keep etd accurate and keep re-filing until it passes.

File a manifest

POST/api/v2/manifests

Requires the manifests:write scope. Answers 202 with a report of what the filing matched and what it changed — read it, it is how you find out that a tracking number reached us that we do not recognise.

Manifest

awbstringrequired
Master air waybill number, e.g. 784-85403894. This is the key the whole consignment hangs on.
rowsarrayrequired
One entry per package, 1–5000. See the table below.
etdstringoptional
Estimated time of DEPARTURE, YYYY-MM-DD HH:MM in destination local time. We file the consignment with customs automatically when this passes. Omit it and nothing is filed automatically — an operator has to do it by hand.
etastringoptional
Estimated time of arrival, YYYY-MM-DD HH:MM in destination local time.
modestringoptional
full (default) — this filing IS the whole consignment and replaces everything sent before for this waybill. part — this is one file of a consignment split across several; parts accumulate. Use full unless you genuinely split the document.
flightstringoptional
Carrier flight or vehicle number, e.g. CZ5795.
origin_countrystringoptional
ISO 3166-1 alpha-2 code, e.g. CN.
origin_facilitystringoptional
The facility that built the consignment.
manifest_datestringoptional
YYYY-MM-DD — the date the document was produced.
totals.shipmentsintegeroptional
Your stated package count. Kept beside what the rows actually come to; a mismatch is reported, never rejected.
totals.bagsintegeroptional
Your stated distinct bag count.
totals.weight_kgnumberoptional
The consignment's GROSS weight — bag tare included. It is expected to be heavier than the row weights sum to, and it is what the waybill is settled on.
dry_runbooleanoptional
Validate and report what WOULD happen, writing nothing. Useful for a first integration run.

rows[]

tracking_numberstringrequired
OUR reference for the package — the tracking_number we returned when the order was created. Not your own internal id.
bag_nostringoptional
The physical bag the package travels in. This becomes the barcode our warehouse scans on arrival, so it must match the label on the bag.
weight_kgnumberoptional
Actual weight, up to 3 decimals.
recipient_namestringoptional
Stored for reconciliation. Not applied to the order.
recipient_phonestringoptional
E.164 preferred (+994XXXXXXXXX). Stored, not applied.
districtstringoptional
Stored, not applied.
addressstringoptional
Stored, not applied.
piecesintegeroptional
Items in the package. Stored, not applied.
declared_valuenumberoptional
Stored, not applied.
currencystringoptional
ISO 4217 code. Stored, not applied.
descriptionstringoptional
Goods description in English, specific — not “gift” or “goods”. Stored, not applied.
hs_codesarrayoptional
Customs tariff codes for the package's contents, as an array of strings. Stored, not applied.
service_methodstringoptional
HOME_DELIVERY or PUDO. Omit to let the destination decide. Any other value is rejected 422.
curl -X POST https://api.starmile.io/api/v2/manifests \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "awb": "784-85403894",
    "flight": "CZ5795",
    "origin_country": "CN",
    "origin_facility": "Corssborder Air Service Limited",
    "eta": "2026-10-16 20:40",
    "etd": "2026-10-15 03:10",
    "manifest_date": "2026-10-12",
    "mode": "full",
    "totals": { "shipments": 2, "bags": 1, "weight_kg": 193 },
    "rows": [
      {
        "tracking_number": "CMX0209075611",
        "bag_no": "BOX109022AZ",
        "weight_kg": 1.345,
        "recipient_name": "Aysel Mammadova",
        "recipient_phone": "+994501234567",
        "district": "Baku",
        "address": "Nizami kuc. 12, apt 4",
        "pieces": 1,
        "declared_value": 72.01,
        "currency": "USD",
        "description": "Sweater, Women Blouse",
        "hs_codes": ["6110200090", "6202409000"],
        "service_method": "HOME_DELIVERY"
      },
      {
        "tracking_number": "CMX0209075612",
        "bag_no": "BOX109022AZ",
        "weight_kg": 2.5,
        "recipient_name": "Rashad Aliyev",
        "recipient_phone": "+994551112233",
        "district": "Sumqayit",
        "address": "Samed Vurgun 8",
        "pieces": 1,
        "declared_value": 31.5,
        "currency": "USD",
        "description": "Trousers",
        "hs_codes": ["6203429000"],
        "service_method": "HOME_DELIVERY"
      }
    ]
  }'

How a filing is applied

Each row is matched to an order you already created through Create an order. A matched order is attached to the waybill, and the bags you name are created so they can be scanned on arrival. Rows we cannot match are listed back to you in not_found — they are not an error, but they are packages that will not clear customs until they are registered, so check that list on every filing.

An order already attached to a different waybill is never moved silently. It is reported under already_on_another_awb for a human to resolve, because a package cannot be on two flights.

Re-filing before departure

Send the manifest again as often as the consignment changes. With the default mode: "full" each filing replaces the previous one for that waybill — which is what lets a package you pulled from the flight actually disappear from the consignment, rather than lingering in a count we then declare to customs.

Filing the identical document twice is harmless: nothing is duplicated and the report simply shows what was already in place.

Weights

Three weights are reported back and they legitimately differ. declared_weight_gramsis your header’s gross figure (bag tare included), rows_weight_grams is what your rows add up to, and matched_weight_gramsis what the packages weigh on our side. The gap between them is how a short-shipped or mis-stated consignment is spotted — none of the three is “wrong”.

Errors

  • 403 — your credential does not hold manifests:write. It is granted explicitly, never by default; ask us to add it.
  • 422 — the body failed validation. The response names the exact field, including the row index (rows.4.service_method).