Nodea — logo

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

MethodPathScopeDescription
GET/api/v1/migrationsaccount:readOwner's runs (newest first), paginated. Filters status, provider. Each row: total_resources, failed_resources.
GET/api/v1/migrations/{run}account:readRun detail with resources (capped at 500) and meta.resource_counts (per-status breakdown).
GET/api/v1/migrations/{run}/logsaccount:readRun log ledger (newest first), paginated. Filter errors_only.
POST/api/v1/migrationsaccount:writeStart a run (provider + write-only credentials). Queues discovery. Returns 201; 5/min limit → 429.
POST/api/v1/migrations/{run}/executeaccount:writeExecute the run (plan + import). Only in awaiting_review. Returns 202/422.
POST/api/v1/migrations/{run}/retry-failedaccount:writeRetry failed resources (reset to pending + redispatch). Returns 202 with retried; none failed → 422.
DELETE/api/v1/migrations/{run}account:deletePermanently 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: the credentials_schema in config/migrator.php enforces the presence of fields marked required (missing → 422 on credentials.<field>).

Error codes

  • 401 — missing/invalid token.
  • 403 — missing required scope.
  • 404 — unknown or foreign run.
  • 422 — validation, executing a run outside awaiting_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.

NameScopeModeDescription
migrations-listaccount:readread-onlyRun history (newest first) with total_resources/failed_resources. Filters status, provider; page, per_page (max 50).
migration-getaccount:readread-onlyRun detail: header, resources (capped at 500), the 50 most recent logs and resource_counts. Param: id. Credentials never returned.
migration-createaccount:writewriteStart a run; provider, credentials (object, write-only). Queues discovery. 5/min limit.
migration-executeaccount:writewriteExecute the run (plan + import); id. Only in awaiting_review.
migration-retry-failedaccount:writewriteRetry failed resources; id. Rejected when none failed.
migration-deleteaccount:deletedestructive (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 }
  }
}