api Xtatee API · v1

Ship from your own system.

Create shipments with one request and get every status change pushed to your database. The same shipping network our own customers use, as a simple JSON API.

An API key comes with an Xtatee account. Create a free account, log in, then buy API access from your dashboard.

Base URLhttps://xtatee.com/api/v1
On this page

Introduction

The Xtatee API lets your business create shipments, read their status and receive updates automatically, without anyone typing into our dashboard. It is built for businesses that already keep their own orders in a database: you send us the shipment, we move it, and every change we make flows back to you.

Ship from our China warehouse to Nigeria (collected from our pickup hub, or delivered to the door) or door to door to other countries such as the UK, Europe and the US. See International shipping.

Shipments created through the API are kept separately from Xtatee's own customers' shipments. Our team is notified the moment you create one, handles it like any other, and updates its status as it moves. Each update reaches you in three ways: through the API, by email, and by webhook.

key

1. Get a key

Sign up for an Xtatee account and apply as a business. After we approve you and you pay the first month, your key appears straight away.

inventory_2

2. Send shipments

POST the items, receiver and route.

webhook

3. Stay in sync

We call your server on every update.

API access is part of the Xtatee partner plan: โ‚ฆ75,000 per month, paid online from your dashboard. Sign up as a business; every application is approved by our team.

  • check_circlePartner rates
    Wholesale freight rates, lower than our public prices, shown to you inside your dashboard.
  • check_circleDeveloper API
    Create and track shipments from your own system with a simple API.
  • check_circleAutomatic updates
    Every status change reaches your system by webhook and your customers by email.
  • check_circleYour own dashboard
    See your shipments, invoices and weekly targets in one place.
  • check_circleFull China-side service
    Receiving, consolidation, repacking and labelling under your own brand at our Guangzhou warehouse.
  • check_circle21 days free storage
    Goods wait at our warehouse for 21 days at no charge.
  • check_circleDirect communication
    A direct line to our team, not a ticket queue.
  • check_circleDoor-to-door options
    Europe, the US and other destinations, quoted per destination.

If a renewal is not paid: you keep your key for a 7-day grace period after the due date. After that, creating new shipments is paused (403 subscription_lapsed). Shipments already in progress keep syncing, reading and tracking still work, nothing is deleted, and paying restores everything at once.

Authentication

You need an Xtatee account firstAPI keys are issued to registered Xtatee users. Create a free account (or log in), sign up as a business, and once you are approved and have paid the first month (โ‚ฆ75,000), your key is created straight away. Visitors without an account can read this documentation but cannot get a key.

Every request must carry your API key as a Bearer token. Find it in your dashboard under Developer API.

Authorization: Bearer YOUR_API_KEY
Keep your key on your serverNever put it in a web page, a mobile app or a public repository. If it leaks, create a new key in your dashboard: the old one stops working immediately. A suspended account receives 403 account_suspended.

Requests & responses

  • Send and receive JSON. Set Content-Type: application/json on requests with a body.
  • Weights are in kilograms, volume in cubic metres (CBM). The API carries no prices (see Prices & billing).
  • Timestamps are ISO 8601 in UTC, for example 2026-10-10T09:41:12+00:00.
  • Limit: 120 requests per minute per key. Over it you get 429 with a Retry-After header.

A successful response looks like this:

{
    "status": "success",
    "message": "Optional human readable note",
    "data": {
        "...": "..."
    }
}

A failed one always has a machine readable code and, for validation problems, the exact fields at fault:

{
    "status": "error",
    "code": "validation_failed",
    "message": "Some fields are missing or invalid.",
    "errors": {
        "receiver.phone": "Required, up to 40 characters.",
        "items[0].quantity": "A whole number, 1 or more."
    }
}

Quickstart

Check that your key works. It returns your business name and webhook address.

GET/me
{
    "status": "success",
    "data": {
        "business": "Ada's Gadget Store",
        "email": "ada@example.com",
        "status": "active",
        "webhook_url": null,
        "subscription": {
            "status": "active",
            "paid_until": "2026-11-10T09:41:12+00:00",
            "can_create_shipments": true
        },
        "rate_limit": {
            "requests_per_minute": 120
        }
    }
}

Then create your first shipment (see Create a shipment) and set a webhook address in your dashboard so updates start flowing back.

