AcrossFlow
Identity & migration

The member export looks fine. Check these five things.

A CSV that opens cleanly is not a migration-ready dataset. What to inventory, count, and reconcile before you scope identity matching or promise anyone their points.

A member export usually starts well. The file opens, the columns have headers, and the first hundred rows look plausible. Then someone sums the points column and gets a different number from the admin dashboard. The phone column contains three formats. Two rows share an email address. Nobody can say whether they are the same person.

None of that means the data is unusable. It means the export is not migration-ready yet. Before identity matching, before a conversion rate, and before anyone promises a member their points, the source data has to survive five checks.

The short version

A migration-ready export is reproducible, normalised, documented, and reconciled at the source. If you cannot regenerate it with the same query and explain its totals, you are testing a different dataset every time you open it.

1. Read the ledger before the balance

A balance answers “how much now”. A ledger answers “why”. Migrations generate disputes: a member is sure they had more points, a refund left a negative balance, an operator adjusted an account by hand three years ago. With only a balance, you cannot verify any of that. You can only believe it.

Ask whether the source system can produce a transaction history for each account: earn, spend, expire, adjust, refund. If it can, export it alongside the balance and check that the opening balance plus the movements equals the closing balance. If it cannot, write that down as a known limitation. Expect every disputed account to become manual work, and price the exception queue accordingly.

The same question applies to stored value: is there a deposit and deduction record, or only a remaining amount? A number without a history is a claim, not a reconciliation.

2. Count rows, duplicates, and empty identifiers

Row count is not member count. Re-registration creates duplicates, one phone number can produce several accounts, staff and test accounts sit in the same table, and a long-running export may overlap itself. Before any matching, compute:

  • Total rows and unique member IDs. If they differ, find out why before importing.
  • Unique normalised phone numbers and emails. This is the denominator for attribute matching, not the row count.
  • Rows with no usable identifier — no verified phone, no verified email, no platform-level link. These members will need user-mediated linking or manual handling.
  • Duplicate clusters: the same phone or email appearing more than once. Do not collapse them yet; count them and agree on a rule.

Also define, in writing, which rows are out of scope: internal test accounts, staff, deleted members, merged accounts, and obvious junk records. Excluding them by a rule is fine. Excluding them by eyeballing the spreadsheet is not, because the next export will not match.

3. Normalise before you match

Most matching failures are formatting failures wearing a data-quality costume. The same person can appear as +86 138 0000 0000, 0086-13800000000, and 13800000000. Email addresses differ by case and whitespace, or use plus-addressing. Dates arrive as 2024/3/7, 07/03/2024, and a Unix timestamp, sometimes in the same file.

Chinese member exports add two more traps. First, encoding: a CSV that looks correct in one tool may be GBK or GB18030, and re-saving it as UTF-8 through a spreadsheet can silently change characters. Test the round trip on a sample that includes names and addresses, not just digits. Second, time zone: a join date at 2024-03-07 00:30 may belong to the previous day in UTC, which changes anniversary logic and period boundaries.

If the phone number is your matching key, the normalisation rules are part of the matching rule. Write them down, version them, and apply the same code path to the old and new data. A match produced by different rules on each side is not a match you can defend.

4. Give every field a purpose

A migration is a good moment to stop carrying fields nobody uses. Sort the columns into four buckets:

  • Identity: the attributes that can prove who a record belongs to — verified phone, verified email, platform-level identifier, member card number.
  • Assets: points, tier, coupons, stored value, and the history needed to verify them.
  • Compliance: consent or notice records, and the timestamps that show when they were captured.
  • Operations: the flags your team needs to resolve exceptions — account status, frozen balance, notes that are safe to keep.

Everything else is a candidate for deletion. That includes national ID numbers, home addresses, and free-text notes. Notes deserve special attention: they are where customer service has pasted phone numbers, order details, and complaints that were never meant to travel. Review them, redact what is unnecessary, and document the decision.

Then produce a data dictionary: field name, type, example, null rate, owner, and purpose. It is the document you hand to a platform reviewer, a lawyer, or the engineer who inherits the import script six months later.

5. Reconcile at the source, then freeze a snapshot

Before exporting, compute the totals from the source system and write them down: total points, points by status, valid coupon count, stored-value total, member count by tier. Compare them with the admin dashboard or the finance report. If they disagree, resolve the difference now — migrating a number you cannot explain only moves the argument to the new platform.

Then freeze the snapshot. Record the export time, the query and filters used, the row count, the operator, and a checksum of the file. From that point on, the file you test is the file you migrate. If the source keeps changing, you will spend the project comparing two moving targets.

After the import, run the same totals against the target system and compare them asset by asset. A row count match proves that rows arrived. It does not prove that the values are right.

What this changes about scoping

Migration effort scales with the exception rate, not the row count. Fifty thousand clean records with one identifier each can be easier than five thousand records with three phone formats, merged accounts, and missing balances. The inventory is what turns “we have a CSV” into an estimate.

It also produces the denominator for every later claim. If 40% of records have no usable identifier, a 95% match rate is not a plan; user-mediated linking and a manual queue are. If a 300-row sample cannot be classified, no schedule or fixed price is credible yet.

The five checks as a gate

  1. History: can you verify a balance, or only read it?
  2. Counts: rows, unique members, usable identifiers, duplicates, and no-identifier records.
  3. Format: normalisation rules for phones, emails, dates, encoding, and booleans, applied identically on both sides.
  4. Purpose: a field dictionary with a reason to keep each column, and unnecessary personal data removed.
  5. Reconciliation: source totals explained and frozen, with a checksum and an owner.

If one of these fails, fix it before the import button. The identity question — “can the new system recognise this person?” — is hard enough on clean data. It becomes unanswerable on data that changes every time it is exported.

AcrossFlow works with merchants on this inventory before any migration is scoped. If you are planning a platform change, start with the export check, then move to the identity question.

Platform rules and APIs change. This article is general guidance, not legal advice, and not a guarantee that a particular migration is permitted. Verify the current requirements with the platform and qualified advisers.