Skip to main content

Bulk Entry Action

POST 

/api/ledger/entries/bulk

Dispatch one tracked bulk action over ledger entries. Eligible/excluded counts are computed server-side via EligibilityResolver BEFORE dispatch, so the response counts are accurate. The job reports progress via the tracked job log endpoints.

Authorization

Any valid API token can call this endpoint — no specific scope required. Manage tokens.

Authentication: Requires Bearer token + accounting.sync permission (403 without it).

Request body fields:

  • action (required) — one of: sync_now (queue an immediate provider push), set_override (force sync on/off — requires override), rebuild (regenerate from source; posted entries corrected via reversal + replacement), unlink (sever the provider sync link so SKU stops tracking the remote doc — a later sync CREATES a new doc), clear_errors (dismiss parked needs_action / failed_retryable operations WITHOUT retrying), fetch_remote (the INBOUND mirror of sync_now — re-read the remote document(s) and re-grade the link state: drift / void / settled-lock; runs as a tracked job), delete (HARD-delete the entry — permanently removes the journal entry, its GL lines, provider sync links and operation history, and nulls any sibling reversal pointer; IRREVERSIBLE; unlike the other actions it runs even when ledger generation is disabled).
  • scope (optional, selected default | all | all_stale) — all targets every entry matching the CURRENT filters (send the same filter[...] / filter_groups query params as the list); all_stale targets every stale pre-cutover (frozen history) entry, so one rebuild call drains the entire stale worklist rather than only its preview page. Both are resolved server-side and capped at 5000; entry_ids is then optional/ignored.
  • entry_ids (array, required unless scope=all or scope=all_stale, max 1000) — the explicitly selected entry ids.
  • override (required when action=set_override) — force_enabled | force_disabled | null (clear).

Eligibility (excluded from the action, counted in excluded_count): sync_now → entries eligible on at least one connection; rebuild → source still exists; unlink and fetch_remote → entry currently has a live remote link; clear_errors → entry carries a clearable parked error; set_override and delete → always applicable.

Note: when ledger generation is disabled (kill switch) no job dispatches — the response carries note: "generation_disabled" and tracked_job_log_id: null. The delete action is the one exception: it always dispatches its job so test/erroneous entries can be cleaned up while the kill switch is on.

Response fields: tracked_job_log_id (null when no job dispatched), total (entries the job will process), eligible_count, excluded_count. When scope=all or scope=all_stale and the match set exceeds the cap (default 5000, ledger.bulk.all_scope_cap), the remainder is dropped and the response adds all_scope_truncated: true, matched_total (total entries matching the filters), and cap (the limit applied); the message then names how many of how many entries were started.

replace_nominal_code action (find & replace account): remaps every line deriving from_nominal_code_id to to_nominal_code_id across the selected/all-filtered entries by pinning a durable per-entry override (re-applied on every rebuild). Extra body fields when action = "replace_nominal_code":

  • from_nominal_code_id (required, integer, exists:nominal_codes)
  • to_nominal_code_id (required, integer, exists:nominal_codes, different from from) Runs as a tracked background job (tracked job log endpoints).

Request

Responses

OK

Response Headers
    Content-Type