Skip to content

Outstanding scheduled occurrences for one or more accounts

GET
/bills/scheduled
curl --request GET \
--url 'https://api.tovarifinancial.com/bills/scheduled?includeMirrors=true' \
--header 'Authorization: Bearer <token>'
accountId
string format: uuid

A single account id. MUTUALLY EXCLUSIVE with accountIds — exactly one of the two must be present; neither is a 400 and both is a 400. This path is unchanged in every particular, including its 404 when the account does not resolve and its deliberate lack of any is_active or account-type filter (a CLOSED account answers 200).

accountIds
string

Comma-separated account UUIDs — the multi-account scope (KAN-2913). MUTUALLY EXCLUSIVE with accountId. At most 10 ids; an over-cap list is a 400 whose field error is DISTINCT from the malformed-id error, so a client can say “too many accounts selected”. workingBalanceCents is the SUM of the resolved accounts’ balances and the occurrences of all of them are merged into one list sorted by effective date, then income before outflow on the same date (KAN-3160), then bill id. A foreign or non-existent id is silently dropped — it contributes zero and is simply absent from the accountIds echo, with no error or status distinguishing the two, because doing so would be an existence oracle. A list that resolves to nothing is a 200 with accountIds: [] and no occurrences, NEVER a 404. No account-type filter is applied: the endpoint honours the ids it is given.

from

Calendar date, ‘YYYY-MM-DD’

string

Window start ‘YYYY-MM-DD’. Honoured ONLY on a purely-past window (a to earlier than today). On every forward-looking window it is ignored and the lower bound stays each bill’s own settle-through cursor, which is what keeps the OVERDUE backlog on screen.

to

Calendar date, ‘YYYY-MM-DD’

string

Window end ‘YYYY-MM-DD’. INERT on a forward-looking window — it is neither obeyed, nor extended on, nor validated there: whatever it says, the far edge stays the end of the current month unless forwardTo moves it. This is deliberate. to is the REGISTER’s ledger filter, an unbounded date input whose value two shipped presets resolve to TODAY and which means “ledger rows up to”; obeying it would delete the rest of the month from the caller’s Scheduled Bills section, extending on it would silently pull months of occurrences and a far-edge projection into that section, and refusing an out-of-range value would make the section disappear behind a 400. A value EARLIER than today is a purely-past window and keeps its existing [from, to] behaviour. Use forwardTo to widen the forward window; whatever is resolved is reported back in windowEndIso.

forwardTo

Calendar date, ‘YYYY-MM-DD’

string

The explicit forward bound ‘YYYY-MM-DD’ (opt-in; no shipped client sends it). EXTENSION-ONLY: the far edge is max(forwardTo, end of the current month), so it can push the window OUT but can never shorten it below end-of-month — narrowing this section would need its own param and its own review. Supplying it makes the request forward-looking even alongside a purely-past to. A value beyond the forward bound — end of the month MAX_FUTURE_MONTHS ahead, the same bound /reports/cash-flow uses — is a 400 on forwardTo naming the bound; so is a malformed date. Neither is ever clamped, and neither message echoes the value sent. Knock-ons of a far edge LATER than end-of-month, all documented and all deliberate: the top-level projectedBalanceCents becomes the balance at that far edge, totalDueCents grows, and the occurrence list grows with it (the statement count does not — this is one account). The overdue backlog is unaffected: windowStartIso stays null on every forward window.

search
string

Optional free-text term. It narrows the RETURNED occurrences (and totalDueCents) to bills whose name, memo, payee name, any occurrence note, or amount magnitude (only when the term contains a digit) matches — per BILL, so every occurrence of a matching bill is returned. The projected balance walk, the bank-match assignment, workingBalanceCents and projectedBalanceCents all run over the FULL outstanding set before the filter, so a searched request never changes the balance line. The term is never echoed or logged.

includeNextOutstanding
string

Opt-in (KAN-3398; sent by the web account register only). ‘1’ appends each active bill’s NEXT outstanding occurrence when it falls after windowEndIso — at most one row per bill, after every in-window row, in effective-date order (income before outflow, then bill id). It applies on every window, including a purely-past one. Those rows carry beyondWindow: true, are never bank-matched (pendingMatch/outOfBandMatch null, ambiguousMatches empty), step the projected balance by ZERO (their projectedBalanceCents is the last in-window running balance) and are EXCLUDED from totalDueCents; their outflow total ships separately as beyondWindowDueCents. Every other field keeps exactly its unflagged value. Absent, ‘’ or ‘0’ leaves it off and the body is unchanged (no beyondWindow, no beyondWindowDueCents). Any other value is a 400 VALIDATION_ERROR with fieldErrors.includeNextOutstanding = [‘includeNextOutstanding must be 1 or 0’], refused before a connection is opened and never echoing the value. It adds no database statement.

