Get Budget Execution Detail
Returns a single budget in full: its identity and state, its customer, its assigned provider, the complete amount breakdown, and every line the quote is made of — materials, labor activities with the…
Request
curl -X GET "https://uat.integration.cl.sodtrack-shared.sodtrack.com" \
-H "Authorization: Bearer $SODTRACK_TOKEN"Base URL: https://uat.integration.cl.sodtrack-shared.sodtrack.com · other environments
Responses
{
"id": 1234,
"reference": "BUD-2026-0031",
"name": "Potable water main repair",
"description": "Replacement of the damaged section, floors 3 to 5.",
"status": "waiting_customer_approval",
"type": "itemized",
"customer": {
"id": 4471,
"reference": "CUST-4471"
},
"provider": {
"id": 9,
"reference": "PRV-9",
"name": "Serviteca",
"lastname": "SpA",
"email": "contacto@serviteca.example.com",
"phone": "+56922222222",
"identificationNumber": "76123456-7",
"assignedTo": {
"name": "Ana",
"lastname": "Rojas",
"email": "ana@serviteca.example.com"
}
},
"booking": {
"id": 2686,
"reference": "BK-2686"
},
"totals": {
"subTotalQuote": 1700,
"baseItemsCost": 1250,
"baseActivitiesCost": 450,
"itemsCost": 1125,
"activitiesCost": 500,
"totalItemDiscount": 125,
"totalActivityDiscount": 0,
"totalSubTotalDiscount": 100,
"totalDiscount": 225,
"totalSurcharge": 212.5,
"totalQuote": 1687.5,
"totalPrice": null,
"totalCost": null
},
"items": [
{
"id": 366,
"name": "Sealing kit",
"description": "Includes clamps, seals and structural adhesive",
"sku": "SLK-220",
"type": "material",
"costType": "metric",
"quantity": 2,
"metric": "u",
"metricCost": 500,
"totalCost": null,
"lineTotal": 1000,
"required": true
},
{
"id": 367,
"name": "Waste disposal",
"description": null,
"sku": null,
"type": "material",
"costType": "total",
"quantity": null,
"metric": null,
"metricCost": null,
"totalCost": 250,
"lineTotal": 250,
"required": false
}
],
"activities": [
{
"id": 812,
"name": "Pipe section replacement",
"description": "Cut, replace and seal the damaged section",
"order": 1,
"quantity": 3,
"metric": "hrs",
"costPerMetric": 100,
"typeCost": "metric",
"globalCost": null,
"lineTotal": 300,
"required": true,
"products": []
},
{
"id": 813,
"name": "Pressure test",
"description": null,
"order": 2,
"quantity": null,
"metric": null,
"costPerMetric": null,
"typeCost": "global",
"globalCost": 150,
"lineTotal": 150,
"required": true,
"products": []
}
],
"discounts": [
{
"id": 55,
"name": "Materials agreement",
"description": null,
"type": "percent",
"entityApplicated": "item",
"percent": 10,
"globalValue": null,
"required": false
},
{
"id": 56,
"name": "Loyalty credit",
"description": null,
"type": "global",
"entityApplicated": "sub_total",
"percent": null,
"globalValue": 100,
"required": false
}
],
"surcharges": [
{
"id": 71,
"name": "After-hours labor",
"description": null,
"type": "global",
"entityApplicated": "labor_force",
"percent": null,
"globalValue": 50,
"required": false
},
{
"id": 72,
"name": "Urgency",
"description": null,
"type": "percent",
"entityApplicated": "sub_total",
"percent": 10,
"globalValue": null,
"required": false
}
],
"attachments": [
{
"id": 50,
"name": "Quote.pdf",
"url": "https://files.example.com/quote.pdf"
}
],
"createdDate": "2026-09-01T14:22:10.000Z",
"updatedDate": "2026-09-03T09:05:41.000Z"
}Error responses for this endpoint follow the shared error reference.
Purpose
Returns a single budget in full: its identity and state, its customer, its assigned provider, the complete amount breakdown, and every line the quote is made of — materials, labor activities with their products, discounts, surcharges, and the attachments shared with the customer.
Field Definitions
📦 Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
budgetExecutionId | number | ✅ | Sodtrack id of the budget to retrieve. |
This endpoint does not accept a request body or query parameters. Anything sent is ignored.
Response Fields
The budget's identity, status, type, customer, provider, booking, totals, createdDate and updatedDate are the same fields documented in Budget Executions By Customer. The sections below cover the line-by-line content this endpoint adds.
🧱 Materials — items[]
| Field | Type | Description |
|---|---|---|
id | number | Unique Sodtrack identifier. |
name | `string | null` |
description | `string | null` |
sku | `string | null` |
type | string | Nature of the line. One of: "material", "product", "component". |
costType | string | How the line is priced. One of: "metric" (by quantity), "total" (lump sum). |
quantity | `number | null` |
metric | `string | null` |
metricCost | `number | null` |
totalCost | `number | null` |
lineTotal | number | Amount this line contributes to the quote, already resolved from whichever pricing the line uses. |
required | boolean | Whether the customer must accept this line, as opposed to being able to opt out of it. |
🔧 Labor activities — activities[]
| Field | Type | Description |
|---|---|---|
id | number | Unique Sodtrack identifier. |
name | string | Name shown to the customer. |
description | `string | null` |
order | `number | null` |
quantity | `number | null` |
metric | `string | null` |
costPerMetric | `number | null` |
typeCost | `string | null` |
globalCost | `number | null` |
lineTotal | number | Amount the activity itself contributes to the quote. Excludes its products. |
required | `boolean | null` |
products | array | Products attached to the activity. Empty when none. May be present on any budget; whether they are priced depends on type. See Whether activity products are priced. |
🧰 Activity products — activities[].products[]
| Field | Type | Description |
|---|---|---|
id | number | Unique Sodtrack identifier. |
name | string | Name shown to the customer. |
description | `string | null` |
quantity | `number | null` |
productMetric | `string | null` |
productMetricCost | `number | null` |
lineTotal | number | quantity × productMetricCost. |
🏷️ Discounts and surcharges — discounts[], surcharges[]
Both arrays share the same shape.
| Field | Type | Description |
|---|---|---|
id | number | Unique Sodtrack identifier. |
name | string | Name shown to the customer. |
description | `string | null` |
type | string | How the amount is expressed. One of: "percent", "global" (fixed amount). |
entityApplicated | string | What the adjustment applies to. One of: "item" (materials), "labor_force" (activities), "sub_total" (whole budget). |
percent | `number | null` |
globalValue | `number | null` |
required | `boolean | null` |
📎 Attachments — attachments[]
| Field | Type | Description |
|---|---|---|
id | number | Unique Sodtrack identifier. |
name | `string | null` |
url | string | Download URL. |
Only attachments explicitly marked to be shared with the customer are included. Internal attachments are never returned.
📐 Units of measure
items[].metric, activities[].metric and activities[].products[].productMetric use one of:
cm, mm, m, cm2, mm2, m2, cm3, mm3, m3, in, ft, yds, in2, ft2, yds2, in3, ft3, yds3, g, kg, t, oz, lbs, mlts, lts, gal, min, hrs, day, month, u, box
Error Responses
| HTTP Status | Error | Description |
|---|---|---|
400 Bad Request | InvalidParameters | budgetExecutionId is not a number. |
401 Unauthorized | — | x-api-key is missing or invalid. |
404 Not Found | BUDGET_EXECUTION_NOT_FOUND: {budgetExecutionId} | No budget exists with that id, or the id belongs to a budget template. |
500 Internal Server Error | — | Unexpected server error. Contact Sodtrack support. |
Business Rules & Constraints
Reconciling the lines against the totals
Every lineTotal is already resolved from whichever pricing its line uses, so a client never has to branch on costType or typeCost to add up the quote:
| Line | lineTotal |
|---|---|
| Material priced by metric | quantity × metricCost |
| Material priced as a lump sum | totalCost |
| Activity priced per metric | quantity × costPerMetric |
| Activity priced as a whole | globalCost |
| Activity product | quantity × productMetricCost |
On budgets that carry a type, summing items[].lineTotal gives totals.baseItemsCost and summing activities[].lineTotal gives totals.baseActivitiesCost. The adjustments in discounts[] and surcharges[] are then what turns those into totals.itemsCost, totals.activitiesCost and finally totals.totalQuote.
An activity's lineTotal excludes its products, which carry their own.
Whether activity products are priced
Activity products are the way budgets expressed materials before items existed. A current budget expresses the same thing as an item of type "product".
Products can appear on a budget of any type, but whether they count toward the quote does not:
type | Behavior of activities[].products[] |
|---|---|
null | They are the materials of the quote and are counted in baseItemsCost and itemsCost. |
"itemized" / "global" | Informational only. They are returned as a detail of the work and contribute 0 to totals. |
So a client should render products whenever they are present, but only add products[].lineTotal into a total for budgets where type is null. On itemized and global budgets, adding them will not match totalQuote.
Order in which amounts are resolved
-
Materials and labor are added up separately, producing
baseItemsCostandbaseActivitiesCost. -
Adjustments targeting
"item"are applied to the materials subtotal and those targeting"labor_force"to the labor subtotal, producingitemsCostandactivitiesCost. -
The two adjusted subtotals are added together.
-
Adjustments targeting
"sub_total"are applied to that result, producingtotalQuote. -
The configured commission is applied to
totalQuote, producingtotalPriceandtotalCost.
Because subtotal-level adjustments are applied after the materials and labor ones, a percentage discount on "sub_total" is calculated over the already-adjusted amount, not over subTotalQuote.
Budgets with no pricing model
On budgets where type is null, items is always empty — the materials live in activities[].products[] instead, as described in Whether activity products are priced.
These budgets carry no discounts or surcharges either, so both arrays are empty and every adjustment total is 0. totalQuote is simply the materials plus the labor.
Budget templates
A template is not a budget of anyone, so requesting one returns 404 Not Found rather than a payload.