TrackJet API v1
A read-only REST API over TrackJet's detection engine and carrier directory — the same logic that powers the website and the MCP server. Multi-vertical: air cargo (MAWB), ocean containers (ISO 6346), Bill of Lading (SCAC), parcels, and postal (UPU S10). EU-hosted. No scraping of third-party aggregators.
https://trackjet.world/api/v1OpenAPI 3.1:
/api/v1/openapi.jsonImport: point Postman, Bruno, Insomnia or any OpenAPI client at
https://trackjet.world/api/v1/openapi.json — they generate an always-current collection from the live spec, so there is nothing hand-maintained to drift.Rate limits: free: 60/min · pro: 600/min · enterprise: 6000/min (per API key). Responses carry
X-RateLimit-* headers; over-limit returns 429.Billing: disabled (all plans free).
Authentication
Send your API key as a Bearer token (the X-Api-Key header is also accepted):
Authorization: Bearer tjk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Keys are issued by the TrackJet team. Treat a key like a password — it grants read access under your plan's rate limit. Every response includes an X-Request-Id you can quote in support requests.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/detect | Detect a number's format/vertical. Query: number. |
GET | /api/v1/track/{number} | Detect + resolve where to track (carrier URL + TrackJet URL). |
GET | /api/v1/carriers | List production carriers. Query: type, limit, offset. |
GET | /api/v1/carrier/{id} | A single carrier's metadata. |
GET | /api/v1/capabilities | Coverage snapshot (formats, counts, MCP info). |
GET | /api/v1/carbon | Rough freight CO2e estimate (GLEC/ISO 14083). Query: mode, weight_kg, distance_km. |
GET | /api/v1/track/{number}/qr | QR image (SVG/PNG) that opens the tracking page. No key required; embeddable in <img>. |
GET | /api/v1/track/{number}/stream | Server-Sent Events live stream (text/event-stream). Emits status events; closes with event: closed + a reason (no_live_feed when the carrier has no licensed event feed — DHL Group numbers stream real updates). |
GET·POST | /api/v1/webhooks | List / register webhooks (HMAC-signed deliveries). POST honours Idempotency-Key. |
POST | /api/graphql | GraphQL (alongside REST). Same key auth. Query: detect, carriers, carrier, capabilities. |
GET | /api/v1 | Service descriptor (no key required). |
Response envelope
{
"ok": true,
"data": { "...": "endpoint-specific" },
"meta": { "request_id": "…", "api_version": "v1" }
}
// errors:
{ "ok": false, "error": { "code": "unauthorized", "message": "…", "status": 401 }, "meta": { … } }
Examples
curl
curl -H "Authorization: Bearer $TRACKJET_API_KEY" \
"https://trackjet.world/api/v1/detect?number=020-12345675"
JavaScript (fetch)
const res = await fetch("https://trackjet.world/api/v1/detect?number=020-12345675", {
headers: { Authorization: `Bearer ${process.env.TRACKJET_API_KEY}` },
});
const { data } = await res.json();
console.log(data.primary, data.normalized);
PHP
$ch = curl_init("https://trackjet.world/api/v1/detect?number=020-12345675");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("TRACKJET_API_KEY")],
]);
$data = json_decode(curl_exec($ch), true);
echo $data["data"]["normalized"];
Python
import os, requests
r = requests.get(
"https://trackjet.world/api/v1/detect",
params={"number": "020-12345675"},
headers={"Authorization": f"Bearer {os.environ['TRACKJET_API_KEY']}"},
)
print(r.json()["data"]["primary"])
GraphQL
curl -X POST "https://trackjet.world/api/graphql" \
-H "Authorization: Bearer $TRACKJET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"{ detect(number:\"020-12345675\"){ normalized matched matches{ type label } } }"}'
EPCIS 2.0 repository
Minimal conformant subset of GS1 EPCIS 2.0 (honest scope: ObjectEvent JSON only — no XML, no subscriptions, no full query language). Events are isolated per API key. EPCs whose digit-run matches a tracked shipment appear on its public page as "EPCIS (partner)", separate from the carrier chain.
curl -X POST "https://trackjet.world/api/v1/epcis/capture" \
-H "Authorization: Bearer $TRACKJET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"ObjectEvent","eventTime":"2026-06-11T09:00:00Z",
"eventTimeZoneOffset":"+00:00","action":"OBSERVE",
"bizStep":"urn:epcglobal:cbv:bizstep:shipping",
"epcList":["urn:epc:id:sscc:0037000.0123456789"],
"readPoint":{"id":"urn:epc:id:sgln:0037000.00729.0"}}'
curl -H "Authorization: Bearer $TRACKJET_API_KEY" \
"https://trackjet.world/api/v1/epcis/events?EPC=urn:epc:id:sscc:0037000.0123456789"
Webhooks
Register an HTTPS endpoint to receive shipment.status_changed events. Each delivery is signed:
curl -X POST "https://trackjet.world/api/v1/webhooks" \
-H "Authorization: Bearer $TRACKJET_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"url":"https://you.example/hook","events":"shipment.status_changed"}'
# response includes a one-time "secret".
Verify each delivery's X-TrackJet-Signature over the timestamp and the raw body, then dedupe on X-TrackJet-Delivery — that id is stable across all retries of one event:
signed = header["X-TrackJet-Timestamp"] + "." + raw_request_body
expected = "sha256=" + hmac_sha256(signed, your_secret)
if not hmac_compare(expected, header["X-TrackJet-Signature"]): reject()
if abs(now - int(header["X-TrackJet-Timestamp"])) > 300: reject()
if seen_before(header["X-TrackJet-Delivery"]): return 200
Sandbox
Reserved TJTEST-* tracking numbers return deterministic, fully-synthetic lifecycles (timestamps in 2030, sandbox:true), so you can build and pin integration tests without a real parcel — happy path and edge cases:
| Number | Scenario |
|---|---|
TJTEST-DELIVERED | Full happy path ending delivered. |
TJTEST-IN-TRANSIT | Mid-journey, latest event in_transit. |
TJTEST-EXCEPTION | Carrier exception (damaged label), no delivery. |
TJTEST-CUSTOMS-HOLD | Held at customs awaiting import duties. |
TJTEST-RETURNED | Refused at the door, returned to sender. |
TJTEST-STALE | No scans for 14 days — exercise your staleness logic. |
TJTEST-NOT-FOUND | Always "no data" — exercise your empty state. |
curl -H "Authorization: Bearer $TRACKJET_API_KEY" \
"https://trackjet.world/api/v1/track/TJTEST-DELIVERED"
Webhook dry-run — fire one signed sandbox.test event at your registered endpoint through the real delivery pipeline (same X-TrackJet-Signature, same retry/log path):
curl -X POST "https://trackjet.world/api/v1/webhooks/<id>/test" \
-H "Authorization: Bearer $TRACKJET_API_KEY"
Transparency & verification
Every tracked chain's history is sealed into a SHA-256 hash chain (tjvt2), and all sealed heads are anchored in a public, append-only Merkle log (tjmt1). Three endpoints are public — no API key — because a transparency log is only credible when anyone can verify:
curl "https://trackjet.world/api/v1/shipments/<uuid>/proof" # tjvt2 chain proof
curl "https://trackjet.world/api/v1/shipments/<uuid>/inclusion" # tjmt1 Merkle inclusion proof
curl "https://trackjet.world/api/v1/transparency/roots" # append-only root history
Verify offline with the single-file, dependency-free tjverify CLI.
Proof-of-Delivery
For disputes, insurance claims and refund decisions: a single call returns a signed, offline-verifiable Proof-of-Delivery (tjpod1) — the TrackJet Passport (Ed25519) scoped to a delivered shipment, with the recorded delivery event surfaced. Public, no API key (the UUID is the capability), and it carries no personal data:
curl "https://trackjet.world/api/v1/shipments/<uuid>/pod" # tjpod1 signed proof-of-delivery
A POD is issued only for a shipment whose recorded status is delivered — a number still in transit returns 409 not_delivered, never a signed claim. Verify the embedded passport against the published key with the same tjverify CLI — no call back to TrackJet. It proves TrackJet signed this exact snapshot, not that the carrier's original scan was true.
Retention window. A POD is built from a shipment's event detail, which TrackJet keeps for ~30 days after the last update. Past that window the endpoint returns 410 pod_outside_retention (an honest, permanent "gone" — not a retryable error), and the sealed hash chain stays available at /proof. So pull and store the POD when a dispute opens, or near delivery. Insurance/claim windows are often 60-90 days; if you need POD coverage to match, ask us about extending retention for delivered shipments.
Notes
/api/v1/track/{number}returns detection + where to track. For live events use/api/v1/track/{number}/stream(SSE) — real updates exist only where TrackJet holds a carrier-licensed feed (DHL Group today); other numbers close immediately withno_live_feedinstead of fabricating events.- Statuses, when present, are normalised to:
pending · in_transit · out_for_delivery · delivered · exception · returned · unknown. - TrackJet is independent and EU-hosted; it does not scrape third-party tracking aggregators.