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
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
/api/v2/manifestsRequires 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 holdmanifests: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).