> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tybritelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Shipping & Logistics

> Reference for the ShippingService class in the Tybrite SDK.

The `ShippingService` class (accessed via `client.shipping`) handles delivery fee calculations, zone checking, and shipping configuration retrieval for your commerce storefront.

## Methods

### `getShippingZones`

Retrieve all active delivery zones and distance-based pricing tiers. This is useful for displaying delivery availability or potential costs to users early in the shopping journey.

```typescript theme={null}
const { pricing_tiers, delivery_zones } = await client.shipping.getShippingZones();

// Display zones on a map or list tiers
console.log(`Configured ${delivery_zones.length} custom delivery zones.`);
```

A store configured with distance tiers returns them ordered by `priority`:

```json theme={null}
{
  "pricing_tiers": [
    { "tier_name": "Local (within 10 km)", "min_distance_meters": 0, "max_distance_meters": 10000, "delivery_fee": 5.99, "free_delivery_threshold": 75, "is_active": true, "priority": 1 },
    { "tier_name": "Metro (10–50 km)", "min_distance_meters": 10000, "max_distance_meters": 50000, "delivery_fee": 9.99, "free_delivery_threshold": 150, "is_active": true, "priority": 2 },
    { "tier_name": "Regional (50–500 km)", "min_distance_meters": 50000, "max_distance_meters": 500000, "delivery_fee": 14.99, "free_delivery_threshold": 250, "is_active": true, "priority": 3 },
    { "tier_name": "National (500 km+)", "min_distance_meters": 500000, "max_distance_meters": 20000000, "delivery_fee": 24.99, "free_delivery_threshold": 400, "is_active": true, "priority": 4 }
  ],
  "delivery_zones": []
}
```

***

### `calculateShipping`

Calculate the exact delivery fee for a customer based on their location and order total.

<CodeGroup>
  ```typescript Via Place Name theme={null}
  // Calculate using a human-readable address or landmark
  // Tybrite internally geocodes this to find the best matching zone or tier.
  const shipping = await client.shipping.calculateShipping({
    requestBody: {
      place_name: "Brooklyn, New York",
      order_total: 50
    }
  });
  // → { fee: 9.99, tier_name: "Metro (10–50 km)", is_free: false, applied_rule: "distance", ... }
  ```

  ```typescript Via Coordinates theme={null}
  // Calculate using exact GPS coordinates (recommended)
  const shipping = await client.shipping.calculateShipping({
    requestBody: {
      latitude: 40.6782,
      longitude: -73.9442,
      order_total: 50 // Order subtotal in the store's currency (USD)
    }
  });
  // → { fee: 5.99, tier_name: "Local (within 10 km)", is_free: false, applied_rule: "distance", ... }
  ```
</CodeGroup>

<Note>
  `order_total` is the order subtotal in the store's currency (e.g. `50` = \$50.00), **not** a
  minor-unit (cents) value. It is used only to decide whether the order reaches a tier's or zone's
  free-delivery threshold.
</Note>

**Response Fields:**

| Field          | Description                                                             |
| :------------- | :---------------------------------------------------------------------- |
| `fee`          | The final calculated delivery cost.                                     |
| `applied_rule` | Indicates calculation logic: `'zone'`, `'distance'`, or `'default'`.    |
| `is_free`      | Boolean indicating if a free delivery threshold was reached.            |
| `coordinates`  | Returns the resolved \[lat, lng] even if you searched via `place_name`. |

### Live carrier rates (multi-carrier)

When the store has multi-carrier shipping connected, pass a destination `address_to` **and** a
`parcel` to `calculateShipping` to ALSO get **live carrier rates** alongside the zone/tier fee. The
response gains a `rates[]` array (real quotes from USPS, UPS, FedEx, DHL…) and `rate_source` becomes
`shippo`. If the store has connected its own **custom carrier** (a Custom Extension shipping provider),
its rates are appended too and `rate_source` becomes `custom`. When neither is connected, `rates[]` is
simply absent and the zone/tier `fee` applies as before — the manual rates always work.

