Skip to main content
The reconciliation cluster is the proof-of-correctness surface. Before flipping the cutover switch, you fire each endpoint against your Arcus entity and tie the numbers to the legacy ERP to the penny. After cutover, the same endpoints give you a permanent audit trail. All endpoints are read-only except POST .../compare, which is also non-destructive (it returns deltas, no writes).

Required scope

The scope table above is the authoritative one for these calls; the API reference lists migration:read alone on several of them. The dual scope on the GETs means a regular operator with accounting:read can run reconciliation reports during normal operations; a migration-specific key with migration:read (and nothing else) can do the same during a cutover window.

Trial balance

Optional query parameters:
  • as_of_date=YYYY-MM-DD (default: today). Snapshot date for the trial balance.
  • external_source=versa (default: all). Restrict to journal entries imported from a single source. Use this to verify that ONLY the migrated GL ties; subsequent native postings are excluded.
Response:
A balanced trial balance has totals.in_balance equal to true and totals.imbalance equal to 0.00. Anything else is a data error and unfreeze will block.

AR aging

Optional query parameter: include_pii=false replaces each customer name with [REDACTED], for sharing a report outside your finance team. The aging buckets are measured against the entity’s current business day. Response:
Sum of totals.total must equal the AR control-account balance in the trial balance. That tie is the primary AR proof.

AP aging

Same idea as AR aging, vendor-keyed instead of customer-keyed. Each row carries vendor_id, display_name, the buckets current, days_1_30, days_31_60, days_61_90 and over_90, and total, with the same bucket names in totals. include_pii=false redacts the vendor names here too, and the object is reconciliation_ap_aging.

Inventory valuation

Query parameters:
  • as_of_date=YYYY-MM-DD (default: today)
  • method (default: fifo) - fifo, avg, or lot_cost; the response names the basis it used
  • location_id (optional) - filter to one warehouse
Response:
total_value must equal the Inventory control-account balance in the trial balance.

Cash balance

Optional query parameters: as_of_date=YYYY-MM-DD (default: today) and bank_account_id to restrict the report to one bank account. Response:
balance is derived from the posted general ledger entries for that bank account as of the date, and equals the trial-balance value for its GL account. total_cash_balance is the entity-wide cash total. The two unreconciled counts show how many bank lines and GL entries are still waiting to be matched, so a non-zero count is a row to review before you certify the cash balance.

Resource counts

Response:
The counts cover the entity’s major resource tables (accounts, products, orders, vendor bills, payments, journal entries, returns, packages, inventory adjustments and transfers, and documents), so the list above grows as new tables are covered. external_source filters to records imported from that source via the bulk endpoint. Omit it to see total counts across all sources, or pass by_source=true to break the journal entry counts down by source type instead.

Compare external records (the penny-match endpoint)

This is the workhorse. Submit external records (from the legacy ERP) and the API returns the per-record verdict against your Arcus data.
external_resource and external_records are required; external_source and tolerance_cents are optional. Each record names the thing it is compared to in a different field depending on the resource, and carries the figures to check: Supported external_resource values:
  • ar_aging_by_customer - per-customer AR aging buckets. external_id is matched to the customer’s Arcus account id; the figures are current, days_1_30, days_31_60, days_61_90, days_90_plus and total.
  • ap_aging_by_vendor - per-vendor AP aging buckets. external_id is matched to the vendor’s Arcus id; the figures are current, days_1_30, days_31_60, days_61_90, over_90 and total.
  • trial_balance_by_account - per-GL-account totals. The record is matched on account_number (or external_id); the figures are total_debit, total_credit and balance.
  • resource_counts - object-level counts. The record names the resource in resource_name and carries its count.
tolerance_cents is the per-record delta you accept (default: 1 cent). Set to 0 for exact ties. Response:
matched records balance to the penny. mismatched records have at least one field outside tolerance_cents. missing_in_arcus records exist in the external file but not in Arcus. extra_in_arcus records exist in Arcus but not in the external file. Hard cap: 5000 records per call; more returns 422 external_records_too_large with a hint to split the comparison into batches of 5000 or fewer. An empty or missing external_records returns 400 invalid_field. An external_resource outside the four above returns 422 unsupported_external_resource with the supported list.

Suggested cutover-day workflow

  1. Pre-cutover (week of): run trial balance + counts daily. Confirm you can reproduce the same totals from the legacy ERP. Spot-check 5 to 10 named accounts via compare.
  2. Cutover Friday EOD (T-0): freeze the entity. Run final reconciliation suite. Snapshot legacy source data alongside the Arcus database snapshot.
  3. Cutover Saturday morning: run a top-up bulk-import sync for any data that changed between Thursday’s full load and Friday EOD freeze. Re-run reconciliation.
  4. Cutover Saturday evening: swap traffic, then run POST .../cutover/verify which fires the full reconciliation suite. This is the gate that unblocks unfreeze.
  5. Cutover Sunday: smoke-test critical user flows. Re-run individual reconciliation reports as needed.
  6. Cutover Monday go-live: keep the reconciliation key live for a week so the operator can investigate any reported discrepancies.

Error envelope

Like the rest of the public API, every 4xx response follows the canonical envelope:
See Errors.