API · 12 operations

Budgets

The top-level container: create, copy, export, set up, templates.

get /v1/budgets/{budget_id}/templates

Starter envelope templates

scope budget:read · listTemplates

Parameters

  • budget_id path · string · required

Returns · 200

{ templates[] }: named envelope lists with suggested targets, each envelope naming the category it lands in. Never enforced.

Example

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

Errors · besides 401, 429, 500

  • 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}/templates/{id}/apply

Create the template's envelopes that do not already exist

scope budget:write · applyTemplate

Parameters

  • budget_id path · string · required

  • id path · string · required

Returns · 200

{ created[], envelope_ids[], skipped[] }. New envelopes go into the template's categories, made if missing; existing envelopes are left untouched.

Example

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

Errors · besides 401, 429, 500

  • 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}/copy/preview

What starting a budget from this one would bring, without writing anything

scope budget:read · previewBudgetCopy

Parameters

  • budget_id path · string · required

Body · JSON

  • include object · required

  • include.envelopes boolean

  • include.targets boolean

  • include.bills boolean

  • include.holdings boolean

  • include.loans boolean

  • include.goals boolean

  • include.outlook boolean

  • close_source boolean

  • accounts object[] · ≤ 100 items

  • accounts[].account_id string · required

  • accounts[].bring "balance" | "history" · required

  • accounts[].bank_feed "stays" | "moves"

Returns · 200

{ source: { id, name, status }, include, counts: { categories, envelopes, targets, bills, holdings, loans, goals, outlook, scenarios, accounts: { available, will_copy } }, warnings: [{ code, count, message }], source_accounts: [{ id, name, type, bank_fed, working_balance_cents }] }. source_accounts are what accounts may pick. available is what the source holds (open envelopes, holdings, goals and loans; bills in any state; targets are envelopes that have one); will_copy is what the given include brings. Warnings name what comes over unlinked (goal_envelope_unlinked, goal_loan_unlinked, bill_envelope_unlinked, loan_holding_unlinked, account_link_unlinked), a scenario reference that would be dropped, and bill_auto_enter_off: a bill that enters itself may only do so in one open budget, so pass close_source: true to keep it. A read on a POST, like /aims/preview; the source may be closed.

Example

curl -X POST 'https://api.mintro.money/v1/budgets/default/copy/preview' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "include": { "envelopes": true, "targets": true } }'

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.

    budget_ambiguous: This tenant holds more than one budget, so the call must say which. GET /v1/budgets for the ids, then pass budget_id.

  • 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}/copy

Start a new budget as a selective copy of this one

scope budget:write · copyBudget

Parameters

  • budget_id path · string · required

Body · JSON

  • name string · required · ≤ 200 chars

  • include object · required

  • include.envelopes boolean

  • include.targets boolean

  • include.bills boolean

  • include.holdings boolean

  • include.loans boolean

  • include.goals boolean

  • include.outlook boolean

  • close_source boolean

  • accounts object[] · ≤ 100 items

  • accounts[].account_id string · required

  • accounts[].bring "balance" | "history" · required

  • accounts[].bank_feed "stays" | "moves"

Returns · 201

The new budget { id, name, status, created_at } plus source_budget_id, copied (the counts that came over) and warnings (as the preview). YNAB's fresh start, chosen bucket by bucket: include.envelopes (categories and envelopes; the card links come with them), targets (needs envelopes), bills, holdings, loans, goals, outlook (assumptions and scenarios; needs goals). Nothing filed or assigned comes along: no entries, no assignments, no valuation or balance history; holdings and loans get one opening row from their current figures. Accounts come only when listed in accounts: each { account_id, bring: balance | history, bank_feed?: stays | moves } gives the copy its own account (one starting row at today's balance, or every row as a settled record), and its positive balance is ready to assign in the copy; bank_feed: moves takes a bank-fed account's feed along, leaving the source's account kept by hand with its rows. With none listed the copy starts with no accounts and zero ready to assign. Links to things you did not bring come over unlinked and are counted in warnings. The source may be closed; close_source: true closes an open one once the copy exists, which also keeps default resolving. Leaving both open means every bare call needs a budget id. Run /copy/preview first.

