Skip to main content

Getting Started

Server

The API is a single Go binary, wirekite-api, listening on HTTPS.
  • Default port: 9443 (override with --port)
  • Base URL: https://<host>:9443
  • Version prefix: every path begins with /v1/
  • TLS: self-signed cert at api/certs/server.crt by default; override with --cert / --key
Start command:
./api/wirekite-api --port 9443 --cert ./api/certs/server.crt --key ./api/certs/server.key

Authentication

All endpoints except /v1/health require a Bearer token:
Authorization: Bearer <api_key>
Keys live in api/config/api_keys.json. Create one before calling protected endpoints:
{
  "keys": {
    "k_a1b2c3...": { "name": "default", "created_at": "2026-04-24T00:00:00Z" }
  }
}
Auth failures return 401 Unauthorized with error.code = "AUTH_FAILED".

Response Envelope

Every response uses the same JSON shape:
{
  "success": true,
  "result": { },
  "error": null,
  "logs": { "extractor": "…tail of log…", "loader": "…" },
  "duration": "2m15s"
}
  • result — endpoint-specific payload; null on error.
  • error{ "code": "...", "message": "...", "details": {...} } on error; null on success.
  • logs — optional, included for synchronous operations.
  • duration — optional, elapsed wall-clock time.

Error Codes

error.codeHTTPMeaning
AUTH_FAILED401Missing or invalid Bearer token
INVALID_REQUEST400Malformed body / bad parameters
MIGRATION_NOT_FOUND404Migration does not exist
SOURCE_NOT_FOUND404 / 400Source does not exist / not created
TARGET_NOT_FOUND404 / 400Target does not exist / not created
OPERATION_CONFLICT409Another operation already running on this migration
OPERATION_NOT_FOUND404Operation id unknown
CONNECTION_REFUSED200 / 500Connection test failed (details in error.details)
SCHEMA_APPLY_FAILED500Schema apply exited non-zero
OBJECTS_APPLY_FAILED500Objects apply exited non-zero
EXTRACTOR_FAILED500Data/change extractor exited non-zero
LOADER_FAILED500Data/change loader exited non-zero
ORCHESTRATOR_FAILED500Orchestrator startup failed
RESET_FAILED500Reset operation failed
VALIDATE_FAILED500Data validation failed
DATA_FAILED500Data migration failed
REPLICATION_FAILED500Replication failed
FIREBOLT_SCHEMA_PUBLIC_ONLY400Firebolt target requires public schema (use schema_rename)

Synchronous vs. Asynchronous

CategoryBehaviorEndpoints
SynchronousHTTP blocks until done (≤ 15 min). Response includes result + logs + duration.All CRUD, /prereqs, /validate, /schema/extract, /schema/apply, /objects/apply, /reset, /validate (data), /sources/test, /targets/test, /queues/test, pause/resume, health
AsynchronousReturns immediately with operation_id. Poll /v1/migrations/:name/status or /v1/operations/:id./data, /replication, /replication-queue, /data-replication
Async endpoints accept an optional X-Request-Id: <uuid> header. Repeating the same request id returns the same operation — safe for retries across flaky networks.

Conventions in examples

The examples below use these shell variables:
HOST=localhost               # or your server host
TOKEN=<your_api_key>
CURL='curl -sk'              # -k skips self-signed cert verification

All Endpoints at a Glance

MethodPathSync?
GET/v1/healthsync (public)
PUT/v1/sources/:namesync
GET/v1/sources/:namesync
GET/v1/sourcessync
DELETE/v1/sources/:namesync
POST/v1/sources/testsync
PUT/v1/targets/:namesync
GET/v1/targets/:namesync
GET/v1/targetssync
DELETE/v1/targets/:namesync
POST/v1/targets/testsync
POST/v1/queues/testsync
PUT/v1/migrations/:namesync
GET/v1/migrations/:namesync
GET/v1/migrationssync
DELETE/v1/migrations/:namesync
POST/v1/prereqssync
POST/v1/validatesync
POST/v1/migrations/:name/schema/extractsync
POST/v1/migrations/:name/schema/applysync
POST/v1/migrations/:name/objects/applysync
POST/v1/migrations/:name/dataasync
POST/v1/migrations/:name/replicationasync
POST/v1/migrations/:name/replication/pausesync
POST/v1/migrations/:name/replication/resumesync
POST/v1/migrations/:name/replication-queueasync
POST/v1/migrations/:name/replication-queue/pausesync
POST/v1/migrations/:name/replication-queue/resumesync
POST/v1/migrations/:name/data-replicationasync
POST/v1/migrations/:name/resetsync
POST/v1/migrations/:name/validatesync
GET/v1/migrations/:name/statussync
GET/v1/operationssync
GET/v1/operations/:idsync
POST/v1/operations/:id/cancelsync
POST/v1/operations/:id/stopsync

Health

GET /v1/health

Public, no auth. Returns 200 OK.
$CURL https://$HOST:9443/v1/health
# → {"status":"ok"}

Sources

Supported type: mysql, mariadb, postgres, oracle, sqlserver.

