> This page is for version 2026-01.
> For other versions, use one of these documentation indexes:
> - 2026-07 (default): https://developer.shipbob.com/2026-07/llms.txt
> - 2026-01: https://developer.shipbob.com/2026-01/llms.txt
> - 2025-07: https://developer.shipbob.com/2025-07/llms.txt
> - v2.0 (deprecated): https://developer.shipbob.com/v2.0/llms.txt
> - v1.0 (deprecated): https://developer.shipbob.com/v1.0/llms.txt
> - Experimental: https://developer.shipbob.com/experimental/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.shipbob.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.shipbob.com/_mcp/server.

# ShipBob Logistics API

This API lets you create shipping labels and sync tracking numbers for orders processed through ShipBob’s logistics system. It’s straightforward to integrate and works in both sandbox and production environments.

## Create an Account

To get started, you’ll need a ShipBob account. Sign up [here](https://web.shipbob.com/app/merchant/#/SignUp?utm_source=app_partner). After signing up, reach out to your ShipBob representative and they’ll guide you through the process and provide the necessary credentials.

## Base URLs

#### Production (Live)

**`Base URL`**

```javascript Base URL
https://logisticslabelprintingapi.shipbob.com
```

#### Sandbox (Testing)

**`Base URL`**

```javascript Base URL
https://logisticslabelprintingapi-stage.shipbob.dev
```

## Authentication

Authentication depends on the API you’re calling:

#### For logisticslabelprintingapi Endpoints

For `https://logisticslabelprintingapi-stage.shipbob.dev/` or `https://logisticslabelprintingapi.shipbob.com/`:

Every endpoint accepts **either** of the following. Pick one — you do not need both.

**Option 1 — Token (SBL_authToken or PAT)**

Use an `SBL_authToken` provided by your ShipBob rep, or a Personal Access Token (PAT) generated in the ShipBob dashboard under **Integrations** > **API Tokens** > **Generate new token**.

```
Authorization: <SBL_authToken>
```

The `Bearer` prefix is optional for this API — both of the following are accepted:

```
Authorization: <Personal_Access_Token>
Authorization: Bearer <Personal_Access_Token>
```

**Option 2 — OAuth 2.0 (Authorization Code with PKCE)**

Recommended for multi-user applications. Register an OAuth app in the ShipBob dashboard under **Integrations** > **OAuth Apps** > **Create App**, run the authorization code flow with PKCE, then send the returned access token as a Bearer token:

```
Authorization: Bearer <OAuth_Access_Token>
```

| Setting               | Value                                                     |
| --------------------- | --------------------------------------------------------- |
| Grant type            | `Authorization Code (With PKCE)`                          |
| Authorization URL     | `https://auth.shipbob.com/connect/authorize`              |
| Access token URL      | `https://auth.shipbob.com/connect/token`                  |
| Code challenge method | `S256` (SHA-256)                                          |
| Scope                 | `orders_write` (add `offline_access` for a refresh token) |
| Client authentication | Send client credentials as a Basic Auth header            |

> **Note**
>
> On sandbox, replace `auth.shipbob.com` with `authstage.shipbob.com`.

The same settings in Postman's **Authorization** tab:

![Postman OAuth 2.0 token configuration for ShipBob Logistics](/_fern-img/ee22f8884691517bce0dfcb93342a2c12eec02b6204acb8d220fe0e452111ec3.webp)

The token exchange call carries the PKCE verifier:

```text
grant_type=authorization_code
code=<authorization-code>
redirect_uri=<your-registered-callback-url>
client_id=<your-client-id>
client_secret=<your-client-secret>
code_verifier=<pkce-code-verifier>
```

Access tokens expire after 1 hour. For the full step-by-step flow, including how to generate the `code_verifier` and `code_challenge`, see [Authentication](/2026-01/auth#oauth-flow).

#### For sandbox-api or api.shipbob.com Endpoints

For `https://sandbox-api.shipbob.com/` or `https://api.shipbob.com/`:

Use a Personal Access Token (PAT), which you can generate in the ShipBob dashboard by going to **Integrations** > **API Tokens** > **Generate new token**.

```json
Authorization: Bearer <Personal_Access_Token>
```

## Create an Order and Purchase a Label

`POST /api/v1/CreateOrderAndPurchaseLabel`

Creates a packed order and purchases its final-mile carrier label in a single synchronous call, returning the carrier, tracking number, and a URL to the label.

> **Note**
>
> If an order with the same `reference_id` already exists but has no final-mile label yet, the existing order is reused and only the label is purchased. If it already has a label, the call returns `409` — retrieve the existing label instead.

**Headers**

| Header          | Value                                                              | Required? | Description                                             |
| --------------- | ------------------------------------------------------------------ | --------- | ------------------------------------------------------- |
| `Authorization` | `<SBL_authToken>` / `Bearer <PAT>` / `Bearer <OAuth_Access_Token>` | Yes       | Any of the methods in [Authentication](#authentication) |
| `Content-Type`  | `application/json`                                                 | Yes       | Accepted content type                                   |

> **Note**
>
> This endpoint does **not** use the `Label-Type` header. Request the label format with the `order.label_file_format` field in the body instead.

**Request Body**

**`POST /api/v1/CreateOrderAndPurchaseLabel`**

```json POST /api/v1/CreateOrderAndPurchaseLabel
{
  "order": {
    "shipping_method": "Standard",
    "recipient": {
      "name": "Test Test",
      "email": "johndoe@shipbob.com",
      "phone_number": "444-333-2222",
      "address": {
        "address1": "223 E 5th St",
        "address2": null,
        "company_name": null,
        "city": "Greenville",
        "state": "NC",
        "country": "US",
        "zip_code": "27957"
      }
    },
    "location_id": 33,
    "reference_id": "101",
    "measurements": {
      "total_weight_oz": 4,
      "length_in": 2,
      "width_in": 4,
      "depth_in": 6
    },
    "products": [
      { "name": "Light Roast Coffee", "sku": "LIGHT-ROAST", "quantity": 1 }
    ],
    "label_file_format": "PNG", // optional
    "requires_signature": true  // optional
  },
  "meta": {
    "order_number": "101",
    "order_id": "101",
    "customer_name": "ABC Merch"
  }
}
```

**Key Fields**

| Field                      | Type             | Required | Description                                                                                                                             |
| -------------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `order.shipping_method`    | string           | Yes      | Shipping option (e.g., `"Standard"`)                                                                                                    |
| `order.recipient`          | object           | Yes      | Recipient details. `name` and `address` are required; `email` and `phone_number` are optional                                           |
| `order.location_id`        | integer          | Yes      | ShipBob location ID. Sandbox: use `33` or `19`. Production: contact your ShipBob rep for your location ID.                              |
| `order.reference_id`       | string           | Yes      | Unique order identifier                                                                                                                 |
| `order.measurements`       | object           | Yes      | **Required for this endpoint.** All four values (`total_weight_oz`, `length_in`, `width_in`, `depth_in`) must be positive whole numbers |
| `order.products`           | object\[]        | Yes      | **Required for this endpoint.** At least one product, each with `name`, `sku`, and `quantity`                                           |
| `order.label_file_format`  | string           | No       | `PNG` or `ZPL`. Defaults to `PNG`.                                                                                                      |
| `order.requires_signature` | boolean          | No       | Defaults to `false`                                                                                                                     |
| `meta.order_number`        | string \| number | Yes      | Your order number                                                                                                                       |
| `meta.order_id`            | string \| number | Yes      | Your order ID                                                                                                                           |
| `meta.customer_name`       | string           | Yes      | Your customer's name                                                                                                                    |

> **Warning**
>
> Unlike `POST /api/order:createLabel`, `measurements` and `products` are mandatory here, and every measurement must be a positive whole number. Unrecognized fields are ignored.

**Response**

On success (HTTP 200):

```json
{
  "interim_order_id": "67c8a7f30c3c1d16d05179db",
  "reference_id": "101",
  "shipment_id": 100000001,
  "final_mile_label": {
    "carrier": "OnTrac",
    "carrier_service": "Ground",
    "tracking_id": "D10010000000000",
    "tracking_url": "https://www.ontrac.com/tracking/?number=D10010000000000",
    "label": {
      "file_type": "PNG",
      "url": "https://.../label.png"
    }
  }
}
```

* `tracking_url` may be `null` if the carrier URL is not yet available. This is never fatal — the label is still valid.

**Error Responses**

| Status | Meaning                                                                                        |
| ------ | ---------------------------------------------------------------------------------------------- |
| `400`  | Request body failed validation, or the address is an Army Post Office / not serviceable        |
| `401`  | Missing or invalid credentials, or the token cannot be mapped to a ShipBob user                |
| `402`  | Payment declined — update the payment information on the account                               |
| `409`  | A final-mile label already exists for this order                                               |
| `422`  | An order with this `reference_id` already exists, or the label could not be purchased/rendered |
| `500`  | Server issue — try again later                                                                 |
| `502`  | Upstream label purchase failed                                                                 |
| `503`  | Payment status could not be verified — retry                                                   |

`400` responses include the failing fields:

```json
{
  "error": "Invalid request to create order and purchase label.",
  "messages": ["\"order.measurements.length_in\" must be a positive number"],
  "fields": ["order.measurements.length_in"]
}
```

**How This Differs From `POST /api/order:createLabel`**

Both endpoints create a ShipBob order and return a shipping label, and both produce a normal ShipBob shipment. Choose based on how much of the order you know upfront:

|                       | `POST /api/v1/CreateOrderAndPurchaseLabel`                                                              | `POST /api/order:createLabel`                                           |
| --------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Use when              | You have a fully packed order — final dimensions, weight, and contents — and want the label immediately | You want a label without committing product or dimension detail upfront |
| `measurements`        | Required, all four values positive whole numbers                                                        | Optional                                                                |
| `products`            | Required, at least one                                                                                  | Optional (used for pick lists and batching)                             |
| Label format          | `order.label_file_format` in the body: `PNG` (default) or `ZPL`                                         | `Label-Type` header: ZPL by default, `application/pdf` for PDF          |
| Label delivery        | Hosted URL in `final_mile_label.label.url`                                                              | Label data inline in `label`                                            |
| Also returns          | Carrier, carrier service, tracking number, tracking URL                                                 | Order and shipment identifiers only                                     |
| Optional order fields | `label_file_format`, `requires_signature` only — anything else is ignored                               | `tags`, `insurance_amount`, `packing_slip`, `requires_signature`        |

> **Tip**
>
> Both endpoints behave identically downstream. Orders created here appear in [Sync Tracking Numbers](#sync-tracking-numbers) — including the `order.shipment.tracking_received` webhook — and can be refunded or resubmitted through [Bulk Cancel Labels](#bulk-cancel-labels) using the same `reference_id`.

> **Note**
>
> Available in sandbox at `https://logisticslabelprintingapi-stage.shipbob.dev`. Use `location_id` `33` or `19` when testing; contact your ShipBob rep for your production `location_id`.

## Create a Shipping Label

`POST /api/order:createLabel`

Generate a shipping label for an order. Labels default to ZPL format, but you can request PDF by adding a `Label-Type` header.

**Headers**

| Header          | Value                                 | Required? | Description                                                                           |
| --------------- | ------------------------------------- | --------- | ------------------------------------------------------------------------------------- |
| `Authorization` | `<SBL_authToken>` or `Bearer <token>` | Yes       | SBL_authToken, PAT, or OAuth 2.0 access token. See [Authentication](#authentication). |
| `Label-Type`    | `application/pdf`                     | No        | Set to get a PDF label (optional)                                                     |

**Request Body**

Send a JSON object with order details:

**`POST /api/order:createLabel`**

```json POST /api/order:createLabel
{
  "order": {
    "shipping_method": "Standard",
    "recipient": {
      "name": "Test Test",
      "email": "johndoe@shipbob.com",
      "phone_number": "444-333-2222",
      "address": {
        "address1": "223 E 5th St",
        "address2": null,
        "company_name": null,
        "city": "Greenville",
        "state": "NC",
        "country": "US",
        "zip_code": "27957"
      }
    },
    "location_id": 33,
    "reference_id": "101",
    "measurements": {
      "total_weight_oz": 4,
      "length_in": 2,
      "width_in": 4,
      "depth_in": 6
    },
    "products": [ // optional
      { "name": "Light Roast Coffee", "sku": "LIGHT-ROAST", "quantity": 1 }
    ],
    "tags": [ // optional
      { "name": "size", "value": "small" }
    ],
    "requires_signature": true, // optional
    "insurance_amount": 400, // optional
    "packing_slip": true // optional
  },
  "meta": {
    "order_number": "101",
    "order_id": "101",
    "customer_name": "ABC Merch"
  }
}
```

**Key Fields**

| Field                | Type      | Required | Description                                                                                                       |
| -------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `shipping_method`    | string    | Yes      | Shipping option (e.g., `"Standard"`)                                                                              |
| `recipient`          | object\[] | Yes      | Recipient details (see Recipient table)                                                                           |
| `location_id`        | integer   | Yes      | ShipBob location ID. Sandbox: always use `33` or `19`. Production: contact your ShipBob rep for your location ID. |
| `reference_id`       | string    | Yes      | Unique order identifier                                                                                           |
| `measurements`       | object    | Yes      | Package dimensions and weight. All values must be whole numbers. Weight in ounces.                                |
| `meta`               | object\[] | Yes      | Extra information for your records                                                                                |
| `products`           | object\[] | No       | Pass products to print pick lists and use batching                                                                |
| `requires_signature` | boolean   | No       | `true` / `false`                                                                                                  |
| `insurance_amount`   | number    | No       | Amount in dollars. If `>= 300`, `requires_signature` automatically becomes `true`.                                |
| `packing_slip`       | boolean   | No       | `true` / `false`                                                                                                  |
| `tags`               | object\[] | No       | Key/value tags                                                                                                    |

**Response**

On success (HTTP 200):

```json
{
  "interim_order_id": "67c8a7f30c3c1d16d05179db",
  "reference_id": "101",
  "shipment_id": 100000001,
  "label": "<label_data>"
}
```

* `label`: ZPL string or PDF data (based on `Label-Type`).

**Error Responses**

* `400`: Bad request (check your JSON).
* `401`: Invalid or missing credentials (`SBL_authToken`, PAT, or OAuth 2.0 access token).
* `500`: Server issue—try again later.

## Bulk Cancel Labels

`POST /api/order:bulk-cancel`

You can use this endpoint to refund or cancel shipping labels. Additionally, it allows you to cancel and resubmit one or more orders using the same `reference_id`. Once an order is canceled through this endpoint, it can be resubmitted as needed. However, please note that canceled orders will no longer be accessible in the UI or via the API.

**Headers**

| Header          | Value                                 | Required? | Description                                                                           |
| --------------- | ------------------------------------- | --------- | ------------------------------------------------------------------------------------- |
| `Authorization` | `<SBL_authToken>` or `Bearer <token>` | Yes       | SBL_authToken, PAT, or OAuth 2.0 access token. See [Authentication](#authentication). |
| `Content-Type`  | `application/json`                    | No        | Accepted content type                                                                 |

**Request Body**

Send a JSON object with order details:

**`POST /api/order:bulk-cancel`**

```json POST /api/order:bulk-cancel
{
  "reference_ids": ["ORDER-1","ORDER-2"]
}
```

## Sync Tracking Numbers

#### Option 1 - Webhook

To receive tracking updates, subscribe to the `order.shipment.tracking_received` webhook that fires usually within 5 minutes after creating a shipping label.

Learn how to subscribe [here](http://developer.shipbob.com/logistics/webhooks).

#### Option 2 - Polling

Sync tracking numbers back to your system using the polling-based approach.

#### Fetch Orders with Tracking

* **Endpoint**: `GET https://api.shipbob.com/2026-07/order?HasTracking=true&IsTrackingUploaded=false`
* **Optional Parameter**: Add `&Limit=250` for up to 250 results.
* **Header**:`Authorization: Bearer <Personal_Access_Token>`
* **Response Example**:

```json highlight={8-13}
[
  {
    "id": 234567890,
    "reference_id": "101",
    "shipments": [
      {
        "id": 100000001,
        "tracking": {
          "carrier": "OnTrac",
          "tracking_number": "D10010000000000",
          "carrier_service": "Ground",
          "tracking_url": "https://www.ontrac.com/tracking/?number=D10010000000000"
        }
      }
    ]
  }
]
```

####

#### Iterate through each order

* Each order has 1 shipment with ShipBob Logistics

#### Sync Tracking

* Extract the `tracking_number` from the shipments array and update your system.

#### Mark shipments as uploaded

* **Endpoint**: `POST https://api.shipbob.com/2026-07/shipment:batchUpdateTrackingUpload`
* **Header**:`Authorization: Bearer <Personal_Access_Token>`
* **Body**:

```json
  {
    "is_tracking_uploaded": true,
    "shipment_ids": [100000001, 100000002, 100000003]
  }
```

#### Repeat

* Poll every 15 to 30 minutes to stay updated.

## Tips for Success

* Test in the sandbox first (use `location_id: 33` for label creation).
* Generate your Personal Access Token in the ShipBob dashboard for `sandbox-api` or `api.shipbob.com` calls.
* Contact your ShipBob rep for your production `location_id` or if you hit a 401 error with your `SBL_authToken`.
* Poll at least every 30 minutes to avoid missing tracking updates.

## Diagram

```mermaid
sequenceDiagram
    title ShipBob Logistics Integration Diagram
    autonumber
    participant Client
    participant ShipBob API

    Client->>ShipBob API: POST /api/order:createLabel
    activate ShipBob API
    ShipBob API-->>Client: 201 Created (labelUrl, shipmentId)
    deactivate ShipBob API

    note over Client,ShipBob API: Prereq (one-time): Client registers tracking_received webhook

    rect rgba(230,255,230,0.6)
    ShipBob API-->>Client: tracking_received (shipmentId, tracking number, url, carrier)
    Client->>Client: Upsert trackingNumber into system
    end

    opt Fallback (only if webhook unavailable)
        loop Every 15 min
            Client->>ShipBob API: GET /2026-07/order?HasTracking=true&IsTrackingUploaded=false
            ShipBob API-->>Client: 200 OK (orders with tracking)
        end
        Client->>Client: Upsert trackingNumber into system
    end

```

## FAQs

#### How to sign up for a sandbox account?

Sign up for a sandbox by following [this link](https://webstage.shipbob.dev/app/merchant/#/SignUp?utm_source=app_partner).

## Other Resources

* Rate Shopping API: [view here](https://developer.shipbob.com/logistics/rate-shopping)
* ShipBob Logisitics webhook: [view here](https://developer.shipbob.com/logistics/webhooks)