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 type | Status | Notes |
|---|---|---|
| Bank transactions | ✅ Available | Imported as external evidence, not ledger entries |
| Contacts | ✅ Available | Created as standard contacts |
| Invoices | Coming soon | Will generate ledger events |
| Opening balances | Coming soon | Will 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
| Field | Format |
|---|---|
amount | Major units (GBP) in CSV, converted to minor units on import |
| Positive | Money into the bank account |
| Negative | Money 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
typemust becustomerorsupplier- 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
| Field | Description |
|---|---|
file | CSV file to import |
type | Import 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
| Code | HTTP | Description |
|---|---|---|
validation_error | 400 | CSV format invalid or row validation failed |
unsupported_type | 400 | Import type not recognised |
file_too_large | 413 | CSV exceeds maximum file size |
unprocessable_entity | 422 | Data violates domain constraints |