Zum Hauptinhalt springen

Export and Delivery

Export produces CSV files for a payment system or an external biller. Delivery decides where those files go.

Vodia Billing 61 Vodia Billing 61

What is exported

Export emits typed rows, one per charge:

TypeSource
USAGERated calls
RECURRINGSeats, DIDs, licences
ONE_TIMECatalogue and one-off charges

Included calls are exported as 0.00 rows rather than omitted, so the receiving system sees the traffic.

Rows carry each charge's own sell amount. Export never writes invoice totals and never writes tax — it is a charge feed, not an invoice feed.

Export is sell-only by construction. Cost prices and margin cannot reach an export file.

Properties

  • Watermarked on rated_at, never on call date, so a recovery import of last month's calls is exported once and only once.
  • Split per currency automatically. A fleet billing in three currencies produces three files.
  • sha256 manifest alongside each file.
  • Immutable. A generated file is never regenerated. A retry re-sends the stored bytes, so the receiving system can never get two different files under one sequence number.
  • Sequenced. Each export carries a sequence number, recorded per charge as exported_seq.

Delivery destinations

Delivery configures where export files are sent. Three transports:

SFTP — uploaded as .part and then renamed, so a watcher on the far side never reads a half-written file. Host, port, user, password or key, and remote path.

HTTPPOST or PUT with the CSV as the request body, and headers the receiver can verify against: X-Content-SHA256, X-Filename, X-Currency and X-Row-Count. Optional bearer token.

Email — the CSV as an attachment to one or more addresses.

Destinations marked deliver automatically receive every new export. Anything can also be delivered or retried by hand.

Attempt tracking

Every attempt is recorded per destination and per file on the export record. A delivery where some files succeeded and others failed is reported as partial, and a retry is limited to the files that actually failed.

Exported but unbilled

The charge_exported_unbilled detector finds charges that were exported but never landed on an invoice. Those charges are otherwise permanently invisible: the export watermark has passed them, so they will not be exported again, and no invoice carries them.

If this alert fires, the charges need to be identified and handled manually. It is the one detector whose findings represent money that is silently gone.

Worked example

The file

One invoice's charges as an export file. Sell-only, one row per charge, no tax and no invoice totals:

seq,type,tenant,account_ref,period,description,quantity,unit,amount,currency
1041,USAGE,hq.northwind-example.com,NW-1041,2026-08,US National,1240.00,min,14.88,USD
1041,USAGE,hq.northwind-example.com,NW-1041,2026-08,United Kingdom,86.00,min,3.01,USD
1041,USAGE,hq.northwind-example.com,NW-1041,2026-08,UK Mobile,12.00,min,1.68,USD
1041,USAGE,hq.northwind-example.com,NW-1041,2026-08,US Toll Free,318.00,min,0.00,USD
1041,RECURRING,hq.northwind-example.com,NW-1041,2026-08,Extensions,42,ea,504.00,USD
1041,RECURRING,hq.northwind-example.com,NW-1041,2026-08,Hunt groups,3,ea,15.00,USD
1041,RECURRING,hq.northwind-example.com,NW-1041,2026-08,DID numbers,18,ea,27.00,USD
1041,RECURRING,hq.northwind-example.com,NW-1041,2026-08,Managed support plan,1,ea,150.00,USD
1041,ONE_TIME,hq.northwind-example.com,NW-1041,2026-08,Desk handset,1,ea,89.00,USD

The toll-free row is present at 0.00 rather than omitted, so the receiving system sees the traffic.

A multi-currency export

A fleet billing in three currencies produces three files from one export, and a manifest:

export-1041-USD.csv    412 rows   sha256 3f9a...c218
export-1041-EUR.csv 88 rows sha256 71bd...04ef
export-1041-GBP.csv 34 rows sha256 aa19...9d5c
export-1041.manifest (all three checksums)

Delivery, and a partial

Export 1041, 3 files, 2 destinations

sftp://biller.example.com/upload
export-1041-USD.csv delivered 2026-09-01 03:14:02
export-1041-EUR.csv delivered 2026-09-01 03:14:05
export-1041-GBP.csv delivered 2026-09-01 03:14:07

https://api.partner-example.com/charges
export-1041-USD.csv delivered 2026-09-01 03:14:11
export-1041-EUR.csv FAILED 504 Gateway Timeout
export-1041-GBP.csv FAILED 504 Gateway Timeout

Status: PARTIAL

A retry touches only the two failed files, and re-sends the stored bytes rather than regenerating them. The biller can never receive two different files under one sequence number.

The HTTP headers a receiver can verify

POST /charges HTTP/1.1
Host: api.partner-example.com
Authorization: Bearer <token>
Content-Type: text/csv
X-Filename: export-1041-USD.csv
X-Content-SHA256: 3f9a...c218
X-Currency: USD
X-Row-Count: 412

The body is the CSV itself. A receiver that checks X-Content-SHA256 against what it read cannot silently ingest a truncated file.

The SFTP rename

Files are uploaded as .part and then renamed:

put  export-1041-USD.csv.part
rename export-1041-USD.csv.part -> export-1041-USD.csv

A watcher on the far side polling for *.csv therefore never reads a half-written file.

Watermarking, and why recovery does not double-export

Export selects on rated_at, never on call date.

2026-09-01  export 1041 runs, watermark advances to 2026-09-01 03:00
2026-09-04 archive CSV for 2026-08-19 imported, 240 legs rated
2026-10-01 export 1042 runs
-> includes the 240 legs from 2026-08-19
(rated 2026-09-04, after the previous watermark)

Those legs appear on the August invoice, because invoice periods are cut on when the call started. But they export in the October file, because export tracks when they were rated. Both are correct, and they are answering different questions.