The sync object
status is idle, running (a run is queued or in progress) or attention (the last run failed or the sync is parked; attention.code says why). latest_run is filled with include[]=latest_run.
Create a sync
run_now: false, latest_run set to the queued first run. Rules:
- The destination must be
active(400 destination_incompleteotherwise) and not already used by a sync (409 destination_in_use). - YNAB needs
account_mappings(budget account per bank account, ids fromGET /v2/destinations/{id}/resources?type=account&parent=<budget>); Sheetsper_accountwithout mappings gets a tab per banking account created for you. - Brokerage accounts need a Sheets destination;
holdings/tradessyncs need brokerage accounts. options.data_fromis clamped to the last 84 months, and a date in the future is clamped to today; the default is 6 months (3 on trial). The response carries the clamped value, so read it back rather than assuming what you sent.- Plan sync limit:
403 sync_limit_reached.
Endpoints
On a
per_account Sheets sync, adding accounts needs account_mappings for the new ones (400 mapping_required); brokerage accounts share the Holdings and Trades tabs and need none.
The sync_run object
status: queued → running → succeeded | failed | cancelled. trigger: schedule, api, dashboard, webhook, resync. A failed run carries failure.code (destination_auth_failed, destination_resource_missing, rate_limited, timeout, superseded, run_failed) and retryable.
Watch a run: poll GET /v2/sync_runs/{id} or subscribe an event destination to sync_run.*.
Categories
Your taxonomy is a tree one level deep: groups (parent: null) contain categories. Each category claims provider category codes (sources); a transaction resolves to the category that claims its subcategory, then its primary category.