Switching partner platforms without losing your history
A step-by-step migration off Rewardful, PartnerStack or FirstPromoter: what carries over, what doesn't, how to keep old links and codes earning, and how to avoid paying a partner twice during the cutover.
Most of the risk in switching partner platforms isn’t technical. It’s bookkeeping. Partners have links in old posts, codes in old videos, money they’re still owed, and commissions due on customers they brought you months ago. Get the switch wrong and some of them stop earning, or get paid twice.
This guide is for teams moving off Rewardful, PartnerStack or FirstPromoter. It covers what carries over to OpenPartner, what doesn’t, and the order to do it in. We’ll be specific about where OpenPartner has an importer and where it doesn’t, because a migration plan built on an importer that doesn’t exist fails on the worst possible day. Everything we say about the other three platforms comes from their public help centers, API docs and terms as of October 2026. Your plan or contract may differ, so check anything you’re relying on.
If you’re still deciding whether to switch at all, start with Rewardful in 2026: when to stay, when to switch or Rewardful alternatives.
What carries over, and how
“History” covers several kinds of records, and each one moves differently:
- Partner roster: yes. CSV import, in one fixed format.
- Custom commission rates: yes. Set per partner, in the dashboard or by API.
- Referral links: the keys usually carry over, the URLs change. Recreate each link with the partner’s old key, and forward old URLs on domains you control.
- Promo codes: yes, if they’re in your Stripe account. Attach each existing code to its partner.
- Payout details: partly. Partners re-enter them, or you enter them for partners.
- Unpaid balances: no importer. Pay them on the old platform before you leave.
- Commissions still due on existing customers: no importer. Let the old platform pay them out, or settle them yourself.
- Past clicks, referrals, commissions and payouts: no importer. Keep the old platform’s exports as your archive.
Be clear-eyed about the last three. OpenPartner has no importer for another platform’s referrals, commissions or payouts. And as of October 2026, we found no documented way to export individual clicks from any of the three platforms: at best you get click totals in reports. Your old reports stay accurate. They just live in files you keep, not in your new dashboard.
Step 1: Export everything while you still have access
Do this first, before you change any tracking. What each platform offers, from their public help centers and API docs as of October 2026:
- Rewardful. The Payouts page downloads a CSV (PayPal Mass Pay, Wise batch or a generic format). We found no self-serve export of the affiliate or referral lists. Rewardful’s cancellation article tells you to save reports and affiliate details before you cancel, and to ask support for a list of referrals. The REST API lists affiliates, referrals (each with its Stripe customer ID), commissions and payouts. It has no clicks endpoint. Once the account closes, links and commission tracking stop.
- FirstPromoter. Reports download as CSV or JSON, with clicks as totals. The bulk payout CSV includes each affiliate’s payout method and saved details. The v2 API covers promoters, referrals, commissions, payouts and promo codes.
- PartnerStack. Partner Management → All partners → Export records emails you CSVs of partners, commissions, transactions, customers and more. The download link expires after 30 minutes. Partners’ payout details aren’t in it. Clicks appear only in the link performance report. PartnerStack’s current public SaaS agreement gives you 30 days after termination to download your data, after which the account goes inactive. Read your own signed agreement before anything else: the same document says payments to partners in your programs go through PartnerStack, and that partners PartnerStack introduced to you can’t be paid outside it. Older contracts may say something different.
Whatever the platform, leave with:
- Partners: name, email, referral token, promo codes, any custom rate, payout email.
- Referred customers: each customer’s Stripe customer ID (or your own user ID) and which partner it belongs to. You’ll need this list during the cutover. If your app has its own user IDs, also export your own table of user ID to Stripe customer ID, so you can match lists that use different IDs.
- Commissions: every one, with its status (pending, due, paid).
- Payouts: every one.
- Click and link reports, even if they’re only totals.
Store them somewhere you’ll still find them in three years. They’re your record for tax questions and partner disputes. You’ll take a second, final export after the cutover too (step 7), so don’t cancel anything yet.
Step 2: Set up programs that match what partners signed up for
Recreate each program’s terms before you invite anyone: the commission rule, whether it’s recurring and for how many months, the holdback before commissions can be approved, and the attribution window. See Programs.
Match the window to what partners had. OpenPartner defaults to 60 days. Rewardful and FirstPromoter also default to 60, but you may have changed it, and PartnerStack’s cookie lasts 90 days. A window that quietly gets shorter during a migration is a pay cut partners will notice.
For subscriptions, leave Renewals stay with the partner who earned the customer on, so renewals keep paying the partner after the window closes.
Step 3: Import the partner roster
OpenPartner’s partner importer reads one CSV format. It doesn’t recognise any other platform’s export, so you reshape yours to four columns:
email,name,activatedAt,metadata[email protected],Ada Lovelace,2025-03-02T00:00:00Z,"{""rewardfulId"":""aff_123"",""token"":""ada""}"[email protected],Grace Hopper,,email and name are required. activatedAt is optional and defaults to now. metadata is
optional JSON. Put the partner’s old platform ID and referral token there: you’ll want them
for reconciling later. Keep each row on one line, because the importer doesn’t accept line
breaks inside quoted fields.
Preview it with a dry run, then run it for real. You need an API key with partners:write,
from Admin → Integrations:
curl -X POST "https://app.openpartner.dev/api/t/<your-slug>/import/partners-csv?dryRun=true" \ -H "Authorization: Bearer $OPENPARTNER_KEY" \ -H "Content-Type: text/csv" \ --data-binary @partners.csvThe response reports every row as created, skipped or invalid. Drop ?dryRun=true to import.
What the importer does:
- Creates partners already active and sends them nothing. No invite email, no webhooks. You tell partners about the move yourself.
- Grants every program you currently have. Narrow a partner’s programs afterwards if they shouldn’t see all of them.
- Skips emails that are already on your roster, so running it again is safe.
- Applies your portal access default. Running an in-app referral program where partners never see our portal? Turn off Admin → Settings → Partner portal access before you import.
What it doesn’t do: set custom rates, create links, attach promo codes or store payout details. Those are the next steps. On the hosted plan, importing needs an approved account, and a plan once your trial ends.
Custom rates. Partners on a special deal get their own terms, for all programs or for one,
from their page in the dashboard or with PUT /partners/:id/commission-snapshot and
PUT /partners/:id/programs/:programId/commission (an admin key). See
Commission terms.
Step 4: Recreate links, and keep the old ones working
Give each partner a link whose key is their old referral token:
curl -X POST "https://app.openpartner.dev/api/t/<your-slug>/partners/<partnerId>/links" \ -H "Authorization: Bearer $OPENPARTNER_KEY" \ -H "Content-Type: application/json" \ -d '{ "linkKey": "ada", "programId": "<programId>" }'That takes a key with links:write. Link keys are 3 to 64 characters of letters, digits,
hyphens and underscores, and each one is unique across your account. A partner whose token
was ada keeps ada, but the URL changes to https://app.openpartner.dev/r/<your-slug>/ada,
or https://partners.yourbrand.com/r/ada with a white-label domain.
Some tokens won’t carry over: one that’s too short, or has other characters in it. Give those partners a new key and note the old one next to it: the forwarder below maps old to new, so their published links keep working. A token that meant different partners in two old campaigns is harder, because nothing in the old URL says which campaign it came from. Pick one partner for it, and settle the other’s clicks with them directly.
What happens to the old URLs depends on whose domain they’re on.
Rewardful and FirstPromoter: your domain, so you can forward them
Rewardful links are your own site with ?via=ada on the end. FirstPromoter’s use ?fpr=ada.
Both platforms track with a script on your site. Once you remove that script, nothing reads
the parameter. Because the site is yours, you can forward those visits to the partner’s new
link:
<script> (function () { // Old token → the link key you created for it in OpenPartner. // Any token not listed here loads the page as normal. var keys = { ada: 'ada', grace: 'grace', jo: 'jo-smith' }; var params = new URLSearchParams(window.location.search); var token = params.get('via') || params.get('fpr'); // A visit that came back through OpenPartner carries cref: never forward it again. if (!token || params.has('cref') || !Object.prototype.hasOwnProperty.call(keys, token)) return; params.delete('via'); params.delete('fpr'); var rest = params.toString(); // UTM tags and sub_id are recorded on the click var url = 'https://app.openpartner.dev/r/<your-slug>/' + encodeURIComponent(keys[token]); window.location.replace(rest ? url + '?' + rest : url); })();</script>Three things to know before you rely on it:
- Only forward keys you created. OpenPartner’s link router answers 404 for a key it doesn’t know, which is why the script checks its list first. Include partners who’ve gone quiet: their old posts still get clicks.
- Visitors land on the program’s destination URL, not the page the old link pointed to.
A link to
yoursite.com/pricing?via=adaarrives wherever the program sends traffic, and the old URL’s other query parameters and#fragmentdon’t reach your page. UTM tags andsub_idare kept on the click record, but your own analytics won’t see them on landing. If one partner sent most of their traffic to a single page, allow your domain for deep links on the program and create that partner’s link with adestinationUrlpointing at that page. Don’t putviaorfprin any destination URL. - FirstPromoter accepts other parameters too (
via,ref,fp_refand a few more). Check which ones your partners actually used, and add them to the script.
Put the forwarder live on cutover day, when you remove the old script. Before that, the old platform still needs to see those visits.
PartnerStack: its domain, unless you set up your own
PartnerStack links live on PartnerStack’s domains, such as yourcompany.partnerlinks.io or
yourcompany.grsm.io, unless you set up a custom link domain. You can’t redirect links on a
domain PartnerStack owns, so plan as if they stop working when your account does. Give
partners their new links well before the cutover, with a date.
If you did set up a custom link domain, such as try.yourcompany.com, you control its DNS.
Before you leave, pull each link’s path and partner from PartnerStack’s export or API. After
the cutover, point the hostname at a small redirect service of your own that sends each old
path to the partner’s new link.
Step 5: Attach existing promo codes to partners
Rewardful and FirstPromoter both create each affiliate’s promo code as a promotion code in your own Stripe account. The code lives in your Stripe, not theirs. (We found nothing about PartnerStack and promo codes in its public docs, so check where yours live.) One warning from Rewardful’s docs: removing a campaign’s coupon deactivates the affiliate codes made from it, so leave it in place while you migrate.
On the hosted plan, first connect your Stripe account under Admin → Integrations → Connect Stripe (how the Stripe App works). Then, as an admin, open Admin → Partners → [partner] → Coupons and add the partner’s existing code. OpenPartner normally refuses a code that already exists in your Stripe, so a partner can’t claim your sitewide promo. An admin can attach one on purpose. From then on, a checkout or invoice that carries that code credits the partner. The discount itself stays whatever the existing promotion code gives.
- Codes are letters, digits and hyphens, 3 to 40 characters. Matching ignores case. A code with an underscore or a space can’t be attached.
- Record one redemption early. After five codes, OpenPartner pauses adding codes until it has seen one redemption, so it knows codes actually attribute. Put one purchase through a mapped code before you attach the rest.
- Renewals carry the code too. A subscription whose discount is still running has the code on every invoice, so OpenPartner starts crediting the partner from the next renewal. If the old platform is still paying on that customer, that’s double pay (see step 7). OpenPartner also treats the first payment it sees as the customer’s first: an On first rule fires on it, and a months cap counts from it.
Step 6: Swap the tracking
The new setup replaces the old one piece by piece:
- The script. Remove Rewardful’s, FirstPromoter’s or PartnerStack’s script and add
OpenPartner’s. On the hosted plan with Stripe connected, that’s one tag:
<script src="https://app.openpartner.dev/op.js" data-api-url="https://app.openpartner.dev/api/t/<your-slug>"></script>. It adds the partner click to Stripe Payment Links, Pricing Tables and Buy Buttons on your pages, and keeps it in a first-partyop_refcookie. Paste it above Stripe’s own embed script. It leaves a reference that’s already on a link or embed alone, so remove any referral value the old setup hard-coded. Custom Payment Link domains and checkout on another subdomain each need one extra attribute (see the Stripe App docs). - Stripe Checkout. If your server sets
client_reference_id: Rewardful.referral(or similar) when it creates a Checkout Session, change it toclient_reference_id: req.cookies.op_ref. - Signups. Where you called the old platform at signup (for example FirstPromoter’s
referral call with the user’s email), call
openpartner.identify(user.id)instead, with your own user ID. See Install + identify. If you create Stripe customers yourself, outside Checkout, also setmetadata.openpartner_user_idon each one to that same user ID before its first payment. That’s how a Stripe payment finds the user, and the partner behind them. - Revenue. With the Stripe App connected, OpenPartner records Checkout purchases,
invoices, renewals and most refunds from Stripe. Stop forwarding those from your server, or
they’re counted twice. Keep reporting what Stripe doesn’t cover with
POST /attribution/events: payments your backend charges directly with a PaymentIntent, and payments outside Stripe. The Stripe App docs list the refunds you report yourself.
Test the whole path before cutover: click a partner link, sign up, pay, and check the commission appears. To test with Stripe test payments, connect a test-mode Stripe account under Admin → Integrations if it’s offered there. A live connection doesn’t record test payments, so check the live setup with its own first purchase.
Note that Stripe Checkout has one client_reference_id, and both platforms want it. That’s
one reason not to run two platforms on new traffic at the same time.
Step 7: Cut over on a date, with one platform per customer
The rule that prevents double payment: every referred customer belongs to exactly one platform.
- Customers referred before the cutover stay with the old platform. It already knows them. By default Rewardful creates a commission on every paid invoice, and FirstPromoter’s commission period is the customer’s lifetime, as long as your account there is active. OpenPartner can’t take these customers over cleanly, because it has no import for referred customers.
- Every click from the cutover date goes to OpenPartner.
On the day:
- Remove the old script. Add OpenPartner’s script and the link forwarder.
- Switch
client_reference_id. - Email partners their new link, their portal sign-in, and what happens to the commissions they’re already earning.
- Keep the old account open, and leave its coupons alone.
The visitors in between
One group falls through any cutover: people who clicked an old link shortly before it and buy after it. They arrive without the old parameter, so the forwarder can’t help, OpenPartner never saw their click, and the old script that remembered them is gone. Some may already be leads or trials on the old platform, and whether it still credits them when they pay depends on how it linked them to a Stripe customer.
New cases stop once the old cookie window has passed (60 to 90 days by default on these platforms). Leads and trials from before the cutover can still pay later than that. Don’t pretend the gap isn’t there:
- Cut over at a quiet point, not in the middle of a partner’s launch.
- When the old window has passed, take a final export from the old platform: leads, referrals, customers, commissions.
- Compare its leads and trials with customers who paid after the cutover, and credit the matches by hand, on the old platform if it lets you, or as a bonus in OpenPartner. Keep checking open trials until they pay, or until a cut-off date you’ve announced.
- Visitors who never became a lead or trial won’t show up in any export. Tell partners how to claim a sale they can show you sent, and by when.
How the tail ends
Decide which of these you’re doing before the cutover email goes out, because partners will ask:
- Run it out. Keep the old subscription until the last commissions it owes are paid. How long that takes depends on each customer’s remaining commission term, late conversions from leads and trials, holds and refunds. A program capped at 12 months of commission still runs past 12 months, because some customers pay their first invoice after the cutover. You pay for two platforms for a while, but nobody’s terms change.
- Settle it. Pick an end date and pay everything owed on the old platform. For commissions partners would have earned after that date, either pay them yourself or, if your program terms allow it, tell partners well in advance that they end. Paying them through OpenPartner as a bonus works, but on the hosted plan a bonus is billed like a bounty: 10% of the bonus on Revshare, 5% on Flex.
Reconcile before every payout run
While both platforms are paying, check for overlap before each OpenPartner payout run, not after. When you pay partners yourself, a run marks its commissions paid as soon as it’s created, before you send any money, and a paid commission can’t be reversed. Runs happen on the payout cadence you set (weekly unless you change it), so either reconcile before each scheduled run or set the cadence to manual until the tail is over.
Two things can put one customer on both platforms: a promo code still riding on their invoices (step 5), or an old customer clicking a partner’s new link. Mapping codes doesn’t enforce one platform per customer, so expect the first kind for as long as old discounts run.
OpenPartner’s commission list doesn’t name the customer, so use the per-table exports: each
Commission points to an Attribution, which points to the Event carrying the customer’s
user ID (the Stripe customer ID, when Stripe recorded the payment and no
openpartner_user_id is set). Translate both lists to the same ID with the user ID to
Stripe customer ID table from step 1, match them, and reverse each duplicate on one side. In OpenPartner that’s POST /commissions/:id/reverse,
which works while a commission is accrued or approved. A holdback at least as long as the time
between your checks gives you room to catch them.
Step 8: Payouts, and who pays whom
Rewardful and FirstPromoter: by default you already pay partners yourself, by PayPal, Wise
or another method, so little changes. On hosted OpenPartner you pay partners yourself too,
using PayPal or Wise batch files from the payout run, and mark each payout paid. Partners add
their payout details in the portal. You can also enter them on a partner’s behalf, in the
dashboard or with PUT /partners/:id/payout-profile. Active partners with portal access get
an email with a masked copy of every change, so tell them first. Partners without portal
access get nothing, so tell them through your own app. Payout exports carry bank details:
handle those files the way you’d handle the partner list itself.
One exception: as of October 2026, Rewardful also offers optional Managed Payouts, where Rewardful pays affiliates and they withdraw from their Rewardful dashboard. If you use it, treat the move like PartnerStack’s below.
PartnerStack is the bigger change. PartnerStack invoices you, and partners withdraw from their own PartnerStack accounts. Their payout and tax details sit with PartnerStack and aren’t in your export. Ask every partner to add payout details before your first OpenPartner payout run. And go back to your PartnerStack agreement (step 1) before you move or pay anyone outside it, especially partners PartnerStack introduced to you.
Step 9: Leave old payments where they are
It’s tempting to replay old payments into OpenPartner so the new dashboard shows your full history. Don’t. OpenPartner doesn’t credit a payment to a click recorded after it (beyond a few minutes’ allowance for clock differences), so old payments sent with their real dates generally can’t attribute to the links you just created: they only add uncredited rows to your reports. Payments sent without their dates look like new ones, and could pay commission again. Keep history in the archive from step 1, and join it with OpenPartner’s export in your own reporting if you want one view.
And if you ever leave us
Two things put your history in an archive instead of moving it: the old platform’s documented exports give you its decisions (this customer belongs to that partner) but not the clicks behind them, and OpenPartner has no importer for another platform’s decisions anyway.
Leaving OpenPartner avoids the first of those. It keeps the raw layers and the decisions side
by side, and exports both. With an admin key, GET /export.json
or GET /export.sql downloads the whole program in one file: partners, programs, links,
coupons, the raw click log, identities, events, attribution, commissions, adjustments and
payouts. Any single table also downloads as JSON, CSV or SQL. A bundle from the hosted plan
restores into a self-hosted instance, so moving off is a supported path.
It isn’t everything. Credentials, API keys and admin accounts are never exported, and a few records (your brand profile and the hosted billing ledger among them) aren’t yet. Backups and portability lists exactly what is and isn’t in the bundle. Another vendor won’t read our format out of the box either. But it’s documented raw rows, not a summary of decisions. Whoever reads it next can trace each commission to what earned it: the click behind a link sale, the code behind a coupon sale, or the bonus record for a bonus.
Checklist
- Read your contract (PartnerStack especially). Export partners, referred customers, commissions, payouts and reports from the old platform.
- Recreate programs with the same terms and attribution window.
- Import the roster. Set custom rates.
- Create a link per partner with their old token where it fits. Plan forwarding for old URLs.
- Connect Stripe. Attach existing promo codes after one redemption.
- Swap the script,
client_reference_idand the signup call. Test end to end. - Cut over on a date: old customers stay with the old platform, new clicks go to OpenPartner. Credit the in-between visitors by hand after a final export. Reconcile before every payout run.
- Collect payout details, especially from PartnerStack partners.
- Keep the old exports. Don’t replay old payments.
Moving from Rewardful specifically? When to stay, when to switch covers the reasons in more depth. Want to talk through your program’s cutover first? Email [email protected], or start free.
Sources
Competitor details checked on October 21, 2026:
- Rewardful: cancelling your subscription, paying commissions, promotion codes, Stripe permissions, Managed Payouts, API reference.
- FirstPromoter: reports, paying promoters, coupon codes in Stripe, referral parameters.
- PartnerStack: exporting partner records, custom link domains, paying partners, SaaS agreement.
Rewardful
PartnerStack
FirstPromoter