Preview how ONE hypothetical change to one occurrence moves the projection
const url = 'https://api.tovarifinancial.com/bills/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/occurrence-preview?forwardTo=example&occurrenceDate=example&skip=true&basis=cash-flow&includeMirrors=true';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.tovarifinancial.com/bills/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/occurrence-preview?forwardTo=example&occurrenceDate=example&skip=true&basis=cash-flow&includeMirrors=true' \ --header 'Authorization: Bearer <token>'READ-ONLY (KAN-3264): writes nothing and takes no idempotency key. Answers “what would moving, re-amounting or skipping THIS occurrence do to my Cash Flow projection?” with two summaries, current and hypothetical, computed by running the SAME pipelines GET /reports/cash-flow and GET /bills/scheduled run — once over the stored per-occurrence exceptions and once with the hypothetical merged over them by the same merge POST /bills/{id}/mark-scheduled saves through. So hypothetical equals what those two surfaces return after the same change is actually saved. Since KAN-3762 every figure comes from ONE walk — the projection engine’s Scheduled ledger walk, the same line GET /bills/projection, GET /reports/cash-flow and GET /accounts/{id}/balance-projection draw: lowest and firstNegativeDateIso are its readings over today to windowEndIso, and balanceAfterCents is the ledger row’s running projected balance for this occurrence, so moving an OVERDUE occurrence moves the low exactly as it moves the chart. Facet encoding mirrors mark-scheduled’s PATCH: an ABSENT key leaves the stored facet unchanged and a PRESENT-BUT-EMPTY value clears it (a restore). An occurrence carrying an “already paid” declaration is refused 409 OCCURRENCE_DECLARED for any facet, with no figures. With basis=ledger only, a bill whose account is not checking or savings is refused 409 OCCURRENCE_PREVIEW_NOT_APPLICABLE (KAN-3658), before any figure is read; the default cash-flow basis is never refused on account type. Both basis values return identical summaries since KAN-3762; the parameter is kept for wire compatibility.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Bill id
Query Parameters
Section titled “Query Parameters”Comma-separated account UUIDs, at most 10 — the same scope, rules and 400s as GET /bills/scheduled. MUTUALLY EXCLUSIVE with accountId; one of the two is REQUIRED (there is no Safe-to-Spend pool fallback). The bill’s own account must be among them, else 400 on occurrenceDate.
A single account id — the one-element form of accountIds.
Calendar date, ‘YYYY-MM-DD’
The forward horizon ‘YYYY-MM-DD’ (REQUIRED), validated exactly as GET /bills/scheduled validates it (malformed or past the shared bound is a 400 on forwardTo). The resolved horizon — max(forwardTo, end of the current month) — is the horizon of BOTH projections and is echoed as windowEndIso.
Calendar date, ‘YYYY-MM-DD’
The ORIGINAL scheduled occurrence date ‘YYYY-MM-DD’. Must be an outstanding occurrence of an active bill within the horizon (overdue outstanding occurrences included), else 400.
The hypothetical forecast date ‘YYYY-MM-DD’; present-but-empty clears the stored one (restore); absent keeps it. A date equal to occurrenceDate is the series restated and clears.
The hypothetical forecast amount as a POSITIVE integer magnitude in cents; present-but-empty clears; absent keeps. An amount equal to the schedule’s own is the series restated and clears.
‘true’ or ‘false’; absent keeps the stored skip.
KAN-3658, widened by KAN-3762. ‘cash-flow’ (the default, also when absent or empty) and ‘ledger’ both read every figure from the ONE projection engine’s Scheduled ledger walk and return identical summaries; neither reads a chart input. Kept for wire compatibility: ‘ledger’ alone refuses a non-checking/savings bill (409 OCCURRENCE_PREVIEW_NOT_APPLICABLE) and alone admits includeMirrors=true. Any other value is a 400 on basis with a fixed message. The shared transaction list sends accountIds=<the bill’s own account>&basis=ledger.
Opt-in (KAN-3670), honoured ONLY with basis=ledger. ‘true’ loads the Scheduled ledger walk with GET /bills/scheduled?includeMirrors=true’s read-only mirrors (bills scheduled from an account outside the scope onto each scoped account a bill’s destination names, of any type, KAN-3683), so balanceAfterCents and the ledger-basis figures equal that route’s walk for the same scope. ‘true’ with the cash-flow basis — explicit, or the default (basis absent or empty) — is a 400 VALIDATION_ERROR with fieldErrors.includeMirrors = [‘includeMirrors=true requires basis=ledger’], decided before a connection is opened. Absent, ‘’ or ‘false’ leaves it off and the response is unchanged. Any other value is a 400 with fieldErrors.includeMirrors = [‘includeMirrors must be true or false’]. A mirrored bill is never the previewed bill: its own account must be in scope (else 400 on occurrenceDate), and then its mirror is not loaded.
Responses
Section titled “Responses”The current and hypothetical projection summaries
object
object
Calendar date, ‘YYYY-MM-DD’
Calendar date, ‘YYYY-MM-DD’
object
object
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
object
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
object
object
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
object
Calendar date, ‘YYYY-MM-DD’
Integer minor units (cents)
Examplegenerated
{ "data": { "billId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "occurrenceDate": "example", "windowEndIso": "example", "current": { "lowest": { "dateIso": "example", "balanceCents": 1 }, "firstNegativeDateIso": "example", "balanceAfterCents": 1, "occurrence": { "effectiveDateIso": "example", "effectiveAmountCents": 1, "skipped": true } }, "hypothetical": { "lowest": { "dateIso": "example", "balanceCents": 1 }, "firstNegativeDateIso": "example", "balanceAfterCents": 1, "occurrence": { "effectiveDateIso": "example", "effectiveAmountCents": 1, "skipped": true } } }, "message": "example", "requestId": "example", "timestamp": "2026-04-15T12:00:00Z"}Validation failed — see errorCode / fieldErrors
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Missing, invalid, or expired bearer token
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
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.
object
Which precondition refused the request. Evaluated in this order (CCR-1); SUBSCRIPTION_REQUIRED is unreachable while BILLING_ENABLED is not exactly true.
object
Active policy versions the user has not yet accepted.
object
True when the user has accepted no policy before — the client renders the new-signup screen rather than the re-acceptance one.
Example
{ "errorCode": "EMAIL_NOT_VERIFIED"}Resource not found or not owned by the caller org
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Conflict. errorCode is IDEMPOTENCY_CONFLICT when an X-Idempotency-Key was replayed with a different request payload; where the operation instead detects a resource-state conflict it carries that endpoint’s own errorCode (a duplicate name, an import that is no longer committable, an account already linked to a bank connection).
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Internal error (no internal detail leaked)
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
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.
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}