API Docs

Pick & Pack

scope: shipments

Pick & Pack shipments are customer orders where the warehouse opens cartons to pick individual product units, repacks them, and ships to a customer address. This incurs additional labor fees per unit. For shipping full cartons without opening, use SPD instead.

Lifecycle

A Pick & Pack shipment goes through these stages:

  1. 1
    Create a draft

    Choose a warehouse. The shipment starts in draft status.

  2. 2
    Configure the shipment

    Set the shipping address, add order items (product + quantity), and optionally add notes.

  3. 3
    Submit

    Validates the shipping address and items are present. Creates a new shipment in pending status and deletes the draft. Use the new ID returned in the response.

  4. 4
    Warehouse fulfills

    The warehouse picks, packs, and ships the order. Status progresses through processing to forwarded.

  5. 5
    Track via API

    Use GET /v0/shipments/:id to check the current status at any time.

Status Flow

Happy path

draftpendingprocessingforwarded

Cancellation

pendingpending_cancelcancelled

Inventory timing

Pick & Pack opens cartons to pick individual units and repack them for the customer. This incurs per-unit labor fees. Inventory is deducted when the warehouse marks the shipment as forwarded (not on submit like SPD). For shipping full cartons without opening, use SPD.

Endpoints

Shipment

POST
/v0/shipments/pnp

Create a new Pick & Pack draft

DELETE
/v0/shipments/pnp/:id

Delete a draft shipment (draft only)

POST
/v0/shipments/pnp/:id/submit

Submit shipment (draft → pending)

POST
/v0/shipments/pnp/:id/cancel

Request cancellation (pending → pending_cancel)

Shipping Address

GET
/v0/shipments/pnp/:id/shipping-address

Get the shipping address

PUT
/v0/shipments/pnp/:id/shipping-address

Set or update the shipping address (draft only)

Order Items

GET
/v0/shipments/pnp/:id/items

Get order items

PUT
/v0/shipments/pnp/:id/items

Set order items — replaces all (draft only)

Notes

GET
/v0/shipments/pnp/:id/notes

Get shipment notes

PUT
/v0/shipments/pnp/:id/notes

Update shipment notes (any status)

Create Draft

Creates a new shipment in draft status. You must specify which warehouse will fulfill the order.

Request Body

ParameterTypeDescription
warehouseId*stringID of the warehouse that will fulfill this shipment
curl
curl -X POST "https://api.3plguys.com/v0/shipments/pnp" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "warehouseId": "1" }'
Node.js
const res = await fetch("https://api.3plguys.com/v0/shipments/pnp", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ warehouseId: "1" }),
});
const draft = await res.json();
Python
res = httpx.post(
"https://api.3plguys.com/v0/shipments/pnp",
json={"warehouseId": "1"},
headers={"Authorization": f"Bearer {token}"},
)
draft = res.json()
Response
{
"id": "15",
"status": "draft",
"type": "outbound-pickpack",
"warehouse": { "id": "1", "name": "Main Warehouse" },
"notes": "",
"invoiceId": null,
"workflowId": null,
"createdAt": "2026-03-04T10:00:00.000Z",
"updatedAt": "2026-03-04T10:00:00.000Z",
}

Shipping Address

The shipping address is where the order will be delivered. It can only be set or changed while the shipment is in draft status. A complete address is required before submitting.

Request Body

All fields are optional — you can send a partial update. However, name, address1, city, state, and zip must be set before submitting.

ParameterTypeDescription
namestringRecipient name (required for submit)
companystringCompany name
address1stringStreet address line 1 (required for submit)
address2stringStreet address line 2
citystringCity (required for submit)
statestringState or province code (required for submit)
zipstringZIP or postal code (required for submit)
phonestringPhone number
curl
curl -X PUT "https://api.3plguys.com/v0/shipments/pnp/15/shipping-address" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"company": "Acme Corp",
"address1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"zip": "90001"
}'
Node.js
await fetch(`https://api.3plguys.com/v0/shipments/pnp/${draftId}/shipping-address`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "John Doe",
company: "Acme Corp",
address1: "123 Main St",
city: "Los Angeles",
state: "CA",
zip: "90001",
}),
});
Request Body
{
"name": "John Doe",
"company": "Acme Corp",
"address1": "123 Main St",
"address2": "Suite 100",
"city": "Los Angeles",
"state": "CA",
"zip": "90001",
"phone": "555-1234"
}
Response
{
"name": "John Doe",
"company": "Acme Corp",
"address1": "123 Main St",
"address2": "Suite 100",
"city": "Los Angeles",
"state": "CA",
"zip": "90001",
"phone": "555-1234"
}

