# Update a shipment

```http
PUT https://secure.yoprint.com/v2/api/store/{team_slug}/v2/shipment/{unique_key}
```

- API version: v2
- Operation ID: `v2_public_shipment_update`
- Section: Shipping › [Shipments](https://developer.yoprint.com/reference/shipments.md)
- Web page: https://developer.yoprint.com/reference/shipments/v2-shipment-update/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Changes a shipment's number, shipping type, address, receiver contact, brand profile, packer and notes. Send only what you change; returns the shipment.

This doesn't change the shipment's status (use Change the status of a shipment), its lines, packages or totals. A shipment that is Completed or has labels can still be edited.

## Permission

Needs permission to edit the shipment's order: the team owner, a role whose Manage Sales Orders permission is "update all", or "update assigned" when you created the order or are assigned to it, or, on a draft order, its creator when their role can create sales orders. Otherwise 403 `user_is_not_authorized`. Reading needs only team membership.

## Business rules

- An id of 0 or below is refused: "Choose a valid shipping address." (and the same for the other ids).
- Only the listed fields can be cleared with null; elsewhere null means "not sent".
- On a shipment linked to Shopify, changes are refused with 422 `shopify_managed_shipment_locked`, `shopify_fulfilled_shipment_locked` or `shopify_broken_link_shipment_locked`; on an order deleted in Shopify with `shopify_order_deleted_externally`.
- On a Shopify order the address and receiver contact can't change: 422 `shopify_shipment_snapshot_update_boundary`.
- An externally synced shipment on a draft order can't be updated: "Publish this order before updating an externally synced shipment."
- Choosing Fulfill By Shopify on a shipment not managed by Shopify: 422 `shopify_system_shipping_type_read_only`.
- Ids are not checked against your records: an address, contact, brand profile, user or shipping type that isn't yours (or isn't the order customer's) is saved as given. Read them from the order's customer and your settings first.

## Side effects

- Saves the fields; no totals change. Open Admin Portal pages refresh, and so do Customer Portal pages the order's customer has open. No webhook or email is sent.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `unique_key` (string, required): The shipment's `id` (a hash id), as its answers show it. Its unique key (`shp_...`) also works, but API answers don't include it. Example `MQyJUnd3oty75h`.

## Request body

Content type `application/json`, required.

Fields:

- `customer_id` (string, optional): Ignored: the shipment's customer is always the order's.
- `customer_profile_id` (string | null, optional): The brand profile whose details print on the documents. Null clears it.
- `delivery_notes` (string | null, optional): Notes for delivery. Null clears them.
- `internal_notes` (string | null, optional): Internal notes. Null clears them.
- `packed_by` (string | null, optional): The user who packed it. Null clears it.
- `receiver_contact_id` (string | null, optional): The receiver contact. Null clears it.
- `scoped_id` (string, optional): The shipment number without the `SHIP-` prefix, as a string. Not checked for duplicates. Don't echo the `SHIP-...` value you read.
- `shipping_address_id` (string | null, optional): The ship-to address. Null clears it.
- `shipping_type_id` (string, optional): The shipping type. Can't be cleared. Fulfill By Shopify is reserved for Shopify shipments.

### Example request

```bash
curl -X PUT 'https://secure.yoprint.com/v2/api/store/your-team/v2/shipment/MQyJUnd3oty75h' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "delivery_notes": "Leave at the loading dock.",
    "shipping_address_id": "117KfwwfreX15N",
    "shipping_type_id": "6OTWtTOpYcBz8q"
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required): A shipment.
  - `billable_fee_total` (decimal pair, optional): Shipping cost lines (what is billed), at the stored scale (4 decimal places). A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `cost_total` (decimal pair, optional): Shipping cost lines (the cost), at the stored scale (4 decimal places). A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `customer` (object | null, optional): The customer (the full customer record).
    - `id` (string, optional): The customer.
    - `name` (string, optional)
  - `customer_due_date` (string (date-time) | null, optional): The order's customer due date. ISO 8601 with offset, or null.
  - `customer_id` (string | null, optional): The customer.
  - `customer_po_number` (string | null, optional)
  - `customer_profile_id` (string | null, optional): The brand profile printed on the documents.
  - `delivery_notes` (string | null, optional)
  - `external_ids` (array of objects, optional): The shipment's links to external systems (a Shopify fulfillment); usually empty.
  - `external_mutation_source` (string, optional): Who may change a Shopify shipment: `shopify` means it is managed in Shopify and locked here; `unknown` on non-Shopify orders. One of: `"shopify"`, `"yoprint"`, `"broken_link"`, `"unknown"`.
  - `external_type` (string | null, optional)
  - `from_legacy` (boolean, optional)
  - `has_third_party_fulfilled_items` (boolean, optional)
  - `id` (string, optional): The shipment.
  - `internal_notes` (string | null, optional)
  - `is_external` (boolean | integer | null, optional)
  - `is_valid` (boolean, optional): Has lines, a shipping type, an address, and the order's customer.
  - `last_sync_source` (string | null, optional)
  - `lock_version` (integer, optional)
  - `order_external_ids` (array of objects, optional): The order's links to external systems.
  - `order_external_type` (string | null, optional)
  - `order_id` (string, optional): The order.
  - `order_object_type` (string | null, optional)
  - `order_quote` (boolean, optional)
  - `order_scoped_id` (string | null, optional): The order number.
  - `packed_by` (string | null, optional): Who packed it.
  - `pdf` (array of objects, optional): The packing slip and the shipment label in Letter, 4" x 6" and 1" x 2-1/8" sizes (`is_custom` absent), then one entry per enabled custom shipment PDF template (`label`, `type`, `description`, `url`, the `show_in_*` flags, `is_custom: true`).
    - Each item:
      - `description` (string, optional)
      - `is_custom` (boolean, optional): True on custom template entries.
      - `label` (string, optional)
      - `object_type` (string, optional)
      - `url` (string, optional): A download link with its own access token.
  - `ready_to_ship` (boolean, optional): Every job of its lines is in a Completed status.
  - `receiver_contact_id` (string | null, optional): The receiver contact.
  - `scoped_id` (string, optional): The shipment number with its prefix. Update a shipment takes only the digits.
  - `ship_due_date` (string (date-time) | null, optional): The order's ship-by date. ISO 8601 with offset, or null.
  - `ship_to_status` (string, optional): One of: `"okay"`, `"empty"`, `"diff_from_order"`, `"diff_from_customer"`.
  - `shipping_address` (object | null, optional): Ship To, the full address, always included (null when none).
    - `address1` (string | null, optional)
    - `address2` (string | null, optional)
    - `city` (string | null, optional)
    - `country` (string | null, optional)
    - `country_code` (string | null, optional)
    - `id` (string, optional): The address.
    - `label` (string | null, optional)
    - `postcode` (string | null, optional)
    - `state` (string | null, optional)
    - `state_code` (string | null, optional)
  - `shipping_address_id` (string | null, optional): Ship To.
  - `shipping_type_id` (string | null, optional): The shipping type.
  - `shopify_sync` (any, optional): Shopify sync state, for Shopify shipments.
  - `status` (object | null, optional): The shipment status, always included.
    - `color` (string | null, optional): Hex color.
    - `customer_label` (string, optional)
    - `id` (string, optional): The status.
    - `label` (string, optional)
    - `pipeline_id` (string, optional): Only on the statuses of a task preset (`role` `pipeline`): the task preset. Absent on other statuses.
    - `position` (integer, optional): Its place in the team's list: sort by it to find the next status.
    - `role` (string, optional): Which list it belongs to: `job` for job statuses, `pipeline` for the statuses of a task preset. One of: `"order"`, `"quote"`, `"job"`, `"purchase_order"`, `"shipment"`, `"customer_message"`, `"pipeline"`.
    - `trashed` (boolean, optional): The status was deleted.
    - `type` (string, optional): The kind of status: `start` (Pending), `pre_production` and `in_progress` (In Progress), `end` (Completed). One of: `"start"`, `"pre_production"`, `"in_progress"`, `"end"`.
  - `status_id` (string | null, optional): The shipment status.
  - `type` (string | null, optional)
  - `version` (integer, optional)
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "billable_fee_total": {
      "dp": 4,
      "value": 124000
    },
    "cost_total": {
      "dp": 4,
      "value": 124000
    },
    "customer": {
      "id": "ifAHmADekGZ6id",
      "name": "Northwind Apparel"
    },
    "customer_id": "ifAHmADekGZ6id",
    "customer_profile_id": null,
    "delivery_notes": "Leave at the loading dock.",
    "external_ids": [],
    "external_mutation_source": "unknown",
    "external_type": null,
    "from_legacy": false,
    "has_third_party_fulfilled_items": false,
    "id": "MQyJUnd3oty75h",
    "internal_notes": null,
    "is_external": false,
    "is_valid": true,
    "last_sync_source": null,
    "lock_version": 1730000000000000,
    "order_external_ids": [],
    "order_external_type": null,
    "order_id": "sFbALVSYD55eXe",
    "order_object_type": "sales_order",
    "order_quote": false,
    "order_scoped_id": "SO1001",
    "packed_by": null,
    "pdf": [
      {
        "description": "Packing slip with white labeling",
        "label": "Packing Slip",
        "object_type": "shipment",
        "url": "https://example.com/pdf/packing-slip.pdf"
      }
    ],
    "ready_to_ship": false,
    "receiver_contact_id": null,
    "scoped_id": "SHIP-12",
    "ship_to_status": "okay",
    "shipping_address": {
      "address1": "100 Example Street",
      "address2": null,
      "city": "Austin",
      "country": "United States",
      "country_code": "US",
      "id": "117KfwwfreX15N",
      "label": "Main office",
      "postcode": "78701",
      "state": "Texas",
      "state_code": "TX"
    },
    "shipping_address_id": "117KfwwfreX15N",
    "shipping_type_id": "6OTWtTOpYcBz8q",
    "status": {
      "color": "#9e9e9e",
      "customer_label": "Pending",
      "id": "AvY6ZbnrTJqpUI",
      "label": "Pending",
      "position": 1,
      "role": "shipment",
      "trashed": false,
      "type": "start"
    },
    "status_id": "AvY6ZbnrTJqpUI",
    "version": 2
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: Your role can't edit this order. Fix: Ask the team owner to adjust your role or assign you to the order.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
  - When: The shipment isn't your team's. Fix: Check the id.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: Validation (`validation_failed`) or a Shopify rule (see rules). Fix: Fix the named field or make the change in Shopify.

## Related

- [Get a shipment](https://developer.yoprint.com/reference/shipments/v2-shipment-show.md): `GET /v2/api/store/{team_slug}/v2/shipment/{unique_key}`
- [Change the status of a shipment](https://developer.yoprint.com/reference/shipments/v2-sales-order-shipment-change-status.md): `PUT /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/shipment/{id}/status`
- [List shipping methods](https://developer.yoprint.com/reference/shipping-methods/v1-shipping-type-index.md): `GET /v1/api/store/{team_slug}/setting/shipping_type`