Account

List locations and options

GET/locations

Returns the values you may use for origin, destination and shipping_method, the two sides, every status a shipment can have on each side, the International methods and goods types, and the items we do not ship (prohibited_items). Call it once and cache it. The destination of a Nigeria shipment must be a location in Nigeria.

{
    "status": "success",
    "data": {
        "locations": [
            {
                "name": "China Warehouse",
                "country": "China",
                "doorstep": false
            },
            {
                "name": "Lagos Pickup Hub",
                "country": "Nigeria",
                "doorstep": false
            }
        ],
        "shipping_methods": [
            {
                "value": "Normal",
                "label": "Normal cargo"
            },
            {
                "value": "Express",
                "label": "Express cargo"
            },
            {
                "value": "Gadget",
                "label": "Gadgets / electronics"
            },
            {
                "value": "HK/Liquid/Battery",
                "label": "HK special cargo: liquids, batteries, powders"
            },
            {
                "value": "Sea Shipping",
                "label": "Sea (Normal Goods) - China to Lagos"
            },
            {
                "value": "Sea Battery",
                "label": "Sea (Pure Battery Goods - Powerbanks, Lithium Batteries etc) - China to Lagos"
            }
        ],
        "sides": [
            "nigeria",
            "international"
        ],
        "statuses": [
            "Pending",
            "Partially Received",
            "Received in China",
            "In Transit",
            "Arrived at Pickup Hub",
            "Delivered",
            "Cancelled"
        ],
        "international_statuses": [
            "Pending",
            "Partially Received",
            "Received in China",
            "In Transit",
            "Delivered",
            "Cancelled"
        ],
        "international_shipping_methods": [
            {
                "value": "Air",
                "label": "Air freight"
            },
            {
                "value": "Sea",
                "label": "Sea freight"
            }
        ],
        "goods_types": [
            {
                "value": "non_copy",
                "label": "Non-copy goods",
                "description": "Original or unbranded items"
            },
            {
                "value": "copy",
                "label": "Copy goods",
                "description": "Replicas of brand-name products"
            }
        ],
        "prohibited_items": [
            "Gadgets (Phones, Tablets, Laptops etc) to Europe, USA, South Africa etc",
            "Weapons, firearms, ammunition, and anything that looks like a weapon (including imitation guns and knives)",
            "Explosives and fireworks",
            "Illegal drugs, narcotics, and controlled or prescription medicines"
        ]
    }
}

(prohibited_items shortened here: the response has the whole list.)

List International destinations

GET/countries

The countries you can ship to door to door, for destination_country. Each one says whether the receiver pays import duty on arrival, the extra items that country does not accept (restricted_items, on top of the general prohibited_items), and the services you can use there: each shipping_method and goods_type with its minimum chargeable weight and usual transit time. quote_required: true means we have not published a rate for that country yet: you can still create the shipment, and our team prices it by hand. No prices are returned (they are in your dashboard). The list changes rarely; cache it for a day.

{
    "status": "success",
    "data": [
        {
            "name": "United Kingdom",
            "duty_on_arrival": false,
            "restricted_goods_note": null,
            "restricted_items": [],
            "quote_required": false,
            "services": [
                {
                    "shipping_method": "Air",
                    "goods_type": "non_copy",
                    "min_weight_kg": 20,
                    "transit_time": "8 - 11 days"
                },
                {
                    "shipping_method": "Air",
                    "goods_type": "copy",
                    "min_weight_kg": 20,
                    "transit_time": "8 - 11 days"
                },
                {
                    "shipping_method": "Sea",
                    "goods_type": "non_copy",
                    "min_weight_kg": 101,
                    "transit_time": "50 - 60 days"
                },
                {
                    "shipping_method": "Sea",
                    "goods_type": "copy",
                    "min_weight_kg": 101,
                    "transit_time": "50 - 60 days"
                }
            ]
        }
    ]
}

Create a shipment

POST/shipments

Creates a shipment and tells Xtatee about it. Send everything we need to receive, process and deliver it. You get back the shipment with its tracking number: keep it next to your own order.

