Skip to main content

Releases

Releases represent scheduled material draws against a Hold-for-Release purchase order (an HFR order, also referred to as a buyout โ€” see OrderType). Each Release records when material is needed, where it should be delivered, which line items are being released and in what quantity, and the extended cost of that draw.

These endpoints expose the same release-level dates and dollar values shown in the Kojo Analytics Dashboard, so they can be pulled into Power BI or another reporting tool.

note

Cancelled releases are excluded by default from both endpoints. Pass includeCancelled=true to return them. If you are reconciling totals against the Analytics Dashboard, make sure both sides agree on whether cancelled releases are counted.

Schemaโ€‹

Releaseโ€‹

NameTypeDescriptionExample
idIDUnique Kojo internal identifier"clr8adymd000112nocbtsetzx"
orderIdIDID of the purchase order this release belongs to"clrrtqmy60007zay5y3kbb6x0"
purchaseOrderNumberString?PO number of the parent order, so bulk consumers can join without a second request"PO-2025-001"
releaseNumberStringHuman-readable release identifier. Not guaranteed unique โ€” use id to identify a release"REL-001"
needByDateDayDateTime?Day the released material is needed on site"2025-09-15T00:00:00.000Z"
needByWindowString?Delivery window within the need-by day"AM"
notesString?Release notes"Deliver to north gate"
additionalNotesString?Further notes on the release"Call site super on arrival"
cancelledAtDateTime?When the release was cancelled; null for active releases"2025-08-02T14:11:07.221Z"
cancelledReasonString?Reason recorded at cancellation"Schedule pushed"
totalCostFloatExtended cost of this release โ€” the sum of totalCost across its releaseItems. See How totalCost is derived12450.00
deliveryLocationLocation?Where the release is being delivered. Only id and name are returned{ "id": "clxyz456", "name": "Main Warehouse" }
releaseItemsReleaseItem[]The line items included in this release
createdAtDateTimeWhen the release was created"2025-08-01T09:14:22.031Z"
createdByIdID?ID of the user who created the release"clr6fkyru0002q3dwbwh2t6j8"
updatedAtDateTimeWhen the release was last updated"2025-08-01T09:20:44.507Z"

ReleaseItemโ€‹

NameTypeDescriptionExample
idIDUnique release item identifier"clxyz789"
releaseIdIDID of the release this item belongs to"clr8adymd000112nocbtsetzx"
lineItemIdIDID of the order line item being released"clxyzabc"
requisitionItemIdID?ID of the originating requisition item, when the release traces back to a requisition"clxyzdef"
quantityReleasedFloatQuantity of the line item released in this release10
backorderQuantityFloatQuantity on backorder for this release item; 0 when none2
totalCostFloatExtended cost of the released quantity. See How totalCost is derived12450.00
lineItemReleaseLineItemPricing detail for the released line item
createdAtDateTimeWhen the release item was created"2025-08-01T09:14:22.031Z"
updatedAtDateTimeWhen the release item was last updated"2025-08-01T09:20:44.507Z"

ReleaseLineItemโ€‹

NameTypeDescriptionExample
idIDID of the order line item"clxyzabc"
descriptionString?Line item description"Steel Beam 10ft"
unitPriceFloatUnit price of the line item, before any unit discount; 0 when unpriced1245.00
extPriceFloatExtended price for the released quantity. Equal to the release item's totalCost12450.00

How totalCost is derivedโ€‹

totalCost is computed at request time from the parent order's line item pricing โ€” it is not a stored field. For each release item:

totalCost = round(quantityReleased ร— unitPrice ร— (1 โˆ’ unitDiscount รท 100) รท pricePerFactor, 2)
  • unitPrice and unitDiscount come from the order line item. An unpriced line item contributes 0.
  • pricePerFactor reflects the line item's unit of measure: 1000 for M, 100 for C, and 1 for everything else (including EA and blank). This matches how Kojo prices "per thousand" and "per hundred" units.

A release's totalCost is the sum of its release items' totalCost, and each lineItem.extPrice repeats the release item's totalCost for convenience.

Queriesโ€‹

GET /orders/:orderId/releasesโ€‹

Fetches every release on a single purchase order. Use this for the drill-down case, when you already have an order ID.

Parametersโ€‹

NameTypeDefaultDescriptionExample
orderIdIDโ€”Path parameter. The Kojo ID of the purchase order"clrrtqmy60007zay5y3kbb6x0"
includeCancelledBoolean?falseInclude cancelled releases in the responsetrue

Results are ordered by createdAt descending. This endpoint is not paginated and does not accept the standard pagination parameters โ€” it returns every release on the order.

Responseโ€‹

A list of Release objects, returned as a bare JSON array.

Errorsโ€‹

StatusDescription
404Order not found โ€” the order does not exist, has been deleted, or belongs to another organization. Kojo does not distinguish these cases, so a valid order ID from a different organization returns 404 rather than 403

Example Queryโ€‹

GET https://api.kojo.tech/orders/clrrtqmy60007zay5y3kbb6x0/releases

Example Responseโ€‹

