Shipments API

Manage the shipments for an order. A shipment records which items were shipped together, along with carrier and tracking details. An order can have several shipments, which is useful when items ship separately.

A shipment counts as sent once it has a tracking number, and the buyer is emailed then. When sent shipments hold every item, the order’s status becomes fulfilled. Canceling or reverting a shipment moves the order back to paid. Stock is taken when the order is paid, so shipments never change inventory.

A store can also buy a shipment’s label in the dashboard through its own ShipStation account. The label sets the shipment’s carrier and tracking, and the status then follows the carrier’s scans, from label_created through in_transit to delivered (or exception). Labels are only bought and voided in the dashboard. Until its label is voided there, a labeled shipment can’t be canceled or reverted, and its carrier and tracking can’t be changed: those requests return 409 Conflict.

See Webhooks to be told when a shipment is created.

Endpoints


List Shipments

Retrieve the shipments for an order. This endpoint is not paginated and does not include line items. Fetch a single shipment to see its items.

Endpoint: GET /api/v1/orders/{order_id}/shipments/

Scope Required: shipments:read

Request

curl -X GET "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/" \
  -H "Authorization: Bearer your_api_key_here"

Response

Status: 200 OK

{
  "data": [
    {
      "id": "shp_d0b6fmv28q6vn14peun0",
      "number": 1,
      "status": "label_created",
      "tracking_number": "1Z999AA10123456784",
      "tracking_url": "https://example.com/track/1Z999AA10123456784",
      "carrier_id": 1,
      "items": [],
      "created_at": "2023-01-02T09:00:00Z",
      "updated_at": "2023-01-02T09:30:00Z"
    }
  ]
}

Create Shipment

Create a shipment for one or more ordered items. Supply the order_variant_id from the order’s items array (see Orders). Quantities are applied against the order. The new shipment is unshipped until you add its tracking with Update Shipment.

Endpoint: POST /api/v1/orders/{order_id}/shipments/

Scope Required: shipments:write

Request Body

{
  "items": [
    {
      "order_variant_id": "orv_d0b6fmv28q6vn14peun0",
      "quantity": 1
    }
  ]
}
Field Type Required Description
items array Yes Line items to ship, with at least one item and a positive total quantity
items[].order_variant_id string Yes Line item public ID from the order’s items array
items[].quantity integer Yes Quantity to ship (non-negative)

Request

curl -X POST "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-12345" \
  -d '{
    "items": [
      { "order_variant_id": "orv_d0b6fmv28q6vn14peun0", "quantity": 1 }
    ]
  }'

Response

Status: 201 Created

{
  "id": "shp_d0b6fmv28q6vn14peun0"
}

Error Responses

No items supplied

Status: 400 Bad Request

{
  "error": "A shipment requires at least one item",
  "code": "INVALID_FIELD"
}

Negative quantity

Status: 400 Bad Request

{
  "error": "Quantity must be non-negative",
  "code": "INVALID_FIELD"
}

Order canceled by the customer

Status: 409 Conflict

{
  "error": "The customer canceled this order",
  "code": "ORDER_CANCELED"
}

Order not found

Status: 404 Not Found

{
  "error": "Resource not found",
  "code": "NOT_FOUND"
}

Get Shipment

Retrieve a single shipment, including its line items.

Endpoint: GET /api/v1/orders/{order_id}/shipments/{shipment_id}/

Scope Required: shipments:read

Request

curl -X GET "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/shp_d0b6fmv28q6vn14peun0/" \
  -H "Authorization: Bearer your_api_key_here"

Response

Status: 200 OK

{
  "id": "shp_d0b6fmv28q6vn14peun0",
  "number": 1,
  "status": "label_created",
  "tracking_number": "1Z999AA10123456784",
  "tracking_url": "https://example.com/track/1Z999AA10123456784",
  "carrier_id": 1,
  "items": [
    {
      "order_variant_id": "orv_d0b6fmv28q6vn14peun0",
      "product_id": "pro_d0b6fmv28q6vn14peun0",
      "product_name": "Premium T-Shirt",
      "variant_name": "Small Red",
      "quantity": 1,
      "price": 1999
    }
  ],
  "created_at": "2023-01-02T09:00:00Z",
  "updated_at": "2023-01-02T09:30:00Z"
}

Update Shipment

Update a shipment’s carrier, tracking details, or item quantities. A canceled shipment can’t be updated and returns 409 Conflict with code SHIPMENT_CANCELED.

Endpoint: PUT /api/v1/orders/{order_id}/shipments/{shipment_id}/

Scope Required: shipments:write

Request Body

