Skip to content

Checkout rates ​

Navexa can quote live shipping rates at your store's checkout from the carriers you set up in Carriers. Settings live under Settings → Live rates (owners and admins): a shipping buffer and pick-and-pack fee added to each rate, free shipping on the cheapest rate above a cart subtotal, how rates appear to customers, the order cutoff and handling days that delivery dates count from, and the default package used to quote.

Shopify ​

Shopify asks Navexa for rates through a carrier service.

  1. In Navexa, open Settings → Live rates and choose Enable live rates.
  2. Your Shopify plan needs third-party carrier-calculated shipping (Advanced or Plus, or a paid add-on). See Shopify's help.
  3. In Shopify admin, open Settings → Shipping and delivery. Under your shipping profile, pick a zone and choose Add rate.
  4. Choose Use carrier or app to calculate rates, select Navexa Shipping as the carrier service and tick the services to offer.
  5. Leave Handling fee and Handling fee percentage blank in Shopify. Navexa already adds your shipping buffer and pick-and-pack fee, so a Shopify handling fee charges customers twice.

Medusa ​

The Navexa Medusa plugin adds a navexa fulfillment provider that quotes checkout rates, and keeps orders, products, inventory and locations in sync with Navexa. It needs Medusa 2.21.1 or later, Node.js 20 or later, and a Medusa backend reachable over HTTPS (Navexa calls it back).

Install ​

The plugin is @conradkurth/navexa-medusa-plugin on GitHub Packages. Use 0.6.24 or later: earlier versions don't sign rate requests (see Request signing). GitHub Packages wants a token even for public packages:

  1. Create a GitHub personal access token (classic) with the read:packages scope.

  2. Add an .npmrc to your Medusa project (commit it, never the token):

    ini
    @conradkurth:registry=https://npm.pkg.github.com
    //npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}
  3. Install, then run the plugin's migration and restart Medusa:

    bash
    export GITHUB_PACKAGES_TOKEN=<your token>
    npm install @conradkurth/navexa-medusa-plugin
    npx medusa db:migrate

Your CI and Docker builds need the same .npmrc and token. A 403 or 404 on install means your GitHub account can't read the package yet: email support.

Connect ​

  1. In Medusa Admin, create a secret API key (Settings → Secret API Keys).

  2. In Navexa, go to Integrations → Medusa → Connect and enter your Medusa backend's public https:// URL, that key, and a webhook secret (any long random string, for example from openssl rand -hex 32; leave it blank to have one generated).

  3. Navexa shows the webhook secret, the plugin API token and the integration id once. Copy them, then register the plugin in medusa-config.ts with the same options on the module and the fulfillment provider:

    ts
    import { defineConfig } from "@medusajs/framework/utils"
    
    const navexa = {
      api_url: "https://api.navexa.co",
      navexa_base_url: "https://api.navexa.co",
      integration_id: process.env.NAVEXA_INTEGRATION_ID,
      api_token: process.env.NAVEXA_API_TOKEN,
      webhook_secret: process.env.NAVEXA_WEBHOOK_SECRET,
    }
    
    export default defineConfig({
      // ...your existing config
      plugins: [{ resolve: "@conradkurth/navexa-medusa-plugin", options: navexa }],
      modules: [
        { resolve: "@conradkurth/navexa-medusa-plugin/modules/navexa", options: navexa },
        {
          resolve: "@medusajs/medusa/fulfillment",
          options: {
            providers: [
              // keep the providers you already use
              { resolve: "@conradkurth/navexa-medusa-plugin/providers/navexa-fulfillment", id: "navexa", options: navexa },
            ],
          },
        },
      ],
    })
  4. Redeploy. Navexa starts a full sync (locations, products, orders) as soon as you connect.

  5. In Medusa Admin (Settings → Locations & Shipping), create shipping options on the navexa provider. It offers Next Business Day Delivery, Express, Standard and Free Shipping; each shows at checkout only when Navexa returns a rate with that name for the cart.

Settings → Navexa in Medusa Admin shows the connection status, and can hold the credentials instead of env vars. It only drives the sync module: checkout rates always use the options in medusa-config.ts.

If Navexa's carrier quote fails, Navexa answers with fallback rates (the route's last good quote, else 90-day average prices). If the plugin can't reach Navexa at all, it uses the cart's last good quote from the past 24 hours, else its fallback_rates_cents option (prices in cents per service name, for example { "Standard": 899 }).

No Navexa rates at checkout? ​

  • Check api_url, integration_id and webhook_secret on the fulfillment provider in medusa-config.ts; the admin page doesn't affect it.
  • Check the shipping options use the navexa provider, and that Navexa has carriers and a ship-from location.
  • If your account requires signed requests, the plugin must be 0.6.24 or later with the current webhook secret.

Request signing ​

Every rate request to Navexa can be signed so nobody else can ask for rates in your name. The Medusa plugin does this for you.

  • Header X-Navexa-Signature: the HMAC-SHA256 of the raw request body, keyed by the integration's webhook secret, as lowercase hex.
  • Sign the exact bytes you send. Re-encoding the JSON after signing breaks the signature.
  • Until signed requests are required for your account, an unsigned or wrongly signed request is still answered (and logged). An owner or admin turns the requirement on under Settings → Shipping controls. After that such a request gets HTTP 401 and no rates.
  • Only Medusa integrations have a webhook secret. Requests for other integrations can't be signed, so they stop getting rates once signing is required.
  • Regenerating the webhook secret in Navexa stops the old one at once: update the plugin straight away.
js
import crypto from "node:crypto"

const body = JSON.stringify(rateRequest)
const signature = crypto
  .createHmac("sha256", process.env.NAVEXA_WEBHOOK_SECRET)
  .update(body)
  .digest("hex")

await fetch(`https://api.navexa.co/v1/rates/${integrationId}`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Navexa-Signature": signature },
  body, // the exact string you signed
})

The rates endpoint ​

POST https://api.navexa.co/v1/rates/{integration_id} is what the Medusa plugin calls. Any platform can call it the same way.

Request ​

json
{
  "origin": {
    "address1": "123 Warehouse Blvd",
    "city": "Austin",
    "province": "TX",
    "postal_code": "78701",
    "country": "US"
  },
  "destination": {
    "address1": "456 Oak Ave",
    "city": "New York",
    "province": "NY",
    "postal_code": "10001",
    "country": "US"
  },
  "items": [
    {
      "name": "Widget",
      "sku": "WID-001",
      "quantity": 2,
      "grams": 500,
      "price": 1999,
      "length_inches": 6.0,
      "width_inches": 4.0,
      "height_inches": 2.0
    }
  ],
  "currency": "USD",
  "subtotal_cents": 3998
}
  • price: item price in cents. grams: item weight, 0 if unknown.
  • subtotal_cents: the cart subtotal in cents, used for the free shipping threshold.
  • length_inches, width_inches, height_inches: optional; without them Navexa uses the default package.

Response ​

Always HTTP 200; on an error the rates array is empty. The exception is a missing or wrong signature once your account requires signed requests: HTTP 401.

json
{
  "rates": [
    {
      "service_name": "UPS Ground",
      "service_code": "UPS_GROUND",
      "total_price": 899,
      "description": "3-5 business days",
      "currency": "USD",
      "estimated_days": 4,
      "min_delivery_date": "2026-06-05",
      "max_delivery_date": "2026-06-09"
    }
  ]
}
  • total_price: the rate in cents.
  • estimated_days, min_delivery_date, max_delivery_date: optional. Business days from the order to delivery, counting the order cutoff, handling days and carrier holidays.

Questions? Chat with us from Help & support in the app, or email support@navexa.co.