Docs: Identity and versions

Statement identity and versions

Store statements under statement_id and keep the latest version_id. Those two ids are what let you take corrections without creating duplicates.

  • A re-extraction keeps the ids. If we extract a bill again, for example after improving how a provider's layout is read, each statement keeps its statement_id and arrives as a new version. Statements are matched on their content (account, address, period, meters, services and total), not on their position in the bill. The new version replaces the one you hold. Every delivered version says is_latest: true, so decide which is newer by extracted_at, compared as a time.
  • The same bill in a later request keeps the ids too. If you send a bill again in a new request, and it is not byte-for-byte the file you sent before, we recognize it by account number, statement date and amount due. Its statements keep their ids and arrive as new versions under the new request_id. This never matches across clients.
  • A statement that can no longer be matched gets a new id, never a neighbor's. If a re-extraction drops a statement you were sent, you receive statement.withdrawn for it.
  • One request can produce several statements, and one statement can arrive under several requests. request_id tells you which request delivered a version; it is not the statement's identity.

A minimal consumer:

on statement.available:
    if version_id already stored: return 2xx          # a repeat delivery
    upsert statements[statement_id] = payload          # unless the stored one has a later extracted_at
on statement.withdrawn:
    mark statements[statement_id] withdrawn

Do not rely on arrival order: a delivery retried after an outage on your side can arrive after a newer one. Of two versions of one statement, keep the one with the later extracted_at, compared as a time. A statement.withdrawn is final: ignore any version of that statement_id that arrives after it.