FieldTypeRequiredDescription
referencestringNoYour own order id (max 100 characters). Strongly recommended: sending the same reference again returns the existing shipment instead of creating a duplicate, so retrying after a timeout is safe.
shipping_methodstringYesOne shipping method for the whole shipment and every item in it. Nigeria: Normal, Express, Gadget, HK/Liquid/Battery, Sea Shipping, Sea Battery (not case sensitive); gadgets are priced per item (phone, tablet or laptop, set by our warehouse). International: Air or Sea, both priced per kg. Goods that need a different method go in a separate shipment.
goods_typestringNoInternational shipments only, and required for them: non_copy (original or unbranded items) or copy (replicas of brand-name products).
confirm_no_prohibitedbooleanNoInternational shipments only, and required for them: true, confirming the shipment holds none of the prohibited_items (/locations) or the country's restricted_items (/countries).
sidestringNonigeria (default) or international. Sending destination_country without side also means International. See International shipping.
originstringYesWhere we collect the goods. A location name from /locations, for example China Warehouse (not case sensitive).
destinationstringYesNigeria shipments: where the goods go, a location in Nigeria from /locations. Not needed for International shipments.
destination_countrystringNoInternational shipments only, and required for them: a country name from /countries (not case sensitive).
last_mile_addressstringNoStreet address for delivery to the door (max 500 characters). Nigeria: leave out to collect from the pickup hub. International: required, the full delivery address in the destination country.
receiver.namestringYesWho receives the goods.
receiver.phonestringYesReceiver's phone number, for the courier.
receiver.emailstringNoReceiver's email address.
itemsarrayYesBetween 1 and 50 line items, described below.
items[].descriptionstringYesWhat it is, up to 255 characters. Be specific: customs and inspection depend on it.
items[].quantityintegerYesNumber of units, 1 or more.
items[].shipping_optionstringNoDeprecated: send shipping_method once instead. Still accepted when every item names the same method; items that name different methods are refused with 422.
items[].weight_kgnumberNoDeclared weight of the whole line (all units), in kg. Used for the estimate.
items[].cbmnumberNoDeclared volume of the whole line in CBM.
items[].local_tracking_nostringNoThe waybill or tracking number of the parcel arriving at our warehouse.
items[].repackbooleanNoAsk us to repack the item (charged separately).
items[].inspectbooleanNoAsk us to inspect the contents (charged separately).
notesstringNoAnything our team should know (max 1000 characters).
One shipping method per shipment (13 Oct 2026)Send shipping_method once for the whole shipment. Items no longer carry their own method, and responses and webhooks show it on the shipment. An older integration that sends shipping_option on each item keeps working as long as every item names the same method.

Response, 201 Created:

{
    "status": "success",
    "message": "Shipment created. Xtatee has been notified.",
    "data": {
        "tracking_number": "XB-4A7DC5DA",
        "reference": "ORDER-1001",
        "status": "Pending",
        "display_status": "Pending",
        "side": "nigeria",
        "shipping_method": "Normal",
        "goods_type": null,
        "prohibited_items_confirmed_at": null,
        "origin": "China Warehouse",
        "destination": "Lagos Pickup Hub",
        "destination_country": null,
        "last_mile_address": "12 Allen Avenue, Ikeja, Lagos",
        "receiver": {
            "name": "Ada Obi",
            "phone": "08012345678",
            "email": "ada@example.com"
        },
        "delivery": null,
        "measured": {
            "weight_kg": 0,
            "cbm": 0,
            "repack_weight_kg": null
        },
        "notes": "Handle with care",
        "sent_out_at": null,
        "pickup_hub_at": null,
        "created_at": "2026-10-10T09:41:12+00:00",
        "updated_at": "2026-10-10T09:41:12+00:00",
        "items": [
            {
                "description": "50x iPhone cases",
                "quantity": 50,
                "local_tracking_no": "SF1234567",
                "received_at": "2026-10-11T10:20:00+00:00",
                "repack": true,
                "inspect": false,
                "declared": {
                    "weight_kg": 12.5,
                    "cbm": 0.08000000000000000166533453693773481063544750213623046875
                },
                "measured": {
                    "weight_kg": 12.699999999999999289457264239899814128875732421875,
                    "cbm": 0.070000000000000006661338147750939242541790008544921875
                }
            },
            {
                "description": "Phone stands",
                "quantity": 20,
                "local_tracking_no": null,
                "received_at": null,
                "repack": false,
                "inspect": true,
                "declared": {
                    "weight_kg": 3,
                    "cbm": null
                },
                "measured": {
                    "weight_kg": 3,
                    "cbm": 0.0200000000000000004163336342344337026588618755340576171875
                }
            }
        ],
        "history": [
            {
                "status": "Pending",
                "note": "Shipment created through the API.",
                "at": "2026-10-10T09:41:12+00:00"
            }
        ]
    }
}
Retrying safelyIf the connection drops and you are not sure whether we received the shipment, send the same request with the same reference. You will get 200 with "idempotent_replay": true and the original shipment, never a second one.