includeMirrors
string
Allowed values: true false

Opt-in (KAN-3670). ‘true’ also returns, on each scoped account a bill’s destination names (any account type since KAN-3683 — a card or loan it pays, a checking or savings account it transfers into), the READ-ONLY MIRROR of every outstanding occurrence of an active bill scheduled FROM an account outside the scope whose destination account is that account: accountId is the destination, amountCents and effectiveAmountCents are the source occurrence’s figures negated, direction is flipped, and mirrorOf names the bill and its source account. A mirror steps projectedBalanceCents by the money arriving (a card or loan toward zero, a savings or checking balance up; by zero when the source occurrence is skipped), is never bank-matched, and is excluded from totalDueCents, truncatedDueCents and beyondWindowDueCents. Source-side exceptions (skip, amount, moved or declared date) show on the mirror. When the scope holds BOTH the source account and the destination, the occurrence appears ONCE, on the source, with no mirror. With ‘true’ every own occurrence carries mirrorOf: null. Absent, ‘’ or ‘false’ leaves it off: the body and every database statement are unchanged and no occurrence carries mirrorOf. Any other value is a 400 VALIDATION_ERROR with fieldErrors.includeMirrors = [‘includeMirrors must be true or false’], refused before a connection is opened and never echoing the value. It adds ONE database statement whenever the scope resolves to at least one account, of any type.

Scheduled occurrences + projection

Media typeapplication/json
object
data
required
object
accountId

The single account this request scoped to, or NULL on the accountIds path. The key is never omitted, so a client reading it unconditionally still parses.

string
nullable
accountIds

The accounts the request ACTUALLY resolved to, derived from the rows the balance read returned — never echoed back from the parsed query string, which would confirm the existence of an id the caller merely guessed. A one-element array on the accountId path; possibly EMPTY on the accountIds path, which is a normal 200.

Array<string>
workingBalanceCents

Integer minor units (cents)

integer format: int64
projectedBalanceCents

The working balance walked forward through every in-window occurrence — the balance at windowEndIso. MEANINGFUL IN accountId MODE ONLY: on the accountIds path it is seeded from the SUM of mixed-sign balances (a checking balance plus a negative card balance), which is no single account’s balance, so a client must not render it (nor the per-occurrence projectedBalanceCents) in all-accounts mode. GET /accounts/{id}/balance-projection draws the same walk as a daily line for one account (KAN-3658).

integer format: int64
totalDueCents

Sum of the OUTFLOW magnitudes of the occurrences ACTUALLY RETURNED in occurrences — a footer total describes the rows beneath it. It therefore follows BOTH narrowings: the search filter and the overdue-backlog slice below. projectedBalanceCents does NOT — that is an account forecast over the full outstanding set. Income occurrences are not “due” and contribute nothing, and neither does a skipped occurrence (KAN-2913) — it is returned in the list but excluded from this figure.

integer format: int64
occurrences
Array<object>
object
billId
string
accountId
required

Which account this occurrence belongs to. ALWAYS emitted, including on the single-account path where it is a constant — a merged multi-account list is otherwise unrenderable.

string
destinationAccountId
required

KAN-3661. The bill’s destination account (bills.destination_account_id), or null when it has none. ALWAYS emitted, on in-window, beyond-window and mirror rows alike. On a mirror row it is still the bill’s destination (the mirror’s own accountId), so a client reads a row’s other side as mirrorOf?.sourceAccountId ?? destinationAccountId.

string
nullable
autoEnterMode
required

KAN-3748. The occurrence’s bill’s Auto-enter setting (a mirror row carries its source bill’s). ask_first means a payment found for this occurrence is offered and never entered unattended. ALWAYS emitted.

string
Allowed values: automatic ask_first
name
string
memo
string
nullable
payeeId
string
nullable
dueDate

Calendar date, ‘YYYY-MM-DD’

string
amountCents

Integer minor units (cents)

