OwnCRM
Exhibit · data migration

Your data moves overnight.

OpenForce doesn't hand you a spreadsheet and a weekend. It reads your org through Salesforce's own Bulk API, works out the correct load order from your object relationships, loads every record into your new PostgreSQL database in one idempotent pass, and produces a signed validation report before anyone flips a switch.

"Overnight" is measured, not promised — it falls out of your record counts, the Bulk API's own limits, and how much parallelism your org allows. Below is the arithmetic, the honest edges, and the report an operator and a separate verifier both sign.

§ 01Ordered · resumable · checksummed · signed offof 05

Your Salesforce data, moved overnight — five things happen, in this order, every run.

  1. 01Automated extract. Bulk API 2.0 query jobs pull every object — including recycled and archived rows — under your own credentials. Your record Ids, created dates, and owners carry across verbatim: the 18-character Salesforce Id becomes the PostgreSQL text primary key.
  2. 02Ordered automatically. Parents load before children. The load order is computed from your object graph by a topological sort; circular and self-referencing relationships are detected and resolved with a second-pass update — no hand-built runbook of "load Accounts, then Contacts, then…".
  3. 03Resumable. Re-run it. Every row upserts by its Salesforce Id (INSERT … ON CONFLICT DO UPDATE), so a repeated or delta pull updates in place instead of erroring — the basis for a tight cutover with a short write-freeze.
  4. 04Checksummed. Every table gets a row-count, a foreign-key integrity scan, and a per-value content checksum. A single altered cell is detectable.
  5. 05Signed off. A PASS / FAIL / INCOMPLETE validation report — nothing estimated, nothing fabricated, blanks shown as blanks — that an operator and a separate verifier both sign before go-live.
§ 02The overnight arithmetic — measured, not promisedof 05

Feasibility is not a vibe — it's records × the Bulk API's published limits × parallelism. Records to roughly 50 million rows sit under every Bulk API 2.0 daily cap. Files are the real overnight risk, and we say so out loud.

Records — well under every cap (CSV assumed ~2 KB/record)

Org sizeCSV volumeResult pages @1 GBvs the 1 TB/day results cap
1 M records~2 GB~20.2%
10 M records~20 GB~202%
50 M records~100 GB~10010%

Bulk API 2.0 daily caps: 150 M records ingested, 1 TB query results, 10,000 query jobs per rolling 24 h, up to 25 concurrent jobs so objects extract in parallel. The binding constraints on records are the per-object 20-minute query timeout and, for a single very large object, a 15 GB single-job retrieval ceiling — split by date/Id range past that. A single object above ~10 M rows is rehearsed against a sandbox before any overnight window is quoted.

Files — one API call each, against your daily entitlement (the honest edge)

Files in orgREST GETs neededOn an EE org with 150,000 requests/day
50,00050,000Fits in one day — about 33% of the pool.
500,000500,000Exceeds a single day — a multi-night run or a purchased-calls bump.
5,000,0005,000,000Multi-week at that entitlement — you raise the entitlement first.

Each attachment is one authenticated REST GET against your standard API allocation — it cannot be batched, one call per file. So "overnight" is realistic for records at typical mid-market sizes, and for files only when the file count fits your org's REST entitlement — otherwise files are a multi-night pass or a temporary entitlement increase. (An Enterprise org with 50 user licenses gets 100,000 + 50 × 1,000 = 150,000 requests/24 h.)

§ 03The ritual you're replacingof 05

The alternative is a person driving Salesforce Data Loader by hand: partitioning CSVs, fighting encodings and timezones, building VLOOKUP sheets to re-parent children, and reconciling error files with no way to resume. Every ritual on the left becomes a pipeline step on the right.

Their ritual — Data Loader, by handOur pipeline step
Hand-partition CSVs under a ~5 M-row ceilingBulk API 2.0 jobs, paginated automatically
Re-encode every CSV to UTF-8 to dodge BOM/Excel errorsTyped load straight from the API, no spreadsheet
Dates shift by your machine's timezoneUTC carried into timestamptz — no drift
Build External-Id + VLOOKUP sheets to re-parent childrenRelationships ordered from your schema automatically
UNABLE_TO_LOCK_ROW: toggle Serial Mode, guess a batch sizeLoads in dependency order into your own database
Reconcile success/error files by hand; no resumeIdempotent upsert — safe to re-run
No checksums, no verification, no reportRow-count + FK + content checksums + a signed report
Load order is a human runbookLoad order computed from your object graph
§ 04The signed validation report — and exactly what it coversof 05

Nothing is estimated and nothing is fabricated. Missing evidence renders as not provided and forces INCOMPLETE — never a fabricated zero. Here is precisely what the verdict checks today, said without inflation.

What each table is checked for. A row-count reconcile, a foreign-key orphan scan that must come back 0, a primary-key-set checksum, a per-value content checksum, and a blank-date guard so an empty Salesforce date loads as NULL, never a fabricated now().

The verdict states. PASS, FAIL (on any orphan or count mismatch), or INCOMPLETE (on any missing count or checksum). Two people sign: the operator runs it, and a separate verifier signs — one person cannot be both.

What the checksums do — stated honestly. Per-column checksums are recorded on both the Salesforce and PostgreSQL sides and compared during the sign-off review; the run is not marked complete until counts, foreign keys, and checksums are reconciled. We deliberately do not claim the run "auto-fails on a checksum mismatch" — automatic source-vs-loaded comparison inside the verdict is on our build list, so today that reconciliation is a step in the human review, not an automated gate. We would rather under-claim here than overstate what the machine does.

§ 05What we need from you — said plainlyof 05
  1. AAPI entitlement. Your Salesforce edition's daily API allocation sets how fast files move; a very file-heavy org may need a few nights or a temporary allocation bump.
  2. BA short write-freeze window. For the final delta and cutover — typically a planned evening, not days. The delta pull watermarks on SystemModstamp, so it catches every save.
  3. CA sandbox rehearsal. We do a full dry run against a sandbox first, so cutover night has no surprises.
  4. DVerification. Per-value checksums are recorded on both the Salesforce and PostgreSQL sides and compared during the sign-off review; the run is not marked complete until counts, foreign keys, and checksums are reconciled.

Honest scope: user identity is preserved as the source Salesforce Id, and mapping those to new app users is a defined follow-up step; Activities (Tasks/Events) and compound Address/Geolocation and multi-currency columns are known, tracked fidelity items — we flag what a run does not yet carry rather than imply 100% object coverage. No time estimates beyond the arithmetic above.