Moving a loyalty program to Cascade

Switching loyalty platforms is mostly a data problem and slightly a promise problem. The data is a CSV of balances. The promise is that a customer who had 1,200 points yesterday still has something worth the same today, and that nobody loses a reward they were three purchases away from.

This guide is the order to do it in. It works whatever platform you are leaving, because every one of them exports a spreadsheet of email addresses and balances, and that is all Cascade needs. If you are coming from LoyaltyLion, Smile or Rivo, Cascade reads their exports as they come: see Migrating from a named platform.

What moves and what does not

Moves through the CSVMoves through the APIDoes not move
Member enrollment, matched by emailTransaction historyStamp card progress
Points balancesCoupon codes your old platform issued
Tier placementReferral relationships

The CSV import is balance-only, and that is the right default. A member's points arrive as a single opening adjustment, so their activity feed in Cascade starts clean rather than replaying years of entries from a system with different rules. What you keep is the number that matters to the customer.

Transaction history is a separate, optional step through the history API. It is worth doing when you use rolling tier thresholds or care about monthly activity, because both of those start from zero otherwise. It is not worth doing to make the activity feed look busy.

The things that do not move each need a decision, and they are covered further down.

1. Build the program first

Do not import anything until the program exists, because the import matches against it. Follow Setting up loyalty and get these right before you touch a CSV:

  • The points names. These are what every balance is written in from then on.
  • The tiers, with the same names your old platform used. The import matches tiers by name, ignoring case, so "gold" in the file finds the tier called Gold.
  • The earning rules, so that the day after you switch, members carry on earning.

Decide the exchange rate now

The single most consequential decision is whether one old point is one new point.

Keeping the ratio at one to one is much easier to explain, and it is worth some awkwardness elsewhere to get it. If you do change the ratio, do the arithmetic in the spreadsheet before you import rather than expecting Cascade to scale anything, and rewrite your reward prices to match in the same sitting. A member whose balance was divided by ten and whose rewards still cost the old price will notice within the hour.

2. Import your customers

Members are matched to customers by email address, and a customer Cascade has never seen is skipped rather than created. So import your customers first if you have not already, the same way you would before importing orders.

3. Import the balances

Export from your old platform and upload it from Activity, Import members. The dialog has a Format picker: leave it on Cascade if you have already reshaped the file, or pick the platform you are leaving and upload its export unchanged. The named formats are covered in Migrating from a named platform below. The full column reference, including how re-runs reconcile, is in Importing loyalty members.

Cascade's own format is the minimum, two columns:

Add tier where you are carrying tier placement across:

email,points,tier
[email protected],1250,Gold
[email protected],80,Silver

The import runs in the background and does not stop at the first bad row. Rows naming an unknown customer, program or tier are skipped and listed with their row numbers under Settings, Imports. Fix those rows and import the file again: everything that already landed is reconciled rather than duplicated.

Run it once against a handful of rows first. Take five members, import them, and look at their customer pages. It costs ten minutes and it is the only way to find out that your export put balances in the wrong column before you do it to everybody.

Tiers on import

Tier comes from the file, not from recalculation. A migrated member has no earning history in Cascade, so recalculating would drop everybody to the bottom tier on day one.

The tier is applied without pinning it, which is the useful middle: future earning can still promote the member, and the periodic tier review will not demote them below what the file said. Leave the column out entirely and each member's opening balance places them instead, which is the right choice if your old tiers do not map onto your new ones.

4. Import the history, if you need it

Skip this unless you use rolling tier thresholds, or you want the months before the switch to count as active. Everything else about the program works from balances alone.

POST /loyalty-history takes a member's earns, spends and adjustments with their original timestamps, and squares them against the opening adjustment the CSV left behind so nobody is counted twice. Run it after the balances, from a script that reads your old platform's export. The order does not matter and re-runs are free, so it is safe to do a few members first and the rest once you are happy.

See Importing loyalty history for the request, the reconciliation and what happens to points expiry.

5. Handle what did not come across

Outstanding coupon codes

