Back to Blog
Integrations

Migrating Off a Legacy Affiliate Network Without Losing Your History

June 21, 20268 min read

Ask any advertiser why they are still running their publisher program on a platform they have quietly hated for two years, and the answer is almost never the software. It is the history. Years of offer configurations, a publisher roster you spent real money recruiting, and the performance record that proves which relationships actually pay — all of it sits inside the old system, and the thought of walking away from it feels like abandoning a patient mid-treatment. So you stay. The switching cost was never the migration weekend. It was the fear that migration means starting your medical chart over from a blank page.

That fear is rational, because most migrations really do work that way. You export a spreadsheet, hand-map columns at two in the morning, import once, and pray nothing changed on the source before you flipped the switch. If a field shifts, if a publisher gets duplicated, if last week's numbers silently fail to come across, you find out weeks later when a payout looks wrong. The whole thing is a one-shot procedure with no follow-up visits.

We built network imports to work the opposite way — as a standing connection to your legacy network that you can run, re-run, and trust, rather than a single risky transplant. This post walks through how that actually works under the hood, because the architecture is the reason the guarantees hold.

The lock-in is the data, not the login

When people say a platform has "high switching costs," they usually picture retraining a team or rebuilding integrations. On a publisher program, the gravity is your operational record:

  • Offers — every commission structure, destination URL, and targeting rule you tuned by hand.
  • Publishers — the roster you approved, vetted, and grew, each with an identity on the old network.
  • Performance — the feed of what those publishers actually drove, which is the only thing that tells you who to keep investing in.

Leaving that behind is not a data-entry inconvenience. It is throwing out the diagnosis you paid for. So the design goal was never "get the rows across once." It was: pull your offers, your publishers, and your performance feeds out of the legacy network on a schedule, keep them faithfully in sync, and never make you choose between switching platforms and keeping your chart.

One contract, one adapter per network

Every affiliate network speaks its own dialect. One uses an API key in a query string against a per-account subdomain; another paginates through a self-hosted endpoint; the next wraps everything in a different envelope with different field names. If you let those differences leak into the import pipeline, you get a tangle of special cases that nobody dares touch.

Instead, the system defines a single contract — a network adapter interface — that every network must satisfy. The contract is deliberately small and describes only what an importer needs:

  • Bind to a specific connection and its stored credentials.
  • Declare which feeds it can actually pull (offers, publishers, or a subset).
  • Authenticate, and fail loudly with a clear reason if the credentials are wrong.
  • Stream remote offers and publishers as they arrive, yielding records rather than loading an entire catalog into memory.
  • Map each remote record into our own offer (Program) and publisher shape.
  • Supply its own default field mappings and detect when the source's schema has shifted.

Every network is then one implementation of that contract, and nothing else in the system knows or cares which network it is talking to. A shared abstract base handles the unglamorous plumbing — the HTTP client, timeouts, and the translation of raw transport failures into a clean vocabulary of authentication errors, rate limits, transient blips, and permanent rejections. A concrete adapter overrides only what makes its network unique.

At runtime, a registry resolves the right adapter for a connection by looking up the network's configured adapter class. This has a quietly useful property: a network we have researched but not yet built simply has no adapter class configured, so the registry raises a clear, specific signal that the UI renders as "configuration unavailable" rather than crashing an import. The roadmap of networks is visible without pretending they all work yet.

AffTrack is the adapter that proves the pattern in production today. Its implementation binds to a per-account subdomain (sanitized down to a safe hostname label so a connection can never be pointed at an arbitrary host), authenticates with an API key, streams the offer feed, and translates each offer's payout vocabulary — cost-per-action, cost-per-lead, revshare, and the rest — into our own commission types. It even preserves each offer's target countries so that intelligence survives the move. Because AffTrack exposes offers but not an importable affiliate list, its adapter declares offers as its only supported feed, and the pipeline honors that declaration: toggling on a feed the network cannot serve is a documented no-op, not an error. More networks are on the roadmap, and each one is a new adapter behind the same contract — never a rewrite of the engine.

