API · 6 operations

Assigning

The month view, ready to assign, and giving money a job.

post /v1/budgets/{budget_id}/assignments

Give money a job: assign, move, record income, cover overspending

scope budget:write · createAssignment

Parameters

  • budget_id path · string · required

Body · JSON

  • month string · required · YYYY-MM or YYYY-MM-DD

  • kind "assign" | "unassign" | "move" | "income" | "rollover_cover" | "card_reserve" · required

  • envelope_id string | null · ≤ 64 chars

  • from_envelope_id string | null · ≤ 64 chars

  • account_id string | null · ≤ 64 chars

  • transaction_id string | null · ≤ 64 chars

  • amount_cents integer · required · > 0

  • note string | null · ≤ 1000 chars

Returns · 201

{ id, month, undo, ready_to_assign_cents, envelopes[] }: the month's ready to assign after this write, and { envelope_id, assigned_cents, available_cents } for each envelope it touched. The plan ledger, append-only: a correction is a compensating row, never an edit. amount_cents is always POSITIVE; direction comes from which envelope is named. income names neither (money arrives unassigned); assign names envelope_id (pool -> envelope); move and rollover_cover name both. Covering credit overspending funds the card: when assign, move or rollover_cover puts money into an envelope that is overspent because of card purchases its money did not cover this month, that much of the amount is also reserved into the card's payment envelope (a card_reserve row), so the bill is funded; that payment envelope then appears in envelopes[] too. Cash overspending covers as before. undo (null for income, which DELETE /v1/budgets/{budget_id}/income/{id} takes back) is the one move { from_envelope_id, envelope_id, amount_cents, card_releases? } that puts every envelope back exactly, the card's payment envelope included: send it as the only move to POST /v1/budgets/{budget_id}/assignments/batch. Nothing here moves a real dollar.

Example

curl -X POST 'https://api.mintro.money/v1/budgets/default/assignments' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "month": "2026-09", "kind": "assign", "envelope_id": "env_01J8Z3W6Q2GROCERIES00000", "amount_cents": 45000 }'

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

  • 404

    not_found: No such record on this budget. Re-read GET /v1/budgets/{id} (or the relevant list) for current ids rather than reusing a remembered one.

  • 409

    idempotency_conflict: This Idempotency-Key was used for a different request, or the first request is still in flight. Retry the in-flight case after a second; use a fresh key for a genuinely different request.

    already_linked: This transaction already pays a loan (or a bank row already pays it). DELETE /v1/transactions/{id}/loan-payment to unlink first; linking twice would drop the principal twice.

get /v1/budgets/{budget_id}/income

Every income row of a month: what made ready to assign

scope budget:read · listIncome

Parameters

  • budget_id path · string · required

  • month query · string

Returns · 200

{ month, income: [{ id, month, amount_cents, note, created_by, created_at, transaction }], total_cents }. transaction is { id, date, name, account } for income counted from an inflow, null for plan-only income. Defaults to the current month; pass month=YYYY-MM.

Example

curl 'https://api.mintro.money/v1/budgets/default/income' \ -H "Authorization: Bearer $MINTRO_KEY"

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

delete /v1/budgets/{budget_id}/income/{id}

Reverse an income row: ready to assign goes down, no balance moves

scope budget:write · reverseIncome

Parameters

  • budget_id path · string · required

  • id path · string · required

Returns · 200

{ reversed: true, amount_cents, transaction_id }. The repair for income counted twice or left behind. If it counted a live inflow, that inflow returns to the inbox to be counted or filed again. Only income rows; money in an envelope goes back with the undo its assignment answered, or an unassign.

Example

curl -X DELETE 'https://api.mintro.money/v1/budgets/default/income/<id>' \ -H "Authorization: Bearer $MINTRO_KEY"

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

  • 404

    not_found: No such record on this budget. Re-read GET /v1/budgets/{id} (or the relevant list) for current ids rather than reusing a remembered one.

  • 409

    idempotency_conflict: This Idempotency-Key was used for a different request, or the first request is still in flight. Retry the in-flight case after a second; use a fresh key for a genuinely different request.

get /v1/budgets/{budget_id}/month

The month view: ready to assign, and available per envelope

scope budget:read · getMonth

Parameters

  • budget_id path · string · required

  • month query · string

Returns · 200

