The three strategies
Pass one ofmatch-or-create / match-only / migration-bot to apply --user-strategy:
| Strategy | What it does | Side effects |
|---|---|---|
match-or-create (default) | For each source user, look for an Ekso user with the same email. If one exists, reuse it. If not, mint a new user (random password, no invite email sent). | Tenant gains new users. Original authoring fidelity preserved. |
match-only | Match by email. Source users with no Ekso match fall back to --fallback-user. | No new users created. Authoring on items where no match was found rolls up to the fallback user. |
migration-bot | Force every author to --fallback-user. | No matching, no creation. All comments and items appear authored by one user. |
How matching works
The CLI matches by email address, case-insensitive. The match check is a singleGET /api/user against your Ekso tenant per unique source-user-email; the result is cached in the migration’s IdMap so the same email isn’t matched twice.
There is no fuzzy matching, no display-name fallback. Two source users with the same email get treated as the same Ekso user (which is usually what you want — the same person used both Jira and DevOps). Two source users with no email get treated separately.
The match runs once per unique email at the start of apply. If a source user is created on the source platform mid-migration (rare), they show up as “no match” and follow the strategy’s fallback path.
How match-or-create mints users
When the strategy is match-or-create and a source user has no Ekso match, the CLI calls POST /api/user with:
email= the source user’s emailname= the source user’s display nameactive=true- A random password generated server-side
inviteOnCreate=false— no welcome email is sent
inviteOnCreate=false path lets you mint the users now, then run a deliberate communication later.
The minted users have a random password they don’t know. Send them through the password-reset flow (or your SSO setup) when you’re ready for them to actually sign in.
When to use which
Default tomatch-or-create unless you have a reason not to. It preserves authorship without spamming users.
match-only when you specifically don’t want to mint users:
- You’re piloting the migration on a sandbox tenant and don’t want test users polluting your production directory.
- Your tenant uses SSO and you provision users centrally — the migrator shouldn’t create them.
- You only need a subset of authoring fidelity preserved (the matched ones).
user_migration_admin. The original source-user identity is preserved in the item’s Meta so you can audit later.
Use migration-bot when authoring fidelity doesn’t matter:
- The source platform’s users aren’t real people (synthetic test tenant).
- You’re consolidating archival data and don’t care who wrote what.
- You want one user to “own” everything for permission simplicity.
user_migration_bot. The original source-user identity is still preserved in Meta.
Source-specific quirks
| Source | Behaviour |
|---|---|
| Jira | Atlassian users have email; match-or-create is reliable. On Data Center / Server, local (non-SSO) accounts may have no visible email, so some users fall back to create rather than match. |
| Linear | Linear users always have email; match-or-create is reliable. |
| Azure DevOps | Service identities ([Project]\Project Collection Build Service, etc.) are filtered out at collect time — they’re not real users and won’t appear as authors. |
| Zendesk | End-users (customers) and agents (staff) are imported. End-users get tagged migrated-from:zendesk-enduser; agents get migrated-from:zendesk-agent. Anonymous end-users (no email) follow --user-strategy rules. |
| Gemini | All Gemini users have email; match-or-create is reliable. |
Anonymous users
If a source user has no email at all (some Zendesk end-users, some legacy DevOps accounts):match-or-create: minted with a placeholder email like<source-id>@anon.<source>.local. Filterable later by tag.match-only: rolls up to--fallback-user.migration-bot: rolls up to--fallback-user.
Tagging migrated users
Every user the CLI mints (or matches) is stamped with a tag of the formmigrated-from:<source> so you can filter, audit, or bulk-update them after the fact. Examples:
migrated-from:jira— minted from a Jira migrationmigrated-from:linearmigrated-from:zendesk-enduser,migrated-from:zendesk-agentmigrated-from:devopsmigrated-from:gemini
ekso user list --tag migrated-from:jira or directly in the Ekso admin UI.
Post-migration cleanup
After apply succeeds:- Audit the user list.
ekso user list --tag migrated-from:<source>shows every user touched by the migration. - Send password resets to users you actually want to onboard. Skip the rest — they’re filed for audit-trail purposes and don’t need to sign in.
- Remove placeholder-email users if you don’t need them. Their original source identity is preserved in their profile metadata for audit.
- Rotate the
--fallback-usercredentials. If you usedmigration-bot, that account briefly held a lot of authority. Rotate it or disable it.
What’s preserved on items
Even when authoring rolls up to a fallback user, the original source identity is preserved on each item, comment, and time entry inMeta. Example:
migration-bot run preserves enough information to reconstruct authorship later if you change strategies.
Where to next
- Command reference — full flag surface.
- Field mapping —
migration.fields.yamlshape. - Troubleshooting — recovering from a partial apply.