# Create a shipment for a sales order

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

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

Creates an empty shipment for an order: one address, one shipping type. Add its lines with the shipment line operations afterwards.

The shipment takes the team's next shipment number, the starting shipment status, the customer's primary brand profile, and the team's default shipping type unless you send one.

**Safe retries:** send your own `operation_key`. Sending the same key with the same fields again returns the shipment already created instead of a second one.

The answer is the shipment. `{unique_key}` is the order's `id` (for example `V4keXwwxMQyXw7`) or its `unique_key` (`ord_…`).

## Permission

Any member of the team.

## Business rules

- A published order needs a customer first: "Select a customer before creating a shipment."
- The same `operation_key` with different fields is refused with 422 `shipment_create_operation_payload_conflict`. Reusing the key of a shipment that was deleted since is refused with 422 `shipment_create_operation_removed`.
- Refused when the Shopify order was deleted in Shopify.

## Side effects

- Refreshes the order for people who have it open in the web app; on a published order, Customer Portal pages the customer has open refresh too.

## 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 sales order's or quote's `id` (a hash id), or its `unique_key` (`ord_…`). The order number (`scoped_id`) does not work. Example `V4keXwwxMQyXw7`.

## Request body

Content type `application/json`, required.

Fields:

- `customer_id` (string, optional): Accepted but ignored: the shipment always belongs to the order's customer.
- `customer_profile_id` (string | null, optional): Accepted but ignored: the shipment takes the customer's primary brand profile.
- `delivery_notes` (string, optional)
- `internal_notes` (string, optional)
- `operation_key` (string | null, optional): Your idempotency key for this create, up to 100 characters. At most 100 characters.
- `receiver_contact_id` (string | null, optional): The contact who receives it.
- `scoped_id` (string, optional): Your own shipment number, without the `SHIP-` the answer puts in front of it. Leave it out for the team's next number.
- `shipping_address_id` (string, optional): The address to ship to.
- `shipping_type_id` (string | null, optional): A shipping type id. Leave it out for the team's default.

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/shipment' \
  -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 reception.",
    "operation_key": "ship-1001-a",
    "shipping_address_id": "RNG2OuYC20YzXs"
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required): A shipment.
  - `id` (string, required)
  - `order_id` (string, required)
  - `status_id` (string, required)
  - `billable_fee_total` (decimal pair, optional): Shipping charged to the customer. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. 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. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 125000, "dp": 4 }` is 12.50). Kept with 4 decimal places; an amount never set can come as `{ "value": 0, "dp": 0 }`. Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `customer_due_date` (string (date-time) | null, optional): The order's in-hands date. ISO 8601 with offset, or null.
  - `customer_id` (string | null, optional)
  - `delivery_notes` (string | null, optional)
  - `internal_notes` (string | null, optional)
  - `is_external` (boolean | null, optional): `null` in this answer for a new shipment; read the shipment back (Get a shipment) for the stored value, `false` unless it was synced from an integration. A repeated create with the same `operation_key` answers the stored shipment, with `false`.
  - `is_valid` (boolean, optional)
  - `items` (array of objects, optional): Shipment lines (list only).
  - `order_scoped_id` (string, optional)
  - `ready_to_ship` (boolean, optional)
  - `receiver_contact_id` (string | null, optional)
  - `scoped_id` (string, optional): The shipment number.
  - `shipment_estimates` (array of objects, optional): Shipping estimates (list only).
  - `shipment_packages` (array of objects, optional): Packages (list only).
  - `shipping_address_id` (string | null, optional)
  - `shipping_type_id` (string | null, optional)
  - `status` (object | null, optional): A status, as the status operations return it.
    - `color` (string, required): A hex color, such as `#2196f3`.
    - `customer_label` (string, required): What customers see in the customer portal. When none is set: `Pending` for `start`, `In Progress` for `in_progress` and `pre_production`, `Completed` for `end`.
    - `id` (string, required)
    - `label` (string, required): The status name people see.
    - `position` (integer, required): Sort order within its list, lowest first.
    - `role` (string, required): Which list the status belongs to: `order`, `quote`, `job`, `shipment`, `purchase_order`, `pipeline` or `customer_message`.
    - `trashed` (boolean, required): true for a deleted status.
    - `type` (string, required): `start` is the first status of its list and `end` the last; `pre_production` and `in_progress` are the ones in between. One of: `"start"`, `"pre_production"`, `"in_progress"`, `"end"`.
    - `pipeline_id` (string, optional): The task preset, for `pipeline` statuses only; absent for other roles.
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "billable_fee_total": {
      "dp": 0,
      "value": 0
    },
    "cost_total": {
      "dp": 0,
      "value": 0
    },
    "delivery_notes": "Leave at reception.",
    "id": "qAKg4jO63uFqjs",
    "is_external": null,
    "order_id": "V4keXwwxMQyXw7",
    "order_scoped_id": "SO1001",
    "ready_to_ship": false,
    "scoped_id": "SHIP-1001",
    "shipping_address_id": "RNG2OuYC20YzXs",
    "status": {
      "color": "#9e9e9e",
      "customer_label": "Pending",
      "id": "43J0pEwV4RKUdO",
      "label": "To pack",
      "position": 1,
      "role": "shipment",
      "trashed": false,
      "type": "start"
    },
    "status_id": "43J0pEwV4RKUdO"
  },
  "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`.
- `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.
- `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: No customer on a published order ("Select a customer before creating a shipment."), or a bad id. Fix: Set the order's customer first, or check the ids.
  - When: The `operation_key` was used before with other fields (`shipment_create_operation_payload_conflict`), or its shipment was deleted (`shipment_create_operation_removed`). Fix: Resend the first request unchanged, or use a new key.

## Related

- [Add, change and remove shipment lines at once](https://developer.yoprint.com/reference/shipment-line-items/v2-shipment-item-bulk.md): `POST /v2/api/store/{team_slug}/v2/shipment/{unique_key}/item/bulk`
- [Update a shipment](https://developer.yoprint.com/reference/shipments/v2-shipment-update.md): `PUT /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`
