Vendsoft CSV imports
How to load Vendsoft sales-by-machine exports into Cafébec, dry-run first, and commit when everything matches.
Vendsoft doesn't have a webhook; operators export a sales-by-machine CSV each period and upload it here. The import is a two-phase flow — dry-run to catch unmatched clients/machines, then commit. Nothing writes to the database until every row resolves.
File format
Exports alternate client-header rows with machine data rows:
Le Groupe Guy,,,,,,,,
[17] grp_guy_k_rs800,1842.10,612.45,240.12,368.42,1229.65,989.53,0.67,0.54
[18] grp_guy_k_tcnncf7n,1420.55,489.02,185.18,284.11,931.53,746.35,0.65,0.53
Holiday Inn Longueuil,,,,,,,,
[31] hi_longueuil_lobby_rs800,2210.45,745.12,288.22,442.09,1465.33,1177.24,0.66,0.53
Grand Totals,0,0,0,0,0,0,0,0- Client header: a single non-empty cell in column 0, rest blank
- Machine row:
[N] slug,Gross,COGS,Tax,Cmsn,GP,Net,GPM,NPM - Grand Totals: footer row, always ignored
Clients are matched by vendsoft_name first, then by name. Machines
are matched by slug exclusively — the [N] Vendsoft ID is
informational only.
Tax split
Vendsoft reports sales tax-inclusive. The import derives:
net_revenue = gross_sales − sales_tax
gst = sales_tax × 5 / 14.975
qst = sales_tax × 9.975 / 14.975Each row must balance within $0.01 of gross = net + tax or it lands in
the balanceWarnings list of the dry-run response. Commit proceeds
despite warnings; the audit log records the count.
Running an import
From the operator console
- Sign in as an
ADMINorOPERATOR. - Open CSV imports in the sidebar.
- Pick the Vendsoft export, set the period start/end dates, click Run dry-run.
- Review the KPI tiles (rows parsed / matched / unmatched / balance warnings) and the Computed rows table.
- If Unmatched is zero, click Commit import. Otherwise resolve the unmatched clients/machines (create them or map to existing rows) and dry-run again.
From the API
# Dry-run
curl -b jar.txt \
-X POST "http://localhost:4004/v1/imports/vendsoft?dry_run=true" \
-F "file=@sales-by-machine.csv" \
-F 'meta={"periodStart":"2026-03-01","periodEnd":"2026-03-31"}'
# Commit (only when unmatched is zero)
curl -b jar.txt \
-X POST "http://localhost:4004/v1/imports/vendsoft?dry_run=false" \
-F "file=@sales-by-machine.csv" \
-F 'meta={"periodStart":"2026-03-01","periodEnd":"2026-03-31"}'Overrides let you resolve unmatched rows without editing the CSV:
{
"periodStart": "2026-03-01",
"periodEnd": "2026-03-31",
"clientOverrides": {
"Nouvelle Entreprise Inconnue": "f3...-client-uuid"
},
"machineOverrides": {
"unknown_new_machine": "a1...-machine-uuid"
}
}What gets written
- One
sales_recordper machine row,source = VENDSOFT, with the derivednet_revenue/gst_amount/qst_amount. Vendsoft's reported commission is stored inreported_commissionfor reconciliation — never used in settlement calculations. - One
audit_logrow per import (actionimport.vendsoft), with the filename, row count, balance-warning count, and which override keys were passed. Per-row audit is intentionally skipped; individualsales_recordIDs are queryable byimported_at. - The entire write is a single Prisma transaction — a partial import is impossible.
Errors you might see
IMPORT_UNRESOLVED_ROWS(409) — commit called with unmatched rows. The response body lists each unmatched client / machine plus suggestion candidates.VALIDATION_ERROR(400) — malformedmetaJSON or missing file field.- Balance warnings are advisory; they never block a commit, they appear in the response body and audit row.