Sum invoices over a window
well_sum_invoicesSum a workspace's billed amounts over a window of whole months, grouped by month, currency and billing context. Arithmetic only — this tool holds no definition of MRR or recurrence, and returns no figure the app renders.
Use it when you are computing a figure whose RULES you are stating yourself: recurring revenue over a window you chose, a total restricted to the billing contexts a reader confirmed, a per-month series behind a trend you are about to describe. The server derives no MRR of its own, so an MRR figure starts here: state the rules, sum exactly those rows, then put the result on a card with well_render_mrr.
The window is whole months. from and to are both the first day of a month, as YYYY-MM-01; from is inclusive and to is EXCLUSIVE, so June to August is 2026-06-01 to 2026-09-01. A bound inside a month is refused rather than widened, and so is a window longer than 36 months.
Which rows are billed amounts is decided here, and stated so you can say it. A draft is left out, because it has not been issued, and so is a canceled invoice. excluded_draft_count counts the drafts left out: a connector or document import can keep a real invoice at draft, so state the count beside any total whenever it is not zero. Only billing documents count: invoices, debit notes, credit notes and subscription billing statements, so a proforma and the invoice it precedes are not summed twice, and an order, a quote or a payment advice never is. A row with no document type is read as an invoice. Every amount is NET of tax (items_total), because tax collected is owed onward rather than earned.
party_scope is required, and it decides whose invoice this is. sales is what the workspace ISSUED — its receivables, and the only side revenue can come from. purchase is what it received. The two are the same rows read from opposite ends, so no default is offered: a server choosing a side would answer a different question from the one asked. intra_self is an invoice between two companies the workspace owns, and unattributed is one Well could place on neither side.
Those four scopes partition every invoice exactly once, which is what makes an incomplete picture visible rather than silent. unattributed_count comes back on every call, whatever scope you asked for: it counts the invoices in the window that landed in that fourth bucket. State it beside any total, because an unattributed invoice may still belong in the figure and nothing here can tell you whether it does.
Every row carries ONE month, ONE currency and ONE billing context. Currency is always a grouping key, named or not: adding EUR to USD gives a number denominated in nothing, and no field on the result would tell you it happened. Convert the per-currency subtotals yourself, at a rate you can state, before you add them.
totals carries one figure per currency over the whole window: the rows of that currency folded across months and billing contexts on the server, rounded to the cent. When your figure covers every row of the call, read it from totals and never add rows yourself. A figure restricted to some billing contexts, such as an MRR over the contexts a reader confirmed, still comes from the rows: totals folds every context in, the null ones too.
sum is already net of credit notes. A credit note subtracts its magnitude from its own month-currency-context bucket, whichever sign it was stored with; credit_note_sum and credit_note_count report what that removed, so you can say what the figure netted. Do not subtract them a second time. A bucket whose credit notes outweigh its invoices nets negative, and that is a real state rather than an error.
billing_context is null on rows that name no billing arrangement — none stored, unknown, or a value Well has no label for — and that is a third answer rather than a kind of one-off. The field is filled by extraction, not by a billing system, so a workspace can carry real recurring revenue on rows that say nothing about it. unclassified_count totals those rows. The recurring-contexts card offers them as one choice, keyed "unclassified", so apply that key to these rows and only these. Counted or not, report the count rather than letting a reader read the remainder as "everything else".
corrected_or_consolidated_count counts the corrected and consolidated invoices among the rows. Each replaces invoices Well holds no link to, so when those originals fall in the same window the sum counts that billing twice. The rows keep them, because dropping them would lose the revenue whenever the originals fall outside the window. State the count beside any total whenever it is not zero.
excluded_malformed counts billing documents in the window with no readable net amount or no currency. They are in none of the rows and none of the sums, so state the count beside any total. It comes back null when the count could not be read, which is NOT 0: zero says every row was readable, null says nobody counted.
When party_scope.side_unknown is true the workspace has no resolved own company, so no side filter was applied: the totals cover every invoice on both sides, and the side is not known. Say the side is unknown, never that the figure is the requested side's.
partial: true means the aggregate was cut short and every figure is a FLOOR rather than a measurement.
When the token authorizes one workspace, call this directly — no workspace call is needed first. When it authorizes several, this read will not guess which one you mean: pass workspace_id on the call.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | EXCLUSIVE end of the window: the first day of the month after the last one you want, YYYY-MM-01. | |
| from | Yes | Inclusive start of the window: the first day of a month, YYYY-MM-01. | |
| party_scope | Yes | Which side of the invoice the workspace occupies: `sales` for what it issued, `purchase` for what it received. Required; see the description. | |
| workspace_id | No | Target workspace. Omit when the token authorizes one workspace. Required when it authorizes several: this read reports one workspace's own figures and will not choose which. | |
| conversation_id | No | The conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation. | |
| workspace_scope | No | Restrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| rows | Yes | ||
| sums | Yes | The four-way own-side partition of the measured set. `proven` cells hold only rows the stored facts prove; `estimated` cells hold the rows a weak claim sides (inside `unattributed`, never inside `proven`); `unattributed` is the unknown remainder with the direction twin's `by_reason` split; `no_currency` counts measured rows with no currency. `available: false` means a cell read failed — a partial block would not add up, so the block is omitted rather than floored. | |
| error | No | ||
| totals | Yes | One figure per currency over the whole window, folded on the server. Never added across currencies. | |
| window | Yes | ||
| partial | Yes | ||
| success | Yes | ||
| party_scope | Yes | What the requested side resolved to. `side_unknown: true` means the workspace resolved no own company, so NO side filter was applied (`applied: false`) and every figure covers invoices on both sides — say the side is unknown, never that the figure is the requested side's. `null` when no scope resolution was reported. | |
| completeness | Yes | ||
| conversation_id | No | The conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation. | |
| newly_connected | No | Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: "<name> is connected.". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later. | |
| excluded_malformed | Yes | ||
| resolved_workspace | No | The workspace that answered, when the caller named none and the token authorizes several. | |
| unattributed_count | Yes | ||
| unclassified_count | Yes | ||
| conversation_id_note | No | Present only when the server opened a fresh lane, stating that no choice recorded earlier was read. | |
| excluded_draft_count | Yes | ||
| conversation_id_source | No | Where the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened. | |
| corrected_or_consolidated_count | Yes |