PUT /v1/sources/:name

Create or replace a source. Request body:
{
  "type": "mysql",
  "parameters": {
    "host": "10.0.0.1",
    "port": "3306",
    "username": "wirekite",
    "password": "secret",
    "database": "app"
  }
}
Response result: { "name": "mysql-prod", "type": "mysql" }
$CURL -X PUT https://$HOST:9443/v1/sources/mysql-prod \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"type":"mysql","parameters":{"host":"10.0.0.1","port":"3306","username":"wirekite","password":"secret","database":"app"}}'

GET /v1/sources/:name

Returns the source; sensitive params (password, etc.) are masked as "***".

GET /v1/sources

Returns { "sources": [ { name, type, parameters } ... ] } (sensitive masked).

DELETE /v1/sources/:name

Deletes the source. Fails with INVALID_REQUEST if any migration still references it.

POST /v1/sources/test

Test a connection without saving. Body has the same shape as PUT. Response result: { "connected": true, "message": "…" }

Targets

Supported type:
  • Database targets: mysql, mariadb, postgres, oracle, sqlserver, snowflake, bigquery, spanner, firebolt, databricks, singlestore
  • Queue targets: kafka, redpanda

PUT /v1/targets/:name

Create or replace a target. Database target body (e.g. Spanner):
{
  "type": "spanner",
  "parameters": {
    "project": "my-project",
    "instance": "my-instance",
    "database": "my-db",
    "credentials_file": "/path/to/sa.json"
  }
}
Queue target body (Kafka/Redpanda):
{
  "type": "kafka",
  "parameters": {
    "brokers": "broker1:9092,broker2:9092",
    "topic": "wirekite-changes",
    "batchSize": "1",
    "compression": "snappy"
  }
}
batchSize optional (default 1); compression optional (none|snappy|lz4|zstd). Response result: { "name": "...", "type": "..." }

GET /v1/targets/:name

Get one (sensitive masked).

GET /v1/targets

List all.

DELETE /v1/targets/:name

Fails if referenced by a migration.

POST /v1/targets/test

Test a database target connection. Body same shape as PUT. Queue types are rejected — use /v1/queues/test instead.

POST /v1/queues/test

Test a Kafka/Redpanda broker + topic without saving.
{ "type": "kafka", "brokers": "broker1:9092,broker2:9092", "topic": "wirekite-changes" }
Response result: { "ok": true, "message": "broker reachable, topic 'wirekite-changes' found" }

Migrations

A migration binds one source to one or both of fileTarget (DB target) and queueTarget (Kafka/Redpanda target). At least one must be provided. Most operations require fileTarget; queue-only CDC uses /replication-queue.

PUT /v1/migrations/:name

Create or replace a migration. Body:
{
  "source": "mysql-prod",
  "fileTarget": "spanner-prod",
  "queueTarget": "kafka-prod",
  "tables": ["app.customers", "app.orders"],
  "threads": 4,
  "rows_per_file": 50000,
  "schema_rename": "app:public",
  "logDetail": "basic"
}
Required: source, tables, and at least one of fileTarget / queueTarget. Optional: threads, rows_per_file, schema_rename, logDetail ("basic" default, or "verbose"). Response result: { "name": "...", "source": "...", "fileTarget": "...", "queueTarget": "...", "tables": <count> }

GET /v1/migrations/:name

Return the migration config.

GET /v1/migrations

List all as { "migrations": [ { name, source, fileTarget, queueTarget, tables: <count> } ... ] }.

DELETE /v1/migrations/:name

Delete migration + working directory. Rejects with OPERATION_CONFLICT if an operation is still running.

Prerequisites & Validation

POST /v1/prereqs

Get SQL that must be run on source and target before migration works. Does not execute anything. Body:
{
  "source": { "type": "mysql", "parameters": {"host":"...","port":"3306","username":"...","database":"app"} },
  "target": { "type": "spanner", "parameters": {"project":"...","instance":"...","database":"..."} },
  "tables": ["app.customers","app.orders"],
  "mode": "data+replication",
  "schema_rename": "app:public"
}
mode: one of data, replication, data+replication (controls which grants are included). Response result:
{
  "source_commands": "GRANT SELECT ON app.customers TO 'wirekite'@'%';\n...",
  "target_commands": "CREATE SCHEMA public;\nGRANT ... ;\n..."
}

POST /v1/validate

Actually connect and check every prerequisite is satisfied. Body same shape as /prereqs, plus valid password values. Response result:
{
  "ready": false,
  "checks": [
    { "name": "source: SELECT on app.customers", "passed": true },
    { "name": "source: binlog_format=ROW", "passed": false, "error": "binlog_format is 'STATEMENT'" }
  ]
}

Schema

POST /v1/migrations/:name/schema/extract

Extract DDL from source. Requires fileTarget. Produces tables-schema.sql, objects-schema.sql, drop-tables.sql inside the migration working directory. Times out after 15 minutes. Response result: { "sql_file": "/path/to/tables-schema.sql", "tables": <count> }

POST /v1/migrations/:name/schema/apply

