3D bin packing service. Given a set of containers and items, computes optimal packing — which items fit into which containers and where.
All values are integers. The service is unit-agnostic — use any consistent unit system (mm, cm, inches, etc.).
POST https://php-testing-day.shipmonk.com/api/v1/pack
Pack items into available containers.
Content-Type: application/json
Unknown keys are rejected.
| Field | Type | Description |
|---|---|---|
containers | array | Available containers required, 1–50 |
items | array | Items to pack required, 1–200 |
Inner (usable) dimensions are assumed equal to outer dimensions. If your containers have wall thickness, subtract it from the dimensions you provide.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier required, non-empty |
width | int | Width required, > 0 |
length | int | Length required, > 0 |
depth | int | Depth required, > 0 |
max_weight | int | Max total weight required, > 0 |
empty_weight | int | Container weight when empty optional, default 0 |
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier required, non-empty |
width | int | Width required, > 0 |
length | int | Length required, > 0 |
depth | int | Depth required, > 0 |
weight | int | Weight required, > 0 |
keep_flat | bool | Prevent vertical rotation optional, default false |
curl -X POST https://php-testing-day.shipmonk.com/api/v1/pack \
-H 'Content-Type: application/json' \
-d '{
"containers": [
{
"id": "box-small",
"width": 300,
"length": 200,
"depth": 150,
"max_weight": 5000,
"empty_weight": 100
}
],
"items": [
{ "id": "item-1", "width": 100, "length": 50, "depth": 50, "weight": 200 },
{ "id": "item-2", "width": 80, "length": 60, "depth": 40, "weight": 150, "keep_flat": true }
]
}'
| Field | Type | Description |
|---|---|---|
packed_containers | array | List of containers with their packed items |
packed_containers[].container_id | string | Container ID |
packed_containers[].items | array | Items placed in this container |
packed_containers[].items[].item_id | string | Item ID |
packed_containers[].items[].x | int | X position within container |
packed_containers[].items[].y | int | Y position within container |
packed_containers[].items[].z | int | Z position within container |
packed_containers[].items[].width | int | Packed width (may differ from input due to rotation) |
packed_containers[].items[].length | int | Packed length |
packed_containers[].items[].depth | int | Packed depth |
packed_containers[].volume_utilization | float | Volume usage 0–100 (%) |
unpacked_items | array | IDs of items that could not fit into any container |
{
"packed_containers": [
{
"container_id": "box-small",
"items": [
{ "item_id": "item-1", "x": 0, "y": 0, "z": 0, "width": 100, "length": 50, "depth": 50 },
{ "item_id": "item-2", "x": 100, "y": 0, "z": 0, "width": 80, "length": 60, "depth": 40 }
],
"volume_utilization": 7.78
}
],
"unpacked_items": []
}
Every error response has the same shape.
{
"code": 400,
"message": "Failed to map data at path /containers/0/width: Expected value greater than 0, got -5",
"event_id": "10175da7-80f3-406c-af3d-4f4b0cae0c27"
}
| Field | Type | Description |
|---|---|---|
code | int | The HTTP status code |
message | string | Human-readable description. For a validation failure, it starts with the path of the invalid field. |
event_id | string | Identifier of the error occurrence |
| Status | When |
|---|---|
| 400 | The body is not valid JSON, or it fails validation (missing key, unknown key, wrong type, value out of range, duplicate id) |
| 404 | The path does not exist |
| 405 | Wrong HTTP method (e.g. GET instead of POST) |
| 415 | The Content-Type is not application/json |
| 429 | More than 60 requests per minute from one IP address. The Retry-After header gives the number of seconds to wait. |
| 500 | Unexpected server error |