Skip to content

Preview how ONE hypothetical change to one occurrence moves the projection

GET
/bills/{id}/occurrence-preview
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.

id
required
string format: uuid

Bill id

accountIds
string

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.

accountId
string format: uuid

A single account id — the one-element form of accountIds.

forwardTo
required

Calendar date, ‘YYYY-MM-DD’

string

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.

occurrenceDate
required

Calendar date, ‘YYYY-MM-DD’

string

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.

forecastDate
string

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.

forecastAmountCents
string

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.

skip
string
Allowed values: true false

‘true’ or ‘false’; absent keeps the stored skip.

basis
string
Allowed values: cash-flow ledger

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.

includeMirrors
string
Allowed values: true false

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.

The current and hypothetical projection summaries

Media typeapplication/json
object
data
required
object
billId
required
string format: uuid
occurrenceDate
required

Calendar date, ‘YYYY-MM-DD’

string
windowEndIso
required

Calendar date, ‘YYYY-MM-DD’

string
current
required
object
lowest
required
object
dateIso
required

Calendar date, ‘YYYY-MM-DD’

string
balanceCents
required

Integer minor units (cents)

integer format: int64
firstNegativeDateIso
required

Calendar date, ‘YYYY-MM-DD’

string
nullable
balanceAfterCents
required

Integer minor units (cents)

integer format: int64
occurrence
required
object
effectiveDateIso
required

Calendar date, ‘YYYY-MM-DD’

string
effectiveAmountCents
required

Integer minor units (cents)

integer format: int64
skipped
required
boolean
hypothetical
required
object
lowest
required
object
dateIso
required

Calendar date, ‘YYYY-MM-DD’

string
balanceCents
required

Integer minor units (cents)

integer format: int64
firstNegativeDateIso
required

Calendar date, ‘YYYY-MM-DD’

string
nullable
balanceAfterCents
required

Integer minor units (cents)

integer format: int64
occurrence
required
object
effectiveDateIso
required

Calendar date, ‘YYYY-MM-DD’

string
effectiveAmountCents
required

Integer minor units (cents)

integer format: int64
skipped
required
boolean
message
string
requestId
required
string
timestamp
required
string format: date-time
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

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

Resource not found or not owned by the caller org

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

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

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

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