Order Items

Order items specify which products and how many units to ship. Setting items replaces all existing items. Items can only be modified while the shipment is in draft status.

Request Body

ParameterTypeDescription
items*arrayList of products to include in the order
items[].productId*stringProduct ID
items[].quantity*numberNumber of units to ship
curl
curl -X PUT "https://api.3plguys.com/v0/shipments/pnp/15/items" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "productId": "1", "quantity": 50 },
{ "productId": "2", "quantity": 100 }
]
}'
Node.js
const res = await fetch(`https://api.3plguys.com/v0/shipments/pnp/${draftId}/items`, {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{ productId: "1", quantity: 50 },
{ productId: "2", quantity: 100 },
],
}),
});
const items = await res.json();
Request Body
{
"items": [
{ "productId": "1", "quantity": 50 },
{ "productId": "2", "quantity": 100 }
]
}
Response
[
{
"productId": "1",
"sku": "ABC123",
"productName": "Egg Shells",
"quantity": 50
},
{
"productId": "2",
"sku": "DEF456",
"productName": "Bubble Wrap",
"quantity": 100
}
]

Notes

Free-text notes visible to the warehouse. Unlike other fields, notes can be updated at any time regardless of shipment status.

PUT /v0/shipments/pnp/15/notes
{
"notes": "Handle with care — fragile items inside"
}
Response
{
"notes": "Handle with care — fragile items inside"
}

Submit

Submitting validates that the shipment has a complete shipping address and at least one order item. On success, a new shipment is created in pending status and the draft is deleted. Use the new shipment ID returned in the response for all subsequent operations.

POST /v0/shipments/pnp/15/submit
{
"id": "16",
"status": "pending",
"type": "outbound-pickpack",
"warehouse": { "id": "1", "name": "Main Warehouse" },
"notes": "Handle with care — fragile items inside",
"invoiceId": null,
"workflowId": null,
"createdAt": "2026-03-04T10:05:00.000Z",
"updatedAt": "2026-03-04T10:05:00.000Z",
}

New ID after submit

Submitting creates a brand new shipment and deletes the draft. The response contains the new shipment ID. Always use the returned ID for tracking, cancellation, or any further API calls.

Cancel

Request cancellation of a submitted shipment. The shipment must be in pending status. This moves the status to pending_cancel. The warehouse will review the request and either cancel or continue processing.

POST /v0/shipments/pnp/16/cancel
{
"id": "16",
"status": "pending_cancel",
"type": "outbound-pickpack",
"warehouse": { "id": "1", "name": "Main Warehouse" },
"notes": "Handle with care — fragile items inside",
"invoiceId": null,
"workflowId": null,
"createdAt": "2026-03-04T10:05:00.000Z",
"updatedAt": "2026-03-04T10:06:00.000Z",
}

Delete Draft

Permanently deletes a draft shipment. Only works while the shipment is in draft status. Returns 204 No Content on success.

DELETE /v0/shipments/pnp/15
204 No Content

Error Responses

400Please provide a complete shipping address before submitting

Returned when submitting without name, address1, city, state, or zip set.

400Please add at least one item before submitting

Returned when submitting without any order items.

400Only draft shipments can be edited

Returned when trying to modify items or address on a non-draft shipment.

400Only draft shipments can be deleted

Returned when trying to delete a non-draft shipment.

400Only draft shipments can be submitted

Returned when trying to submit a shipment that is no longer in draft status.

400Only pending shipments can request cancellation

Cancellation is only available for shipments in pending status.

404Shipment not found

The shipment ID does not exist or does not belong to your organization.

404Warehouse not found

The warehouse ID in the create request does not exist or is archived.

404Product not found: <id>

A product ID in the items list does not exist, is archived, or does not belong to your organization.

Important

  • Shipping address and items can only be modified while the shipment is in draft status.
  • Notes can be updated at any time, regardless of status.
  • Submit and cancel endpoints do not require a request body.
  • The warehouse opens cartons, picks individual units, and repacks them. This incurs per-unit labor fees.
  • Inventory is deducted when the warehouse forwards the shipment, not on submit.
  • For shipping full cartons without opening, use SPD instead.