Budget
Create and manage budgets, budget mappings, and available balance for white label programs
Overview
Budgets define the spending pool available to cardholders and spend permissions. Each budget has a time period (granularity), an amount, a currency, and a category that maps to a GL account code. Budget mappings route spend to the correct budget based on scope (user, cost center, activity type, and so on).
Typical flow:
- Create a budget:
POST /md/api/Budgets - Create a budget mapping:
POST /md/api/BudgetMappings(links the budget to spend scope) - Check available balance:
GET /md/api/Budgets/{id}/available
Authentication: All endpoints require Authorization: Bearer {access_token} and x-appid: {clientId}. The company is resolved from your access token scope: do not send companyId in request bodies. See Authentication.
category field is the GL account code. If your client has GL codes imported into Custodia, use the appropriate code from their chart of accounts. Otherwise, use the platform default: 1001.
Create Budget
Creates a new budget for a company. The platform sets disbursedById from the authenticated user on create. After creating the budget, create at least one budget mapping so spend can be routed to it.
Request Body: mandatory fields
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
number | Yes | Total budget amount in the budget currency. |
currency |
string | Yes | ISO currency code (for example USD, EUR). |
name |
string | Yes | Display name for the budget. |
description |
string | Yes | Additional details about the budget. |
status |
string | Yes | Budget status. Use active for a live budget. Values: new, active, archived, deleted. |
fiscalYear |
number | Yes | Calendar year for the budget period. |
granularity |
string | Yes | Time period: annual, quarterly, monthly, or custom. |
category |
string | Yes | GL account code. Use a code from the client’s imported chart of accounts, or 1001 as the default. |
Optional fields
Depending on granularity, you may also send quarter, month, start, end, ownerId, timezone, or externalId. Confirm additional requirements with Custodia for your program.
Example Request
Annual budget using the default GL code 1001:
Using cURL:
curl -X POST https://sandbox.custodia-tech.com/md/api/Budgets \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "USD",
"name": "Team Activity",
"description": "Used for quarterly activities",
"status": "active",
"fiscalYear": 2026,
"granularity": "annual",
"category": "1001"
}'
Using JavaScript (fetch):
const response = await fetch('https://sandbox.custodia-tech.com/md/api/Budgets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'x-appid': clientId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 5000,
currency: 'USD',
name: 'Team Activity',
description: 'Used for quarterly activities',
status: 'active',
fiscalYear: 2026,
granularity: 'annual',
category: '1001'
})
});
const budget = await response.json();
Response
Success Response (200 OK)
{
"id": "9f91b6f5-9a23-4e0a-bce2-079062394aaf",
"amount": 5000,
"currency": "USD",
"name": "Team Activity",
"description": "Used for quarterly activities",
"status": "active",
"category": "1001",
"fiscalYear": 2026,
"granularity": "annual",
"start": "2026-01-01",
"end": "2026-12-31",
"startDate": "2026-01-01T00:00:00.000Z",
"endDate": "2026-12-31T23:59:59.999Z",
"timezone": "Europe/London",
"companyId": "your-company-id",
"disbursedById": "your-user-id",
"selectInAdvance": false,
"activation": null,
"activationDate": null,
"createdOn": "2026-06-10T17:53:55.185Z",
"utilizationCheckDate": "2026-06-10T17:53:55.185Z",
"updatedOn": "2026-06-10T17:53:55.185Z",
"default": false
}
Update Budget
Partially updates an existing budget. Only include fields you want to change. When you update period or currency fields on an active budget, linked budget mappings are updated automatically.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
budgetId |
string | Yes | UUID of the budget to update. |
Example Request
Increase the budget amount:
Using cURL:
curl -X PATCH https://sandbox.custodia-tech.com/md/api/Budgets/9f91b6f5-9a23-4e0a-bce2-079062394aaf \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"amount": 75000,
"description": "Increased Q2 budget after approval"
}'
Response
Success Response (200 OK)
{
"id": "9f91b6f5-9a23-4e0a-bce2-079062394aaf",
"amount": 75000,
"currency": "USD",
"name": "FY2026 Q2 General Budget",
"description": "Increased Q2 budget after approval",
"status": "active",
"category": "1001",
"fiscalYear": 2026,
"quarter": 2,
"granularity": "quarterly",
"start": "2026-04-01",
"end": "2026-06-30",
"companyId": "your-company-id",
"ownerId": "your-user-id",
"updatedOn": "2026-06-10T15:00:00.000Z"
}
Create Budget Mapping
Creates a mapping rule that routes spend to a budget. Scope fields use * as a wildcard to match any value. A catch-all mapping (all scope fields set to *) routes all eligible spend for the period to the linked budget.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
budgetId |
string | Yes | Id of the budget this mapping applies to. |
expenseType |
string | No | Expense type code, or * for any. Default: *. |
activityType |
string | No | Activity type code, or * for any. Default: *. |
lineItemExpenseType |
string | No | Line-item expense type, or * for any. Default: *. |
userId |
string | No | Specific user id, or * for any. Default: *. |
teamId |
string | No | Team id, or * for any. Default: *. |
costCenterId |
string | No | Cost center id, or * for any. Default: *. |
projectId |
string | No | Project id, or * for any. Default: *. |
customerId |
string | No | Customer id, or * for any. Default: *. |
subsidiaryId |
string | No | Subsidiary id, or * for any. Default: *. |
productId |
string | No | Product id, or * for any. Default: *. |
classId |
string | No | Class / profit center id, or * for any. Default: *. |
currency |
string | No | Currency filter. Usually matches the budget currency. |
fiscalYear |
number | No | Should align with the linked budget period. |
quarter |
number | No | Should align with the linked budget when granularity is quarterly. |
month |
number | No | Should align with the linked budget when granularity is monthly. |
granularity |
string | No | Should match the linked budget (annual, quarterly, monthly, custom). |
startDate |
string (ISO date) | No | UTC start of the mapping period. Usually copied from the budget after create. |
endDate |
string (ISO date) | No | UTC end of the mapping period. Usually copied from the budget after create. |
timezone |
string | No | Timezone for the mapping period. |
Example Request
Catch-all mapping for a quarterly budget:
Using cURL:
curl -X POST https://sandbox.custodia-tech.com/md/api/BudgetMappings \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"budgetId": "9f91b6f5-9a23-4e0a-bce2-079062394aaf",
"expenseType": "*",
"activityType": "*",
"lineItemExpenseType": "*",
"userId": "*",
"teamId": "*",
"costCenterId": "*",
"projectId": "*",
"customerId": "*",
"subsidiaryId": "*",
"productId": "*",
"classId": "*",
"currency": "USD",
"fiscalYear": 2026,
"quarter": 2,
"granularity": "quarterly",
"startDate": "2026-04-01T04:00:00.000Z",
"endDate": "2026-07-01T03:59:59.999Z",
"timezone": "America/New_York"
}'
Response
Success Response (200 OK)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"budgetId": "9f91b6f5-9a23-4e0a-bce2-079062394aaf",
"companyId": "your-company-id",
"expenseType": "*",
"activityType": "*",
"lineItemExpenseType": "*",
"userId": "*",
"teamId": "*",
"costCenterId": "*",
"projectId": "*",
"customerId": "*",
"subsidiaryId": "*",
"productId": "*",
"classId": "*",
"currency": "USD",
"fiscalYear": 2026,
"quarter": 2,
"granularity": "quarterly",
"startDate": "2026-04-01T04:00:00.000Z",
"endDate": "2026-07-01T03:59:59.999Z",
"timezone": "America/New_York",
"createdOn": "2026-06-10T14:35:00.000Z"
}
Update Budget Mapping
Partially updates an existing budget mapping. Use this to narrow or broaden scope (for example, restrict a mapping to a specific costCenterId or userId).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
budgetMappingId |
string | Yes | UUID of the budget mapping to update. |
Example Request
Restrict mapping to a specific cost center:
Using cURL:
curl -X PATCH https://sandbox.custodia-tech.com/md/api/BudgetMappings/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"costCenterId": "your-cost-center-id"
}'
Response
Success Response (200 OK)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"budgetId": "9f91b6f5-9a23-4e0a-bce2-079062394aaf",
"companyId": "your-company-id",
"expenseType": "*",
"activityType": "*",
"lineItemExpenseType": "*",
"userId": "*",
"teamId": "*",
"costCenterId": "your-cost-center-id",
"projectId": "*",
"customerId": "*",
"subsidiaryId": "*",
"productId": "*",
"classId": "*",
"currency": "USD",
"fiscalYear": 2026,
"quarter": 2,
"granularity": "quarterly",
"startDate": "2026-04-01T04:00:00.000Z",
"endDate": "2026-07-01T03:59:59.999Z",
"timezone": "America/New_York",
"createdOn": "2026-06-10T14:35:00.000Z"
}
Get Available Balance
Returns the budget’s available balance: total amount minus reserved and utilized spend. Use this before creating spend permissions or approving large transactions.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
budgetId |
string | Yes | UUID of the budget. |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
targetCurrency |
string | No | Convert the result to this currency. Defaults to the budget’s currency. |
Example Request
Using cURL:
curl -X GET https://sandbox.custodia-tech.com/md/api/Budgets/9f91b6f5-9a23-4e0a-bce2-079062394aaf/available \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID"
Using JavaScript (fetch):
const response = await fetch(
'https://sandbox.custodia-tech.com/md/api/Budgets/9f91b6f5-9a23-4e0a-bce2-079062394aaf/available',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'x-appid': clientId
}
}
);
const available = await response.json();
Response
Success Response (200 OK)
The response body is a numeric value (root JSON number), not an object:
42500.00
Tip: Available balance = amount − taken (see Calculate budget metrics). Compare this value against planned spend permission amounts in Spend permission.
Calculate Budget Metrics
Returns utilization metrics for one or more budgets: reserved spend, posted card usage, expired allocations, and sub-budget rollups. Use this when you need a detailed breakdown rather than a single available balance number from Get available balance.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
array of strings | Yes | Budget ids to calculate metrics for. Pass an array of budget UUIDs. |
forceCurrency |
string | No | Convert all monetary values in the response to this currency. Defaults to each budget’s currency. |
Example Request
Using cURL:
curl -X POST https://sandbox.custodia-tech.com/md/api/Budgets/calcMetrics \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "x-appid: YOUR_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{
"query": ["9f91b6f5-9a23-4e0a-bce2-079062394aaf"]
}'
Using JavaScript (fetch):
const response = await fetch('https://sandbox.custodia-tech.com/md/api/Budgets/calcMetrics', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'x-appid': clientId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: ['9f91b6f5-9a23-4e0a-bce2-079062394aaf']
})
});
const metrics = await response.json();
Response
Success Response (200 OK)
The response is an object keyed by budget id. Each value contains utilization fields in the budget currency:
{
"9f91b6f5-9a23-4e0a-bce2-079062394aaf": {
"reserved": 0,
"grossReserved": 0,
"expired": 119.86,
"expiredGross": 119.86,
"total": 119.86,
"totalGross": 119.86,
"used": 119.86,
"taken": 119.86,
"currency": "USD",
"last": "2026-06-11T16:28:20.302Z",
"subBudgetsAmount": 0,
"subBudgetsUsed": 0,
"subBudgetsTaken": 0,
"subBudgetsReserved": 0
}
}
Response Fields
| Field | Description |
|---|---|
reserved |
Net amount currently reserved on active or locked spend permissions (budget allocations) linked to this budget. This is allocation headroom already committed but not yet fully spent. |
grossReserved |
Gross reserved amount on active or locked allocations, based on allocatedAmount (before net adjustments or buffers are applied). |
expired |
Net amount from expired or ended spend permissions that still counts toward budget utilization. |
expiredGross |
Gross allocated amount from expired or ended spend permissions. |
total |
Total net reserved across active and expired allocations: reserved + expired. |
totalGross |
Total gross reserved across active and expired allocations: grossReserved + expiredGross. |
used |
Actual card spend posted against this budget (settled transactions, net of adjustments), expressed in the budget currency. |
taken |
Total budget consumption used for availability checks. For active allocations this is the greater of reserved vs used; expired allocation usage is included. Parent budgets also roll up child sub-budget amounts into taken. Available balance ≈ budget.amount − taken. |
currency |
Currency code for all monetary values in this metrics object. |
last |
Timestamp of the most recent expense activity recorded against this budget. |
subBudgetsAmount |
For parent budgets: rolled-up allocated capacity from child sub-budgets (based on each child’s budget amount vs consumption). |
subBudgetsUsed |
Sum of used from child sub-budgets. |
subBudgetsTaken |
Sum of taken from child sub-budgets. |
subBudgetsReserved |
Sum of reserved from child sub-budgets. |
Related documentation
- Spend permission: allocate spend against a budget
- Authentication: obtain access tokens
- Glossary: allocation and budget terminology