[
{
"id": "clr8adymd000112nocbtsetzx",
"orderId": "clrrtqmy60007zay5y3kbb6x0",
"purchaseOrderNumber": "PO-2025-001",
"releaseNumber": "REL-001",
"needByDateDay": "2025-09-15T00:00:00.000Z",
"needByWindow": "AM",
"notes": "Deliver to north gate",
"additionalNotes": null,
"cancelledAt": null,
"cancelledReason": null,
"totalCost": 12450.0,
"createdAt": "2025-08-01T09:14:22.031Z",
"createdById": "clr6fkyru0002q3dwbwh2t6j8",
"updatedAt": "2025-08-01T09:20:44.507Z",
"deliveryLocation": {
"id": "clxyz456",
"name": "Main Warehouse"
},
"releaseItems": [
{
"id": "clxyz789",
"releaseId": "clr8adymd000112nocbtsetzx",
"lineItemId": "clxyzabc",
"requisitionItemId": null,
"quantityReleased": 10,
"backorderQuantity": 0,
"totalCost": 12450.0,
"createdAt": "2025-08-01T09:14:22.031Z",
"updatedAt": "2025-08-01T09:14:22.031Z",
"lineItem": {
"id": "clxyzabc",
"description": "Steel Beam 10ft",
"unitPrice": 1245.0,
"extPrice": 12450.0
}
}
]
}
]

GET /releasesโ€‹

Fetches releases across your whole organization, with date and order filters and pagination. This is the endpoint to use for scheduled bulk pulls into a BI tool.

Parametersโ€‹

NameTypeDefaultDescriptionExample
orderIdID?โ€”Return only releases on this purchase order"clrrtqmy60007zay5y3kbb6x0"
needByDateFromDateTime?โ€”Return releases with needByDateDay on or after this instant (ISO 8601)"2025-09-01T00:00:00.000Z"
needByDateToDateTime?โ€”Return releases with needByDateDay on or before this instant (ISO 8601)"2025-09-30T00:00:00.000Z"
includeCancelledBoolean?falseInclude cancelled releases in the responsetrue
limitInt?100Maximum number of releases to return. Values above 500 are capped at 500250
offsetInt?0Number of releases to skip100

Results are ordered by createdAt descending.

note

This endpoint takes explicit query parameters rather than the filter[...] and orderBy conventions described in Filters โ€” filter[needByDateDay_gte] and orderBy are not supported here. Its pagination also differs from the standard defaults: limit defaults to 100 (not 500) and is capped at 500.

Notes on filter behavior:

  • Date filters are independent. Supplying only needByDateFrom leaves the upper bound open; supplying only needByDateTo leaves the lower bound open.
  • Releases with no need-by date are excluded when you filter by date. A release whose needByDateDay is null cannot satisfy a range bound, so it drops out of the result set as soon as either date filter is supplied. Omit both filters to include undated releases.
  • limit above the cap does not error. Requesting limit=1000 returns at most 500 records and reports meta.limit: 500, so read your effective page size from meta.limit rather than assuming it echoes your request.
  • Releases on deleted orders are excluded.

Responseโ€‹

An object with pagination metadata and an array of releases.

NameTypeDescription
metaobjectPagination metadata
meta.totalIntTotal releases matching the filters, before pagination
meta.limitIntEffective page size actually applied, after the 500 cap
meta.offsetIntNumber of releases skipped
dataRelease[]The page of releases

An offset past the end of the result set returns an empty data array with an accurate meta.total, so you can page until offset + data.length >= meta.total.

Errorsโ€‹

StatusDescription
400limit must be a non-negative integer or offset must be a non-negative integer โ€” a negative value was supplied
404Order not found โ€” the orderId filter does not resolve to an order in your organization. An empty orderId= is treated as a lookup for an order with a blank ID and also returns 404; omit the parameter entirely to query all releases

Example Queryโ€‹

GET https://api.kojo.tech/releases?needByDateFrom=2025-09-01T00:00:00.000Z&needByDateTo=2025-09-30T00:00:00.000Z&limit=100

Example Responseโ€‹

{
"meta": {
"total": 342,
"limit": 100,
"offset": 0
},
"data": [
{
"id": "clr8adymd000112nocbtsetzx",
"orderId": "clrrtqmy60007zay5y3kbb6x0",
"purchaseOrderNumber": "PO-2025-001",
"releaseNumber": "REL-001",
"needByDateDay": "2025-09-15T00:00:00.000Z",
"needByWindow": "AM",
"notes": "Deliver to north gate",
"additionalNotes": null,
"cancelledAt": null,
"cancelledReason": null,
"totalCost": 12450.0,
"createdAt": "2025-08-01T09:14:22.031Z",
"createdById": "clr6fkyru0002q3dwbwh2t6j8",
"updatedAt": "2025-08-01T09:20:44.507Z",
"deliveryLocation": {
"id": "clxyz456",
"name": "Main Warehouse"
},
"releaseItems": [
{
"id": "clxyz789",
"releaseId": "clr8adymd000112nocbtsetzx",
"lineItemId": "clxyzabc",
"requisitionItemId": null,
"quantityReleased": 10,
"backorderQuantity": 0,
"totalCost": 12450.0,
"createdAt": "2025-08-01T09:14:22.031Z",
"updatedAt": "2025-08-01T09:14:22.031Z",
"lineItem": {
"id": "clxyzabc",
"description": "Steel Beam 10ft",
"unitPrice": 1245.0,
"extPrice": 12450.0
}
}
]
}
]
}

Paging Through a Full Exportโ€‹

GET https://api.kojo.tech/releases?limit=500&offset=0
GET https://api.kojo.tech/releases?limit=500&offset=500
GET https://api.kojo.tech/releases?limit=500&offset=1000

Stop when offset + data.length reaches meta.total.