Codes your old platform issued are not in Cascade and Cascade will not honor them. You have two workable options:

  • Let them run out on the old platform's terms, and keep that platform's redemption working for as long as the codes last. This is the honest option and usually the shortest.
  • Reissue them. Cascade can issue a reward to a member by hand from the Rewards screen, with its own expiry date. This is reasonable for a short list and painful for a long one.

Whichever you choose, say so in the announcement email. A customer holding a code that stops working without warning is the single worst outcome of a migration.

Stamp card progress

Stamp balances are not part of the import, so members start with a fresh card. For a card that is only a few stamps long this is a rounding error. For a longer one, the fair fix is to stamp the cards yourself: set the card type's earning to Custom (through the API) for the migration, award each member their old stamp count with the stamps API, and then switch it back to its real earning type.

Alternatively, credit the difference in points and let the new card start clean. That is less faithful and considerably less work.

Referral relationships

Referrals in Cascade are built from share codes that Cascade issues, so relationships from another platform have nowhere to land. Existing referrals stay as history on the old platform. New ones start when members first share their new code.

6. Check the balances before you launch

Export the members straight back out and compare it against what you uploaded. Activity, Export members writes one row per member with their tier and points balance, which is the exact reverse of the import.

A spreadsheet comparison of the two files, matched on email, should show no differences except for rows you know were skipped. If the totals disagree, the import job under Settings, Imports lists every row it did not apply and why.

While you are there, Export activity writes the whole ledger, which is what your finance team wants for the points liability on the day you switch.

7. Turn it on

The order that causes the least trouble:

  1. Stop earning on the old platform, so no member earns points in a system you are about to stop reading.
  2. Export and import the final balances. Doing this last means you do not have to reconcile a moving target, though a re-run handles it if you do.
  3. Put the widget on your storefront. See Storefront widgets.
  4. Email your members. Tell them the balance carried over, what the points are called now if that changed, and what happened to any codes they were holding.
  5. Watch the activity feed for a day. New entries appearing with sensible amounts is the quickest confirmation that your rules are doing what you meant.

Consider running the program paused for the first hour, with the widget live. Members can see their balances and everything you are offering, and nothing earns or spends until you are satisfied it is right. Unpausing takes one switch.

If you have to do it twice

Re-running an import file is safe and reconciles each member to the file's values:

  • Nothing changed means nothing happens.
  • If the opening adjustment is still the only entry in a member's ledger, it is updated in place.
  • If the member has earned or spent since, a correcting adjustment moves the balance by the difference between the two files, so the activity in between is preserved.

That is what makes a partial or a mistaken import recoverable, and it is why a dry run on five members costs you nothing.

Migrating from a named platform

The Format picker on the import dialog reads a competitor's export as it comes, so you do not have to rename a column before you upload. Pick the platform, upload the file, and Cascade maps the columns for you.

Warning

These mappings were written from each platform's published documentation, not from a real export file. Compare the table below against your own export before you upload. If a column name differs, tell us and we will fix the mapping; in the meantime, rename the column in a spreadsheet, or reshape the file and use the Cascade format.

Three things are true of every named format:

  • The picker is not a guess. Cascade uses the format you picked, and refuses the file if its columns are not that format's. Two platforms use column names that overlap, so detecting the format from the headers would be a coin toss on exactly the files where getting it wrong is most expensive.
  • Columns are matched loosely on case and punctuation. Points Balance, points_balance and points balance are the same column.
  • Nothing is dropped quietly. Every column the mapping does not carry into Cascade is listed on the import job under Settings, Imports, with the reason. A completed job with a list of notices is normal for a competitor import.

None of the formats carries a program name, because none of these platforms has more than one program. If you run several programs in Cascade, add a program column to the file by hand: every named format takes Cascade's own program column alongside its own.

Migrating from LoyaltyLion

Export your customers from LoyaltyLion and upload it with Format set to LoyaltyLion.

