For AI agents: markdown of this page — /docs-content-en/entities/shipments/aggregate.md documentation index — /llms.txt
Shipment aggregation
POST /v1/shipments/aggregate
Counts shipments and computes the sum, average, minimum and maximum of the delivery cost, with filtering and grouping by order or delivery service.
Standard fields
| Field | Purpose |
|---|---|
priceDelivery |
Delivery cost — the main field for sum / avg / min / max. Also available for groupBy |
basePriceDelivery, discountPrice |
Base delivery cost and delivery discount — numeric fields for sum / avg / min / max |
orderId |
Order ID — used in groupBy to break shipments down by order |
deliveryId |
Delivery service ID — used in groupBy to break shipments down by service |
The fields for groupBy are the aggregatable array in GET /v1/shipments/fields. Numeric functions accept any field of type number from the same schema, including fields outside aggregatable.
User fields. For a non-existent field name in sum / avg / min / max, the 400 INVALID_PARAMS response lists the allowed numeric fields. That list contains only standard shipment fields — basePriceDelivery, companyId, deliveryId, discountPrice, empAllowDeliveryId, empCanceledId, empDeductedId, empMarkedId, empResponsibleId, id, orderId, priceDelivery, responsibleId, with no user fields. A rejection for a field outside aggregatable in groupBy names only orderId, priceDelivery, deliveryId as allowed.
Request fields (body)
| Field | Type | Required | Description |
|---|---|---|---|
aggregate |
array | no | Array of expressions { "field": "priceDelivery", "function": "sum" }, up to 5 per request. Functions: count, sum, avg, min, max. For count the field is "*". Without the parameter, only count is returned |
filter |
object | no | Filtering by the fields of GET /v1/shipments/fields.Filtering syntax. Example: { "orderId": 1027 } |
groupBy |
string | string[] | no | A field or an array of fields to group by, up to 5 fields. Allowed values come from the aggregatable array: orderId, priceDelivery, deliveryId |
groupOrderBy |
array | no | Group sorting: [{ "field": "priceDelivery:sum", "direction": "desc" }]. field accepts count, a field name from groupBy, or <field>:<function> from aggregate. Works only together with groupBy |
groupLimit |
number | no | Limits the number of returned groups, from 1 to 1000. Works only together with groupBy |
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/shipments/aggregate" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter": { "orderId": 1027 },
"aggregate": [
{ "field": "*", "function": "count" },
{ "field": "priceDelivery", "function": "sum" },
{ "field": "priceDelivery", "function": "avg" }
],
"groupBy": "deliveryId"
}'
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/shipments/aggregate" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": { "orderId": 1027 },
"aggregate": [
{ "field": "*", "function": "count" },
{ "field": "priceDelivery", "function": "sum" },
{ "field": "priceDelivery", "function": "avg" }
],
"groupBy": "deliveryId"
}'
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/shipments/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
filter: { orderId: 1027 },
aggregate: [
{ field: '*', function: 'count' },
{ field: 'priceDelivery', function: 'sum' },
{ field: 'priceDelivery', function: 'avg' },
],
groupBy: 'deliveryId',
}),
})
const { success, data } = await res.json()
// data.groups — delivery cost by delivery service
console.log(data.groups)
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/shipments/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
filter: { orderId: 1027 },
aggregate: [
{ field: '*', function: 'count' },
{ field: 'priceDelivery', function: 'sum' },
{ field: 'priceDelivery', function: 'avg' },
],
groupBy: 'deliveryId',
}),
})
const { success, data } = await res.json()
Other scenarios
The blocks below are request bodies.
Shipment count for an order only — without the aggregate array no records are fetched, and data.meta.recordsProcessed in the response is 0:
{ "filter": { "orderId": 1027 } }
Grouping by two fields — by order and delivery service:
{
"aggregate": [{ "field": "*", "function": "count" }],
"groupBy": ["orderId", "deliveryId"]
}
Sum and average of the delivery cost without grouping:
{
"filter": { "orderId": 1027 },
"aggregate": [
{ "field": "*", "function": "count" },
{ "field": "priceDelivery", "function": "sum" },
{ "field": "priceDelivery", "function": "avg" }
]
}
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.count |
number | Number of shipments matching the filter |
data.aggregates |
object | Results of numeric functions by field: { "priceDelivery": { "sum": 500, "avg": 500 } }. An empty object if aggregate contains only count |
data.groups |
array | Present only with groupBy. Each element holds the grouping field values, count and aggregates for the group |
data.meta.totalRecords |
number | Total number of records matching the filter |
data.meta.recordsProcessed |
number | Number of processed records, up to 5000 |
data.meta.truncated |
boolean | true if the numbers were not computed over all records matching the filter: fewer records were read than data.count promised — including when more than 5000 matched the filter — or the slice was cut short by a subpage error. The size of the shortfall arrives in data.meta.recordsShortfall, the cut-short slice in data.meta.pageErrorSample. Always present, false on a complete response. If the request has neither groupBy nor numeric functions, no records are fetched and the flag is always false |
data.meta.groupTotal |
number | Present only with groupBy — the number of groups before groupLimit is applied |
data.meta.groupsTruncated |
boolean | Present only with groupBy. true if the group list was cut by groupLimit |
Response example
{
"success": true,
"data": {
"count": 1,
"aggregates": {
"priceDelivery": {
"sum": 500,
"avg": 500
}
},
"groups": [
{
"deliveryId": 1,
"count": 1,
"aggregates": {
"priceDelivery": {
"sum": 500,
"avg": 500
}
}
}
],
"meta": {
"totalRecords": 1,
"recordsProcessed": 1,
"truncated": false,
"groupTotal": 1,
"groupsTruncated": false
}
}
}
Error response example
400 — the field is not in the schema, the message lists the allowed numeric fields:
{
"success": false,
"error": {
"code": "INVALID_PARAMS",
"message": "Field 'weight' not found. Available numeric fields: basePriceDelivery, companyId, deliveryId, discountPrice, empAllowDeliveryId, empCanceledId, empDeductedId, empMarkedId, empResponsibleId, id, orderId, priceDelivery, responsibleId."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_PARAMS |
count is passed with a field other than "*" |
| 400 | INVALID_PARAMS |
aggregate is passed as a non-array or an empty array |
| 400 | INVALID_PARAMS |
aggregate has more than 5 expressions |
| 400 | INVALID_PARAMS |
Unknown function — the message lists the allowed ones |
| 400 | INVALID_PARAMS |
sum / avg / min / max without a field or with the field "*" |
| 400 | INVALID_PARAMS |
The field for sum / avg / min / max is not numeric, for example trackingNumber — the message names its type |
| 400 | INVALID_PARAMS |
The field for sum / avg / min / max is not in the schema — the message contains the list of numeric fields |
| 400 | INVALID_PARAMS |
The op, field or function fields are passed at the top level of the body instead of the aggregate array |
| 400 | INVALID_PARAMS |
groupBy is neither a string nor an array of strings |
| 400 | INVALID_PARAMS |
A field in groupBy is outside the aggregatable array — the message contains the list of allowed ones |
| 400 | INVALID_PARAMS |
groupBy has more than 5 fields |
| 400 | INVALID_PARAMS |
groupBy contains a reserved name: count, aggregates, meta, groups |
| 400 | INVALID_PARAMS |
groupOrderBy or groupLimit is passed without groupBy, or its value is invalid |
| 400 | UNKNOWN_FILTER_FIELD |
Unknown field in filter — the message contains the list of shipment fields |
| 400 | INVALID_FILTER_FIELD |
A field name in filter starts with the unsupported prefix @ or !@ |
| 400 | INVALID_FILTER_OPERATOR |
Unsupported operator in filter, an empty object in a condition value, or the $or logical condition |
| 400 | INVALID_FILTER_SHAPE |
filter is not an object of conditions, for example a string, a number or an array |
| 400 | INVALID_DUPLICATE_FILTER_FIELD |
Two filter conditions reduce to one Bitrix24 condition, for example $ne and $nin on the same field. Pass only one of them |
| 403 | SCOPE_DENIED |
The API key has no sale scope |
| 401 | MISSING_API_KEY |
The X-Api-Key header is not passed |
| 401 | TOKEN_MISSING |
The API key has no configured tokens |
| 429 | RATE_LIMITED |
Rate limit exceeded: 300 requests per minute per portal, all API keys of the portal share one limit. The exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas). Retry after the delay in the Retry-After header |
Full list of common API errors — Errors.
Known specifics
count versus numeric functions. A request without groupBy and without numeric functions is handled by a single request to Bitrix24 — no records are fetched, and the response time does not depend on the number of shipments. As soon as the request has sum / avg / min / max or groupBy, including together with a single count, records are loaded page by page, up to 5000, and computed on the Vibecode side. With meta.truncated: true, the truncated: true mark is also set inside the object of each field in data.aggregates and on each element of data.groups, and data.meta.warnings carries the AGGREGATE_TRUNCATED warning. For an exact number on a large selection, use a request without groupBy and numeric functions, or narrow the filter, for example by orderId. Full breakdown — Aggregation POST — the 5000-record ceiling.