Skip to content

Backup Import

POST
/api/v1/import/backup
curl --request POST \
--url https://example.com/api/v1/import/backup \
--header 'Content-Type: multipart/form-data' \
--form file=example \
--form sections=example

Import a full JSON backup file with all account metadata and snapshots.

Single-step import (no parse/commit split) — JSON is already structured. Matches accounts by (name, institution) pair. Creates missing accounts with full metadata (type, color, category, is_liability, etc.). Updates existing records with backup values (upsert mode).

Optional sections form field (comma-separated section names) restricts the import to only those sections. Omitting it imports everything present in the file. Unknown section names are rejected with a 422 (import_unknown_backup_section).

Media typemultipart/form-data
Body_backup_import_api_v1_import_backup_post
object
file
required
File
string
sections
Any of:
string

Successful Response

Media typeapplication/json
ImportSummary

Response from the /api/import/commit endpoint.

partial success with summary. Import proceeds even if some snapshots fail — the summary reports counts and error details.

records_updated: count of existing records whose fields were updated from the backup (institutions, categories, accounts, snapshots, etc.).

sections/duplicate_details: a NEW, independent per-section breakdown computed alongside the existing aggregate counters above — the aggregate counters’ existing behavior is unchanged for backward compatibility (purely additive, non-breaking response shape). The new per-section entries additionally distinguish a true no-op duplicate (skipped_duplicate — matched an existing row whose fields are already identical) from a genuine update (updated — matched an existing row where at least one field changed), a distinction the legacy aggregate records_updated counter does not make.

categorized_from_payee: count of imported rows that took their category from the payee’s default category, counted only once the row was stored.

object
accounts_created
Accounts Created
integer
0
institutions_imported
Institutions Imported
integer
0
snapshots_imported
Snapshots Imported
integer
0
duplicates_skipped
Duplicates Skipped
integer
0
records_updated
Records Updated
integer
0
errors
Errors
integer
0
error_details
Error Details
Array<object>
ImportError

Error detail for a single failed import row.

section: which importable section this error belongs to (mirrors ImportDuplicate.section) – lets the frontend filter error_details per section when rendering the per-section breakdown.

object
section
required
Section
string
row
required
Row
integer
account_name
required
Account Name
string
date
required
Date
string
error
required
Error
string
sections
Sections
Array<object>
ImportSectionSummary

Per-section created/updated/skipped/error breakdown for one of the 13 importable backup sections.

object
section
required
Section
string
created
Created
integer
0
updated
Updated
integer
0
skipped_duplicate
Skipped Duplicate
integer
0
errors
Errors
integer
0
duplicate_details
Duplicate Details
Array<object>
ImportDuplicate

Detail for a single row skipped as a true no-op duplicate.

Mirrors ImportError’s field shape, plus section (which importable section this row belongs to) and description — a human-readable identifier for the skipped record (e.g. “Transaction 2026-01-15 − 45.00€”), analogous in spirit to ImportError.error but describing what was skipped rather than what failed.

object
section
required
Section
string
row
required
Row
integer
account_name
required
Account Name
string
date
required
Date
string
description
required
Description
string
rules_timed_out
Rules Timed Out
Array<object>
RuleTimedOut

A categorization rule whose regex condition ran out of time during an import and was skipped for the rest of that run (the rule itself is unchanged).

object
id
required
Id
integer
name
required
Name
string
categorized_from_payee
Categorized From Payee
integer
0

Example

{
"accounts_created": 0,
"institutions_imported": 0,
"snapshots_imported": 0,
"duplicates_skipped": 0,
"records_updated": 0,
"errors": 0,
"sections": [
{
"created": 0,
"updated": 0,
"skipped_duplicate": 0,
"errors": 0
}
],
"categorized_from_payee": 0
}

Validation Error

Media typeapplication/json
HTTPValidationError
object
detail
Detail
Array<object>
ValidationError
object
loc
required
Location
Array
msg
required
Message
string
type
required
Error Type
string
input
Input
ctx
Context
object

Example generated

{
"detail": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example",
"input": "example",
"ctx": {}
}
]
}