Labels & dispatch
After POST /v1/shipments succeeds, Merdi books with the carrier and the label PDF URL appears on the shipment record. This page explains when label_pdf_url is populated, how to poll for it, and how the test mode differs.
When the label_pdf_url is populated
The create call runs quote → persist → dispatch synchronously. When dispatch returns successfully, dispatch.status becomes "dispatched" and label_pdf_url is populated with a presigned OSS URL.
In most cases dispatch completes inside the POST call itself — meaning the 201 response body already contains label_pdf_url. In rare cases (carrier API hiccup, internal retry pending) you'll see status="pending" + label_pdf_url=null; poll GET /v1/shipments/{id} until either status flips.
Polling pattern (only when pending)
Dispatch runs synchronously inside POST /api/v1/shipments (sub-second), so under normal conditions label_pdf_url is already present in the 201 create response — no polling needed. Only poll when dispatch.status comes back "pending" (rare: carrier API hiccup / internal retry). In that case: poll every 1s up to 30s. If still not dispatched at 30s it's almost certainly a carrier-side issue — read dispatch.status + dispatch.error, then POST cancel + try another service_tier.
# Poll until label_pdf_url is non-null OR dispatch.status === "failed". TN="IM-2026-0528-9ZNCP3-1" for i in $(seq 1 30); do resp=$(curl -sS "https://merdiexpress.com/api/v1/shipments/$TN" \ -H "Authorization: Bearer mk_live_***") url=$(echo "$resp" | jq -r '.data.label_pdf_url') status=$(echo "$resp" | jq -r '.data.dispatch_status') echo "$i: status=$status url=$url" if [[ "$url" != "null" || "$status" == "failed" ]]; then break; fi sleep 1 done
Fetching the PDF
label_pdf_url is a presigned OSS URL — fetch it directly, no Authorization header needed. Best practice: re-upload to your own object storage right after download. The presigned URL has a carrier-side expiry; once it lapses you need to fetch a fresh one from /v1/shipments/{id}.
# Direct download — no auth header. curl -fL "$url" -o "label-$TN.pdf" # Or pipe through to your storage: curl -fL "$url" | aws s3 cp - "s3://my-labels/$TN.pdf"
Which number do I use?
A shipment carries up to three numbers — don't mix them up:
| Field | Looks like | Use it for |
|---|---|---|
| tracking_number | IM-2026-... | Merdi's canonical number — use it against our API (GET /api/v1/shipments/{id}) and our buyer-facing tracking page. |
| waybill_number | YT... | First-leg carrier waybill (e.g. Yun Express YT…), assigned at dispatch. |
| last_mile_tracking_number | 9214… (USPS) | Final-mile carrier number (e.g. USPS). Once present, THIS is the number to push to eBay / Amazon and show the buyer — the delivering carrier's own tracker recognises it. Null until the last-mile leg is assigned. |
Practical rule: prefer last_mile_tracking_number for marketplaces once it's non-null; fall back to waybill_number while it's still null.
Buyer-facing tracking page
Want to show buyers the progress? The public tracking page is at https://merdiexpress.com/track?no={tracking_number} (pass tracking_number, not waybill_number).
Test-mode behaviour
When you create with an mk_test_* key, the dispatch step is skipped entirely: no carrier API call, no wallet deduction, no label_pdf_url. The response shows dispatch = null.
Want to test the PDF-download flow in staging? Mock a 200-byte PDF (e.g. https://www.example-files.com/test.pdf) and stub your fetcher. We don't yet have a sandbox carrier that returns a fake label_pdf_url; coming in the next sprint.