> ## Documentation Index
> Fetch the complete documentation index at: https://ekso.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Before you start

> Prerequisites, secrets policy, and token rotation for ekso migrate.

A short checklist before you run any `ekso migrate` command. None of these are optional.

## Prerequisites

You need:

1. **An Ekso install.** If you don't have one yet, see [ekso.app/pricing](https://ekso.app/pricing) for license options. Stand up the install (Docker is the supported path) and complete the `/startup` wizard so you have a `PublicUrl` (e.g. `https://ekso.acme.com`) you can hit.
2. **An Ekso API key with admin scope.** If you have the Ekso CLI installed and you're authenticated as a Super Admin, run:

   ```bash theme={null}
   ekso api-key create --name "Migration key" --url <https://ekso.acme.com>
   ```

   `ekso migrate` writes containers, items, users, files, and labels, so the key needs admin write permissions for the duration of the run. Rotate or delete the key when the migration finishes.
3. **A destination Ekso process.** The migrator writes every item to a single `DataProcess` you choose at apply time via `--process <process-id>`. Create the process in Ekso first (or pick an existing one). The Apply layer auto-creates any custom fields you reference in your field-map; system fields (Status, Priority, Severity, etc.) must already exist on the process.
4. **A destination Ekso board.** Cycles need a board to attach to. Pass `--board <board-id>` at apply time, or let the CLI auto-discover when the install has exactly one board.
5. **Read access to the source platform.** Whatever credential type the source needs — API token, PAT, SQL user, GraphQL key. See the per-source page for shape:
   * [Jira](/cli/migrate/jira) — Cloud: email + API token; Data Center/Server: Personal Access Token (PAT)
   * [Linear](/cli/migrate/linear) — Personal API key
   * [Azure DevOps](/cli/migrate/devops) — Personal Access Token (PAT)
   * [Zendesk](/cli/migrate/zendesk) — Email + API token
   * [Gemini](/cli/migrate/gemini) — SQL connection string *or* API key
6. **The Ekso CLI itself.** Run `ekso --version` to confirm. If it's not installed, follow [Installation](/cli/installation).

## The configuration file

Every `ekso migrate` command takes `--config <FILE>` (default: `./migration.config.json`). This is the file that holds your source-platform credentials.

Start from the sample shipped with the CLI source tree:

```json theme={null}
{
  "_comment": "Copy this to migration.config.json and fill in real values. The working file is gitignored. Do not commit secrets.",

  "ekso": {
    "url": "https://ekso.acme.com",
    "apiKey": "ek_live_..."
  },

  "source": "jira",

  "jira": {
    "url": "https://acme.atlassian.net",
    "username": "you@acme.com",
    "apiToken": "ATATT3..."
  },

  "linear": {
    "apiKey": "lin_api_..."
  },

  "devops": {
    "organisation": "your-org",
    "personalAccessToken": "..."
  },

  "zendesk": {
    "url": "https://acme.zendesk.com",
    "email": "you@acme.com",
    "apiToken": "..."
  },

  "gemini": {
    "connectionMode": "api",
    "connectionString": "Server=gemini-db;Database=Gemini761;TrustServerCertificate=True;",
    "url": "https://gemini.acme.local",
    "username": "manager",
    "apiKey": "..."
  },

  "fieldMap": "migration.fields.yaml",

  "attachments": { "import": "db" }
}
```

You only need the block for the source you're migrating from — the others can stay as placeholders.

## Secrets policy

This file holds live credentials for both the source platform *and* your Ekso install. Treat it like any other secret:

* **Never commit `migration.config.json`.** Add it to your `.gitignore`. The Ekso repo's own gitignore already blocks `migration.config.json`, `*.migration.json`, and `migration.*.json` patterns.
* **Use environment variables for CI/CD.** Each per-source token has an env-var override that takes precedence over the file:

  | Source                    | Env var                                                 | Overrides                                              |
  | ------------------------- | ------------------------------------------------------- | ------------------------------------------------------ |
  | Jira (Cloud)              | `EKSO_MIGRATE_JIRA_TOKEN`                               | `jira.apiToken`                                        |
  | Jira (Data Center/Server) | `EKSO_MIGRATE_JIRA_PAT`                                 | `jira.personalAccessToken`                             |
  | Linear                    | `EKSO_MIGRATE_LINEAR_KEY`                               | `linear.apiKey`                                        |
  | Azure DevOps              | `EKSO_MIGRATE_DEVOPS_PAT`                               | `devops.personalAccessToken`                           |
  | Zendesk                   | `EKSO_MIGRATE_ZENDESK_TOKEN`                            | `zendesk.apiToken`                                     |
  | Gemini                    | `EKSO_MIGRATE_GEMINI_KEY`, `EKSO_MIGRATE_GEMINI_DB_PWD` | `gemini.apiKey`, password in `gemini.connectionString` |

  In CI, set the env vars and ship a config file with placeholder strings. The CLI reads env first, file second.
* **Rotate source-platform tokens after the migration finishes.** A migration credential needs read-everything scope on the source; that's a lot of authority to leave hanging around. Rotate the source-platform credential as soon as the apply phase succeeds. The Ekso API key can stay (or rotate it too — your call).
* **The cache file is local-only.** `~/.ekso/migrate/<source>-<timestamp>.sqlite` holds the data you fetched from the source platform — descriptions, comment bodies, attachment bytes. Treat the cache file the same way you'd treat the source data itself: don't paste it into a public bug tracker, don't commit it, delete it once apply has succeeded.

## What the cache contains

The SQLite cache holds raw source data — every comment body, every attachment blob, every assignee email. It is **not** encrypted at rest. If you're handling a regulated data set, run the migration on an encrypted volume and delete the cache file once apply has succeeded.

The cache does **not** contain credentials. Tokens stay in the config file or environment, never in the SQLite file.

## Verifying you're set up

`collect` itself preflights your Ekso credentials with a single cheap API call before pulling any source data — wrong URL or API key fails fast with **exit 3** in under a second, not after a long collect run. So you no longer need a separate sanity-check step on the Ekso side.

If you also want to verify your **source-platform** credentials before kicking off a real `collect`, the per-source list commands make a read-only call:

```bash theme={null}
# Jira — should print your accessible projects
ekso migrate jira list-projects --config migration.config.json --url https://ekso.acme.com

# Linear — should print your projects
ekso migrate linear list-projects --config migration.config.json --url https://ekso.acme.com

# Azure DevOps — should print your projects
ekso migrate devops list-projects --config migration.config.json --url https://ekso.acme.com

# Zendesk — should print your orgs (or empty if your instance is flat)
ekso migrate zendesk list-orgs --config migration.config.json --url https://ekso.acme.com

# Gemini — should print your projects
ekso migrate gemini list-projects --config migration.config.json --url https://ekso.acme.com
```

If any of those fail with an auth error (exit code `3`), fix the credentials before continuing. If they work, you're ready for `collect`.

## Where to next

* **[Command reference](/cli/migrate/command-reference)** — every command, every flag.
* **[Field mapping](/cli/migrate/field-mapping)** — write a `migration.fields.yaml` for your custom fields.
* Pick your source: [Jira](/cli/migrate/jira) / [Linear](/cli/migrate/linear) / [Azure DevOps](/cli/migrate/devops) / [Zendesk](/cli/migrate/zendesk) / [Gemini](/cli/migrate/gemini).