{ month, ready_to_assign_cents, assigned_in_future_cents, envelopes[], aims[], aim }. ready_to_assign_cents is income through this month minus money given a job in ANY month, so a later month's assignments are already out of it (YNAB's "assigned in future"); assigned_in_future_cents is that later-month share, for showing the line. Each envelope carries carried_in_cents, assigned_cents, activity_cents, available_cents, target_cents, target_kind, target_date, needed_cents, last_assigned_cents, last_activity_cents, average_assigned_cents, average_spent_cents, upcoming_cents (bills on the envelope still due by month end, overdue included), upcoming_short_cents (how far available falls short of them), state (funded | underfunded | overspent | neutral; underfunded also when upcoming_short_cents > 0, meaning a bill is coming and the money is not there, YNAB's yellow with the calendar icon) and outlook (ten percent of this envelope toward retirement every month, or null). needed_cents is what THIS month needs assigned for the target to be on track; assign against that, not against target_cents: a monthly target needs the shortfall against what already carried in (leftover is the job), a by_date target needs what is still missing divided by the months left (all of it once the date has passed), and a balance target needs only its shortfall, so a full one needs nothing and reads funded with zero assigned. state is computed against needed_cents. Last-month and 3-month averages (spent is the magnitude of negative activity; averages round up) come from the same cache walk, so do not fetch last month twice. This is what a budget screen reads first. On a card's payment envelope, money set aside for card purchases (and for covered credit overspending) counts in activity_cents, not assigned_cents: assigned_cents is only what the person assigned, so summing it gives "assigned this month". available = carried_in + assigned + activity, and carried_in is DERIVED by walking earlier months, so unspent money rolls forward and overspending rolls forward too. Defaults to the current month; pass month=YYYY-MM. aims[] is every goal in priority order with its standing and this month's shortfall computed on read; aim is the headline goal, or null, kept one release. toward_nest_egg_cents is accounts + holdings − loans.

Example

curl 'https://api.mintro.money/v1/budgets/default/month' \ -H "Authorization: Bearer $MINTRO_KEY"

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

post /v1/budgets/{budget_id}/auto-assign

Fund the month: YNAB's whole auto-assign menu, in one call

scope budget:write · autoAssign

Parameters

  • budget_id path · string · required

Body · JSON

  • month string

  • envelope_ids string[] · ≤ 500 items

  • strategy "underfunded" | "overspent" | "assigned_last_month" | "spent_last_month" | "average_assigned" | "average_spent" | "reset_available" | "upcoming" · required

  • dry_run boolean

Returns · 200

{ strategy, month, dry_run, applied, total_cents, envelopes: [{ envelope_id, name, amount_cents }] }. Strategies: underfunded (top every envelope up to its needed_cents, or by its upcoming_short_cents if that asks more), overspent (assign from ready-to-assign until available is back to zero; overspending caused by card purchases funds that card's payment envelope too), assigned_last_month, spent_last_month, average_assigned and average_spent (both over the last 3 months, rounded up so they are never a cent short), reset_available (take back what is available so every envelope reads zero, returning it to ready-to-assign), and upcoming (top each envelope up until what is available covers the bills still due on it this month, one per bill falling due by month end). Every strategy TOPS UP rather than re-assigning: an envelope that already has enough gets nothing, so running it twice is safe. dry_run: true returns exactly the same plan without writing. Use it first, because this moves money across a whole budget. reset_available leaves overspent envelopes alone; pulling money out of one that is already negative is the opposite of a reset. Each envelope is still its own assignment row through the one write path, so a failure part-way leaves the ones that succeeded. Defaults to the current month. envelope_ids narrows it to a selection; that is how a person ticking a few rows, or a whole category, funds just those.

Example

curl -X POST 'https://api.mintro.money/v1/budgets/default/auto-assign' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "strategy": "underfunded" }'

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

  • 404

    not_found: No such record on this budget. Re-read GET /v1/budgets/{id} (or the relevant list) for current ids rather than reusing a remembered one.

  • 409

    idempotency_conflict: This Idempotency-Key was used for a different request, or the first request is still in flight. Retry the in-flight case after a second; use a fresh key for a genuinely different request.

post /v1/budgets/{budget_id}/assignments/batch

Move money between envelopes several times at once, all or none

scope budget:write · moveMoneyBatch

Parameters

  • budget_id path · string · required

Body · JSON

  • month string · required · YYYY-MM or YYYY-MM-DD

  • moves object[] · required · ≤ 50 items

  • moves[].from_envelope_id string | null · ≤ 64 chars

  • moves[].envelope_id string | null · ≤ 64 chars

  • moves[].amount_cents integer · required · > 0

  • moves[].card_releases object[] · ≤ 20 items

  • moves[].card_releases[].payment_envelope_id string · required · ≤ 64 chars

  • moves[].card_releases[].amount_cents integer · required · > 0

  • note string | null · ≤ 1000 chars

Returns · 201

{ month, count, total_cents, moves, undo, ready_to_assign_cents, envelopes }. Each move { from_envelope_id, envelope_id, amount_cents } is one assignment: both named is a move between envelopes, only envelope_id assigns from Ready to assign, only from_envelope_id returns money to Ready to assign. amount_cents is positive. A move into an envelope overspent on a credit card also sets that much aside in the card's payment envelope, so the card is paid for; moves says so in each card_reserves { payment_envelope_id, amount_cents }, and the matching undo move carries the same as card_releases, which take the card's set-aside back as the money leaves (only with from_envelope_id, and never more than is still set aside for that card from that envelope this month). Every move lands in one database transaction: if any is refused (an envelope not in this budget, say), none land. moves echoes each with its assignment id and kind; undo is the same moves the other way round, with their card_releases, which put every envelope back exactly, card payment envelopes included, when sent to this endpoint as they came. envelopes { envelope_id, assigned_cents, available_cents } is each envelope touched, after. Up to 50 moves. This is what Fix my envelopes applies. Nothing here moves a real dollar between bank accounts.

Example

curl -X POST 'https://api.mintro.money/v1/budgets/default/assignments/batch' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "month": "2026-10", "moves": [ { "from_envelope_id": "env_01J8Z3W6Q2FUNMONEY000000", "envelope_id": "env_01J8Z3W6Q2GROCERIES00000", "amount_cents": 12000 }, { "envelope_id": "env_01J8Z3W6Q2TRUCKLOAN0000", "amount_cents": 20000 } ], "note": "Fix my envelopes" }'

Errors · besides 401, 429, 500

  • 400

    validation_failed: The request body or query is malformed. The message names the offending field; fix it and retry.

  • 403

    forbidden: The key is valid but lacks the scope this call needs. Ask the human to mint a key with the scope named in the message.

  • 404

    not_found: No such record on this budget. Re-read GET /v1/budgets/{id} (or the relevant list) for current ids rather than reusing a remembered one.

  • 409

    idempotency_conflict: This Idempotency-Key was used for a different request, or the first request is still in flight. Retry the in-flight case after a second; use a fresh key for a genuinely different request.