Migrations API
Start a Migration
POST /api/migrations/project/{projectId}/startStarts a migration run for the given project. Returns the migration run object.
Request body:
{
"type": "FULL",
"enginePayload": {
"source": {
"org": "yourorg",
"project": "YourProject",
"pat": "your-ado-pat",
"area_filter": null,
"type_filter": null
},
"target": {
"base_url": "https://yourorg.atlassian.net",
"email": "migration@yourcompany.com",
"api_token": "your-jira-token",
"project_key": "PROJ"
},
"options": {
"migrate_attachments": true,
"migrate_comments": true,
"migrate_links": true,
"dry_run": false,
"type_overrides": { "Feature": "Epic" },
"state_overrides": { "Resolved": "Done" }
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
type | FULL | SAMPLE | INCREMENTAL | Yes | Migration scope |
enginePayload.source.org | string | Yes | ADO organisation name |
enginePayload.source.project | string | Yes | ADO project name |
enginePayload.source.pat | string | Yes | ADO Personal Access Token or OAuth Bearer token |
enginePayload.source.area_filter | string | No | Filter to a specific ADO area path |
enginePayload.source.type_filter | string | No | Comma-separated work item types to include |
enginePayload.target.base_url | string | Yes | Jira Cloud base URL |
enginePayload.target.email | string | Yes | Jira service account email |
enginePayload.target.api_token | string | Yes | Jira API token |
enginePayload.target.project_key | string | Yes | Jira project key |
enginePayload.options.dry_run | boolean | No | Preview only; no writes to Jira |
enginePayload.options.type_overrides | object | No | Override AI type mappings |
enginePayload.options.state_overrides | object | No | Override default state mappings |
Response (200 OK):
{
"id": "run-uuid",
"projectId": "project-uuid",
"status": "RUNNING",
"type": "FULL",
"totalItems": 0,
"successCount": 0,
"failureCount": 0,
"progressPercent": 0,
"startedAt": "2024-06-03T10:00:00Z",
"createdAt": "2024-06-03T10:00:00Z"
}Get Migration Status
GET /api/migrations/{runId}/statusReturns the current status of a migration run including progress counts.
Response:
{
"id": "run-uuid",
"status": "RUNNING",
"totalItems": 5000,
"successCount": 2341,
"failureCount": 3,
"skippedCount": 0,
"progressPercent": 47,
"currentPhase": 2,
"startedAt": "2024-06-03T10:00:00Z"
}| Status | Description |
|---|---|
PENDING | Queued, not yet started |
RUNNING | Active migration in progress |
PAUSED | Paused by user or API call |
COMPLETED | Finished successfully |
PARTIAL | Finished with some item failures |
FAILED | Terminal failure — could not complete |
CANCELLED | Cancelled by user |
Stream Live Progress
GET /api/migrations/{runId}/streamServer-Sent Events (SSE) stream delivering live migration events.
Event types:
event: progress
data: {"type":"progress","current":250,"total":5000,"percent":5,"speed":45.2}
event: log
data: {"type":"log","message":"✓ ADO #10432 [User Story] → PROJ-142"}
event: status
data: {"type":"status","status":"completed"}
event: done
data: {"type":"done","stats":{"created":4997,"skipped":0,"failed":3}}EventSource connections cannot set custom headers. Pass the JWT as a query parameter: /api/migrations/{runId}/stream?token={accessToken}
Pause a Migration
POST /api/migrations/{runId}/pauseSends a pause signal to the running migration. The migration stops cleanly at the next checkpoint (within seconds). No partial item writes are left open.
Response (200 OK):
{ "id": "run-uuid", "status": "PAUSED" }Resume a Migration
POST /api/migrations/{runId}/resumeResumes a paused migration from the last checkpoint. No items are duplicated.
Response (200 OK):
{ "id": "run-uuid", "status": "RUNNING" }Cancel a Migration
POST /api/migrations/{runId}/cancelPermanently cancels a migration. Cannot be undone. Items already migrated to Jira are not removed.
Response (200 OK):
{ "id": "run-uuid", "status": "CANCELLED" }List Migration Runs
GET /api/migrations?projectId={projectId}Lists all migration runs for a project, most recent first.
Query parameters:
| Param | Type | Description |
|---|---|---|
projectId | UUID | Filter by project (required) |
status | string | Filter by status |
Error Codes
| Code | HTTP | Description |
|---|---|---|
MIGRATION_NOT_FOUND | 404 | Run ID does not exist or belongs to another tenant |
MIGRATION_NOT_PAUSED | 400 | Attempted to resume a non-paused run |
MIGRATION_ALREADY_RUNNING | 409 | Attempted to start when a run is already active |
ENGINE_DISPATCH_FAILED | 502 | Could not start the migration worker (infrastructure issue) |