{
  "carrier_id": 1,
  "tracking_number": "1Z999AA10123456784",
  "tracking_url": "https://example.com/track/1Z999AA10123456784",
  "items": [
    {
      "order_variant_id": "orv_d0b6fmv28q6vn14peun0",
      "quantity": 1
    }
  ]
}
Field Type Required Description
carrier_id integer|null No Carrier ID. See Carriers for the list of IDs
tracking_number string|null No Tracking number
tracking_url string|null No Tracking URL
items array No New line item quantities (non-negative)

carrier_id and the tracking fields are optional. Set tracking_number and tracking_url directly if you do not have a carrier ID. Adding a tracking number marks an unshipped shipment as sent and emails the buyer; changing it later emails them the new one.

A shipment with a label bought in the dashboard keeps that label’s carrier and tracking. Changing either returns 409 Conflict; updating only its items is allowed.

Request

curl -X PUT "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/shp_d0b6fmv28q6vn14peun0/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-67890" \
  -d '{
    "carrier_id": 1,
    "tracking_number": "1Z999AA10123456784"
  }'

Response

Status: 204 No Content

Error Responses

Negative quantity

Status: 400 Bad Request

{
  "error": "Quantity must be non-negative",
  "code": "INVALID_FIELD"
}

Cancel Shipment

Cancel a shipment, such as one recorded by mistake or a parcel lost before it reached the carrier. Shipments are never deleted, so the order keeps a record of every parcel. A canceled shipment keeps its items and tracking for reference, but they count as unshipped again, so the order moves back to paid until they ship in another shipment. A canceled shipment can’t be updated, reverted, or canceled again. The cancellation is recorded in the order’s events. A shipment with a label returns 409 Conflict until the label is voided in the dashboard.

The buyer is not emailed. If they were already sent the shipping email, the next shipment you record emails them its own tracking.

Endpoint: POST /api/v1/orders/{order_id}/shipments/{shipment_id}/cancel/

Scope Required: shipments:write

Request Body

{
  "reason": "Recorded by mistake"
}
Field Type Required Description
reason string|null No Why the shipment was canceled, up to 256 characters

Request

curl -X POST "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/shp_d0b6fmv28q6vn14peun0/cancel/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-13579" \
  -d '{ "reason": "Recorded by mistake" }'

Response

Status: 204 No Content

Error Responses

Already canceled

Status: 409 Conflict

{
  "error": "Shipment is canceled",
  "code": "SHIPMENT_CANCELED"
}

Reason too long

Status: 400 Bad Request

{
  "error": "Reason must be at most 256 characters",
  "code": "INVALID_FIELD"
}

Revert Shipment

Revert a shipment to unshipped and clear its carrier and tracking. The shipment keeps its items. A shipment with a label returns 409 Conflict until the label is voided in the dashboard.

Endpoint: POST /api/v1/orders/{order_id}/shipments/{shipment_id}/revert/

Scope Required: shipments:write

Request

curl -X POST "https://studio.easel.engineering/api/v1/orders/ord_d0b6fmv28q6vn14peun0/shipments/shp_d0b6fmv28q6vn14peun0/revert/" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Idempotency-Key: unique-request-id-24680"

Response

Status: 204 No Content


Labeled Shipments

A change that would leave a bought label behind returns 409 Conflict with the CONFLICT error code:

{
  "error": "This shipment has a shipping label. Void it in the dashboard first.",
  "code": "CONFLICT"
}

Void the label in the dashboard, on the shipment’s Shipping label page, then retry. Voiding returns the shipment to unshipped and clears its carrier and tracking.


Shipment Object

Fields

Field Type Description
id string Shipment public ID
number integer Shipment number within the order
status string Shipment status (see below)
tracking_number string|null Tracking number
tracking_url string|null Tracking URL
carrier_id integer|null Carrier ID
items array Line items (single shipment only)
canceled_at string|null ISO 8601 timestamp when the shipment was canceled
cancel_reason string|null Why it was canceled, when a reason was given
created_at string ISO 8601 timestamp
updated_at string ISO 8601 timestamp

Shipment Item Object

Fields

Field Type Description
order_variant_id string Ordered line item public ID
product_id string Product public ID
product_name string Product name
variant_name string Variant name
quantity integer Quantity in this shipment
price integer Unit price in the store’s smallest currency unit

Shipment Status Values

  • unshipped - recorded but not yet sent
  • prelabel
  • on_hold
  • label_created - has tracking; for a label bought through ShipStation, not yet scanned by the carrier
  • in_transit - accepted or moving, from the carrier’s scans
  • out_for_delivery
  • failed_attempt - the carrier tried to deliver
  • delivered
  • available_for_pickup - waiting at a pickup point
  • exception - the carrier reported a delivery problem

Statuses after label_created are set from tracking, which Easel checks every few hours for labels bought through ShipStation. The buyer is emailed once when a shipment reaches delivered, and once if it reaches exception. - shipped - sent without tracking, such as a letter by regular post, recorded from the dashboard - canceled - canceled; its items count as unshipped (see Cancel Shipment)