Import members from another platform

Move your community from another platform without making members start over: their points, finished quests and invited friends come with them.

Moving your community from another quest platform? A member import brings your members over with their point balances, the quests they already finished and who invited whom, so nobody starts from zero.

What members see:

  • They sign in as before. Each member is matched by the account they'll sign in with: their login in your app, their wallet, or their X account. Signing in reaches their imported points and progress.
  • Their points are there. Opening balances count on balance leaderboards, but not on boards that rank points earned or net points.
  • Finished quests stay finished. A member who already did "Welcome" doesn't do it again, and quests that need it are unlocked. Imported progress pays no rewards.
  • Invited friends stay theirs. An inviter keeps the friends they brought, and earns their referral program's share of the points those friends earn from now on.
  • Their marketing consent comes along. A member who opted in to email on the old platform stays opted in, with the time and the wording they agreed to, so your record of their opt-in comes with them.

You check the import first, which changes nothing and shows what would happen, and then apply it. Your developer, or a coding agent with the Domino skill, prepares the file and runs the check with the CLI. Console cannot start an import, but it shows the imports you prepared. In live, a person with Publish permission approves the import in Console.

Prepare the file

Write one member per line, as JSON. ref is the member's ID on the platform they come from; it must be unique in the file. The member imports reference has every row field and refusal code.

{"ref": "legacy-1", "identity": {"kind": "app:legacy", "subject": "u1"}, "accounts": [{"kind": "x", "subject": "1001"}, {"kind": "telegram", "subject": "5551234"}, {"kind": "wallet", "chain": "solana", "address": "So11111111111111111111111111111111111111112"}], "points": [{"ledger": "xp", "balance": 1200}], "quests": [{"quest": "daily-check-in", "actions": [{"at": "2026-09-01T10:00:00Z"}, {"at": "2026-09-02T10:00:00Z"}]}]}
FieldWhat it holds
identityHow the member will sign in, so their sign-in reaches the imported member: an app:<name> subject your app login exchanges, a wallet ({"kind": "wallet", "chain", "address"}, plus network for Cosmos), or an X account ({"kind": "x", "subject": "<numeric user ID>"}).
accountsUp to 20 accounts your organization vouches belong to the member: wallets in the same shape as identity, and at most one each of X ({"kind": "x", "subject"}), Telegram ({"kind": "telegram", "subject"}) and Discord ({"kind": "discord", "subject"}), each by its numeric user ID. They do not sign in.
pointsOpening balances, by point ledger key or ID.
questsProgress, by quest key or ID: the member's accepted actions, each with its time at (epoch milliseconds or an ISO date-time) and, where the quest needs one, a key.
referralWho invited the member and when they accepted: {"inviter", "acceptedAt", "qualified"}. inviter is the ref of another row of the import, or the identity of a member who already signs in. qualified lists the referral programs, by key or ID, the member already qualified in, each with its time at.
consentThe member's marketing consent on the old platform, one entry per channel: {"channel": "email", "state": "granted" or "withdrawn", "at", "wording"}. at is when the member chose, in the past. wording is the text they agreed to, at most 2,000 characters; a grant needs it.

A row with the same ref as an earlier row of the import replaces it, so you can fix a row and send the file again.

A friend and their inviter can be in the same file, in any order: the inviter's row is applied before their friends'.

{"ref": "legacy-2", "identity": {"kind": "app:legacy", "subject": "u2"}, "referral": {"inviter": "legacy-1", "acceptedAt": "2026-08-01T10:00:00Z", "qualified": [{"program": "invite-friends", "at": "2026-08-03T10:00:00Z"}]}}

A member who opted in to your newsletter on the old platform:

{"ref": "legacy-3", "identity": {"kind": "app:legacy", "subject": "u3"}, "consent": [{"channel": "email", "state": "granted", "at": "2025-11-20T09:30:00Z", "wording": "Email me news and offers. Privacy policy: https://example.com/privacy"}]}

