Getting e-commerce orders into Salesforce reliably: chained Queueables and external IDs

Syncing a storefront's orders, payouts, and transactions into Salesforce fails when you fight API rate limits with brute force. The reliable pattern: a sequenced chain of Queueable jobs with idempotent external-ID upserts.

A direct-to-consumer retailer ran sales through a hosted storefront and operations through Salesforce, and the finance team lived in the gap between them, reconciling orders against payouts in spreadsheets. The integration request sounded simple: get orders, transactions, payouts, and products into Salesforce. The storefront’s API rate limits and pagination made “simple” the wrong word.

The short answer

The sync is a chain of five Queueable jobs, each with one responsibility, each enqueueing the next:

  1. Orders
  2. Order transactions
  3. Payouts
  4. Payout transactions
  5. Enrichment (line items and supplementary detail from a secondary API)

Each job pulls one page from the storefront API, writes it to Salesforce, and either enqueues itself for the next page or hands off to the next step. The sequence is deliberate: transactions reference orders, payout lines reference both, so loading order eliminates the orphaned-reference reconciliation that plagues parallel loaders.

Idempotency is the entire game

Every storefront entity carries a stable unique ID. Each maps to an external ID field in Salesforce, and every write is an upsert against it. The consequences are bigger than they look:

  • A failed run re-runs from the top with zero risk of duplicates.
  • A historical backfill and the nightly sync are the same code.
  • “Did record X make it across?” is answerable with one query.

Integrations without this property accumulate reconciliation debt forever. With it, the sync is boring, which is the highest compliment an integration earns.

Respecting rate limits without babysitting

Storefront APIs lease capacity (per-second budgets, page cursors) and punish bursts. Because each Queueable processes one page and re-enqueues, the platform’s natural job spacing keeps consumption inside the budget with no sleep loops or clock math. Configuration lives in custom metadata: which endpoints, what page sizes, which fields map where. Tuning the sync is an admin edit.

What finance got

Orders, fees, refunds, and payouts reconciled inside Salesforce, where the rest of the business already lived. Month-end close stopped requiring a spreadsheet ritual, and revenue questions became report questions. Once payout lines linked to their orders, per-product margin reporting fell out almost for free.

Design rules worth stealing

  • One job, one entity type. The chain reads like the data model, and failures point at exactly one step.
  • Sequence dependencies; never load children before parents.
  • Externalize configuration so field mappings evolve without deployments.
  • Log per page, not per run, because “step 3, page 14 failed” is actionable and “the sync failed” is an afternoon of forensics.

Common questions

What is the best way to sync e-commerce orders into Salesforce?

A sequenced chain of asynchronous jobs, each handling one data type (orders, then transactions, then payouts), each respecting the storefront API's rate limits, and each writing with external-ID upserts so re-runs can never create duplicates.

How do you avoid duplicate records when an integration retries?

Idempotency through external IDs. Every storefront entity carries a unique ID; storing it in an external ID field and using upsert instead of insert makes any retry safe by construction rather than by careful coding.

Why use Queueable Apex instead of a nightly Batch job?

Queueables chain: each job processes a page of data and enqueues the next step, which lets the sync respect external API pagination and rate limits naturally. Batch Apex shines for crunching data already inside Salesforce; chained Queueables fit consuming paged external APIs.