LoyaltyLion columnBecomesNotes
email or customer_emailemailRequired. Matched against a customer you have already imported.
points_approvedpointsRequired. The opening balance.
tier, loyalty_tier or tier_nametierMatched to your tier by name, ignoring case.
programprogramCascade's own column. Add it only if you run several programs.
points_pendingNot importedCascade has no pending points, so only approved points become the balance.
points_spent, points_earnedNot importedThe approved balance already accounts for both.
merchant_id, customer_idNot importedMembers are matched by email, not by store id.
referral_url, referral_idNot importedCascade issues its own share codes.
stateNot importedEvery row in the file is enrolled. Remove members who left.
created_atNot importedMembers join on the day they are imported.
name, first_name, last_nameNot importedNames belong to the customer record.
total_spentNot importedSpend comes from imported orders.

The pending-points decision is the one to check. LoyaltyLion holds points as pending until an order clears. Cascade imports the approved balance, which is the number the customer could actually spend on the day you exported. If you would rather honor the pending points too, add the two columns together in a spreadsheet and import the result with the Cascade format.

Migrating from Smile.io

Export your customers from Smile and upload it with Format set to Smile.io.

Smile columnBecomesNotes
Email or customer_emailemailRequired.
Points Balance, points_balance or pointspointsRequired. The opening balance.
VIP Tier, vip_tier or vip_tier_nametierMatched to your tier by name, ignoring case.
programprogramCascade's own column. Add it only if you run several programs.
Points Earned, Points SpentNot importedThe balance already accounts for both.
Customer ID, external_idNot importedMembers are matched by email, not by store id.
First Name, Last NameNot importedNames belong to the customer record.
Referral URL, Referral CodeNot importedCascade issues its own share codes.
BirthdayNot importedBirthdays belong to the customer record.
StateNot importedEvery row in the file is enrolled.
Created AtNot importedMembers join on the day they are imported.

Migrating from Rivo

Export your customers from Rivo and upload it with Format set to Rivo.

Rivo columnBecomesNotes
email or customer_emailemailRequired.
points_balance or pointspointsRequired. The opening balance.
vip_tier, tier or tier_nametierMatched to your tier by name, ignoring case.
programprogramCascade's own column. Add it only if you run several programs.
points_earned, points_spentNot importedThe balance already accounts for both.
shopify_customer_id, customer_idNot importedMembers are matched by email, not by store id.
first_name, last_nameNot importedNames belong to the customer record.
referral_code, referral_linkNot importedCascade issues its own share codes.
birthdayNot importedBirthdays belong to the customer record.
state, statusNot importedEvery row in the file is enrolled.
created_at, joined_atNot importedMembers join on the day they are imported.

When Cascade refuses the file

If the columns are not the format's, the import fails before it writes anything and the job says what it looked for and what it found:

This file does not look like a LoyaltyLion export.
Missing the points balance column: expected "points_approved" or "approved_points".
Your file has: "email", "points", "tier".
Check that the Format picker matches the file you uploaded. The migration guide lists the
columns each format expects.

That is almost always the wrong format picked, or the wrong export downloaded. Nothing is imported, so fix whichever it was and upload again.

If you exported the transactions instead of the members

Every one of these platforms exports two different files: a list of members with a balance, and a list of points transactions. The members import takes the first. Upload the second and Cascade refuses it and points you at the history API, which is what a ledger belongs in:

This looks like a LoyaltyLion transaction export, not a members export.
The members import takes one row per member with a points balance.
To bring transaction history across, send the entries to POST /loyalty-history.

The transaction export maps onto POST /loyalty-history a column at a time, so the script that walks the CSV is short:

LoyaltyLion transaction columnSmile activity columnPOST /loyalty-history field
idIDexternalId
customer_emailCustomer EmailcustomerEmail
created_atDateoccurredAt
pointsPointsamount
typeTypekind
titleDescriptionreason

kind has to be one of earn, spend or adjustment, so map whatever your platform calls it onto those three; leave it out and Cascade infers it from the sign of amount. Rivo's transaction export is not documented here, because we have not been able to confirm its columns. Its members export still imports.

Do the balances first and the transactions after. The history reduces the opening adjustment by whatever it explains, so nothing is counted twice, and re-runs are free.

Where to go next