International shipping

Besides Nigeria, we ship door to door from our China warehouse to other countries. An International shipment is created with the same endpoint and the same items; these are different in the request:

  • "side": "international",
  • destination_country: a name from GET /countries instead of a destination location,
  • last_mile_address is required: the full delivery address in that country,
  • shipping_method is Air or Sea, and goods_type is non_copy or copy (both required; the country's services in /countries say which it has),
  • "confirm_no_prohibited": true is required: the shipment holds none of the prohibited_items from /locations or the country's restricted_items.
Air or Sea, copy or non-copy (14 Oct 2026)International shipments no longer use the Nigeria methods. Send "shipping_method": "Air" or "Sea", a goods_type and "confirm_no_prohibited": true. Both methods are priced per kg at your partner price for the country, with a minimum chargeable weight per country (in services[].min_weight_kg): a lighter shipment is billed at the minimum. Nigeria shipments are unchanged.

How an International shipment differs once it is created:

TopicInternational shipments
JourneyPending, Partially Received, Received in China, In Transit, then Delivered. There is no pickup hub, so Arrived at Pickup Hub never happens and pickup_hub_at stays null.
DeliveredMeans delivered to the receiver's door, so display_status is Delivered too (for Nigeria it reads "Available for pickup").
deliveryWhen the goods are handed to the delivery company in the destination country, delivery.company and delivery.tracking_number are filled in and you receive the shipment.delivery_updated webhook. Give that tracking number to your customer: the delivery company contacts them about duty.
Dutydelivery.duty_on_arrival: true means import duty and taxes are charged by the destination country's customs, paid by the receiver to customs or the delivery company. They are never part of what Xtatee charges.
BillingIn US dollars, per kg at your partner price for that country, method and goods type, with the country's minimum weight, plus China storage after the free days. No demurrage. You see and pay the invoice in your dashboard, as for Nigeria shipments.
Prohibited goodsWe never carry the prohibited_items listed by /locations, and some countries refuse more (restricted_items in /countries). Check both before you accept an order: every International shipment must confirm it with confirm_no_prohibited.

The response is the usual shipment object. Here is one that has been delivered:

{
    "status": "success",
    "data": {
        "tracking_number": "XB-9C21E07B",
        "reference": "ORDER-2001",
        "status": "Delivered",
        "display_status": "Delivered",
        "side": "international",
        "shipping_method": "Air",
        "goods_type": "non_copy",
        "prohibited_items_confirmed_at": "2026-10-10T09:41:12+00:00",
        "origin": "China Warehouse",
        "destination": "United Kingdom",
        "destination_country": "United Kingdom",
        "last_mile_address": "Flat 4, 21 Baker Street, London NW1 6XE",
        "receiver": {
            "name": "Tunde Bello",
            "phone": "+447700900123",
            "email": "tunde@example.com"
        },
        "delivery": {
            "company": "DHL",
            "tracking_number": "JD014600006281",
            "duty_on_arrival": true
        },
        "measured": {
            "weight_kg": 6.20000000000000017763568394002504646778106689453125,
            "cbm": 0.0299999999999999988897769753748434595763683319091796875,
            "repack_weight_kg": null
        },
        "notes": null,
        "sent_out_at": "2026-10-12T14:03:55+00:00",
        "pickup_hub_at": null,
        "created_at": "2026-10-10T09:41:12+00:00",
        "updated_at": "2026-10-12T14:03:55+00:00",
        "items": [
            {
                "description": "Ankara fabric, 30 yards",
                "quantity": 1,
                "local_tracking_no": "YT889900112",
                "received_at": "2026-10-11T10:20:00+00:00",
                "repack": false,
                "inspect": false,
                "declared": {
                    "weight_kg": 6,
                    "cbm": null
                },
                "measured": {
                    "weight_kg": 6.20000000000000017763568394002504646778106689453125,
                    "cbm": 0.0299999999999999988897769753748434595763683319091796875
                }
            }
        ],
        "history": [
            {
                "status": "Pending",
                "note": "Shipment created through the API.",
                "at": "2026-10-10T09:41:12+00:00"
            },
            {
                "status": "In Transit",
                "note": "Departed Guangzhou",
                "at": "2026-10-12T14:03:55+00:00"
            },
            {
                "status": "In Transit",
                "note": "Handed to DHL, tracking number JD014600006281.",
                "at": "2026-10-16T08:12:40+00:00"
            },
            {
                "status": "Delivered",
                "note": null,
                "at": "2026-10-18T15:30:02+00:00"
            }
        ]
    }
}
ValidationAn International shipment without a known destination_country, a last_mile_address, Air or Sea, a goods_type or "confirm_no_prohibited": true gets 422 validation_failed with the field named in errors. So does a Nigeria shipment whose destination is outside Nigeria: send it as International instead.

Retrieve & list

Retrieve one shipment

GET/shipments/{tracking_number}

Returns the full shipment: items, measured weights and the complete status history.

curl https://xtatee.com/api/v1/shipments/XB-4A7DC5DA \
  -H "Authorization: Bearer YOUR_API_KEY"
{
    "status": "success",
    "data": {
        "tracking_number": "XB-4A7DC5DA",
        "reference": "ORDER-1001",
        "status": "In Transit",
        "display_status": "In Transit",
        "side": "nigeria",
        "shipping_method": "Normal",
        "goods_type": null,
        "prohibited_items_confirmed_at": null,
        "origin": "China Warehouse",
        "destination": "Lagos Pickup Hub",
        "destination_country": null,
        "last_mile_address": "12 Allen Avenue, Ikeja, Lagos",
        "receiver": {
            "name": "Ada Obi",
            "phone": "08012345678",
            "email": "ada@example.com"
        },
        "delivery": null,
        "measured": {
            "weight_kg": 15.699999999999999289457264239899814128875732421875,
            "cbm": 0.0899999999999999966693309261245303787291049957275390625,
            "repack_weight_kg": 14.9000000000000003552713678800500929355621337890625
        },
        "notes": "Handle with care",
        "sent_out_at": "2026-10-12T14:03:55+00:00",
        "pickup_hub_at": null,
        "created_at": "2026-10-10T09:41:12+00:00",
        "updated_at": "2026-10-12T14:03:55+00:00",
        "items": [
            {
                "description": "50x iPhone cases",
                "quantity": 50,
                "local_tracking_no": "SF1234567",
                "received_at": "2026-10-11T10:20:00+00:00",
                "repack": true,
                "inspect": false,
                "declared": {
                    "weight_kg": 12.5,
                    "cbm": 0.08000000000000000166533453693773481063544750213623046875
                },
                "measured": {
                    "weight_kg": 12.699999999999999289457264239899814128875732421875,
                    "cbm": 0.070000000000000006661338147750939242541790008544921875
                }
            },
            {
                "description": "Phone stands",
                "quantity": 20,
                "local_tracking_no": null,
                "received_at": null,
                "repack": false,
                "inspect": true,
                "declared": {
                    "weight_kg": 3,
                    "cbm": null
                },
                "measured": {
                    "weight_kg": 3,
                    "cbm": 0.0200000000000000004163336342344337026588618755340576171875
                }
            }
        ],
        "history": [
            {
                "status": "Pending",
                "note": "Shipment created through the API.",
                "at": "2026-10-10T09:41:12+00:00"
            },
            {
                "status": "In Transit",
                "note": "Departed Guangzhou on flight CZ399",
                "at": "2026-10-12T14:03:55+00:00"
            }
        ]
    }
}

List shipments

GET/shipments

Newest first. Each row has the same fields as above without items and history; fetch one shipment for those.

FieldTypeRequiredDescription
statusstringNoOnly shipments in this status.
sidestringNonigeria or international.
referencestringNoFind the shipment for one of your order ids.
pageintegerNoPage number, starting at 1.
per_pageintegerNo1 to 100, default 20.
curl "https://xtatee.com/api/v1/shipments?status=In%20Transit&per_page=50" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
    "status": "success",
    "data": [
        "[ shipments ]"
    ],
    "meta": {
        "page": 1,
        "per_page": 50,
        "total": 132,
        "total_pages": 3
    }
}