integer format: int64
direction
string
Allowed values: expense income
frequency
string
Allowed values: once monthly quarterly weekly biweekly twice-monthly annually
budgetCategoryId
string
nullable
categoryName
string
nullable
categoryColorId
string
nullable
sectionName
string
nullable
isOverdue
boolean
projectedBalanceCents

The running balance after this occurrence, walked in display order (effective date, income before outflow, bill id). An occurrence already in the register (registerReflection non-null, KAN-3762) or skipped steps by ZERO; every other occurrence — overdue, due today, future, marked scheduled — steps once (projectionStepCents). Meaningful in accountId mode only (see the top-level field).

integer format: int64
accountProjectedBalanceCents

PRESENT ONLY in accountIds mode — then on every in-window occurrence (KAN-3761). The projected balance of this occurrence’s OWN account (accountId) after it: that account’s opening balance walked through its own rows by the same rule as projectedBalanceCents, plus the money arriving from a scheduled transfer out of another account in the scope. It equals the projectedBalanceCents that account’s own accountId read (with includeMirrors=true) returns for the same row. Absent in accountId mode and on beyondWindow rows.

integer format: int64
pendingMatch

TIER 1. ALWAYS emitted — null when no pending bank row covers this occurrence. pendingMatch !== null is exactly occurrenceStatus === 'payment_pending'.

object
transactionId
required
string
amountCents
required

SIGNED ledger cents, passed through exactly as stored.

integer format: int64
transactionDate
required

Calendar date, ‘YYYY-MM-DD’

string
description
required

The register’s display text — transactions.description, never bank_description.

string
outOfBandMatch

TIER 2. ALWAYS emitted — null when there is none. Non-null implies pendingMatch is null (the tiers are mutually exclusive by construction). It does NOT widen occurrenceStatus: a row carrying only this still renders as upcoming/overdue.

object
transactionId
required
string
amountCents
required

SIGNED ledger cents, passed through exactly as stored.

integer format: int64
transactionDate
required

Calendar date, ‘YYYY-MM-DD’

string
description
required

The register’s display text — transactions.description, never bank_description.

string
status
required

The bank row’s own status, so a confirmed Enter claims the row with the value it was offered under.

string
Allowed values: cleared pending
reason

WHICH ROUTE offered the row. Always emitted by the server, but deliberately NOT required: a client that does not recognise the value must keep rendering its existing copy rather than discarding the match.

string
Allowed values: amount_differs network_descriptor exact_amount_unique declared_date_float
ambiguousMatches

TIER 3. ALWAYS emitted — an EMPTY array (never null, never absent) when the occurrence is not ambiguous. Non-empty implies occurrenceStatus === 'match_ambiguous' and that both other tiers are empty.

Array<object>
object
transactionId
required
string
amountCents
required

SIGNED ledger cents, passed through exactly as stored.

integer format: int64
transactionDate
required

Calendar date, ‘YYYY-MM-DD’

string
description
required

The register’s display text — transactions.description, never bank_description.

string
status
required

The bank row’s own status, so a confirmed Enter claims the row with the value it was offered under.

string
Allowed values: cleared pending
registerReflection

KAN-3762 — WHY this occurrence is already in the register, or null. pending_match: the tier-1 pending bank row (pendingMatch). payment_found: any other assigned bank row — a cleared “Payment found” not yet settled, or a pending row only the attended ask admitted. tied_cover: a tie the server would not break, with enough unclaimed tied rows to cover this occurrence one-for-one (the earliest occurrences first). manual_entry: a hand-typed register row at exactly this occurrence’s amount, inside the attended date window, on or after the cursor, nearest to this occurrence. A reflected occurrence’s money is already in workingBalanceCents, so it steps the projected balance by ZERO. ALWAYS emitted by this server (explicit null when none), always null on a mirror row. It never changes occurrenceStatus (payment_pending ⇔ pendingMatch !== null still holds) or totalDueCents, and nothing is settled or claimed on the strength of it. Older servers omit the key; a client must tolerate an unknown future member.

string
nullable
Allowed values: pending_match payment_found tied_cover manual_entry
projectionStepCents

KAN-3762 — EXACTLY the delta the projected-balance walk added for this row: 0 for a skipped or reflected row and for every beyondWindow row, otherwise effectiveAmountCents. On a single-account read it equals this row’s projectedBalanceCents minus the previous row’s (the first row’s previous is workingBalanceCents). Clients render it; they never re-derive it.