```typescript theme={null}
const quote = await client.shipping.calculateShipping({
  requestBody: {
    latitude: 40.748, longitude: -73.985, order_total: 120,
    address_to: { street1: '350 5th Ave', city: 'New York', state: 'NY', zip: '10118', country: 'US' },
    parcel: { length: '10', width: '8', height: '4', distance_unit: 'in', weight: '2', mass_unit: 'lb' },
  },
});
// quote.rate_source === 'shippo'
// quote.rates === [ { rate_id, provider: 'USPS', service: 'Ground Advantage', amount: 10.43, currency: 'USD', estimated_days: 5 }, … ]
```

Show `rates[]` to the shopper, let them pick, and carry the chosen `rate_id` into checkout (pass it as
`shippo_rate_id` on `createOrder` so the order's shipping is validated against that real quote).

<Note>
  Buying a shipping label is done by the merchant from their admin — it charges the merchant's carrier
  account, marks the order shipped, and emits `order.shipped` — so there is no SDK method for it. From a
  storefront you quote rates (`calculateShipping`) and track parcels (`trackShipment`).
</Note>

### `trackShipment`

Returns live tracking status for a parcel by carrier + tracking number — use this when you want to
render the status timeline **inside** your own UI.

```typescript theme={null}
const status = await client.shipping.trackShipment({ carrier: 'usps', number: order.tracking_number });
// → { carrier, tracking_number, status, status_details, eta, history: [...] }
```

<Tip>
  The lightest way to let a shopper track is to **link to the carrier's own tracking page** — no API
  call. When the label is bought, the carrier tracking URL is saved on the order at
  `shipping_metadata.shippo.tracking_url`; render it as a "Track your package" link. Use
  `trackShipment` only when you want the status shown natively in your storefront.
</Tip>

### How Fees Are Calculated

Tybrite uses a priority-based logic to determine the shipping fee:

1. **Custom Zones**: If the location falls within a defined polygon, the specific zone fee is applied.
2. **Distance Tiers**: If the customer is outside all zones, the system calculates the distance from your store and matches it to a pricing tier (e.g., 5-10km).
3. **Free Delivery**: If the `order_total` exceeds the threshold for that zone/tier, the fee is waived.

***

<Tip>
  **Precision Tip:** For the best user experience, use the browser's Geolocation API to get high-precision coordinates for the `calculateShipping` method.
</Tip>

***

<Note>
  **Input Flexibility:** `calculateShipping` accepts **either** `{ latitude, longitude, order_total }` **or** `{ place_name, country_code?, order_total }`. When you supply a `place_name`, Galactic Core resolves it to coordinates for you. The response shape is identical regardless of input mode: `{ fee, zone_name, tier_name, distance_meters, is_free, reason, applied_rule, coordinates }`.
</Note>

## Response Codes

Both methods accept **publishable** and **secret** keys. `calculateShipping` uses `POST` purely to carry a structured request body — it is read-only.

### `getShippingZones` (`GET /v1/shipping/zones`)

| Code  | Meaning                                            |
| :---- | :------------------------------------------------- |
| `200` | Zones and pricing tiers returned.                  |
| `401` | Invalid or missing API key.                        |
| `403` | Forbidden (e.g., key disabled or store suspended). |
| `429` | Rate limit exceeded.                               |
| `500` | Internal server error.                             |

This method has no `400` because it takes no input to validate.

### `calculateShipping` (`POST /v1/shipping/calculate`)

| Code  | Meaning                                                                                                                             |
| :---- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Fee calculated and returned.                                                                                                        |
| `400` | Invalid coordinates (out-of-range lat/lng), missing `order_total`, or `geocoding_failed` when a `place_name` could not be resolved. |
| `401` | Invalid or missing API key.                                                                                                         |
| `403` | Forbidden (e.g., key disabled or store suspended).                                                                                  |
| `404` | Store has no shipping configuration (no zones and no distance tiers).                                                               |
| `429` | Rate limit exceeded.                                                                                                                |
| `500` | Internal server error — including upstream geocoding or geographic-lookup failure.                                                  |
