← All stories

BRANCH · ef-093-salesforce-campaign-import-export

Salesforce campaign import/export

EF-093Persona: OrganizerRoots in: event-setupMatrix: Absent gap probe

Campaign member import/export should move Salesforce campaign members into Voyage registrations and export Voyage registrations back as campaign members, with email dedupe, member-status mapping, and explicit per-row outcomes. The matrix marks this absent, so the story ships as a gap probe. Tier-3 tightening: Salesforce campaign import/export now references ui-async-job-admin-page and ui-csv-import-flow for job and import foundations.

Happy path / Lifecycle

  1. Choose a Salesforce campaign.

    Organizer opens campaign sync, selects a campaign, previews member count, and maps Salesforce member status to Voyage registration status.

  2. Import members.

    Campaign members become registrations, deduped by normalized email, with row-level import outcomes.

  3. Export registrations.

    Voyage registrations are written back to Salesforce campaign members and an audit row records counts and transaction id.

Failure modes

Expired token refresh

Expired OAuth token refreshes and retries the campaign member request.

429 backoff

Salesforce 429 backs off with jitter without compounding independent sync runs.

Webhook replay

Campaign callback replay does not duplicate imports or exports.

Schema drift

Unknown CampaignMember fields are ignored; optional status fields default.

Missing credential

Invalid credential shows re-auth CTA and no stack trace.

Sandbox isolation

Mock fixture cannot read live Salesforce data.

Audit row

Each import/export run writes tx-id, status, imported, exported, skipped, and failed counts.

Partial rollback

Mid-batch failure either rolls back or shows durable per-row partial success.

Email dedupe

Duplicate emails map to one registration with conflict outcome.

Status mapping missing

Unmapped member status blocks commit until the organizer maps or skips it.

Export conflict

Salesforce duplicate campaign member conflict is surfaced per row.

Parity gap

Gap panel remains visible until campaign import/export exists.

Stable test attributes

Visibility teeth. Preview, commit, outcome, and gap attributes must be visible whenever active.

data-testWherePurpose
salesforce-campaign-sync-panelcampaign syncmain surface
salesforce-campaign-pickercampaign synccampaign selection
salesforce-member-status-mapcampaign syncstatus mapping
salesforce-import-previewcampaign syncrow preview
salesforce-import-commit-ctacampaign syncimport commit
salesforce-export-commit-ctacampaign syncexport commit
salesforce-row-outcome-tablecampaign syncper-row outcomes
salesforce-campaign-gap-panelcampaign syncabsent gap marker

Agent test plan

- salesforce-campaign-sync-opens
- salesforce-campaign-preview-import
- oauth-refresh-on-expired-token
- rate-limit-backoff
- webhook-replay-idempotent
- schema-drift-degrades-gracefully
- missing-credential-graceful-error
- sandbox-vs-prod-isolation
- audit-log-row-per-sync
- partial-import-rollback
- email-dedupe
- member-status-map-required
- export-conflict-row-outcome
- salesforce-campaign-gap-probe