integer format: int64
manualEntryMatch

KAN-3764 — the HAND-TYPED register row covering this occurrence, non-null EXACTLY when registerReflection === 'manual_entry'; explicit null otherwise and always null on a mirror row. Ids and signed cents only — no description, payee or memo. POST /bills/{id}/enter on such an occurrence answers 409 BILL_MANUAL_ENTRY_UNCONFIRMED until the caller names this row in settleAgainstTransactionId or sends postWithoutSettling: true, so a client can open that confirm before the click. Older servers omit the key; read a missing key as null.

object
transactionId
required
string format: uuid
transactionDate
required

Calendar date, ‘YYYY-MM-DD’

string
amountCents
required

SIGNED ledger cents, exactly as stored.

integer format: int64
enterBlockedByDate

KAN-3762 — the earliest EARLIER original date of this bill in [cursor, dueDate) still outstanding and not exception-skipped — the occurrence to Enter or Skip first — or null when none (always null for a NULL-cursor bill and on a mirror row). The SAME rule POST /bills/{id}/enter and POST /bills/{id}/skip refuse with 400 BILL_EARLIER_OCCURRENCE_OUTSTANDING.

string
nullable
occurrenceStatus

The render discriminator, ALWAYS emitted. payment_pending ⇔ pendingMatch !== null, else match_ambiguous ⇔ ambiguousMatches is non-empty, else marked_scheduled ⇔ scheduledMark !== null, else overdue ⇔ isOverdue and not skipped (KAN-3365 — a skipped occurrence is never rendered late; isOverdue keeps its date meaning), else upcoming.

string
Allowed values: upcoming overdue payment_pending match_ambiguous marked_scheduled
scheduledMark
object
scheduledPaymentDate
required

Calendar date, ‘YYYY-MM-DD’

string
amountCents
required

Integer minor units (cents)

integer format: int64
effectiveDueDate

The occurrence’s effective date, resolved server-side as declaration → forecast → schedule (KAN-2926): scheduledMark ? its scheduledPaymentDate : forecastDate ?? this row’s own dueDate. dueDate keeps its own meaning and value.

string
effectiveAmountCents

SIGNED (income +, expense −), server-computed as declaration → forecast → schedule (KAN-2926). Equals the schedule’s own signed amount when no override is present. Never re-derive a sign on the client.

integer format: int64
note
required

The household’s own free text on THIS occurrence, or null (KAN-2913). ALWAYS emitted. A SIBLING of scheduledMark, never a member of it: a note asserts nothing about a payment. Spelled identically on GET /bills/scheduled and GET /bills/due; /bills/projection omits it, having no consumer that renders free text.

string
nullable
skipped
required

“Do not count this one” (KAN-2913). ALWAYS emitted. The row STAYS in the list and keeps its own amountCents and effectiveAmountCents; what changes is that it contributes a ZERO delta to projectedBalanceCents and is excluded from totalDueCents. It is not POST /bills/{id}/skip — no cursor moves and no payment history is written.

boolean
forecastDate
required

The FORECAST-ONLY date override on this occurrence, or null (KAN-2926). ALWAYS emitted. Outranked by scheduledMark when both are present. It moves presentation and the projected step only — never month bucketing, window membership or Safe to Spend.

string
nullable
forecastAmountCents
required

The FORECAST-ONLY amount override, as the POSITIVE MAGNITUDE it is stored as, or null (KAN-2926). ALWAYS emitted. The signed figure is effectiveAmountCents; never re-sign this on the client. Outranked by scheduledMark when both are present.

integer format: int64
nullable
beyondWindow

PRESENT ONLY when includeNextOutstanding=1 — then on EVERY occurrence (KAN-3398). false for an in-window row; true for an appended next-outstanding row past windowEndIso (see the includeNextOutstanding parameter).

boolean
mirrorOf

PRESENT ONLY when includeMirrors=true (KAN-3670) — then on EVERY occurrence: null on an own occurrence, and on a read-only mirror the bill and the account it is scheduled from. A mirror appears on each scoped account a bill’s destination names, of any account type (KAN-3683). A mirror is display-only: Enter, Skip and Mark-scheduled act on the bill from its source account.

object
billId
required

The mirrored bill — scheduled on the source account, not this one.

string format: uuid
sourceAccountId
required

The account the bill is scheduled from.

string format: uuid
truncatedCount