Check and apply in test

Run a dry run

domino import --file members.jsonl --source legacy-domino --dry-run --json

The command checks every row against the project and prints the import. Nothing in the project changes. The summary says what applying would do:

Summary fieldMeaning
members.created, members.existingRows that create a member, and rows whose identity already names one. An existing member gets the row's accounts, balances and progress.
accountsIdentities and accounts the ready rows attach.
points, actionsOpening balance totals by ledger, and imported actions by quest.
referrals.accepted, referrals.qualifiedAccepted referrals the ready rows record, and their qualifications by program.
refused, refusalsRows that will not be applied, by reason.

List the refused rows and why with domino imports rows IMPORT --status refused --json.

Apply

Fix refused rows in the file if you need them, then run the import again without --dry-run, naming the import to add to it:

domino import --file members.jsonl --import IMPORT --json

In test, the command checks the rows and applies the ready ones. domino imports apply IMPORT applies an import that is already checked. Both wait until the import is applied.

You can also follow a test import in Console under Members → Imports, and check, apply, cancel, stop or revert it there with Publish permission.

Apply a live import

Run the dry run with --environment live. A live import is applied only by a person signed in to Console with Publish permission.

Check it from the CLI

domino import --file members.jsonl --source legacy-domino --environment live --dry-run

Without --dry-run, the command stops before uploading in live.

Approve it in Console

In the live environment, open Members and select Imports. Open the import, review the summary and the refused rows, and select Approve and apply to live. A confirmation states how many live members the import creates and links, the opening balances it records, and the referrals it records without referral rewards; select Approve and apply to live again to apply it. API keys and CLI sessions can prepare, check, cancel, stop and revert a live import, but applying it with one is refused with console-approval-required.

Stop an import while it applies

A large import applies one row at a time. To stop one partway, open it in Console under Members → Imports and select Stop import, then confirm. The row being applied finishes, no further row is applied, and the import shows as Stopped with how many rows were applied. Stopping needs Publish permission, in test or live.

A stopped import can't be resumed or applied again. To bring over the remaining members, put their rows in a new import. Rows that were already applied stay applied.

Revert an import

To take back an import that was wrong or ran twice, open it in Console under Members → Imports and select Revert import. Give a reason, then select Revert import again. You can revert an Applied or Stopped import, once, in test or live, with Publish permission. The import shows as Reverting while the revert goes through its applied rows, one at a time. For each row:

  • Opening balances are reversed, as far as the member still has the points. Points a member already spent or reserved stay spent: no balance goes below zero, and the result lists those points by member.
  • Imported quest completions are revoked, so the member can complete those quests again.
  • Wallets, accounts and identities the import linked are removed, unless the member signed in with one since, or proved an imported wallet themselves. The result lists the links it kept.

Members the import created stay members of the project, and imported referrals stay. When every row is done, the import shows as Reverted, with who reverted it, why, and what the revert left: points members had already spent, and links it kept.

An import applied before Relay recorded the links each row creates keeps all its links: reverting it reverses only its balances and completions, and the result says so.