Example

curl -X POST 'https://api.mintro.money/v1/budgets/default/copy' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "<name>", "include": { "envelopes": true, "targets": true } }'

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.

    budget_ambiguous: This tenant holds more than one budget, so the call must say which. GET /v1/budgets for the ids, then pass budget_id.

  • 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}/setup

Where setup stands: mode, the current step, what is done, skipped or empty

scope budget:read · getSetup

Parameters

  • budget_id path · string · required

Returns · 200

{ mode: self|assisted|null, current_step, started, completed_at, steps: [{ id: assets|loans|goal|accounts|envelopes, status: done|skipped|empty, count }], progress: { done, of } }. done means the budget holds at least one open holding / loan / retirement goal / account / envelope (read every time, never stored), so this is also the honest answer to "what has this person set up so far". started is false until the first PATCH. The order is the one setup walks: what you own, what you owe, retirement, where the money is, then the envelopes.

Example

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

Errors · besides 401, 429, 500

  • 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.

patch /v1/budgets/{budget_id}/setup

Move setup along: choose the mode, set the step, skip a step, finish or reopen

scope budget:write · updateSetup

Parameters

  • budget_id path · string · required

Body · JSON

  • mode "self" | "assisted" | null

  • current_step "start" | "assets" | "loans" | "goal" | "accounts" | "envelopes" | "done"

  • skipped "assets" | "loans" | "goal" | "accounts" | "envelopes"[] · ≤ 5 items

  • completed boolean

Returns · 200

The setup after the write, same shape as GET. Every field optional: mode (self|assisted|null), current_step, skipped (replaces the list; a step with data is done regardless), completed (true finishes; false is Run setup again). An agent helping with setup calls this to mark the step it just finished: the data it added is what makes the step done; this only moves the pointer. Starting setup is a PATCH too: the row exists from the first one.

Example

curl -X PATCH 'https://api.mintro.money/v1/budgets/default/setup' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "mode": "self", "current_step": "start" }'

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.

  • 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

Every budget on the account

scope budget:read · listBudgets

Returns · 200

{ budgets: [{ id, name, status, currency, time_zone, created_at }] }. A budget is the top-level container (its own envelopes and entries) and counts in exactly one currency (default CAD): every envelope, account and transaction is in it; a holding or loan may be in another and converts to it on read. time_zone is the IANA zone the household lives in (e.g. America/Edmonton): today, the current month and every day written from a moment are read in it; null until set, and then read as UTC. An account may hold as many as it likes: budgets are never limited by the plan, which gates bank connections only.

Example

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

Errors · besides 401, 429, 500

  • 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

Start a new budget

scope budget:write · createBudget

Body · JSON

  • name string · required · ≤ 200 chars

  • currency string · matches ^[A-Z]{3}$ · ≥ 3 chars · ≤ 3 chars

  • time_zone string · matches ^[A-Za-z_]+(?:\/[A-Za-z0-9_+-]+)*$|^UTC$ · ≤ 64 chars

Returns · 201

The new budget. currency (ISO 4217, default CAD) is the one currency it counts in and is the way to keep a USD life beside a CAD one. time_zone (an IANA name such as America/Edmonton) is the zone its days are read in; left out, the app fills it from the person's browser on their next visit. This is how someone starts over or separates two financial lives without losing history: the existing budget stays whole, and the bank connections keep feeding all of them, so a new budget never costs a re-link.

Example

curl -X POST 'https://api.mintro.money/v1/budgets' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "<name>" }'

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.

  • 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}

One-call budget snapshot

scope budget:read · getBudget

Parameters

  • budget_id path · string · required

Returns · 200