Apply DDL to target. Idempotent. Body (optional): { "sql_file": "/custom/path.sql" } — defaults to the extracted tables-schema.sql. Response result: { "tables_created": <count> }

Objects

POST /v1/migrations/:name/objects/apply

Apply database objects (views, procedures, triggers, functions) to the target. Runs after schema/extract. Partial failures are reported rather than aborting. Response result: { "objects_succeeded": <count>, "objects_failed": <count> }

Data Migration (async)

POST /v1/migrations/:name/data

Start data load (extract → move → load). Returns immediately. Body (optional):
{ "resume": false }
Optional header: X-Request-Id: <uuid> for safe retries. Response result:
{ "operation_id": "op-20260424-a1b2c3", "status": "running", "started_at": "2026-04-24T10:00:00Z" }
Poll /v1/migrations/:name/status or /v1/operations/:id for progress.

Replication (async)

POST /v1/migrations/:name/replication

Start CDC replication to fileTarget. Requires schema/extract to have been run. Body (optional):
{ "resume": false, "position": "mysql-bin.000123:456" }
position format is source-type-specific (MySQL: file:offset; Oracle: SCN; SQL Server: hex LSN). Ignored when resume=true. Response result: { "operation_id": "...", "status": "running", "started_at": "..." }

POST /v1/migrations/:name/replication/pause

Pause the running replication (process stays alive).

POST /v1/migrations/:name/replication/resume

Resume a paused replication. Response result: { "migration": "...", "action": "paused" | "resumed" }

Replication to Queue (async)

Use when the migration has queueTarget only (no fileTarget). Migrations that have a fileTarget are rejected by this endpoint.

POST /v1/migrations/:name/replication-queue

Start queue-only CDC. No schema extract required. Body (optional): { "resume": false, "position": "..." } Response result: { "operation_id": "...", "status": "running", "started_at": "...", "mode": "change-queue" }

POST /v1/migrations/:name/replication-queue/pause and /resume

Same shape as /replication/pause|resume.

Data + Replication (async, chained)

POST /v1/migrations/:name/data-replication

Two-phase: data load → hand off position → start CDC. Requires fileTarget. Body (optional): { "resume": false, "position": "..." } Smart resume: if position.pkt already exists the data phase is skipped and CDC resumes from there; otherwise the data phase re-runs. Response result: { "operation_id": "...", "status": "running", "started_at": "..." }

Reset

POST /v1/migrations/:name/reset

Reset the target. Requires fileTarget. Body (optional): { "mode": "drop" | "truncate" | "metadata" } (default drop). Response result:
  • drop: { "mode": "drop", "tables_dropped": N, "drop_failed": M }
  • truncate: { "mode": "truncate", "tables_truncated": N, "truncate_failed": M }
  • metadata: { "mode": "metadata" }

Validate Data

POST /v1/migrations/:name/validate

Compare row counts / checksums between source and target. Safe to run during active replication. Requires fileTarget. Body (optional): { "sample_percent": 100 } (0 or 100 = all rows; 1–99 = sample). Response result:
{ "tables_checked": 12, "tables_matched": 11, "tables_failed": 1, "mismatches": ["app.orders"] }

Status

GET /v1/migrations/:name/status

Get live progress of the currently running operation, or a summary of the last completed operation when idle. Running (data phase):
{
  "operation_id": "op-20260424-a1b2c3",
  "mode": "data",
  "status": "running",
  "phase": "loading",
  "started_at": "2026-04-24T10:00:00Z",
  "tables": { "total": 12, "extracted": 8, "loaded": 5 },
  "rows":   { "extracted": 3200000, "loaded": 2100000 },
  "current_table": "orders",
  "log_tail": "…"
}
Running (change phase):
{
  "operation_id": "...",
  "mode": "change",
  "status": "running",
  "phase": "replication",
  "rows": { "inserts": 45000, "updates": 12000, "deletes": 800 },
  "lag":  { "pending_files": 2 },
  "log_tail": "…"
}
Idle:
{
  "operation_id": null,
  "status": "idle",
  "last_operation": { "id": "...", "mode": "data", "status": "completed", "finished_at": "...", "duration": "2h15m" }
}

Operations

Generic long-running-operation tracking across all async ops.

GET /v1/operations

List with filters + pagination. Query params (all optional): migration=<name>, status=<running|completed|failed|cancelled|stopped>, mode=<data|change|change-queue|data-replication>, page_size=<1-200> (default 50), page_token=<offset>. Response result:
{
  "operations": [
    { "id":"…", "migration":"…", "mode":"data", "status":"running",
      "started_at":"…", "finished_at":"…", "pid":12345,
      "request_id":"…", "error": null }
  ],
  "total": 42,
  "next_page_token": "50"
}

GET /v1/operations/:id

Full detail including result, error, and per-binary logs tails.

POST /v1/operations/:id/cancel

Graceful termination (SIGTERM). Response result: { "operation_id":"…", "status":"cancelled" }

POST /v1/operations/:id/stop

Force kill (SIGKILL) + cleanup of orphaned child processes. Response result: { "operation_id":"…", "status":"stopped" }