Appearance
API
Navexa's external API lets your own systems read your locations and packages, bin-pack items, quote shipping rates and work with labels. (The checkout rates endpoint stores call is separate: see Checkout rates.)
Base URL: https://api.navexa.co/api/v1/external
Authentication
- In Navexa, open API tokens in the menu (owners and admins) and choose the scopes the token needs. An expiry is optional.
- Copy the token when it is shown. It is shown once; if you lose it, revoke it and create another.
- Send it as a bearer token on every request:
bash
curl https://api.navexa.co/api/v1/external/locations \
-H "Authorization: Bearer nfx_..."Tokens start with nfx_. A token acts for the account it was created in.
| Scope | Allows |
|---|---|
location | Reading ship-from locations |
shipping:rates | Packages, bin-packing and rate quotes |
returns:labels | Quoting and buying customer return labels (spends money) |
orders:labels | Reading an order's labels and what they cost |
A missing, malformed, expired or revoked token gets HTTP 401; a token without the scope an endpoint needs gets HTTP 403. Errors are JSON: { "error": "...", "message": "...", "code": "..." }.
Locations
GET /locations (scope location): your active ship-from locations.
json
[
{
"id": "<location id>",
"name": "Main warehouse",
"city": "Austin",
"province": "TX",
"zip": "78701",
"country": "US",
"is_active": true
}
]Packages
GET /packages (scope shipping:rates): your package templates (boxes, polybags, tubes, pallets). Dimensions in inches, weights in grams.
json
[
{
"id": "<package id>",
"name": "Small box",
"type": "box",
"length_inches": 8,
"width_inches": 6,
"height_inches": 4,
"weight_grams": 120
}
]Bin-pack items
POST /shipping/bin-pack (scope shipping:rates): which of your packages the items fit in, and how many boxes. Give a sku and Navexa uses the product's dimensions, or give the dimensions yourself. Up to 500 items and 10,000 units per request.
json
{
"items": [
{ "id": "line-1", "name": "Widget", "quantity": 2, "sku": "WID-001" },
{
"id": "line-2",
"name": "Poster tube",
"quantity": 1,
"weight_grams": 300,
"length_inches": 24,
"width_inches": 3,
"height_inches": 3
}
]
}json
{
"package_assignments": [
{
"package_name": "Small box",
"length_inches": 8,
"width_inches": 6,
"height_inches": 4,
"weight_grams": 1120,
"item_count": 2
}
],
"skipped_items": [{ "item_id": "line-2", "name": "Poster tube", "reason": "..." }],
"partial_quote": true,
"total_packages": 1
}partial_quote is true when some items were skipped; skipped_items says why.
Rates
POST /shipping/rates (scope shipping:rates): carrier rates for packages you describe, totalled per service. US addresses only for now; up to 100 packages; weights in ounces.
json
{
"location_id": "<location id>",
"destination": {
"address1": "456 Oak Ave",
"city": "New York",
"state": "NY",
"zip": "10001",
"country": "US"
},
"destination_is_residential": true,
"packages": [{ "length_inches": 8, "width_inches": 6, "height_inches": 4, "weight_oz": 18 }],
"items": [{ "sku": "WID-001", "quantity": 2 }]
}location_idis optional. Without it, Navexa ships from the location that has theitemsin stock, else your default location.itemsis optional; with it,inventory_warningslists SKUs the chosen location can't fully cover.
json
{
"rates": [
{
"id": "USPS_GroundAdvantage_USD",
"carrier": "USPS",
"service": "GroundAdvantage",
"service_code": "GroundAdvantage",
"cost_cents": 742,
"currency": "USD",
"estimated_days": 3,
"package_count": 1
}
],
"location_id": "<location id>",
"quoted_at": "2026-11-03T17:00:00Z",
"expires_at": "2026-11-03T17:30:00Z",
"quote_id": "sq_..."
}A carrier that fails to quote gets HTTP 502 with code RATE_PROVIDER_UNAVAILABLE.
Order labels
GET /orders/{orderId}/labels (scope orders:labels): the live labels bought for a Medusa order, by its Medusa order id. An empty list means nothing has shipped yet; an unknown order is HTTP 404.
json
{
"labels": [
{
"tracking_number": "9400...",
"carrier": "USPS",
"postage_cents": 742,
"insurance_cents": 0,
"is_freight": false,
"orders_on_shipment": 1,
"purchased_at": "2026-11-03T17:05:00Z"
}
]
}postage_cents is null when the purchase recorded no price. orders_on_shipment above 1 means a consolidated box whose postage is shared with other orders.
Return labels
For a return created in Medusa, keyed by its Medusa return fulfillment id (scope returns:labels). The Navexa Medusa plugin uses these.
| Endpoint | Does |
|---|---|
POST /returns/{fulfillmentId}/rates | Quotes the return label rates, cheapest first. Free. Optional body: { "order_id": "..." } |
POST /returns/{fulfillmentId}/buy-label | Buys the rate you picked: { "rate_id": "..." }. Charges your account |
POST /returns/{fulfillmentId}/cancel | Cancels the return, refunding its label first if one was bought |
Buying twice, or with a rate from an expired quote, is refused with HTTP 409 (RETURN_LABEL_ALREADY_BOUGHT, RETURN_RATE_STALE). Get rates again and retry.