Cancel a shipment

POST/shipments/{tracking_number}/cancel

Cancels a shipment that is still Pending. Once we have started handling it, cancelling is no longer possible through the API and you get 409 cannot_cancel: please contact support.

curl -X POST https://xtatee.com/api/v1/shipments/XB-4A7DC5DA/cancel \
  -H "Authorization: Bearer YOUR_API_KEY"

The shipment object

The same object is returned by the API and sent inside webhooks, so what you store from a webhook always matches what you would read from the API.

FieldTypeRequiredDescription
tracking_numberstringYesXtatee's tracking number, like XB-4A7DC5DA. Unique and permanent.
referencestring|nullNoYour order id, if you sent one.
statusstringYesCurrent status, a stable code. See below.
display_statusstringYesWhat YOUR customers should read. The same as status, except that on a Nigeria shipment Delivered is shown as Available for pickup (see below).
sidestringYesnigeria or international.
origin / destinationstringYesThe locations you chose. For an International shipment destination is the country.
destination_countrystring|nullYesThe International destination country, null for Nigeria.
shipping_methodstring|nullYesThe one method for the whole shipment: a Nigeria method, or Air / Sea for International. null only on an older shipment whose items had different methods.
goods_typestring|nullYesInternational only: non_copy or copy. null for Nigeria.
prohibited_items_confirmed_atstring|nullYesInternational only: when you confirmed the shipment holds no prohibited items, ISO 8601 UTC.
last_mile_addressstring|nullNoDoor delivery address, if any (always set for International).
receiverobjectYesname, phone, email.
deliveryobject|nullYesInternational only (null for Nigeria): company and tracking_number of the delivery company in the destination country (null until it has the goods), and duty_on_arrival.
measuredobjectYesweight_kg and cbm measured at our warehouse (both 0 until we weigh the shipment), and repack_weight_kg: the whole shipment's weight after repacking, which repacking is charged on (null until recorded, 0 when not repacked).
sent_out_atstring|nullYesWhen the shipment left China (first marked In Transit), ISO 8601 UTC. null until then.
pickup_hub_atstring|nullYesWhen it arrived at the pickup hub and we sent the pickup message, ISO 8601 UTC. null until then, and always null for International.
items[]arrayYesEach line with declared and measured weight / CBM (null until measured), received_at (the day the package reached our warehouse, or null), plus repack, inspect and local_tracking_no.
history[]arrayYesEvery status the shipment has had, oldest first: status, note, at.
created_at / updated_atstringYesISO 8601, UTC.

