Pick batching (beta)
Group warehouse orders into pick batches and receive the walk through the bins for each batch.
picking.batch
Beta
job
How to call it
REST
POST
/v1/picking/batch
GraphQL
mutation
openPickingBatch
MCP
open_picking_batch
Group warehouse orders into pick batches (carts) and give the walk through the bins for each batch. Every order line names its bin; bins are given as positions along the pick path in metres, or as a matrix of metres. Limits per batch: weight_kg, volume_l, orders, skus, units, slots. Billed one job.
Request schema
Generated from the same validation rules the endpoint runs, so a field cannot be documented without being validated.
POST /v1/picking/batch
{
"type": "object",
"properties": {
"callback": {
"type": [
"object",
"null"
],
"properties": {
"events": {
"type": "array",
"items": {
"type": "string",
"maxLength": 32
}
},
"format": {
"type": [
"string",
"null"
],
"enum": [
"native",
"ce_binary",
"ce_structured"
]
},
"secret": {
"type": [
"string",
"null"
],
"minLength": 16,
"maxLength": 255,
"description": "Required when callback.signature is standard_webhooks."
},
"signature": {
"type": [
"string",
"null"
],
"enum": [
"native",
"standard_webhooks"
]
},
"url": {
"type": "string",
"maxLength": 2048,
"format": "uri",
"description": "Required when callback is present."
}
}
},
"end": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"fixed_batches": {
"type": "array",
"maxItems": 5000,
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"maxLength": 128
}
}
},
"group_by": {
"type": "array",
"maxItems": 5,
"items": {
"type": "string"
}
},
"limits": {
"type": [
"object",
"null"
],
"properties": {
"orders": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 1000
},
"skus": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"slots": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 200
},
"units": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"volume_l": {
"type": [
"number",
"null"
],
"exclusiveMinimum": 0
},
"weight_kg": {
"type": [
"number",
"null"
],
"exclusiveMinimum": 0
}
}
},
"locations": {
"type": "array",
"minItems": 1,
"maxItems": 20000,
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"maxLength": 128
},
"position": {
"type": "number",
"minimum": 0
}
},
"required": [
"id",
"position"
]
}
},
"matrix": {
"type": [
"object",
"null"
],
"properties": {
"ids": {
"type": "array",
"minItems": 1,
"maxItems": 2000,
"description": "Required when matrix is present.",
"items": {
"type": "string",
"maxLength": 128
}
},
"meters": {
"type": "array",
"description": "Required when matrix is present.",
"items": {
"type": "array",
"items": {
"type": "number",
"minimum": 0
}
}
}
}
},
"mode": {
"type": [
"string",
"null"
],
"enum": [
"auto",
"sync",
"async"
]
},
"objective": {
"type": [
"string",
"null"
],
"enum": [
"walk",
"batches",
"balance"
]
},
"orders": {
"type": "array",
"minItems": 1,
"maxItems": 5000,
"items": {
"type": "object",
"properties": {
"attributes": {
"type": [
"array",
"null"
]
},
"id": {
"type": "string",
"maxLength": 128
},
"lines": {
"type": "array",
"minItems": 1,
"maxItems": 500,
"items": {
"type": "object",
"properties": {
"location": {
"type": "string",
"maxLength": 128
},
"quantity": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 100000
},
"sku": {
"type": "string",
"maxLength": 128
},
"volume_l": {
"type": [
"number",
"null"
],
"minimum": 0
},
"weight_kg": {
"type": [
"number",
"null"
],
"minimum": 0
}
},
"required": [
"location",
"sku"
]
}
},
"packages": {
"type": "array",
"maxItems": 100,
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"properties": {
"quantity": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"sku": {
"type": "string",
"maxLength": 128
}
},
"required": [
"sku"
]
}
}
}
},
"required": [
"id",
"lines",
"packages"
]
}
},
"rules": {
"type": "array",
"maxItems": 50,
"items": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
],
"maxLength": 64
},
"then": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"own_batch",
"exclude",
"limits"
]
},
"limits": {
"type": [
"object",
"null"
],
"properties": {
"orders": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 1000
},
"skus": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"slots": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 200
},
"units": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"volume_l": {
"type": [
"number",
"null"
],
"exclusiveMinimum": 0
},
"weight_kg": {
"type": [
"number",
"null"
],
"exclusiveMinimum": 0
}
}
}
},
"required": [
"action"
]
},
"when": {
"type": "array"
}
},
"required": [
"then",
"when"
]
}
},
"single_piece_batches": {
"type": [
"boolean",
"null"
]
},
"sorting": {
"type": [
"string",
"null"
],
"enum": [
"in_cart",
"after_pick"
]
},
"start": {
"type": [
"string",
"null"
],
"maxLength": 128
},
"time_limit_seconds": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 30
}
},
"additionalProperties": false,
"required": [
"locations",
"orders",
"rules"
]
}
Request example
{
"orders": [
{
"id": "SO-1001",
"lines": [
{
"sku": "MUG-BLUE",
"quantity": 2,
"location": "A-01-02",
"weight_kg": 0.4,
"volume_l": 0.9
},
{
"sku": "TEA-50",
"location": "C-04-01",
"weight_kg": 0.2,
"volume_l": 0.5
}
],
"attributes": {
"carrier": "canada_post"
}
},
{
"id": "SO-1002",
"lines": [
{
"sku": "TEA-50",
"quantity": 3,
"location": "C-04-01",
"weight_kg": 0.2,
"volume_l": 0.5
}
],
"attributes": {
"carrier": "canada_post"
}
},
{
"id": "SO-1003",
"lines": [
{
"sku": "KETTLE",
"location": "B-02-03",
"weight_kg": 1.6,
"volume_l": 6
},
{
"sku": "MUG-BLUE",
"location": "A-01-02",
"weight_kg": 0.4,
"volume_l": 0.9
}
],
"attributes": {
"carrier": "ups"
}
},
{
"id": "SO-1004",
"lines": [
{
"sku": "KETTLE",
"quantity": 2,
"location": "B-02-03",
"weight_kg": 1.6,
"volume_l": 6
}
],
"attributes": {
"carrier": "ups"
}
},
{
"id": "SO-1005",
"lines": [
{
"sku": "SPOON-SET",
"location": "D-01-01",
"weight_kg": 0.3,
"volume_l": 0.4
}
],
"attributes": {
"carrier": "canada_post"
}
}
],
"locations": [
{
"id": "A-01-02",
"position": 4
},
{
"id": "B-02-03",
"position": 18
},
{
"id": "C-04-01",
"position": 31
},
{
"id": "D-01-01",
"position": 46
}
],
"limits": {
"orders": 3,
"weight_kg": 15,
"slots": 3
},
"group_by": [
"carrier"
],
"single_piece_batches": false
}
Response
What a successful call returns: the schema of `data` and `meta`, with a description of each field, followed by an example response from a real call.
POST /v1/picking/batch
Response schema
{
"description": "The pick batches with the walk through the bins for each, and the orders that fit no batch. Billed one job.",
"data": {
"type": "object",
"properties": {
"batches": {
"type": "array",
"description": "The pick batches, in the order they were made.",
"items": {
"type": "object",
"properties": {
"number": {
"type": "integer",
"description": "The batch's number, from 1."
},
"kind": {
"type": "string",
"enum": [
"in_cart",
"after_pick",
"single_piece"
],
"description": "How the batch is sorted: `in_cart` (each order in its own slot), `after_pick` (sorted after picking) or `single_piece` (orders of one unit of one SKU, grouped by SKU)."
},
"orders": {
"type": "array",
"items": {
"type": "string"
},
"description": "The orders in the batch, in slot order."
},
"stops": {
"type": "array",
"description": "The bins in walking order, with what to pick at each.",
"items": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The bin id."
},
"picks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"order": {
"type": "string",
"description": "The order the units are for."
},
"sku": {
"type": "string",
"description": "The SKU."
},
"quantity": {
"type": "integer",
"description": "Units to pick."
},
"slot": {
"type": "integer",
"description": "The cart slot of the order, from 1. Absent when the batch is sorted after picking."
},
"package": {
"type": "integer",
"description": "The order's package (from 1, in the order of `packages`) the units go in. Present only when the order gave packages."
}
}
}
}
}
}
},
"walk_meters": {
"type": "number",
"description": "The walk from `start` through every stop to `end`, in metres (in the matrix's unit when a matrix was given)."
},
"weight_kg": {
"type": "number",
"description": "Total weight of the batch."
},
"volume_l": {
"type": "number",
"description": "Total volume of the batch."
},
"units": {
"type": "integer",
"description": "Units in the batch."
},
"skus": {
"type": "integer",
"description": "Different SKUs in the batch."
},
"group": {
"type": "object",
"description": "The `group_by` values the batch's orders share, and `rule` when a rule made the batch."
},
"over_limits": {
"type": "array",
"items": {
"type": "string"
},
"description": "The limits a fixed batch exceeds. Present only with `fixed_batches`."
}
}
}
},
"unplaced": {
"type": "array",
"description": "Orders not placed in any batch.",
"items": {
"type": "object",
"properties": {
"order": {
"type": "string",
"description": "The order id."
},
"reason": {
"type": "string",
"enum": [
"excluded",
"too_large",
"bad_location"
],
"description": "`excluded`: a rule left it out; `too_large`: the order alone exceeds a limit; `bad_location`: a line could not be placed on the pick path."
}
}
}
},
"totals": {
"type": "object",
"properties": {
"batches": {
"type": "integer",
"description": "Number of batches."
},
"orders": {
"type": "integer",
"description": "Orders placed."
},
"walk_meters": {
"type": "number",
"description": "The walks of all batches together."
}
}
}
},
"required": [
"batches",
"unplaced",
"totals"
]
},
"meta": {
"type": "object",
"properties": {},
"additionalProperties": true
}
}
Response example
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "picking.batch",
"billing": {
"unit": "job",
"quantity": 1,
"counted": true,
"breakdown": {
"orders": 5,
"lines": 7
}
},
"data": {
"batches": [
{
"number": 1,
"kind": "in_cart",
"orders": [
"SO-1001",
"SO-1002",
"SO-1005"
],
"stops": [
{
"location": "A-01-02",
"picks": [
{
"order": "SO-1001",
"sku": "MUG-BLUE",
"quantity": 2,
"slot": 1
}
]
},
{
"location": "C-04-01",
"picks": [
{
"order": "SO-1001",
"sku": "TEA-50",
"quantity": 1,
"slot": 1
},
{
"order": "SO-1002",
"sku": "TEA-50",
"quantity": 3,
"slot": 2
}
]
},
{
"location": "D-01-01",
"picks": [
{
"order": "SO-1005",
"sku": "SPOON-SET",
"quantity": 1,
"slot": 3
}
]
}
],
"walk_meters": 92,
"weight_kg": 1.9,
"volume_l": 4.2,
"units": 7,
"skus": 3,
"group": {
"carrier": "canada_post"
}
},
{
"number": 2,
"kind": "in_cart",
"orders": [
"SO-1003",
"SO-1004"
],
"stops": [
{
"location": "A-01-02",
"picks": [
{
"order": "SO-1003",
"sku": "MUG-BLUE",
"quantity": 1,
"slot": 1
}
]
},
{
"location": "B-02-03",
"picks": [
{
"order": "SO-1003",
"sku": "KETTLE",
"quantity": 1,
"slot": 1
},
{
"order": "SO-1004",
"sku": "KETTLE",
"quantity": 2,
"slot": 2
}
]
}
],
"walk_meters": 36,
"weight_kg": 5.2,
"volume_l": 18.9,
"units": 4,
"skus": 2,
"group": {
"carrier": "ups"
}
}
],
"unplaced": [],
"totals": {
"batches": 2,
"orders": 5,
"walk_meters": 128
}
},
"meta": {
"distances": "path",
"solve_ms": 1,
"units": {
"walk": "m",
"weight": "kg",
"volume": "L"
},
"mode": "sync"
}
}
Example response when queued (202)
{
"request_id": "req_01k8zq3m5n7p9r1t3v5x7z9b2d",
"capability": "packing.solve",
"billing": {
"unit": "job",
"quantity": 1,
"counted": true,
"breakdown": {
"packagings": 2,
"item_rows": 2,
"units": 5
}
},
"data": {
"job": {
"id": "job_01k8zq4a6c8e0g2j4l6n8q0s2u",
"status": "queued",
"capability": "packing.solve",
"poll_after_ms": 1000,
"result_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/result",
"cancel_url": "/v1/jobs/job_01k8zq4a6c8e0g2j4l6n8q0s2u/cancel"
}
},
"meta": {
"mode": "async"
}
}
What it costs
One solve run, whatever the item count.
Every billable response carries a billing block with the unit, the quantity and how the quantity was derived.
Errors it can return
| Status | Code | Description |
|---|---|---|
| 401 | TOKEN_INVALID |
Unknown or revoked API token. |
| 401 | TOKEN_MISSING |
No API token. Send Authorization: Bearer <your token>. |
| 402 | QUOTA_EXHAUSTED |
The included allowance for this capability is used up for the current period. |
| 403 | CAPABILITY_NOT_ENABLED |
This capability is not enabled for your account. See :doc to enable it. |
| 403 | CAPABILITY_NOT_GRANTED_TO_KEY |
Your account has this capability, but this API key was not granted it. |
| 403 | OPEN_API_DISABLED |
Open Platform access is not enabled for this account. |
| 422 | CALLBACK_NOT_VERIFIED |
Verify the callback endpoint first: POST /v1/webhooks/verify and echo the challenge back. |
| 422 | CALLBACK_URL_UNSAFE |
The callback URL must be a public HTTPS endpoint. |
| 422 | VALIDATION_FAILED |
The request failed validation. See details. |
| 429 | CONCURRENCY_LIMIT |
Too many solves are already running for this account. Retry when one finishes. |
| 429 | RATE_LIMITED |
Too many requests. Please retry after the indicated delay. |
| 500 | INTERNAL_ERROR |
An unexpected error occurred. Quote the request_id when contacting support. |
| 502 | ENGINE_UNAVAILABLE |
The routing engine is temporarily unreachable. Please retry. |