hosting
POST /v1/sites/{siteId}/migrations/managed-request
Ask our team to migrate this site for you — free, no limit.
Authentication
Send an API key as a bearer token. The key must carry the hosting.import.manage permission; a key without it is refused with 403, not 404.
This endpoint takes no organisation id. Your key already identifies the organisation it belongs to, and the response is scoped to it.
Try it
Replace anything in angle brackets with your own values, and the key placeholder with a key from your dashboard.
curl -X POST https://api.zinndigital.com/v1/sites/{siteId}/migrations/managed-request \
-H "Authorization: Bearer zdk_live_…" \
-H "Content-Type: application/json" \
-d '{ }'Signed in? The API console in your dashboard fills in your real organisation id and your own key, and runs the request against the live API so you can see the actual response. Open this endpoint in the API console
Details
Books a free manual migration: our staff move the site for the customer. Owner ruling 2026-10-14 — migrations are free for every customer with no limits, and self-service (createSiteMigration, the zinn-migrate plugin archive import, cPanel, Plesk, DirectAdmin, FTP/SFTP and SSH sources) is the main, fastest route; this is the route when the customer would rather we did it, or a source cannot be automated. It records an ExtraPurchase of the managed_migration service at a price of 0 (waived for every organization, whatever the catalogue row says), not a migration job: the fulfilment workflow raises it to the staff Extras queue, staff are alerted by e-mail and Telegram, and the 72-hour SLA clock starts. Returns the purchase, so the client can show its status and SLA due time. There is no per-site or per-account limit — a second request is a second job. An environment whose catalogue has never been seeded answers 503 rather than 404, because the service exists and is advertised. The staff note travels on the purchase. ⛔ Send no credentials here — the body has nowhere to put them, and staff collect them out of band from a human they can verify. Requires hosting.import.manage, the same authority as a self-service migration: nothing is charged.
Parameters
| Name | Type | Required | What it is |
|---|---|---|---|
siteId (path) | Uuid | Yes | Site ID (UUIDv7). |
Idempotency-Key (header) | string | No | Client-generated key that makes an unsafe request replay-safe: the server stores the first response and returns it verbatim for repeats. |
Request body
| Name | Type | Required | What it is |
|---|---|---|---|
source_host | string | No | Where the site is hosted today, so the fulfilment queue is actionable without a round trip. A hostname, never a login. Optional. |
note | string | No | Anything else staff should know before they start. |
currency | string | No | The currency the customer was quoted in. Validated and refused if no gateway can charge it — this is a charge, and silently substituting the base currency would bill a different… |
Response
| Name | Type | Required | What it is |
|---|---|---|---|
id | Uuid | Yes | UUIDv7 identifier — sortable by creation time (docs/02 §8). |
org_id | Uuid | Yes | UUIDv7 identifier — sortable by creation time (docs/02 §8). |
extra_id | Uuid | Yes | UUIDv7 identifier — sortable by creation time (docs/02 §8). |
site_id | object | No | — |
domain_id | object | No | — |
order_id | object | No | The order this purchase was charged on — or, while awaiting_payment (Q225), is waiting to be paid on: settle it with payOrder. null for a staff comp or a purchase paid from… |
status | ExtraPurchaseStatus | Yes | An extra purchase's lifecycle state. awaiting_payment (Q225): placed by a one-click buy with nothing on file to charge, waiting for the customer to pay its order on-session —… |
category | string | Yes | — |
fulfilment_type | ExtraFulfilmentType | Yes | How an extra is delivered. |
interval | ExtraInterval | Yes | One-off charge or a recurring subscription. annual is a PURCHASE term only — an SEO tool bought twelve months at a time at V1's yearly saving (D19, 2026-09-25). No catalogue row… |
price | MoneyAmount | Yes | A money value — integer minor units + an ISO 4217 code (CLAUDE.md §2.8). |
recurrence | string<none, active, ended> | Yes | Whether the recurrence behind this purchase is still live. none = it never recurred (a one-off); active = a scheduled charge exists; ended = it recurred and no longer does.… |
next_renewal_on | object | No | The next charge date, or null. Also null for a live charge that has no date scheduled yet, so read recurrence — not this field — to decide whether a purchase renews at all. |
renews_as | null | object | No | Q321 — the smaller tier of the same SEO tool chosen for the next renewal (scheduleExtraTierSwitch), or null when the purchase renews as itself. |
arrears | null | ExtraPurchaseArrears | No | s1014 R40 GAP-5/6/7 (docs/747 §2b, V1 parity). null, or this add-on's unpaid renewal: grace — it keeps working until freezes_on (3 days after the renewal fell due); frozen… |
sla_due_at | object | No | — |
completed_at | object | No | — |
failure_reason | string | No | — |
created_at | string | Yes | — |
updated_at | string | Yes | — |
Errors this endpoint can return
401 · 403 · 404 · 422 · 429 · 503