{"openapi":"3.1.0","info":{"title":"Mintro API","version":"1.0.0","description":"Agent-first envelope budgeting. Mintro holds the budget; the agent keeps it current. A budget is the top-level container — envelopes, entries and papers — and an account may hold several; the plan gates bank connections, never budgets.\n\n**Auth.** A Clerk API key as a bearer token on every request. Scopes: `budget:read`, `budget:write`, `connections:read`, `connections:write`, `webhooks:manage`. `GET /v1/me` reports the calling key's scopes, its budgets, its plan limits and its rate limit — call it first. Budget-scoped paths accept the literal `default` in place of a budget id when the account holds exactly one OPEN budget, so a bare key needs no discovery call; closed budgets do not count against it (adr 0014).\n\n**Errors.** Always `{ error: { code, message } }`. Codes are stable and machine-readable; each documented response below names the codes it can carry and what to do about them.\n\n**Money** is always an integer number of cents, signed, with spending negative.\n\n**Idempotency.** Every POST/PATCH/PUT/DELETE accepts an `Idempotency-Key` header; replays return the stored response.\n\n**Rate limit.** 120 requests per minute per key. Every authenticated response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`; a 429 also carries `Retry-After`."},"servers":[{"url":"https://api.mintro.money"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A Mintro (Clerk) API key."}}},"security":[{"bearerAuth":[]}],"paths":{"/v1/budgets/{budget_id}/assignments":{"post":{"summary":"Give money a job: assign, move, record income, cover overspending","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"month":{"type":"string","pattern":"^\\d{4}-\\d{2}(-\\d{2})?$"},"kind":{"type":"string","enum":["assign","unassign","move","income","rollover_cover","card_reserve"]},"envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"from_envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"account_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"transaction_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"amount_cents":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["month","kind","amount_cents"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ id, month }`. The plan ledger (adr 0011), 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. Nothing here moves a real dollar.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/month":{"get":{"summary":"The month view: ready to assign, and available per envelope","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"month","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ month, ready_to_assign_cents, envelopes[], aim }`. 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`, `state` (funded | underfunded | overspent | neutral) and `outlook` (ten percent of this envelope toward the plan every month, or null — adr 0042). **`needed_cents` is what THIS month needs assigned for the target to be on track — assign against that, not against `target_cents`** (adr 0017): 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 — do not fetch last month twice. This is what a budget screen reads first. `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`. `aim` is what you want this budget to do, with this month's shortfall and nest-egg progress computed on read (adr 0038, adr 0042), or null. `toward_nest_egg_cents` is accounts + holdings − loans.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/auto-assign":{"post":{"summary":"Fund the month: YNAB's whole auto-assign menu, in one call","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"month":{"type":"string"},"envelope_ids":{"maxItems":500,"type":"array","items":{"type":"string","minLength":1}},"strategy":{"type":"string","enum":["underfunded","assigned_last_month","spent_last_month","average_assigned","average_spent","reset_available"]},"dry_run":{"type":"boolean"}},"required":["strategy"],"additionalProperties":false}}}},"responses":{"200":{"description":"`{ strategy, month, dry_run, applied, total_cents, envelopes: [{ envelope_id, name, amount_cents }] }`. Six strategies: `underfunded` (top every envelope up to its `needed_cents`), `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), and `reset_available` (take back what is available so every envelope reads zero, returning it to ready-to-assign). 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 (adr 0027).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/recurring/{id}/enter":{"post":{"summary":"Enter a bill: write it, file it, move to its next date","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"additionalProperties":false}}}},"responses":{"200":{"description":"`{ recurring, transaction_id, filed }`. Creates the transaction in the bill's `account_id`, files it into its `envelope_id` when it has one, and advances `next_due` — one call for what was three. `date` and `amount_cents` override this occurrence without changing the standing bill, which is what a variable bill like a power invoice needs. Returns 400 validation_failed when the bill has no account, because Mintro does not guess where money comes from. **Still not a scheduler** (§13): nothing reads these rows on a timer and a bill past its date sits there looking overdue until somebody calls this.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/report":{"get":{"summary":"Spending and funding across a window of months","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ from, to, total_income_cents, total_assigned_cents, total_activity_cents, months: [{ month, income_cents, assigned_cents, activity_cents }], envelopes: [{ envelope_id, name, status, total_assigned_cents, total_activity_cents, months: [{ month, assigned_cents, activity_cents }] }] }`. Answers \"where did it go\" and \"is this month unusual\" in one call. Activity is signed like entries, so spending is NEGATIVE. Every month in the window appears even when nothing happened, so a gap means a quiet month rather than a missing one. Defaults to the last six months including the current one; `from`/`to` take YYYY-MM (or a full date, which is read as its month) and the window is capped at 24 months — past that, export the budget and work on the whole thing. Drawn entirely from the same cached monthly totals the month view reads: Mintro stores no history beyond them (adr 0016).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/recurring":{"get":{"summary":"Bills you know are coming","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"due_by","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ recurring: [{ id, name, envelope_id, amount_cents, cadence, next_due, status, note, created_at }] }`, soonest first. `due_by=YYYY-MM-DD` narrows to what falls on or before a date — pass the last day of the month to answer \"what is still owed\". **Mintro does NOT run these.** Nothing reads them on a timer and no row here becomes a transaction on its own; `next_due` moves only when something calls the advance endpoint. They exist so a budget can show what is coming before the money goes (adr 0020).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Declare a recurring bill","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cadence":{"type":"string","enum":["weekly","monthly","yearly"]},"next_due":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"account_id":{"type":["string","null"]},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["name","amount_cents","cadence","next_due"],"additionalProperties":false}}}},"responses":{"201":{"description":"The created row.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/recurring/{id}":{"patch":{"summary":"Change or pause a recurring bill","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cadence":{"type":"string","enum":["weekly","monthly","yearly"]},"next_due":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"account_id":{"type":["string","null"]},"status":{"type":"string","enum":["active","paused"]},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated row. `status: paused` keeps it without it counting as due.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Remove a recurring bill","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"204":{"description":"204. Transactions already entered from it are untouched.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/recurring/{id}/advance":{"post":{"summary":"Move a recurring bill on to its next occurrence","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The updated row with the new `next_due`. Call this AFTER entering the transaction — it is the only thing that moves the date, and it moves it exactly one step. Month steps clamp to the end of a short month rather than rolling into the next one, so a bill due on the 31st is due on the 30th in November.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/categories":{"get":{"summary":"List categories in render order","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ categories[] }` — id, name, `sort_order`. A category is a named set of envelopes and nothing more: it carries no total of its own, no period, and no behaviour.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Create a category","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sort_order":{"type":"integer","minimum":0,"maximum":100000}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"The category.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/categories/{id}":{"patch":{"summary":"Rename a category or change its position","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"sort_order":{"type":"integer","minimum":0,"maximum":100000}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated category.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a category; its envelopes survive, uncategorized","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"204":{"description":"Empty. Deleting a category never deletes envelopes — members simply become uncategorized (`category_id: null`), which is a normal resting state.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/envelopes/order":{"put":{"summary":"Bulk placement: reorder categories and envelopes in one call","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"categories":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"sort_order":{"type":"integer","minimum":0,"maximum":100000}},"required":["id","sort_order"],"additionalProperties":false}},"envelopes":{"maxItems":1000,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"category_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"sort_order":{"type":"integer","minimum":0,"maximum":100000}},"required":["id","sort_order"],"additionalProperties":false}}},"additionalProperties":false}}}},"responses":{"200":{"description":"`{ categories, envelopes }` — how many of each were placed. Idempotent: sending the same body twice lands in the same layout. Use this instead of N PATCHes; it is one database transaction and one rate-limit unit.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/entries":{"get":{"summary":"Filed history — search entries","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"envelope_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"min_cents","in":"query","required":false,"schema":{"type":"string"}},{"name":"max_cents","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"`{ entries: [{ id, envelope_id, transaction_id, amount_cents, note, filed_by, created_at, transaction: { id, date, name, merchant_name, amount_cents, currency, account_id, pending } }], next_cursor }`, newest filed first. This is where past spending lives: the inbox holds only what is NOT yet filed. Filter by envelope_id, an inclusive from/to on the transaction date (YYYY-MM-DD), q (case-insensitive substring of name or merchant) and min_cents/max_cents on the entry's magnitude — so \"restaurants in July\" is envelope_id + from + to, and \"everything over $500 this year\" is min_cents=50000 + from. Page with cursor/limit (max 100).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`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.\n\n`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["budget_ambiguous","validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"File a transaction into envelopes (splits = several elements)","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"transaction_id":{"type":"string","minLength":1},"splits":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"envelope_id":{"type":"string","minLength":1},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["envelope_id","amount_cents"],"additionalProperties":false}}},"required":["transaction_id","splits"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ transaction_id, entries[] }` — the entries just written.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`envelope_closed` — Closed envelopes reject new entries. File into an open envelope.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","envelope_closed"]},"message":{"type":"string"}}}}}}}},"422":{"description":"`currency_mismatch` — The envelope's currency differs from the transaction's. Currency is immutable after creation, so file into an envelope of the matching currency or create one.\n\n`over_filed` — The transaction is already fully filed; splits cannot exceed its amount. GET /v1/transactions/{id} to see the existing entries, then PATCH or DELETE them instead of filing again.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["currency_mismatch","over_filed"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/entries/{id}":{"patch":{"summary":"Move or adjust an entry","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"envelope_id":{"type":"string","minLength":1},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated entry.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`envelope_closed` — Closed envelopes reject new entries. File into an open envelope.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","envelope_closed"]},"message":{"type":"string"}}}}}}}},"422":{"description":"`currency_mismatch` — The envelope's currency differs from the transaction's. Currency is immutable after creation, so file into an envelope of the matching currency or create one.\n\n`over_filed` — The transaction is already fully filed; splits cannot exceed its amount. GET /v1/transactions/{id} to see the existing entries, then PATCH or DELETE them instead of filing again.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["currency_mismatch","over_filed"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Undo an entry","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`. The transaction returns to the inbox once its last entry goes.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/envelopes":{"post":{"summary":"Create an envelope","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"category_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"sort_order":{"type":"integer","minimum":0,"maximum":100000},"target_cents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"target_kind":{"type":"string","enum":["monthly","weekly","by_date","balance","debt"]},"target_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}]},"target_note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"The envelope, with `cached_total_cents: 0`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"get":{"summary":"List envelopes","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"`{ envelopes[], next_cursor }` with cached totals and targets.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/envelopes/{id}":{"get":{"summary":"Envelope with recent entries","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The envelope plus its most recent entries.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Rename, close (force for non-zero), set or clear target","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["open","closed"]},"force":{"type":"boolean"},"category_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"sort_order":{"type":"integer","minimum":0,"maximum":100000},"target_cents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"target_kind":{"type":"string","enum":["monthly","weekly","by_date","balance","debt"]},"target_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}]},"target_note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated envelope.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`envelope_not_empty` — The envelope has a non-zero cached total. Move or undo its entries first, or PATCH with force: true to close it and keep them.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","envelope_not_empty"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/papers":{"get":{"summary":"List papers (contents omitted)","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ papers[] }` — slug, title, `updated_by`, `updated_at`. Discovery only.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/papers/{slug}":{"get":{"summary":"Read a paper verbatim","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The paper with its full contents. Mintro never parses or runs paper.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"put":{"summary":"Create or replace a paper (last write wins)","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"content":{"type":"string","maxLength":65536}},"required":["title","content"],"additionalProperties":false}}}},"responses":{"200":{"description":"The stored paper.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"413":{"description":"`paper_too_large` — The paper exceeds the 64KB cap. Split it across several papers.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["paper_too_large"]},"message":{"type":"string"}}}}}}}},"422":{"description":"`paper_limit` — The budget is at its paper cap. Delete a paper before writing another.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["paper_limit"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a paper","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/templates":{"get":{"summary":"Starter envelope templates","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ templates[] }` — named envelope lists with suggested targets. Never enforced.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/templates/{id}/apply":{"post":{"summary":"Create the template's envelopes that do not already exist","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ created[] }`. Existing envelopes are left untouched.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/holdings":{"get":{"summary":"List holdings on this budget","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ holdings[] }`. Declared positions: quantity, last unit price, lean-on `value_cents`. Not a bank account and not a market warehouse.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Declare a holding; look up a public price when a symbol is given","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"kind":{"type":"string","enum":["crypto","equity","other"]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"quote_symbol":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"quantity":{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},"unit_price":{"anyOf":[{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},{"type":"null"}]},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["name","kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"The holding plus `quote_found` (`true`/`false` when a lookup ran, `null` when the caller set `unit_price` or the kind is `other`). A miss is not an error — POST .../valuations to set the price.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/holdings/{id}":{"get":{"summary":"One holding","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The current snapshot.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Rename, close, change symbol or quantity","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["open","closed"]},"quote_symbol":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"quantity":{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},"kind":{"type":"string","enum":["crypto","equity","other"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated holding. A quantity change at an existing unit price appends a valuation.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a holding and its valuations","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/holdings/{id}/refresh":{"post":{"summary":"Look up a public price and append a valuation if found","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The holding plus `quote_found`. False leaves the snapshot unchanged; set a price yourself.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/holdings/{id}/valuations":{"get":{"summary":"Price history for one holding (newest first, cap 100)","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ valuations[] }` — quantity, unit_price, value_cents, source, as_of.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Set the unit price by hand and append a valuation","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"unit_price":{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},"quantity":{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["unit_price"],"additionalProperties":false}}}},"responses":{"201":{"description":"The holding with the new lean-on value. Always allowed, even when a feed exists.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/holdings/{id}/buy":{"post":{"summary":"Buy more: money leaves an account, quantity is added","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"account_id":{"type":"string","minLength":1,"maxLength":64},"amount_cents":{"type":"integer","minimum":1,"maximum":9007199254740991},"quantity":{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["account_id","amount_cents","quantity","date"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ holding, outflow }`. One transaction on the paying account, filed on arrival. Existing unit price stays; a first buy without a price takes the fill. Not a transfer — a holding is not an account (adr 0044).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/picture":{"get":{"summary":"The entire picture: accounts, holdings, loans, net, aim projection","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ accounts_cents, holdings_cents, loans_cents, net_cents, holdings[], loans[], aim }`. Holdings carry `equity_cents` and the loans against them. Net is accounts working balances + open holdings − open loans. Aim includes interpolation when a target year is set. Numbers come from caches and declared snapshots; nothing here scans entries.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/loans":{"get":{"summary":"List installment loans on this budget","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ loans[] }`. Declared remaining principal, optional holding, optional paying account, `next_payment` split from the terms (`estimated: true`). Not a credit account.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Declare an installment loan; optionally against a holding and a paying account","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"holding_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"account_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3},"original_principal_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"remaining_principal_cents":{"type":"integer","minimum":0,"maximum":9007199254740991},"annual_rate":{"anyOf":[{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},{"type":"null"}]},"payment_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"payments_per_year":{"type":"integer","minimum":1,"maximum":52},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["name","remaining_principal_cents"],"additionalProperties":false}}}},"responses":{"201":{"description":"The loan. `next_payment` is the estimated interest/principal split of `payment_cents` against remaining. Does not write an entry.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/loans/{id}":{"get":{"summary":"One loan","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The current remaining plus the next-payment split.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Rename, retarget a holding or paying account, change terms, or agree remaining","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"holding_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"account_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"status":{"type":"string","enum":["open","paid"]},"original_principal_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"remaining_principal_cents":{"type":"integer","minimum":0,"maximum":9007199254740991},"annual_rate":{"anyOf":[{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},{"type":"null"}]},"payment_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"payments_per_year":{"type":"integer","minimum":1,"maximum":52}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated loan. A remaining change appends a balance event noted as agreed with the lender.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a loan and its balance events","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/loans/{id}/balances":{"get":{"summary":"Payment history for one loan (newest first, cap 100)","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ balances[] }` — remaining, payment, interest, principal, as_of.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/loans/{id}/payments":{"post":{"summary":"Record a payment: split from the terms, update remaining","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"amount_cents":{"type":"integer","minimum":1,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["amount_cents"],"additionalProperties":false}}}},"responses":{"201":{"description":"The loan after the split. `next_payment.estimated` is always true — fees exist. This is not a bank payment and does not write an entry. File the transaction separately. PATCH remaining to agree with the lender.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/loans/{id}/pay-from-account":{"post":{"summary":"Pay the loan from an account: write the outflow and the estimated split","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"account_id":{"type":"string","minLength":1,"maxLength":64},"amount_cents":{"type":"integer","minimum":1,"maximum":9007199254740991},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]}},"required":["account_id","amount_cents","date"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ loan, outflow }`. One transaction on the paying account, filed on arrival, plus the estimated interest/principal split. Not a transfer — a loan is not an account (adr 0044). Use POST .../payments when the bank row already exists.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/aim":{"get":{"summary":"What this budget is for, plus this month's progress","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ aim }` or `{ aim: null }`. The aim is the declared want (retire in a year, a nest egg, a monthly set-aside). Progress is the picture: accounts + holdings − loans. Crypto and equity compound; a house sits; loans interpolate remaining (adr 0042). Mintro never schedules the contribution.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"put":{"summary":"Set or replace what you want this budget to do","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["retirement","house","custom"]},"label":{"type":"string","minLength":1,"maxLength":80},"want":{"anyOf":[{"type":"string","maxLength":280},{"type":"null"}]},"target_year":{"anyOf":[{"type":"integer","minimum":2026,"maximum":2100},{"type":"null"}]},"nest_egg_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"monthly_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"assumed_return":{"anyOf":[{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},{"type":"null"}]},"envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The aim with this month's progress and, when `target_year` is set, interpolation of the pile (`assumed_return` percent per year, 0% if omitted). `monthly_cents` finds or creates the contribution envelope with a monthly target — remaining-vs-target is then the reflection. Skip any field to keep the previous value. `envelope_id: null` unlinks.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Clear the aim; the contribution envelope stays","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/aim/preview":{"post":{"summary":"See what this plan would do, without writing it","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"kind":{"type":"string","enum":["retirement","house","custom"]},"label":{"type":"string","minLength":1,"maxLength":80},"want":{"anyOf":[{"type":"string","maxLength":280},{"type":"null"}]},"target_year":{"anyOf":[{"type":"integer","minimum":2026,"maximum":2100},{"type":"null"}]},"nest_egg_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"monthly_cents":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"assumed_return":{"anyOf":[{"type":"string","pattern":"^(0|[1-9]\\d*)(\\.\\d{1,12})?$"},{"type":"null"}]},"envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The same shape as GET /aim, computed from the draft plus the current picture (accounts, holdings, loans). Does not create the aim, the envelope, or an assignment. PUT /aim applies.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/health":{"get":{"summary":"Liveness plus instance feature flags","security":[],"parameters":[],"responses":{"200":{"description":"`{ ok, service, bank_linking }`. `bank_linking` is false when no bank can be linked on this instance, in which case use POST /v1/transactions/import."},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/openapi.json":{"get":{"summary":"This document","security":[],"parameters":[],"responses":{"200":{"description":"The OpenAPI 3.1 document."},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/llms.txt":{"get":{"summary":"Plaintext orientation for agents","security":[],"parameters":[],"responses":{"200":{"description":"A short plaintext briefing: what Mintro is, how auth works, and the jobs."},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/plans":{"get":{"summary":"Every plan, its price and its included bank connections","security":[],"parameters":[],"responses":{"200":{"description":"`{ plans[], free_limits }`. The one plan table (adr 0009): the app, the pricing page and any agent read the same bytes. `connections_included: null` means unlimited. The free tier is limited by manual ACCOUNTS, not by how many transactions you import."},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/me":{"get":{"summary":"Who this key is: scopes, budgets, plan limits, rate limit","description":"Requires scope: `budget:read`.","parameters":[],"responses":{"200":{"description":"`{ key: { principal, scopes, subject_kind }, tenant: { tenant_id, name, kind }, budgets: [{ id, name, status, url }], default_budget_id, plan: { id, label, price, connections_included, connections_allowed }, limits: { ... }, features: { bank_linking_available }, endpoints: { api, mcp, openapi, llms } }`. Call this first: it is the cheapest way to learn what the key may do before attempting it, and it is where the budget ids come from. A budget is the top-level container and an account may hold several; `default_budget_id` is set only when exactly one is open (a closed budget never counts against it), and it always names what the literal `default` will resolve to.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets":{"get":{"summary":"Every budget on the account","description":"Requires scope: `budget:read`.","parameters":[],"responses":{"200":{"description":"`{ budgets: [{ id, name, status, created_at }] }`. A budget is the top-level container — its own envelopes, entries and papers. An account may hold as many as it likes: budgets are never limited by the plan, which gates bank connections only.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Start a new budget","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"The new budget. 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.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}":{"get":{"summary":"One-call budget snapshot","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ budget, envelopes[], budget_total_cents, inbox: { count, total_cents }, connections[], papers[], holdings[], holdings_value_cents, aim }`. Envelope numbers come from caches; 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, papers, holdings and the aim are this budget's.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["budget_ambiguous"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Rename a budget, or close/reopen it","description":"Requires scope: `budget:write`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["open","closed"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The updated budget. A closed budget stays readable and exportable but rejects writes — the way to retire last year without deleting it.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/budgets/{budget_id}/export":{"get":{"summary":"One budget as a single JSON document","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"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, papers with full contents, holdings with their valuation events, the aim, and connection metadata without tokens. The portability guarantee: the plan and the spend, not just the spend.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["budget_ambiguous"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/inbox":{"get":{"summary":"Unfiled, unexpired transactions, newest first","description":"Requires scope: `budget:read`.","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"`{ transactions[], next_cursor }`. Unfiled transactions expire after ~45 days.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transactions":{"get":{"summary":"Stored transactions: the register","description":"Requires scope: `budget:read`.","parameters":[{"name":"account_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"since","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"filed","in":"query","required":false,"schema":{"type":"string"}},{"name":"include","in":"query","required":false,"schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string"}},{"name":"dir","in":"query","required":false,"schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"`{ transactions[], next_cursor }` — filed and unfiled together, only what Mintro stores. Default order is transaction `date` descending (a register, not an insert log); `sort=date|payee|amount` and `dir=asc|desc` change that. `payee` sorts `lower(coalesce(merchant_name, name))`. Cursor is keyset on the active sort plus id. `filed=true|false|any` narrows (default any); `q` matches name or merchant; `from`/`to` bound the transaction date; `include=entries` attaches each row's entries in one extra read. Every row carries `cleared`, so you can see what a statement has already accounted for without guessing — pair it with `account_id` to work an account down against its statement. A transfer leg carries `transfer_id` (adr 0023): delete the pair, never one side. `origin` is bank | recurring | import | transfer | income | opening: `recurring` is a bill you entered, and that is what `POST /v1/transactions/{id}/match` absorbs into a later bank arrival.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transactions/import":{"post":{"summary":"Manual import: the agent brings normalized transactions (spending negative); idempotent per identical row","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"account":{"type":"string","minLength":1,"maxLength":100},"transactions":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"name":{"type":"string","minLength":1,"maxLength":300},"merchant_name":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Z]{3}$"},"external_id":{"type":"string","minLength":1,"maxLength":200}},"required":["date","amount_cents","name"],"additionalProperties":false}}},"required":["transactions"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ imported, skipped, skipped_rows[] }`. `skipped_rows` names the rows that collided with an existing transaction, so a re-import of the same export is safe and legible.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"402":{"description":"`plan_limit` — The tenant's plan does not allow this. Report the limit to the human and let them choose to upgrade at https://mintro.money/pricing; do not retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["plan_limit"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transactions/{id}":{"patch":{"summary":"Edit a manually entered transaction","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"name":{"type":"string","minLength":1,"maxLength":300},"merchant_name":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"`{ updated, needs_reconciliation, touched_envelope_ids }`. Manual rows only — a bank-synced transaction is a record of what happened at the bank and returns 409 not_editable. If a new amount no longer covers what is already filed against it, entries are NOT truncated: the row is flagged needs_reconciliation and you decide.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`not_editable` — Only manually entered transactions can be edited — a bank-synced row is a record of what happened at the bank, and Plaid would re-sync it anyway. Unfile it if it is misfiled, or correct it at the bank.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","not_editable"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a manually imported transaction","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true, entries_deleted }`. Only transactions on manual accounts — the agent's own imports — can be deleted; filed entries come off with it and envelope totals recompute. A bank-synced transaction is a fact from the feed: 409 not_deletable (unfile it, or close the connection).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`not_deletable` — Only manually imported transactions and manual accounts can be deleted — a bank-synced transaction is a fact from the feed. Unfile it (delete its entries) if it is misfiled, or close the connection to stop the feed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","not_deletable"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"get":{"summary":"Transaction with its entries","description":"Requires scope: `budget:read`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The transaction plus `entries[]`, which is empty when it is still unfiled.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transactions/{id}/match":{"post":{"summary":"This bank row is the bill I already entered","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"with_transaction_id":{"type":"string","minLength":1}},"required":["with_transaction_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"`{ bank_transaction_id, deleted_transaction_id, needs_reconciliation }`. `enter_recurring` writes a real transaction so the budget is true on the day you pay; the bank then delivers the same payment. Without this call you have spent it twice. Mintro does not guess the pair (§13): you name the other id (`with_transaction_id`). One side must be `origin: recurring`, the other a bank row, same account, same sign. Entries move onto the bank row through the one write path; the entered row is deleted. Amounts that differ flag `needs_reconciliation` rather than silently resizing entries. Order of the two ids does not matter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts":{"post":{"summary":"Add an account by hand","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"type":{"type":"string","enum":["checking","savings","credit","cash"]},"starting_balance_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"as_of":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"budget_id":{"type":"string"}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"The created account. Until this existed an account could only appear as a side effect of importing into it, so there was no way to start the way every budgeting app starts. `starting_balance_cents` creates a transaction dated `as_of` and leaves it UNFILED in the inbox on purpose: for a chequing account it is money to budget, for a card it is a debt, and Mintro does not decide which. `type` accepts checking | savings | credit | cash — use `credit` to get card payment envelopes (adr 0010). Manual accounts count against the free plan limit.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"402":{"description":"`plan_limit` — The tenant's plan does not allow this. Report the limit to the human and let them choose to upgrade at https://mintro.money/pricing; do not retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["plan_limit"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"get":{"summary":"Every account on the tenant","description":"Requires scope: `budget:read`.","parameters":[],"responses":{"200":{"description":"`{ accounts: [{ id, name, type, subtype, mask, connection_id, institution, manual, transaction_count, working_balance_cents, payment_envelope_id, low_cents, ok_cents }] }`. `working_balance_cents` is everything the account holds, cleared or not — one call, rather than a reconciliation read per account. On a credit account it is NEGATIVE when money is owed. `manual: true` accounts are hand-entered or imported and count against the free plan's two-account allowance; bank accounts come from connections. `payment_envelope_id` on a credit account is the envelope holding its payment money (adr 0010, 0025). `low_cents` is the cashflow warn-below (adr 0035); default 0 means overdrawn. `ok_cents` is enough remaining (adr 0036); null means no middle band.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transfers":{"post":{"summary":"Move money between the person's own accounts","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"from_account_id":{"type":"string","minLength":1},"to_account_id":{"type":"string","minLength":1},"amount_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["from_account_id","to_account_id","amount_cents","date"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ transfer_id, from_transaction_id, to_transaction_id, amount_cents, date, payment_envelope_id }`. Writes BOTH legs in one database transaction and links them by `transfer_id`. **A transfer is not spending**: it never enters the inbox, never files into an envelope, and does not move ready-to-assign — the money was already yours and already had whatever job you gave it. Only the two account balances change. **The one exception is paying a credit card** (adr 0025): when the destination is a card with a payment envelope, the outgoing leg is FILED against it, spending the reservation those purchases created, and `payment_envelope_id` says so. Two hand-entered legs do not do this and the payment envelope would grow forever. `amount_cents` is positive; the direction is `from_account_id` and `to_account_id`. Do NOT record a transfer as two ordinary transactions: they would count as real income and real spending and would make age of money read younger than it is.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transfers/confirm":{"post":{"summary":"These two existing rows are a transfer — you said so","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"transaction_ids":{"type":"array","prefixItems":[{"type":"string","minLength":1},{"type":"string","minLength":1}],"items":false,"minItems":2,"maxItems":2}},"required":["transaction_ids"],"additionalProperties":false}}}},"responses":{"201":{"description":"`{ transfer_id, from_transaction_id, to_transaction_id, amount_cents, date, payment_envelope_id }`. The bank delivers both legs as ordinary unfiled transactions with no link. Mintro does not guess the pair (§13): you name both ids. They must be opposite signs, the same magnitude, two different accounts, neither already a transfer, neither filed. They leave the inbox. Paying a card still files the outgoing leg (adr 0025).","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/transfers/{id}":{"delete":{"summary":"Remove both legs of a transfer","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted }`, always 2. The pair is the unit: deleting one side leaves money that arrived from nowhere. Returns 409 not_deletable if either leg has been filed into an envelope — unfile it first, because filing it was a deliberate decision to treat it as spending.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`not_deletable` — Only manually imported transactions and manual accounts can be deleted — a bank-synced transaction is a fact from the feed. Unfile it (delete its entries) if it is misfiled, or close the connection to stop the feed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","not_deletable"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/age-of-money":{"get":{"summary":"How long the money you are spending sat here first","description":"Requires scope: `budget:read`.","parameters":[],"responses":{"200":{"description":"`{ days, sample_size, reason, as_of }`. YNAB's signature metric, computed on read and stored nowhere (adr 0022): inflows are queued oldest-first and each outflow is paid from the oldest money still unspent, so every dollar spent carries the age it had when it left; `days` averages that over the last 10 outflows. **`days` is null when the answer would be a guess** — `reason` is then `no_outflows` (nothing has been spent yet) or `not_enough_inflow_history` (the 400-day window does not contain enough income to cover what went out, so the queue ran dry and any number would read falsely young). Transfers recorded through POST /v1/transfers are excluded. Two ordinary transactions that happen to be a transfer still look like spending.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/cashflow":{"get":{"summary":"Projected cash: posted history plus declared recurrences","description":"Requires scope: `budget:read`.","parameters":[{"name":"account_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"until","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ account_id, from, until, today, working_balance_cents, lowest_cents, lowest_on, days_below, next_below_on, cause, points, accounts }`. Omit `account_id` for every account summed into one line. Past days walk posted transactions; future days walk upcoming bills and income. Window is thirty days before today and thirty after; earlier `from` and later `until` are clamped. Each point has `ratio` (0..1) and `tone` (overspent|underfunded|funded) so a screen can draw without money arithmetic. A READ: nothing is entered and next_due does not move. All-accounts colours a day overspent if ANY account is at or below its own low_cents.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/streaks":{"get":{"summary":"Clerk-work streaks, computed on read, off until asked","description":"Requires scope: `budget:read`.","parameters":[{"name":"budget_id","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ mode, ahead, funded, agreed, filed, prompt }`. Off (the default) returns nulls and computes nothing. Quiet and on share the numbers: `ahead` is consecutive months age of money held or rose (the headline, adr 0031); `funded` is months every open envelope had what it needed; `agreed` is months every account with activity was reconciled; `filed` is 0 while the inbox has a row and the repaired empty window once it does not — Mintro does not store a midnight snapshot, so a previous filing run is not reconstructed. `prompt` is set only in on, and only when there is something to file. `?budget_id=` picks the budget funded is walked for; omit it on a single-budget account.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"patch":{"summary":"Set streaks to off, quiet, or on","description":"Requires scope: `budget:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mode":{"type":"string","enum":["off","quiet","on"]}},"required":["mode"],"additionalProperties":false}}}},"responses":{"200":{"description":"`{ mode }`. Off stops computing. There are no stored counts to delete: streaks are a bounded read (adr 0031), the same shape as age of money.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts/{id}/reconciliation":{"get":{"summary":"Where this account stands against the bank","description":"Requires scope: `budget:read`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ cleared_balance_cents, cleared_count, uncleared_count, uncleared_total_cents, working_balance_cents, last_reconciled_at, last_statement_balance_cents }`. Cleared rows are what a statement balance is compared to; the uncleared ones are where a difference hides. `working_balance_cents` is everything the account holds, agreed with the bank or not — the figure a person means by \"how much is in there\".","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts/{id}/cleared":{"patch":{"summary":"Tick transactions on or off the cleared list","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"transaction_ids":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"cleared":{"type":"boolean"}},"required":["transaction_ids","cleared"],"additionalProperties":false}}}},"responses":{"200":{"description":"`{ updated }`. All-or-nothing: if any id belongs to another account it is 404 and nothing changes.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts/{id}/forecast":{"get":{"summary":"Project this account's cash through a horizon","description":"Requires scope: `budget:read`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"until","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"`{ account_id, until, working_balance_cents, low_cents, lowest_cents, lowest_on, starts_below, below, events }`. A read of working balance plus every occurrence of this account's active recurring rows until `until` (YYYY-MM-DD, default last day of this month). Weekly bills expand; Upcoming stays one row per declaration. Same calendar day applies income first. Does NOT write a transaction and does NOT move `next_due`. `below` is each hit after which the running total is at or below `low_cents`. `lowest_on` is null when the low point is the starting working balance.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts/{id}/reconcile":{"post":{"summary":"Compare cleared rows against a statement, and lock if they agree","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"statement_balance_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"statement_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}(-\\d{2})?$"},{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}]}},"required":["statement_balance_cents","statement_date"],"additionalProperties":false}}}},"responses":{"200":{"description":"`{ reconciled, difference_cents, cleared_balance_cents, statement_balance_cents, reconciliation_id }`. A mismatch is 200 with `reconciled: false` — that is the useful answer, and the difference is exactly what has not been entered yet. Mintro will NOT create an adjustment row to force agreement; papering over a difference is how a budget quietly stops being true.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/accounts/{id}":{"patch":{"summary":"Point a credit account at its payment envelope, and/or set the forecast warn-below","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"budget_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"payment_envelope_id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"low_cents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ok_cents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"`{ account_id, payment_envelope_id, low_cents, ok_cents }`. Credit-card payment categories (adr 0010): once set, spending on this card sets aside the money to pay it — the funded portion of each purchase is reserved in that envelope, so the statement never surprises you. Only the funded portion moves; the rest shows as overspending. Pass null to unlink. `?budget_id=` picks the budget the envelope belongs to. `low_cents` (adr 0035) is when cashflow should warn; default 0 is overdrawn. `ok_cents` (adr 0036) is enough remaining; null drops the middle band. Setting either does not need a budget.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Delete a manual account and everything imported into it","description":"Requires scope: `budget:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true, transactions_deleted, entries_deleted }`. Every transaction in the account goes, filed entries come off and envelope totals recompute. Bank accounts cannot be deleted — 409 not_deletable — close the connection instead.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.\n\n`not_deletable` — Only manually imported transactions and manual accounts can be deleted — a bank-synced transaction is a fact from the feed. Unfile it (delete its entries) if it is misfiled, or close the connection to stop the feed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict","not_deletable"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/connections":{"get":{"summary":"Linked banks","description":"Requires scope: `connections:read`.","parameters":[],"responses":{"200":{"description":"`{ connections[] }` — id, institution, status, `last_synced_at`, accounts.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/connections/link":{"post":{"summary":"Hosted Plaid link URL — hand it to the human; only they authenticate","description":"Requires scope: `connections:write`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"201":{"description":"`{ hosted_link_url, expires_at }`. Give the URL to the human and wait; the connection appears in GET /v1/connections once they finish.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"402":{"description":"`plan_limit` — The tenant's plan does not allow this. Report the limit to the human and let them choose to upgrade at https://mintro.money/pricing; do not retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["plan_limit"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}},"503":{"description":"`bank_linking_unavailable` — Bank linking is not available on this instance. Have the human export transactions from their bank and use POST /v1/transactions/import instead.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["bank_linking_unavailable"]},"message":{"type":"string"}}}}}}}}}}},"/v1/connections/{id}/relink":{"post":{"summary":"Hosted link URL in update mode","description":"Requires scope: `connections:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"201":{"description":"`{ hosted_link_url, expires_at }` for repairing a `reauth_required` connection.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}},"503":{"description":"`bank_linking_unavailable` — Bank linking is not available on this instance. Have the human export transactions from their bank and use POST /v1/transactions/import instead.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["bank_linking_unavailable"]},"message":{"type":"string"}}}}}}}}}}},"/v1/connections/{id}":{"delete":{"summary":"Remove the bank; the budgets and filed transactions survive","description":"Requires scope: `connections:write`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ closed: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/webhook-endpoints":{"post":{"summary":"Register an endpoint (secret returned once)","description":"Requires scope: `webhooks:manage`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","maxLength":2000,"format":"uri"},"events":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","minLength":1}}},"required":["url","events"],"additionalProperties":false}}}},"responses":{"201":{"description":"The endpoint plus `secret`, which appears in this response and never again. Store it; it signs every delivery.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"400":{"description":"`validation_failed` — The request body or query is malformed. The message names the offending field; fix it and retry.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["validation_failed"]},"message":{"type":"string"}}}}}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}},"get":{"summary":"List endpoints with their delivery health","description":"Requires scope: `webhooks:manage`.","parameters":[],"responses":{"200":{"description":"`{ endpoints[] }` — each with `active`, `consecutive_failures`, `last_delivery_at`, `last_status` (the receiver's HTTP status, or null when the attempt never got one), `last_latency_ms` and `last_error`. This is how you check whether a test or a real event actually landed.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/webhook-endpoints/{id}":{"delete":{"summary":"Delete an endpoint","description":"Requires scope: `webhooks:manage`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ deleted: true }`.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}},"/v1/webhook-endpoints/{id}/test":{"post":{"summary":"Queue a ping event and show exactly what will be delivered","description":"Requires scope: `webhooks:manage`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque key, max 200 chars. The first request with a key executes and its response is stored; a replay with the same key AND an identical body returns that stored response verbatim with Idempotency-Replayed: true. A different body under the same key is 409 idempotency_conflict, as is a replay while the first request is still in flight. Only successful responses are stored, so a corrected retry after a 4xx runs for real. Keys expire after 24h.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"`{ queued: true, event_id, payload, signature: { header, format, signed_string } , verify: { ... }, check_delivery }`. Delivery is asynchronous (Inngest, with retries), so the response cannot carry the receiver's answer: poll GET /v1/webhook-endpoints and read `last_status`, `last_latency_ms` and `last_error` for that outcome.","headers":{"RateLimit-Limit":{"description":"Requests allowed per key in the current 60s window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window. Pace on this rather than on 429s.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}}},"401":{"description":"`unauthenticated` — The bearer token is missing, invalid, revoked, or not mapped to a tenant. Ask the human for a fresh key from https://app.mintro.money.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated"]},"message":{"type":"string"}}}}}}}},"403":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["forbidden"]},"message":{"type":"string"}}}}}}}},"404":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"}}}}}}}},"409":{"description":"`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.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["idempotency_conflict"]},"message":{"type":"string"}}}}}}}},"429":{"description":"`rate_limited` — Too many requests. Wait the number of seconds in Retry-After, and use the RateLimit-Remaining header to pace subsequent calls.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["rate_limited"]},"message":{"type":"string"}}}}}}}},"500":{"description":"`internal` — A fault on Mintro's side. Retry once after a short pause; if it persists, stop.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["internal"]},"message":{"type":"string"}}}}}}}}}}}}}