Transfer a domain to Hostup from another registrar
7 steps · Checked against the API code 3 November 2026
A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.
A transfer moves the registration of a domain you already own from its current registrar to Hostup. You order it like any other product and pay the invoice. Then the registry takes over. The API tells you where the transfer stands and what to fix when the registry refuses it.
Before you start
Section titled “Before you start”- An API key with these scopes (Keys and scopes):
write:ordersto order the transfer.read:domainsto check the domain and follow the transfer.write:domainsto retry the transfer or save a new code.write:billingto pay the invoice through the API. Leave it out if the account holder pays in the client area.
- The requests read the key from
$HOSTUP_API_KEY. The Quickstart shows how to set it. - At the current registrar:
- Turn off the transfer lock on the domain.
- Get the transfer code. Registrars also call it the EPP code, auth code or authorization code.
- For .com and many other generic extensions, check that the domain was registered more than 60 days ago. A younger domain cannot be transferred yet.
- An account profile with the holder details the domain extension asks for. Step 1 shows which.
uuidgenfor the order’s retry key in step 3. Any other way to make a random UUID works too.
1. Check that the domain can be transferred
Section titled “1. Check that the domain can be transferred”Ask for the domain’s availability with GET /api/v2/domains/availability. Put your own domain in place of example.com here and in the requests below.
curl "https://cloud.hostup.se/api/v2/domains/availability?name=example.com" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "name": "example.com", "state": "unavailable", "actions": { "canTransfer": { "allowed": true, "reason": null }, "…": "…" }, "premium": false, "requiresRegistrarFeeAcceptance": false, "eppRequired": true, "supportedTransferYears": [1], "…": "…"}Go on when actions.canTransfer.allowed is true. It is false when the name is free to register or already at Hostup. It is also false when the check did not finish. It does not look at the lock or the code. The registry checks those later.
eppRequired:falsewhen the extension uses no code. LeaveeppCodeout of the order then. A .uk transfer never uses one.supportedTransferYears: the values you can send asyears.premiumorrequiresRegistrarFeeAcceptanceistrue: the registrar charges more than usual for this domain. The order charges that price. The preview in step 2 shows it.registryRequirements.transfer: what the order must carry for this extension, for example a phone number and an identity number for .se. The order takes them from the account profile.
2. Choose the nameservers and preview the order
Section titled “2. Choose the nameservers and preview the order”Always send nameserverMode in the order item. Without it, the account’s setting for transfers in the client area decides where the domain ends up.
preserve_currentkeeps the nameservers the domain has today. Your current DNS provider keeps answering for the domain. Nothing changes for the website or e-mail. The order reads the current nameservers when you send it.hostupmoves the domain to Hostup’s nameservers,primary.ns.hostup.seandsecondary.ns.hostup.se. The order creates a DNS zone for the domain at Hostup. Add"copyExistingDns": trueto copy the records found on the current nameservers into it.copyExistingDnsScopelimits the copy towebsiteoremailrecords.customwith at least twonameserverssends a new set.
POST /api/v2/orders/preview takes the same body as the order. It creates nothing. eppCode is the transfer code from the current registrar.
curl -X POST "https://cloud.hostup.se/api/v2/orders/preview" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "type": "domain", "action": "transfer", "domainName": "example.com", "years": 1, "eppCode": "Xy7-kP2q-9LmR", "nameserverMode": "preserve_current" } ] }'{ "items": [ { "domainName": "example.com", "type": "domain", "nameserverMode": "preserve_current", "…": "…" } ], "total": 186.25, "currencyCode": "SEK", "…": "…"}total is the price with tax. The amount here is an example. items[0].nameserverMode is the mode the order will apply. Read it when you left the mode out. The preview does not check the code.
3. Order the transfer
Section titled “3. Order the transfer”Make a new attemptKey for this order and keep it in a variable:
export ATTEMPT_KEY="$(uuidgen)"Today the key is not tied to your account. If anyone sent the same key in the last hour, you get that earlier order back with 200. Nothing is ordered for you. Never use a key copied from a page or an example.
Send the same item to POST /api/v2/orders. The call waits until the order exists and answers 201.
curl -X POST "https://cloud.hostup.se/api/v2/orders" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "attemptKey": "'$ATTEMPT_KEY'", "paymentMethod": "card", "items": [ { "type": "domain", "action": "transfer", "domainName": "example.com", "years": 1, "eppCode": "Xy7-kP2q-9LmR", "nameserverMode": "preserve_current" } ] }'{ "id": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3", "status": "pending", "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "invoice": { "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "status": "unpaid", "paymentUrl": "/billing?invoice=202600001", "…": "…" }, "paymentStatus": { "status": "unpaid", "reason": "Invoice has not been paid yet." }, "actions": { "canCancel": { "allowed": true, "reason": null }, "…": "…" }, "…": "…"}Keep the order ID and the invoice ID:
export ORDER_ID="ord_…" # id aboveexport INVOICE_ID="inv_…" # invoiceId aboveIn the body, '$ATTEMPT_KEY' steps out of the single quotes. The shell then puts the key in. After a timeout or a lost answer, send the same request with the same key. Sending a change twice explains the key.
The order creates an invoice. id is the order. invoiceId is the invoice you pay in step 4. paymentMethod is a hint for how the invoice will be paid. It charges nothing.
4. Pay the invoice
Section titled “4. Pay the invoice”A transfer ordered with an API key does not start before its invoice is paid. When paymentStatus.status in the order answer is already paid, go to step 5.
Send {} to POST /api/v2/billing/invoices/{id}/actions/pay to charge the primary saved card.
curl -X POST "https://cloud.hostup.se/api/v2/billing/invoices/$INVOICE_ID/actions/pay" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{}'{ "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "status": "paid", "totals": { "outstanding": 0, "…": "…" }, "…": "…"}If you pay another way
Section titled “If you pay another way”- Without a saved card the call answers
409 payment_method_invalid. - To pay from account credit, send an
amounttoPOST /api/v2/billing/invoices/{id}/actions/apply-credit. It can be no more than the account’s credit and no more than the invoice still owes. When the credit covers only part of the invoice, the rest stays unpaid. - The account holder can pay in the client area:
https://cloud.hostup.sefollowed byinvoice.paymentUrlfrom the order, for examplehttps://cloud.hostup.se/billing?invoice=202600001.
Pay an invoice covers every way to pay.
If you want to stop
Section titled “If you want to stop”Cancel the order with POST /api/v2/orders/{id}/actions/cancel and $ORDER_ID while actions.canCancel.allowed on the order is true. That also cancels its unpaid invoice.
5. Find the domain ID
Section titled “5. Find the domain ID”The order answer has no domain ID. The domain is in your domain list from the moment the order exists. Look it up by name with GET /api/v2/domains. Every domain endpoint takes its dom_… ID. IDs explains the prefix.
curl "https://cloud.hostup.se/api/v2/domains?name=example.com" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "data": [ { "id": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "name": "example.com", "serviceStatus": "pending", "awaitingPayment": false, "lifecycle": { "type": "transfer", "transferInProgress": true, "…": "…" }, "…": "…" } ], "…": "…"}export DOMAIN_ID="dom_…" # data[0].id aboveawaitingPayment is true while the invoice is unpaid. transferInProgress turns true when the transfer has reached the registry.
6. Follow the transfer
Section titled “6. Follow the transfer”GET /api/v2/domains/{id}/lifecycle says where the transfer stands. There is no job to poll. A transfer at the registry can take days. Poll a few times a day, not in a tight loop.
curl "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/lifecycle" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "actionRequired": false, "transferFailure": null, "transferStatus": { "kind": "pending_transfer_in_progress", "reason": "Transfer is being processed at the registry.", "actions": { "canRetryTransfer": { "allowed": false, "reason": "Transfer is already in progress at the registry." }, "…": "…" }, "progress": { "daysPassed": 1, "daysRemaining": 4, "maxDays": 5, "percent": 20 }, "…": "…" }, "…": "…"}transferStatus.kind is the state. reason says it in words. actionRequired is true when the registry refused the transfer for a reason someone has to fix. Find your kind in the table:
transferStatus.kind | What to do |
|---|---|
awaiting_payment | The invoice is unpaid. Go back to step 4. |
pending_transfer_in_progress, pending_transfer_generic | The registry has the transfer. Wait. progress estimates the approval window. For a generic extension such as .com it counts five days unless the registry states another window. progress is null when the window is not known. |
registry_locked | The transfer lock at the current registrar is still on. Turn it off there. Then retry without a new code. |
registry_locked_and_epp_invalid | The transfer lock is still on and the code was wrong as well. Turn off the lock. Then retry with a new code. |
too_new_for_transfer | The domain was registered less than 60 days ago. Wait until the 60 days have passed. The API does not return the date when that is. The current registrar shows when the domain was registered. A retry before then answers 429 cooldown_active. |
registry_suspended | The registry has suspended the domain. The current registrar has to have the suspension lifted first. |
transfer_error_known, transfer_failed_at_registry | The transfer stopped. transferFailure.category says why. epp_invalid means the code was wrong: retry with a new code. For other categories, transferFailure.reason says what to fix. When transferFailure.recovery is set, fill in its suggestedBody and send it with the retry. |
pending_epp_fallback | The transfer waits for a valid code. Retry with a new code. |
transfer_not_at_registry | The registry no longer has the transfer. Retry without a new code. |
registrar_push_request_required (.uk) | No code is used. Retry without a new code. |
registrar_push_required (.uk) | Ask your current provider to set the tag in nextStep.registrarTag within nextStep.deadlineDays days. |
active | The transfer is done. Go to step 7. |
expired, unknown | The domain is no longer a transfer in progress, for example because the order was cancelled. Nothing can be retried. A retry answers 409 transfer_not_in_progress. |
7. Check the domain and its nameservers
Section titled “7. Check the domain and its nameservers”When transferStatus.kind is active, the domain is registered through Hostup. Check its nameservers with GET /api/v2/domains/{id}/nameservers.
curl "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/nameservers" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "nameservers": ["ns1.example.net", "ns2.example.net"], "actions": { "canChangeNameservers": { "allowed": true, "reason": null } }, "…": "…"}With preserve_current you see the nameservers the domain had before. With hostup you see primary.ns.hostup.se and secondary.ns.hostup.se. Nameservers cannot be changed while the transfer runs. canChangeNameservers is false until it is done. After that, Change a domain’s nameservers shows how to change them.
Retry the transfer
Section titled “Retry the transfer”Do this when step 6 sends you here. Read the lifecycle again first (step 6). transferStatus.actions.canRetryTransfer.allowed must be true. When it is false, its reason says why a retry would not help now.
The retry itself does not check the invoice. Sent while the invoice is unpaid, it starts the transfer at the registry before payment. Retry only when canRetryTransfer allows it.
POST /api/v2/domains/{id}/actions/retry-transfer sends the transfer to the registry again. It answers when the registry has answered. A retry creates no new invoice. What you send depends on why the transfer stopped.
If the registry wants more details
Section titled “If the registry wants more details”A 422 transfer_recovery_required answer means the registry wants more holder details. errors[].pointer names each one. Its recovery.suggestedBody is the body to fill in and send back. When the lifecycle shows transferFailure.recovery, its suggestedBody works the same way. Update the account profile and send "profileUpdated": true. Send details meant for the transfer itself in recoveryValues.
With a new code
Section titled “With a new code”Get a newly issued code from the current registrar. Send it as eppCode. The call saves it before it sends the transfer.
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/actions/retry-transfer" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"eppCode": "Qw4-tR8n-2VbZ"}'{ "domainId": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "transferStatus": "pending_transfer_in_progress", "reason": "Transfer is being processed at the registry.", "actions": { "canRetryTransfer": { "allowed": false, "reason": "Transfer is already in progress at the registry." }, "…": "…" }, "…": "…"}Here transferStatus is the kind as a string. The lifecycle has it as transferStatus.kind. Follow the transfer with step 6 again.
transferAttempts.currentCodeRejected in the lifecycle says whether the registry has already refused the saved code. Such a code is not sent again. Add "force": true only when you know the same code is valid now.
Without a new code
Section titled “Without a new code”Send {} as the body. The saved code is sent again. A .uk transfer sends no code. Some registrars issue a new code when you turn off the lock. Then retry with the new code instead.
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/actions/retry-transfer" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{}'{ "domainId": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "transferStatus": "pending_transfer_in_progress", "reason": "Transfer is being processed at the registry.", "…": "…"}Save a code without a retry
Section titled “Save a code without a retry”When canRetryTransfer is not allowed but canUpdateEpp is, save the code on its own with POST /api/v2/domains/{id}/actions/update-epp. That happens while the invoice is unpaid, for example.
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/actions/update-epp" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"eppCode": "Qw4-tR8n-2VbZ"}'{ "domainId": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "eppCodePersisted": true, "transferRetryReady": true, "updatedAt": "2026-11-02T10:15:41.000Z"}This only saves the code. A later retry with {} sends the saved code. transferRetryReady is always true. It does not mean a retry is allowed now. Check canRetryTransfer in the lifecycle before you retry.
What happens next
Section titled “What happens next”- E-mails. The account holder gets an e-mail when the transfer starts at the registry and another when it is complete. For .se and .nu the start e-mail is skipped because those transfers finish quickly. When the registry refuses a transfer, the account holder can get an e-mail about that too. No transfer e-mail goes out when domain notifications are turned off for the account.
- Money. The order creates one invoice. The transfer waits until it is paid. Retries cost nothing extra.
- DNS. The domain ends up on the nameservers you chose in step 2. With
hostupthe DNS zone at Hostup becomes the one the internet reads.
Next: Point a domain at your server when the domain is on Hostup’s nameservers. For DNS somewhere else, change the nameservers.
Errors you may hit
Section titled “Errors you may hit”- 403
human_verification_requiredfrom the availability check: the key lacksread:domains. Without it the API treats the call as an anonymous visitor. Add the scope to the key. - 502
upstream_failedfrom the availability check: the check did not finish within about 20 seconds. Send it again. - 400
invalid_requestfrom the order: something the extension needs is missing.errors[].pointernames it./items/0/eppCodemeans the code is missing. A pointer under/clientDatameans the account profile lacks a holder detail. Add it to the profile or send it inclientData. - 422
invalid_requestfrom the order, with the pointer/items/0/nameserverMode:preserve_currentfound no current nameservers on the domain. Choose another mode. - 502
upstream_failedfrom the order:preserve_currentcould not read the current nameservers. Nothing was ordered. Send the order again later or choose another mode. - 409
domain_conflictfrom the order: the domain is already at Hostup or already ordered.conflicts[].reasonCodesays which, for examplealready_in_account. A name that is not registered anywhere answers409 domain_not_availableinstead. Register it instead of transferring it. - 503
domain_verification_unavailablefrom the order: the check that the name is registered did not finish. Nothing was ordered. Wait a little. Then send the same body with the sameattemptKeyagain. - 504 with the code
TIMEOUTfrom the order: the API stopped waiting for the answer. The order may still be placed. Send the same body with the sameattemptKeya little later. If the first request placed the order, you get that order back with200. - 409
transfer_blockedfrom a retry:errors[].codenames the blocker.registry_lockmeans unlock at the current registrar.epp_invalidandepp_code_unchangedmean get a new code. Retry when it is fixed.registrar_fee_confirmationandregistrant_not_eligiblecannot be fixed with a retry. Contact support with therequestIdfrom the answer. - 422
transfer_recovery_requiredfrom a retry: the registry needs more holder details. See If the registry wants more details.