Skip to main content

CSV Import

Import historical and external data into SpeyBooks in a controlled, auditable way.

CSV import exists to help you bootstrap or migrate data — not to bypass accounting rules. All imported data is validated against SpeyBooks' core invariants before it is accepted.

Design intent

CSV import is deliberately conservative.

SpeyBooks does not support:

  • Blind bulk writes to the ledger
  • Implicit balance adjustments
  • "Magic" fixes for inconsistent data

Instead:

  • CSV files are parsed, validated, and normalised
  • Imported records become first-class domain objects
  • Ledger entries are created only when explicitly allowed

This ensures imports remain reproducible and auditable.

Supported imports

Data typeStatusNotes
Bank transactions✅ AvailableImported as external evidence, not ledger entries
Contacts✅ AvailableCreated as standard contacts
InvoicesComing soonWill generate ledger events
Opening balancesComing soonWill require explicit contra accounts

General CSV rules

All CSV imports must satisfy the following:

  • UTF-8 encoded
  • Header row required
  • Comma-separated
  • One record per row
  • No formulas or macros
  • Deterministic parsing (no locale-dependent formats)

Invalid rows are rejected with structured errors. Partial imports are not committed.


Bank transaction import

Format

date,description,amount,reference
2026-01-15,Payment received,1200.00,INV-0001
2026-01-16,AWS hosting,-45.00,

Semantics

FieldFormat
amountMajor units (GBP) in CSV, converted to minor units on import
PositiveMoney into the bank account
NegativeMoney out of the bank account

Behaviour

  • Imported bank transactions are not ledger entries
  • They are treated as external statements
  • They must be matched or categorised during bank reconciliation

Re-importing the same data is safe and idempotent.


Contact import

Format

name,email,type,phone
Acme Corp,accounts@acme.com,customer,+44 20 7123 4567
Widget Ltd,ap@widget.co.uk,supplier,

Behaviour

  • Each row creates a new contact
  • type must be customer or supplier
  • Duplicate detection is performed on name + email
  • Existing contacts are not silently overwritten

Contacts imported via CSV behave identically to those created via the API.


Upload endpoint

CSV imports are submitted via a single endpoint.

curl -X POST https://api.speybooks.com/v1/import \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: multipart/form-data" \
-F "file=@data.csv" \
-F "type=contacts"

Parameters

FieldDescription
fileCSV file to import
typeImport type (contacts, bank_transactions)

Response

{
"success": true,
"data": {
"imported": 2,
"skipped": 0,
"errors": []
}
}

Error response

{
"success": false,
"error": {
"code": "validation_error",
"message": "Import validation failed",
"details": [
{ "row": 3, "field": "type", "message": "Must be 'customer' or 'supplier'" },
{ "row": 5, "field": "email", "message": "Invalid email format" }
]
}
}

Validation and errors

Before any data is committed, SpeyBooks performs:

  • Schema validation
  • Type validation
  • Domain constraint checks
  • Duplicate detection

If any row fails validation:

  • The import is rejected
  • No partial data is written
  • Errors are returned with row numbers and reasons

This guarantees imports are all-or-nothing.


Import guarantees

CSV import in SpeyBooks guarantees:

  • No implicit ledger mutations
  • No silent balance changes
  • No partial writes
  • Deterministic outcomes
  • Full auditability

Imported data always enters the system through the same code paths as API-created data.


Coming soon

Invoices

  • CSV rows will generate invoice business events
  • Ledger transactions will be derived automatically
  • VAT rules will be enforced

Opening balances

  • Will require explicit equity or suspense accounts
  • No direct balance injection
  • Fully traceable starting position

When to use CSV import

CSV import is best suited for:

  • Initial migration from another system
  • Backfilling historical bank statements
  • Bulk contact creation

For ongoing operations, prefer the API-first workflow.


Error codes

CodeHTTPDescription
validation_error400CSV format invalid or row validation failed
unsupported_type400Import type not recognised
file_too_large413CSV exceeds maximum file size
unprocessable_entity422Data violates domain constraints