How many OVERDUE occurrences (dueDate < today) the server dropped from occurrences (KAN-80), 0 when none. A bill whose first payment is far in the past is capped to the most recent MAX_OVERDUE_OCCURRENCES overdue rows nearest today; occurrences due today or later are never truncated. projectedBalanceCents is computed over the full outstanding set and does not move when rows are dropped; totalDueCents DOES follow the returned rows, with the dropped remainder reported as truncatedDueCents.

integer
truncatedDueCents

Sum of the OUTFLOW magnitudes of the occurrences the overdue-backlog slice dropped, 0 when none. Emitted on EVERY response, so totalDueCents + truncatedDueCents always equals the total over the un-truncated set — that identity is what lets a client show “plus $X in N older hidden bills” without recomputing anything. Do not add it into the section total; the server has already excluded it.

integer format: int64
truncatedBeforeDate

The due date of the earliest RETURNED occurrence when the overdue backlog was truncated, else null.

string
nullable
windowStartIso

The lower bound actually applied, or null for “no lower bound beyond each bill’s own settle-through cursor”. NULL on every forward-looking window — including one widened by forwardTo — so the overdue backlog always surfaces; non-null only on a purely-past window, where it is the from that was sent.

string
nullable
windowEndIso

The upper bound actually applied, emitted on EVERY 200 including the default unqueried path. This is what makes the window rule observable: a caller whose forwardTo was floored to end-of-month — or one that sent only the inert to — reads exactly what it got rather than inferring it. On the default path it is the last day of the current month.

string
beyondWindowDueCents

PRESENT ONLY when includeNextOutstanding=1 (KAN-3398). The OUTFLOW magnitude of the beyondWindow: true occurrences (skipped ones excluded), summed by the same rule as totalDueCents, which those rows are excluded from. 0 when there are none.

integer format: int64
message
string
requestId
required
string
timestamp
required
string format: date-time
Example
{
"data": {
"occurrences": [
{
"autoEnterMode": "automatic",
"direction": "expense",
"frequency": "once",
"outOfBandMatch": {
"status": "cleared",
"reason": "amount_differs"
},
"ambiguousMatches": [
{
"status": "cleared"
}
],
"registerReflection": "pending_match",
"occurrenceStatus": "upcoming"
}
]
}
}

Validation failed — see errorCode / fieldErrors

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Missing, invalid, or expired bearer token

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Authenticated, but the tenant gate refuses the request until the caller resolves a precondition. errorCode is one of EMAIL_NOT_VERIFIED, POLICY_ACCEPTANCE_REQUIRED, or SUBSCRIPTION_REQUIRED, evaluated in exactly that order (contract term CCR-1: email verification first, then policy acceptance, then subscription — so a subscriber who has merely not re-accepted the current policies always sees POLICY_ACCEPTANCE_REQUIRED). A POLICY_ACCEPTANCE_REQUIRED body additionally carries details.outstanding (the policy versions still to accept) and details.firstAcceptance. The SUBSCRIPTION_REQUIRED arm is INERT unless the server-side BILLING_ENABLED flag is exactly the string true; while it is off, only the first two codes are reachable. Not retryable as sent — resolve the named condition, then resend.

Media typeapplication/json
object
errorCode
required

Which precondition refused the request. Evaluated in this order (CCR-1); SUBSCRIPTION_REQUIRED is unreachable while BILLING_ENABLED is not exactly true.

string
Allowed values: EMAIL_NOT_VERIFIED POLICY_ACCEPTANCE_REQUIRED SUBSCRIPTION_REQUIRED
message
required
string
details
object
outstanding
required

Active policy versions the user has not yet accepted.

Array<object>
object
id
required
string format: uuid
policyType
required
string
version
required
integer
title
required
string
firstAcceptance
required

True when the user has accepted no policy before — the client renders the new-signup screen rather than the re-acceptance one.

boolean
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "EMAIL_NOT_VERIFIED"
}

Internal error (no internal detail leaked)

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Service temporarily unavailable — errorCode is SERVICE_UNAVAILABLE. A TRANSIENT, RETRYABLE condition rather than a defect in the request: a query that exceeded its time budget, a lost or refused database connection, a saturated connection pool, or an outage at an upstream provider the request depends on (the authentication provider, or the bank data provider on a bank-connection operation). The identical request may succeed on retry — back off briefly and, on a write, resend the same X-Idempotency-Key where the operation accepts one.

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}