> ## Documentation Index
> Fetch the complete documentation index at: https://docs.finta.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Budgets

> How periods, the two edit scopes, and account scope work in the budget endpoints.

Budget plans hold budgets, one per category or category group. Every request reads and writes the default plan unless you pass `plan`, which takes a plan's name or id. See the [OpenAPI reference](https://docs.finta.io/api/v2/openapi.json) for each endpoint's fields.

During the rollout, these endpoints return `404` with `NOT_FOUND` for accounts that don't have budgets yet.

| Endpoint | What it does |
| - | - |
| `GET`, `POST /budget-plans` | List plans, or create one |
| `PATCH`, `DELETE /budget-plans/{id}` | Rename, change accounts, make default, or delete. `{id}` also accepts the plan's name, URL-encoded. |
| `GET`, `POST /budgets` | List a plan's budgets, or set one by category or group |
| `PATCH`, `DELETE /budgets/{id}` | Edit or delete a budget by id |
| `POST /budgets/reallocate` | Move money between two budgets for one month |
| `GET /budgets/status` | Planned, spent, available and pace for one month |

## Periods

Budgets run monthly. A `period` is either `current` or a month written as `YYYY-MM`. `current` resolves in the user's timezone, so the same request can return a different month around midnight on the first.

A budget has a `startPeriod`, the first month it applies to. Editing a month before it returns `PERIOD_BEFORE_START`. In those months, the category's spending reports as unbudgeted.

Finta computes every status number when you ask for it, so a transaction that arrives late changes that month's `spent` and every later month's `rollover`. Read status again rather than storing the numbers.

## Template and period edits

`POST /budgets` and `PATCH /budgets/{id}` require a `scope`.

* `scope: "template"` changes the amount from the current month forward. Past months keep the amount they had, and the response's `snapshot` reports how many months Finta wrote down. A month with its own amount keeps it until you set that month again.
* `scope: "period"` changes one month and nothing else, and needs `period`. It can't create a budget, so set a template first.

`budget.amount` in a response is always the template amount. A one-month amount is in `periodOverride`.

```bash theme={null}
curl https://api.finta.io/v2/budgets \
  -H "Authorization: Bearer $FINTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"categoryId": "<category-id>", "scope": "period", "period": "2026-11", "amount": 650}'
```

`POST /budgets` returns `201` when it created the budget and `200` when it edited one. `created` in the body says the same.

`POST /budgets/reallocate` changes only the two budgets' amounts for that month. It isn't safe to repeat, so send an `Idempotency-Key` header if you retry.

## Envelope plans

Set `method: "envelope"` with `PATCH /budget-plans/{id}`. Changing method changes every past month's numbers too.

In an envelope plan, a month's `planned` is only what you assigned with `scope: "period"`, and `amount` is that month's total assignment, not an addition. `scope: "template"` assigns nothing. It sets `suggestedAmount` in status.

Status returns `readyToAssign` for the current month: the plan's depository balances minus `totals.available` minus every payment envelope's `available`. It is `null` for other months and standard plans. It goes negative when you assign more than you have, and that isn't an error. `balancesAsOf` is the time of the oldest balance behind it.

An overspent envelope shows a negative `available` this month and resets to 0 next month. The shortfall comes out of `readyToAssign`.

## Credit card payments

Every credit card in an envelope plan's accounts has one entry in `paymentEnvelopes`, with or without an assignment. To set money aside, send `POST /budgets` with the card's `accountId`:

```bash theme={null}
curl https://api.finta.io/v2/budgets \
  -H "Authorization: Bearer $FINTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"accountId": "<card-account-id>", "scope": "period", "period": "current", "amount": 200}'
```

A card target takes `scope: "period"` only. A non-credit account returns `ACCOUNT_NOT_CREDIT`, a card outside the plan's accounts returns `ACCOUNT_NOT_IN_PLAN`, and a standard plan returns `METHOD_NOT_ENVELOPE`.

| Field | What it means |
| - | - |
| `assigned` | What you set aside this month |
| `rollover` | Carried from earlier months |
| `movedIn` | Budgeted spending on this card this month, net of refunds. It leaves the spending envelope and lands here. |
| `paid` | Inflows on the card in a transfer category |
| `available` | `assigned` plus `rollover` plus `movedIn` minus `paid` |
| `owed` | The card's current balance. It isn't subtracted from `readyToAssign`. |

Finta sorts inflows on a card by category type. An expense category is a refund, and a transfer category is a payment. An income category or no category counts in `unrecognizedCardInflows`, which is neither. A rule that puts those inflows in a transfer category makes them payments.

## Account scope

A plan covers every account (`accountScope: "All"`) or the ones you select (`"Selected"`). Every number in its status counts only those accounts. Changing a plan's accounts recomputes every past month too.

An API key or OAuth client limited to certain accounts can still read every plan and its totals. Transactions stay limited to the accounts it was granted. When a plan's totals include other accounts, status returns `includesAccountsOutsideConsent: true` and names them in `accountsOutsideConsent`:

```json theme={null}
{
  "includesAccountsOutsideConsent": true,
  "accountsOutsideConsent": [{ "id": "<account-id>", "name": "Business Checking" }]
}
```

A limited key can only build a plan from accounts it may use. Selecting any other account, or asking for every account, returns `NOT_FOUND`.

Account limits apply to which accounts a key can put in a plan, not to the budgets inside it. A limited key can change or delete any budget and any plan, and can assign to any credit card in a plan. These calls change planning numbers only. They move no money and don't return transactions.

## Errors

Errors use the usual `{ "error": { "code", "message", "requestId" } }` body.

* A bad `plan` returns `404` with `NOT_FOUND`, and `message` lists the user's plan names.
* A user without Storage Mode gets `403` with `STORAGE_MODE_REQUIRED`.
* A request the budget rules refuse returns `400` with a specific code, such as `INSUFFICIENT_PLANNED`, `DIFFERENT_PLANS` or `PLAN_NAME_TAKEN`. `message` says what to change.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.