Idempotent re-runs are the whole safety story

Here is the property that turns a scary one-shot migration into a routine you can run before your coffee is cold: running the import twice does not create anything twice.

Every imported record is keyed by the combination of your organization, the source network, and the record's external ID on that network. When a sync sees an offer it has imported before, it does not insert a second copy — it locates the existing one and reconciles it. The test suite runs the same fixture through the pipeline twice and asserts that no duplicate rows appear, because that is exactly the failure everyone has been burned by on a legacy export.

That single guarantee is what makes everything else safe. You can point a connection at your old network, run an import, look at the result, adjust your field mappings, and run it again — as many times as you like — without accumulating garbage. A migration stops being a cliff you jump off once and becomes a dial you turn until it is right.

Reconciliation is not blunt, either. Each connection and each individual record carries an overwrite toggle, so you can let the source keep a record fresh or freeze your local edits in place. And imported offers respect the same lifecycle locks the rest of the product enforces: once an offer moves past draft, its commission terms and destination defaults are protected, so a re-sync can never quietly rewrite the economics that publishers are already earning against. When the importer skips a locked field, it records that it did and why.

Nothing happens silently

Every non-trivial outcome of a run is written down as a run item — created, updated, skipped, errored, or tombstoned — with a reason and, for updates, a field-level diff of what changed. If one malformed offer comes across with a broken shape, it records a single per-record error and the run keeps going, rather than aborting the whole procedure over one bad row. The result is an operating record you can actually audit: not "import complete," but exactly which offers changed, which publishers were held back, and which records the source stopped sending.

That last case matters more than it sounds. When an offer or publisher disappears from the legacy network, the importer does not delete your local copy and erase the history with it. It tombstones the record — an offer is paused, a publisher is suspended — and logs it as "absent from source." The relationship and its record survive; the status is reversible from the ordinary editing screens with a single change. Disappearing from the feed is treated as a status decision you can review, never as a destructive purge.

New arrivals get the same caution in the other direction. Imported offers land as drafts and imported publishers land as pending, so they flow through your normal review and approval gates before anyone can run traffic or earn against them. Migrating your roster does not mean blindly trusting whatever the old system labeled "active."

A standing connection, not a one-time event

Because re-running is safe, the natural next step is to stop running it by hand. A scheduled command fans the import out across every active connection, dispatching a run per integration so your offers and publishers stay current with the source on a daily cadence while you finish moving off it. During the overlap period — when some campaigns still live on the legacy network and some already run on ours — both stay reconciled instead of drifting apart. The cutover becomes a gradual transfer of care rather than a hard stop.

And when the legacy network changes its own field shape mid-migration, as older platforms tend to do without warning, the adapter compares the current schema against the last one it saw and surfaces a structured diff. Added fields are informational; a removed field your mapping depends on is raised as a warning. You get told the source moved under you, instead of discovering it through corrupted data three runs later.

The point of all this

A pluggable adapter behind a small, strict contract; a registry that resolves the right one per network; idempotent re-runs that make repetition safe; a full record of every create, update, skip, and tombstone; and a scheduler that keeps the whole thing current — none of that is architecture for its own sake. It exists so that the sentence "we're switching platforms" no longer has to end with "and we'll lose our history." Your offers, your publishers, and their performance are yours. The right migration tool treats them like a chart to be carried forward intact, and as more network adapters come online, the set of legacy platforms you can walk away from — without leaving your years of data behind — only grows.

See it in your own program

Start your free 7-day trial and put these ideas to work — no credit card required.

Start free trial

We use essential cookies to run the platform. With your consent, we also collect privacy-friendly, first-party usage analytics — no third-party trackers and no cross-site tracking cookies. Choose “Essential Only” to opt out of analytics. Privacy Policy