Statuses

A shipment normally moves from top to bottom. Our team sets each status as the goods progress and may add a note, which you receive with the update.

PendingPartially ReceivedReceived in ChinaIn TransitArrived at Pickup HubDeliveredCancelled

Partially Received means some of your items have reached our warehouse and others have not. When you create a shipment for goods that are already in our warehouse, it starts at Received in China (or Partially Received) instead of Pending. Arrived at Pickup Hub means the goods are ready to be collected. Cancelled is final.

International shipments skip the pickup hub:

PendingPartially ReceivedReceived in ChinaIn TransitDeliveredCancelled
Delivered means "collected from Xtatee" (Nigeria)When we mark a Nigeria shipment Delivered, you have collected it from us, but your own customer may not have collected it from you yet. Show Available for pickup on your website for that status. The display_status field already does this for you; keep status for your own logic. An International shipment marked Delivered has reached the receiver's door.

Prices & billing

What Xtatee charges you is never sent through the API or in a webhook, so nothing about our prices can end up on your own website. You add your own margin before billing your customer.

You see what we charge in your own Xtatee account instead: once your goods have been weighed we issue an invoice, you receive an email, and you can see and pay it from Developer API → Invoices in your dashboard (card or bank transfer through Paystack, which adds its transaction charge on its payment page).

Nigeria shipments are invoiced in naira on your partner rate card. International shipments are invoiced in US dollars at your partner price for the destination country, shown per country in your dashboard. Import duty in the destination country is never on our invoice.