{ budget: { id, name, status, currency, time_zone }, envelopes[], budget_total_cents, inbox: { count, total_cents }, connections[], holdings[], holdings_value_cents, aim }. Envelope numbers come from caches. Each envelope carries forecast_basis (what the cashflow line projects for it, or null) and scheduled_bills (how many active bills with an account are filed to it; the cashflow line walks those on their own, so a projection never counts them twice). An open spending envelope also carries forecast_options { last_month_cents, average_3_cents, target_cents } is what each basis would add a month, worked out by the same rules the cashflow line uses and after the envelope's scheduled bills, so each option's amount is shown before you choose (for target, next month's amount; target_cents is null when there is no monthly or weekly target, so target would project nothing). The amounts are not a snapshot: they are worked out on every read, so last month is always the latest full month and the average the last three full months; they move on the 1st, and target_cents follows the envelope's target. Null for a closed or card payment envelope. spending { last_month_cents, average_3_cents, bills_due_this_month, bills_due_this_month_cents } is what the envelope spent with its bills in (the latest full month, and the three full months before this one averaged, rounded up), and how many scheduled bill payments are still due from today to this month's last day and what they take. Sort or filter envelopes by these; null for a closed or card payment envelope. holdings_value_cents is the sum of open holding snapshots. aim is the declared want or null (progress lives on GET /month). The inbox and connections are the account's; envelopes, holdings and the aim are this budget's.

Example

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

Errors · besides 401, 429, 500

  • 400

    budget_ambiguous: This tenant holds more than one budget, so the call must say which. GET /v1/budgets for the ids, then pass budget_id.

  • 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.

patch /v1/budgets/{budget_id}

Rename a budget, close/reopen it, set its currency or time zone, or set All cash's Alarm and Warning

scope budget:write · updateBudget

Parameters

  • budget_id path · string · required

Body · JSON

  • name string · ≤ 200 chars

  • status "open" | "closed"

  • currency string · matches ^[A-Z]{3}$ · ≥ 3 chars · ≤ 3 chars

  • cash_low_cents integer | null · ≥ -9007199254740991

  • cash_ok_cents integer | null · ≥ -9007199254740991

  • time_zone string · matches ^[A-Za-z_]+(?:\/[A-Za-z0-9_+-]+)*$|^UTC$ · ≤ 64 chars

Returns · 200

The updated budget. A closed budget stays readable and exportable but rejects writes, which is the way to retire last year without deleting it. currency can change only while nothing counts in it yet (no entries, holdings or loans); after that, start a new budget. time_zone (an IANA name such as America/Edmonton; a name the server does not know is refused) is where the household lives: today, the current budget month, cashflow days, plan dates, streaks, age of money, Activity's days and the days in emails are read in it, so 10 PM in Alberta is still that day and not tomorrow in UTC. Changing it changes no stored row, only what today is. cash_low_cents / cash_ok_cents set the All cash cashflow line's Alarm (cash_low_cents: at or below it the line turns red) and Warning (cash_ok_cents: at or below it the line turns yellow) (null cash_low_cents goes back to the cash accounts' own lines summed); GET /v1/cashflow reports them as bands.

Example

curl -X PATCH 'https://api.mintro.money/v1/budgets/default' \ -H "Authorization: Bearer $MINTRO_KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "<name>", "status": "open" }'

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}/export

One budget as a single JSON document

scope budget:read · export

Parameters

  • budget_id path · string · required

Returns · 200

Categories, envelopes with targets, the assignment ledger (every dollar given a job, with its month and kind), filed transactions, entries with filed_by, the unfiled inbox, holdings with their valuation events, loans with their balances, every goal (aims, in priority order; aim is the first, kept for older readers), bills (recurring), the outlook's assumptions, scenarios, cashflow plans, and connection metadata without tokens. The portability guarantee: the plan and the spend, not just the spend.

Example

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

Errors · besides 401, 429, 500

  • 400

    budget_ambiguous: This tenant holds more than one budget, so the call must say which. GET /v1/budgets for the ids, then pass budget_id.

  • 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.

get /v1/export

Every budget in the household as one JSON document

scope budget:read · exportHousehold

Returns · 200

{ exported_at, tenant_id, budgets[], transactions[], inbox[], connections[], payees[], payee_rules[] }. Each budget carries what GET /v1/budgets/{budget_id}/export returns for it: categories, envelopes, entries, the assignment ledger, holdings, loans, goals, bills, outlook, scenarios and cashflow plans; the bank layer is listed once for the household. Connection metadata only, never tokens. The copy to take before leaving: deleting an account deletes every budget, not just the open one.

Example

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

Errors · besides 401, 429, 500

  • 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.