Migration

The Migration Wizard imports data from other accounting providers into SpeyBooks. It orchestrates contact imports, opening balances, outstanding invoices, and bills through a state machine that validates each step before allowing execution.

This guide walks through a complete migration from start to finish. For the full endpoint reference, see Migration API.


Supported providers

ProviderFile formatStatus
FreeAgent.xlsx (multi-sheet export)Supported - automated parsing
XeroManual CSVPlanned
QuickBooksManual CSVPlanned
OtherManual CSVSupported - manual attachment

FreeAgent is fully automated: upload a single .xlsx export and the wizard parses sheets for contacts, trial balance, invoices, and bills. Other providers use the manual attachment workflow where you upload each data set separately through the standalone importers.


The state machine

Every migration moves through a defined set of states. The status is computed automatically based on which imports are attached and confirmed.

setup → files_attached → preview_ready → ready_to_execute → executing → completed
                                                                      ↘ abandoned
StatusMeaning
setupCreated, no files attached yet
files_attachedAt least one import linked
preview_readyAll required imports present
ready_to_executeAll validation gates passed
executingExecution in progress
completedSealed - data imported (terminal)
abandonedManually cancelled (terminal)

Only one active (non-terminal) migration may exist per organisation at any time. This singleton constraint is enforced by a partial unique index. Attempting to create a second active migration returns 409 Conflict.


Starting a migration

Specify the source provider and a cutover date (the date from which SpeyBooks takes over). The cutover date determines how opening balances are calculated - all transactions in the source system up to this date are summarised into opening balances rather than imported individually.

The API validates that the organisation does not have an existing active migration, and that the organisation is not using the cash VAT scheme (which is not supported for migration).


Uploading provider data

FreeAgent (automated)

For FreeAgent migrations, upload the .xlsx export file to the upload-provider endpoint. The wizard automatically:

  1. Validates the file is .xlsx and under 10MB
  2. Parses the workbook into separate sheet buffers (contacts, trial balance, invoices, bills)
  3. Validates that required sheets are present
  4. Imports contacts through the Universal Contact Importer (UCI)
  5. Imports opening balances through the Opening Balance Confirmation Engine (OBCE)
  6. Stores invoice and bill sheets as deferred data for later processing
  7. Attaches the resulting import IDs to the migration
  8. Advances the migration status

The response includes a sheets array listing which sheets were found, and an uploads object showing the status of each import.

Deferred sheets

Invoices and bills are deferred because they depend on the opening balance being confirmed first. After reviewing and confirming the opening balance import, call POST /migrations/{id}/upload-deferred to process the invoice and bill sheets. This two-step approach ensures the clearing account reconciliation is correct before outstanding documents are imported.

Manual attachment

For other providers, import each data set separately using the standalone importers (contact import, opening balance import, invoice/bill import), then attach the resulting import IDs to the migration:

curl -X POST https://api.speybooks.com/v1/migrations/mig_1/upload \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"contactsImportId": "dimp_5", "balancesImportId": "dimp_6"}'

All fields are optional - provide the ones you want to attach. Set a field to null to detach an import. The migration status is recomputed after each update.


Validation gates

Before a migration can be executed, it must pass a set of validation gates. Call the validate endpoint to run all checks and see which gates pass or fail.

Each gate returns a code, passed boolean, and human-readable message. Common gates include:

Gate codeWhat it checks
CONTACTS_CONFIRMEDContact import is attached and confirmed
BALANCES_CONFIRMEDOpening balance import is attached and confirmed
RECONCILIATION_VALIDImported debits equal credits

If all gates pass and the migration is in an eligible status, the validate endpoint automatically advances it to ready_to_execute. If any gate fails, the response shows "ready": false with details on what needs fixing.


Executing the migration

Once validated, execute the migration to atomically import all data into SpeyBooks. This operation is irreversible - there is no undo.

On success, the migration advances to completed and returns a reconciliation proof showing total debits, total credits, and net (which should be zero for a balanced import).

The endpoint is idempotent: if the migration is already completed, it returns the existing reconciliation proof without re-executing. This makes it safe to retry on network failures.

On failure, the migration stays in ready_to_execute and returns the specific error. Fix the underlying issue and retry.


Reviewing the result

After execution, retrieve the full migration details to see per-import summaries. Each import shows row counts, created/skipped/error counts, and status.

The reconciliation object provides the mathematical proof that the import is balanced. A net of zero confirms that every debit has a matching credit.


Abandoning a migration

If you need to start over, abandon the active migration with DELETE /migrations/{id}. This sets the status to abandoned and releases the singleton constraint so you can create a new migration.

Only non-terminal, non-executing migrations can be abandoned. If the migration is already completed or currently executing, the endpoint returns 422.

curl -X DELETE https://api.speybooks.com/v1/migrations/mig_1 \
  -H "Authorization: Bearer sk_live_..."

Migration history

List past migrations with GET /migrations/history. Returns up to 50 entries including completed and abandoned migrations, ordered by creation date descending.


Worked example: migrating from FreeAgent

  1. Create the migration - POST /migrations with "provider": "freeagent" and your cutover date

  2. Export from FreeAgent - in FreeAgent, go to Settings and export your data as .xlsx

  3. Upload the export - POST /migrations/mig_1/upload-provider with the .xlsx file. The wizard parses contacts and trial balance automatically

  4. Review contacts - check the contact import via the standalone importer endpoints. Confirm when ready

  5. Review opening balances - check the opening balance import. Confirm when ready

  6. Process deferred sheets - POST /migrations/mig_1/upload-deferred to import invoices and bills now that opening balances are confirmed

  7. Validate - POST /migrations/mig_1/validate to run all gate checks

  8. Execute - POST /migrations/mig_1/execute to import everything atomically

  9. Verify - GET /migrations/mig_1 to check the reconciliation proof and per-import summaries


Related endpoints