Need a price?Your rates are in your Xtatee dashboard. The API gives you statuses, dates, weights and CBM, which is everything your own pricing needs.

Webhooks

Add a webhook address in your dashboard. Whenever we change one of your shipments we send a POST with the new state, so your own database can follow along without you polling. You also get an email for every update.

Events

EventWhen it is sent
shipment.status_updatedWe changed the status (and optionally added a note). data.previous_status and data.note are included.
shipment.measuredWe weighed and measured the goods. measured on the shipment and on each item is filled in.
shipment.delivery_updatedInternational only: the goods are with the delivery company in the destination country. delivery.company and delivery.tracking_number on the shipment are filled in.
shipment.method_updatedOur team set or changed the shipment's shipping_method (or, International, its goods_type). data.previous_method is included (null if there was none), and data.previous_goods_type on International shipments.
webhook.testSent when you press "Send test event" in your dashboard.

Payload

{
    "id": "evt_9f2c41d0a7b3e85c6d12",
    "event": "shipment.status_updated",
    "created_at": "2026-10-12T14:03:56+00:00",
    "data": {
        "shipment": {
            "tracking_number": "XB-4A7DC5DA",
            "reference": "ORDER-1001",
            "status": "In Transit",
            "...": "the full shipment object, as above"
        },
        "previous_status": "Pending",
        "note": "Departed Guangzhou on flight CZ399"
    }
}

Headers

FieldTypeRequiredDescription
X-Xtatee-EventstringYesThe event name.
X-Xtatee-DeliverystringYesA unique id for this event. It is the same on every retry, so store it and ignore ones you have already processed.
X-Xtatee-TimestampintegerYesUnix time the request was signed.
X-Xtatee-SignaturestringYessha256= followed by an HMAC-SHA256 of {timestamp}.{raw body} using your signing secret.

Verify the signature

Anyone can post to a public URL, so check every webhook before you trust it. Compute the signature over the raw request body exactly as received, compare it in constant time, and reject requests whose timestamp is more than 5 minutes old. Your signing secret is in your dashboard.

Delivery & retries

  • Answer with any 2xx status within 5 seconds. Do slow work after you respond.
  • Anything else, or a timeout, counts as a failure. We retry after 1 min, 5 min, 30 min, 2 hours and 12 hours, then stop. Our team can also resend any delivery by hand.
  • Events can arrive more than once or slightly out of order. Use X-Xtatee-Delivery to skip duplicates and the shipment's updated_at to ignore stale updates.
  • Webhook addresses must use https and be reachable from the internet. We do not follow redirects.

Errors

HTTPcodeMeaning
400invalid_jsonThe body is not valid JSON.
401unauthorized / invalid_api_keyMissing, malformed or revoked key.
403account_suspendedYour API account is suspended. Contact support.
403subscription_requiredYour first subscription payment has not been made yet.
403subscription_lapsedThe subscription lapsed: new shipments are paused. Pay the renewal to resume.
404not_foundNo such shipment on your account, or unknown endpoint.
409cannot_cancelThe shipment is past Pending.
422validation_failedA field is missing or invalid. See errors for which.
429rate_limitedOver 120 requests a minute. Wait for Retry-After seconds.
500server_errorSomething went wrong on our side. Retry; with a reference it is safe.

Go live checklist

  • checkStore the API key and webhook secret in environment variables, never in code.
  • checkSave our tracking_number next to every order, and send your own order id as reference.
  • checkShipping abroad? Load GET /countries, send "side": "international" with the country and the full address, and show your customer the delivery company's tracking number when shipment.delivery_updated arrives.
  • checkVerify the signature and timestamp on every webhook, and respond with a 2xx quickly.
  • checkSkip webhook deliveries you have already handled (X-Xtatee-Delivery).
  • checkHandle 422 by showing the errors to whoever can fix the data, and 429 / 5xx by retrying with backoff.
  • checkPress "Send test event" in your dashboard and confirm your endpoint answers 2xx.

Need help?

Tell us your business name and what you are trying to do. Contact us and we will help you get connected.

We use cookies

We use the cookies the site needs to work, such as keeping you signed in. With your permission we also use Meta's cookies to measure our Facebook and Instagram ads. You can change your choice at any time under Cookie settings at the bottom of every page. Cookie policy