Migrator — API v1 and MCP | Nodea Documentation
The Migrator module imports monitors, contacts, status pages and WHMCS entities from external providers into Nodea. A migration run (MigratorRun) follows the lifecycle: start → discovery (reading resources through the provider API) → review → execute (plan + import). This document describes the REST API v1 layer (/api/v1) and the module's MCP tools.
Authentication and scopes
API v1 authenticates with a Passport personal access token via the Authorization: Bearer <token> header. This module's routes are guarded by the account scope matching the operation tier:
account:read— listing, detail, logs.account:write— start a run, execute, retry failed resources.account:delete— permanently delete a run (cascades resources and logs).
The whole module is additionally gated by the Pennant flag features:migrator — disabling it hides the module from the web UI and the API. Rate limit: throttle:api-v1 = 240/min per token user. Additionally, starting a run is limited to 5 attempts per minute per user (a shared budget across web and MCP, code discovery_rate_limited / 429). In MCP, each tool additionally requires the transport scope mcp:use and the migrator flag.
Ownership and visibility
Migration runs have no policy class or sharing model — every access is strictly owner-scoped by user_id. A foreign or unknown id consistently yields 404 (not 403), so the API never confirms that a foreign run exists. In MCP this maps to a single "not found or not accessible" error. The run id is a UUID (string).
Sensitive-data safety
Source-provider credentials are write-only: accepted on start, stored encrypted (encrypted:json + $hidden on the model) and never returned by any resource. Internal columns — the resumable checkpoint cursor, the raw plan blob, discovery_summary internals, credentials_purge_at — are likewise not exposed. Run resources do not reveal the raw provider blobs (external_data/normalized_data) or the internal import linkage (target_model/target_id).
Side effects — asynchronous and run state
Start, execute and retry run out-of-band through the queue (dedicated discovery/planning/resources queues) — those actions return 202 Accepted, while starting a run returns 201 Created with the resource. Only a run in awaiting_review can be executed (otherwise 422, code run_not_awaiting_review). Retry is rejected when there are no failed resources (422, code no_failed_resources).
API v1 — endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /api/v1/migrations | account:read | Owner's runs (newest first), paginated. Filters status, provider. Each row: total_resources, failed_resources. |
| GET | /api/v1/migrations/{run} | account:read | Run detail with resources (capped at 500) and meta.resource_counts (per-status breakdown). |
| GET | /api/v1/migrations/{run}/logs | account:read | Run log ledger (newest first), paginated. Filter errors_only. |
| POST | /api/v1/migrations | account:write | Start a run (provider + write-only credentials). Queues discovery. Returns 201; 5/min limit → 429. |
| POST | /api/v1/migrations/{run}/execute | account:write | Execute the run (plan + import). Only in awaiting_review. Returns 202/422. |
| POST | /api/v1/migrations/{run}/retry-failed | account:write | Retry failed resources (reset to pending + redispatch). Returns 202 with retried; none failed → 422. |
| DELETE | /api/v1/migrations/{run} | account:delete | Permanently delete the run (cascade). Returns 204. |
GET /migrations — query parameters
status(enum, optional) —pending,discovering,awaiting_review,executing,paused_conflicts,completed,completed_with_errors,failed,cancelled.provider(enum, optional) —uptime_robot,statuscake,pingdom,better_stack,oh_dear,hetrix_tools,site24x7,whmcs,phpipam.per_page(int 1–100, default 20),page(int ≥1).
Run row (MigrationRunResource): id (UUID), provider, status, stats, total_resources, failed_resources, error_message, started_at, finished_at, created_at, updated_at. Detail adds resources[] (resource: id, resource_type, external_id, action, status, attempts, error_message). Log (MigrationLogResource): id, level, message, context, logged_at.
POST /migrations — body parameters
provider(string, required) — one of the providers above.credentials(object of key→string, optional) — write-only. Required fields depend on the provider: thecredentials_schemainconfig/migrator.phpenforces the presence of fields marked required (missing →422oncredentials.<field>).
Error codes
401— missing/invalid token.403— missing required scope.404— unknown or foreign run.422— validation, executing a run outsideawaiting_review(run_not_awaiting_review), or no failed resources to retry (no_failed_resources).429— start limit exceeded (discovery_rate_limited).
Examples — API
List runs filtered by status and provider
curl -s "https://app.nodea.io/api/v1/migrations?status=awaiting_review&provider=uptime_robot" \
-H "Authorization: Bearer $NODEA_TOKEN" \
-H "Accept: application/json"
Start a migration run (write-only credentials)
curl -s -X POST https://app.nodea.io/api/v1/migrations \
-H "Authorization: Bearer $NODEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "uptime_robot",
"credentials": { "api_key": "u123456-abcdef..." }
}'
Execute a reviewed run
curl -s -X POST https://app.nodea.io/api/v1/migrations/{run}/execute \
-H "Authorization: Bearer $NODEA_TOKEN" \
-H "Accept: application/json"
Retry failed resources
curl -s -X POST https://app.nodea.io/api/v1/migrations/{run}/retry-failed \
-H "Authorization: Bearer $NODEA_TOKEN" \
-H "Accept: application/json"
MCP — tools
The MCP tools share the authentication and scope model with API v1 (Bearer + scope in ensureScope) and the migrator flag (a tool on an inactive flag disappears from tools/list). The run id is a UUID (string). A missing and a foreign id return the same error. Start, execute and retry are asynchronous (a queued acknowledgement). The destructive tool migration-delete is marked destructive and requires an explicit confirm: true. Provider credentials remain write-only.
| Name | Scope | Mode | Description |
|---|---|---|---|
| migrations-list | account:read | read-only | Run history (newest first) with total_resources/failed_resources. Filters status, provider; page, per_page (max 50). |
| migration-get | account:read | read-only | Run detail: header, resources (capped at 500), the 50 most recent logs and resource_counts. Param: id. Credentials never returned. |
| migration-create | account:write | write | Start a run; provider, credentials (object, write-only). Queues discovery. 5/min limit. |
| migration-execute | account:write | write | Execute the run (plan + import); id. Only in awaiting_review. |
| migration-retry-failed | account:write | write | Retry failed resources; id. Rejected when none failed. |
| migration-delete | account:delete | destructive (confirm) | Permanently delete the run with its resources and logs. Requires confirm: true; id. |
Examples — MCP (JSON-RPC)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "migration-create",
"arguments": {
"provider": "uptime_robot",
"credentials": { "api_key": "u123456-abcdef..." }
}
}
}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "migration-delete",
"arguments": { "id": "9b1f...uuid", "confirm": true }
}
}