What an import records

  • Members. A row whose identity names no member creates one and joins them to the project. A member who later signs in with that identity is the imported member. An import never merges two members.

  • Vouched accounts. Quests, trackers and wallet checks trust the X, Telegram and Discord accounts and wallets an import attaches like accounts the member proved, from the moment the import attached them, and Console marks them Imported. A Telegram or Discord message or reaction from a vouched account counts for its member, so reaction tipping keeps paying without members linking again. When the member links an account of that kind themselves, their link replaces the vouched one, even a different account. Nobody else can link a vouched account while the member holds it, and a vouched Telegram or Discord account does not sign in. If the import attached the wrong account, a reviewer unlinks it on the member's page: an account the member never signed in with or proved is then released, so its owner can link it, and no later import attaches it to that member again. See operator identity changes. For members who join after the import, your app's server can vouch for their accounts under the same rules each time it signs them in: see vouch for your members' accounts.

  • Opening balances. Each shows as imported in the member's point history. Balance leaderboards count it; earned and net boards do not.

  • Quest progress. Each action goes through the quest's completion rule as an accepted action, without rewards: a quest that completes once completes on the first action, a keyed quest once per key, a streak counts each action's calendar day, and a counter counts distinct keys (an action without a key counts on its own). Imported completions count toward completion limits and prerequisites, and pay nothing. An action past a completion limit records no completion.

  • Referrals. The friend's inviter and when they accepted, and the programs they already qualified in. An import never replaces an inviter a member already has. Imported referrals pay nothing: no reward for the inviter or the friend, no milestone, and no share of points earned before the import. Each imported qualification counts toward the inviter's qualified friends and milestones from then on, and points the friend earns after the import pay the program's share like any qualified friend's. A share limited to a number of days counts them from the imported qualification time. Console marks the referral and its qualifications Imported under Referrals. A signup program qualifies a friend only when the row names it.

  • Marketing consent. Each channel's consent is recorded as the member's, with its original time and wording, and marked imported so you can tell it from a choice the member made in your app. If the member already chose on that channel in Domino, their choice stays, whatever the date in the file. Consent from an earlier import is replaced only by a later-dated one. Reverting an import leaves the consent it recorded: it stays the member's record of what they agreed to, and the member changes it in your app.

Applying is resumable: each row is applied once, and checked again first. A row that became invalid after the dry run, such as an account another member linked meanwhile, is refused instead of applied.

Why a row is refused

CodeFix
account-linked-elsewhereAnother member holds the identity or account. Remove it from the row.
duplicate-accountAn earlier row of the import names the same identity or account.
identity-unlinkedAn operator or the member unlinked the identity or wallet, or an operator released a vouched account from this member. Relink it first if Console offers Relink, or remove it.
x-account-selected, telegram-account-selected, discord-account-selectedThe row names a second account of that kind, or the member already has another one.
limit-reachedThe member would hold more than 5 wallets of one chain family besides their sign-in wallet.
unknown-ledgerNo active point ledger has that key or ID in this environment.
unknown-quest, quest-not-publishedNo quest has that key or ID, or it has no active release in this environment.
quest-not-importableThe quest completes per content item, which an import cannot record.
key-requiredThe quest completes once per key: give every action a key.
key-usedThe quest completes each key once across all members, and another member, or an earlier row of the import, already used one of the row's keys. Remove that action from the row.
future-actionAn action, an acceptance, a qualification or a consent is dated in the future.
sign-in-disabledThe row's sign-in wallet or X account is linked to its member without sign-in.
duplicate-entryThe row names one point ledger, quest or referral program twice, by any of its names, or one consent channel twice.
unsupported-consent-channelA consent names a channel Domino doesn't record consent for. Only email is recorded.
consent-wording-required, consent-wording-too-long, consent-wording-invalidA granted consent has no wording, its wording is longer than 2,000 characters, or it has characters that aren't plain text.
unknown-inviterThe inviter is no row of the import, and no member of the environment signs in with that identity.
self-referralThe row names its own member as the inviter.
inviter-already-acceptedThe member already has an inviter. Remove referral from the row.
referral-cycleThe member invited the inviter, directly or through others.
inviter-refusedThe inviter's row is refused. Fix it, and the friend's row is checked again with it.
unknown-referral-programNo referral program has that key or ID in this environment, or it is archived.

domino imports cancel IMPORT cancels an open or checked import. Applied rows stay applied: correct a balance with a point reversal and a completion with a revocation. An imported referral stays too: under Referrals, suspend it to stop its payouts, or revoke a qualification. Qualifying a friend again counts like a new qualification, so it can pay a milestone the import reached without paying. An inviter is never replaced, so check the dry run before applying.

On this page