Skip to main content

Documentation Index

Fetch the complete documentation index at: https://ekso.dev/llms.txt

Use this file to discover all available pages before exploring further.

ekso migrate linear pulls teams, issues, comments, attachments, cycles, and labels from Linear (GraphQL API) into your Ekso tenant. Two commands — collect and apply. Read Migrate overview and Before you start first.

What gets imported

Linear conceptEkso shape
Team (ENG, OPS)DataContainer
Issue (ENG-123)DataItem
CommentDataAnnotation
File attachmentDataFile (multipart upload)
Subtask, Relates, Blocks, DuplicateDataLink
CycleDataCycle
LabelConfigLabel
Cross-team ProjectConfigLabel (prefix project:)
UserDataUser (matched or minted — Linear users always have email)
Estimate (decimal)DataItem.Field[] (via --field-map)
Linear stores descriptions and comments as markdown directly — no HTML rendering step. The migrator preserves markdown bodies through to Ekso.

What does NOT get imported

  • Workflow states. Linear’s per-team workflow is custom; only the current state of each issue is preserved.
  • URL-bookmark “attachments”. Linear lets you attach a URL as if it were a file; these get logged in Meta.linear_attachments[] rather than uploaded.
  • Triage queues. Triage is a Linear-specific workflow concept; the items themselves migrate but the triage state doesn’t.
  • Customer requests. Linear’s customer-feedback feature isn’t modelled in Ekso v1.
  • Roadmap initiatives. Out of scope for v1.

Before you start

Pre-flight checklist on top of the general one:
  • A Linear workspace where you have admin or read-everything access.
  • A Linear personal API key (free tier supports it).
Add a linear block to your migration.config.json:
{
  "ekso": { "tenant": "acme", "apiKey": "ek_live_..." },

  "source": "linear",

  "linear": {
    "apiKey": "lin_api_..."
  }
}
Or use the env-var override EKSO_MIGRATE_LINEAR_KEY for CI use cases.

Step 1 — list teams

Verify your credentials work and see what Linear shows you:
ekso migrate linear list-teams \
    --config migration.config.json \
    --tenant acme
Sample output:
NAME            KEY    ID                                       MEMBERS
Engineering     ENG    01a3b9c8-1f44-4eaa-9c6e-7d5b2c1d3f4e     12
Operations      OPS    02b4ca99-2055-5fbb-ad7f-8e6c3d2e4a5f     6
Linear’s list-teams is the equivalent of the other adapters’ list-projects — Linear’s “team” is the security/billing boundary that the migrator treats as a container.

Step 2 — collect

Pull a single team’s issues, comments, and attachments into a local SQLite cache:
ekso migrate linear collect \
    --config migration.config.json \
    --team ENG \
    --tenant acme
Repeat --team for multiple. Sample run:
fetching teams... 1 fetched
fetching ENG issues...  843/843  ok
fetching comments...   2204/2204 ok
fetching attachments... 156/156  ok
saved cache: ~/.ekso/migrate/linear-2026-04-29T143205.sqlite
Useful flags:
  • --no-attachments — skip downloads.
  • --no-comments — skip comments.
  • --resume — pick up where a killed collect left off.

Step 3 — dry-run apply

ekso migrate linear apply \
    --config migration.config.json \
    --process proc_engineering \
    --tenant acme \
    --dry-run
Sample output:
DRY-RUN — no writes.
would create:  1 container, 28 users, 843 items, 2204 annotations,
               156 files, 47 links, 6 cycles
field-map check: ok (1 process field would be auto-created — Estimate)

Step 4 — apply for real

ekso migrate linear apply \
    --config migration.config.json \
    --process proc_engineering \
    --tenant acme
If the run is interrupted, re-run with --resume:
ekso migrate linear apply \
    --config migration.config.json \
    --process proc_engineering \
    --resume

Identity resolution for Linear

Linear users always have an email — the default --user-strategy match-or-create is reliable here. No special handling needed. Read Identity resolution.

Custom fields for Linear

Linear’s main custom field is the first-class estimate (a decimal). Most teams need only this one mapping:
linear:
  estimate: { ekso: Estimate, kind: decimal }
Pass it to apply:
ekso migrate linear apply \
    --config migration.config.json \
    --process proc_engineering \
    --field-map migration.fields.yaml
If your destination process doesn’t yet have an Estimate field, the Apply layer auto-creates it before any item write. See Field mapping. Without --field-map, the estimate falls through to DataItem.Meta.linear_estimate losslessly.

Cycles, Labels, Cross-team Projects

LinearEkso
CycleDataCycle (always — no flag)
LabelConfigLabel (tenant-scoped)
Cross-team projectConfigLabel (prefix project:)
Linear labels are team-scoped; Ekso ConfigLabels are tenant-scoped. To avoid name collisions across teams, the migrator prefixes labels with the team key when migrating multiple teams: ENG/Bug rather than Bug. Linear “projects” can span multiple teams — these become project:-prefixed labels rather than containers, so the security/billing moat that team boundaries represent stays intact on the Ekso side. Cycles attach to a board on the Ekso side. Pass --board <BOARD_ID> if the tenant has more than one board.

Markdown bodies

Linear stores issue descriptions and comments as markdown natively. The migrator passes the markdown through unchanged — Ekso renders it the same way Linear did. You shouldn’t see any visual drift on body content.

Iron rule — cursor pagination terminator

Linear’s GraphQL API paginates with cursors. The pagination loop terminates only when the response returns both HasNextPage=false and a non-null EndCursor. An earlier version of the upstream adapter terminated only on HasNextPage, which produced infinite loops on certain edge-case responses (Linear sometimes returns HasNextPage=true with a null EndCursor). The current CLI handles this correctly with a regression test. If you ever see Inconsistent pagination response from Linear, the CLI is detecting the edge case and exiting cleanly rather than looping. Re-run collect to retry.

Troubleshooting Linear-specific issues

SymptomFix
exit 3 — Linear API key invalidMint a new key at linear.app → Settings → API → Personal API keys.
URL-bookmark attachment skippedLogged in Meta.linear_attachments[]. Documented limitation — Linear “attachments” can be URLs rather than files.
Inconsistent pagination response from LinearThe pagination terminator detected an edge-case response. Re-run collect.
exit 7 — Linear rate-limit exceededLinear’s GraphQL complexity budget got exhausted. Wait, then re-run with --resume.
See Troubleshooting for the full per-source error table.

Why migrate to Ekso

Linear is excellent at issue tracking. Ekso adds financial intelligence, multi-tenant SaaS or self-host, and AI-native primitives that Linear doesn’t model. See Ekso vs Linear for the broader comparison.

Where to next