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:

  1. Create a budget: POST /md/api/Budgets
  2. Create a budget mapping: POST /md/api/BudgetMappings (links the budget to spend scope)
  3. 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 (GL code): The 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

POST /md/api/Budgets

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

PATCH /md/api/Budgets/{budgetId}

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

POST /md/api/BudgetMappings

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

PATCH /md/api/BudgetMappings/{budgetMappingId}

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

GET /md/api/Budgets/{budgetId}/available

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 = amounttaken (see Calculate budget metrics). Compare this value against planned spend permission amounts in Spend permission.

Calculate Budget Metrics

POST /md/api/Budgets/calcMetrics

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.