Register a new domain and wait until it is active
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.
Registering a domain is an order. You check that the name is free, order it and pay the invoice. Hostup then registers the domain with the registry. This page goes from the first check to a registered domain with its dom_… ID.
Before you start
Section titled “Before you start”- An API key with these scopes (Keys and scopes):
read:domainsfor the availability check and for finding the new domain.write:ordersfor the order.write:billingfor paying the invoice.write:domainsonly if a registration fails and you retry it.
- The requests read the key from
$HOSTUP_API_KEY. The Quickstart shows how to set it. - A saved card on the account. Account credit that covers the invoice works too.
uuidgenfor the order’s retry key in step 4. Any other way to make a random UUID works too.jqfor the polling loop in step 7.
1. Check that the name is free
Section titled “1. Check that the name is free”Ask for the name with GET /api/v2/domains/availability. Send the full name, extension included. The call waits for the check and answers with the name’s row. That can take up to about 20 seconds. Put your own name in place of example.se here and in the requests below.
curl "https://cloud.hostup.se/api/v2/domains/availability?name=example.se" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "name": "example.se", "state": "available", "actions": { "canRegister": { "allowed": true, "reason": null }, "…": "…" }, "billing": { "amount": 79, "currencyCode": "SEK", "billingCycle": "annually" }, "renewalAmount": 169, "premium": false, "supportedRegisterYears": [1, 2, 3, 5], "registryRequirements": { "registration": [ { "key": "phoneNumber", "required": true, "…": "…" }, { "key": "registrationIdentifier", "required": true, "…": "…" }, { "key": "acceptedTerms", "required": true, "acceptedTermsKey": "se_registration_terms", "…": "…" } ], "…": "…" }, "…": "…"}Go on when actions.canRegister.allowed is true. state says where the name stands:
available: free to register.unavailable: someone has registered the name already. If it is yours at another registrar, transfer it instead.already_owned: the name is already registered at Hostup.existingDomainIdis set when it is in your own account.unknown: the check did not finish.unknownReasonisupstream_timeoutwhen the registry did not answer in time andupstream_unavailableotherwise. The name is not confirmed taken. Check it again.
supportedRegisterYears lists the periods you can order. billing.amount is the registration price for the default period. renewalAmount is the price to renew for one year. These prices are in SEK. The preview in step 3 gives the total in your account’s currency. GET /api/v2/products/domains/{tld} has the price of every period.
A name with premium: true is priced by the registry. The preview and the order treat a premium name as one year, whatever years says. Send "years": 1 for it. The preview in step 3 shows its price.
registryRequirements.registration is what the registry needs for a new registration. For .se and .nu that is:
- a phone number for the holder (
phoneNumber); - a personal identity number or organisation number (
registrationIdentifier); - acceptance of the registry’s terms (
acceptedTerms). Copy itsacceptedTermsKeyinto the order item’sacceptedTerms.
2. Decide who holds the domain
Section titled “2. Decide who holds the domain”The holder is the person or organisation the registry lists as the owner. By default it is the account holder. The order takes the holder’s name, address, phone number and identity number from the account profile. If the profile lacks something the registry needs, the order stops with 400 and names the field. Fill it in with Update your account profile or in the client area.
You can also send the missing fields in the order’s clientData, next to items. They then count for this order only. The profile stays as it is.
{ "clientData": { "phoneNumber": "+46701740605", "registrationIdentifier": { "value": "556000-0000", "countryCode": "SE" } }, "…": "…"}Replace 556000-0000 with the holder’s real number.
If someone else holds the domain
Section titled “If someone else holds the domain”To register the domain to someone else, add registrantContact to the order item. The order uses it as it is and fills no gaps from the profile. Send every field.
Today the order’s check of the registry rules reads a missing phoneNumber or registrationIdentifier from the account profile instead. It then accepts the order. The registration fails after payment. Always send both in registrantContact.
Check the names first with POST /api/v2/orders/validate-contact. It needs no key.
curl -X POST "https://cloud.hostup.se/api/v2/orders/validate-contact" \ -H "Content-Type: application/json" \ -d '{ "firstName": "Alex", "lastName": "Example", "companyName": "Example AB", "registrationIdentifier": { "value": "556000-0000", "countryCode": "SE" }, "accountType": "organisation", "countryCode": "SE" }'{ "valid": true, "warnings": []}A problem still answers 200, with valid: false. Each entry in warnings has the field, a code and a reason you can show. Fix each field it names and check again.
The check names the holder type accountType. In registrantContact the same value goes in type. registrantContact refuses accountType with 400. For an organisation, firstName and lastName are the contact person there.
This check covers the names and the organisation name. It does not check the identity number. The order does: a Swedish number in registrantContact must have 10 or 12 digits and a valid check digit.
Send this item in steps 3 and 4 in place of the item shown there:
{ "type": "domain", "action": "register", "domainName": "example.se", "years": 1, "acceptedTerms": ["se_registration_terms"], "registrantContact": { "type": "organisation", "firstName": "Alex", "lastName": "Example", "companyName": "Example AB", "phoneNumber": "+46701740605", "street": "Exempelgatan 1", "city": "Stockholm", "state": "Stockholm", "postalCode": "111 22", "countryCode": "SE", "registrationIdentifier": { "value": "556000-0000", "countryCode": "SE" } }}Replace 556000-0000 with the holder’s real number. This placeholder fails the order’s check on purpose: an order with it answers 400.
3. Preview the order
Section titled “3. Preview the order”POST /api/v2/orders/preview prices the order without creating anything. Send the same items as the order.
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": "register", "domainName": "example.se", "years": 1, "acceptedTerms": ["se_registration_terms"] } ] }'{ "items": [ { "domainName": "example.se", "type": "domain", "periodYears": 1, "nameserverMode": "hostup", "…": "…" } ], "total": 98.75, "currencyCode": "SEK", "reverseCharge": false, "…": "…"}total is what the invoice will come to, with VAT where VAT applies. With the key the preview uses the account’s country and currency.
nameserverMode hostup puts the domain on Hostup’s nameservers. To use other nameservers from the start, add "nameserverMode": "custom" and your nameservers to the item. Change a domain’s nameservers moves them later.
The preview does not check the terms, the phone number or the identity number. The order checks those.
4. Place the order
Section titled “4. Place the order”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 order to POST /api/v2/orders:
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": "register", "domainName": "example.se", "years": 1, "acceptedTerms": ["se_registration_terms"] } ] }'{ "id": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3", "status": "pending", "invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "invoice": { "id": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3", "status": "unpaid", "…": "…" }, "paymentStatus": { "status": "unpaid", "reason": "Invoice has not been paid yet." }, "actions": { "canCancel": { "allowed": true, "reason": null }, "…": "…" }, "domains": [ { "name": "example.se", "…": "…" } ], "…": "…"}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 now exists with an unpaid invoice. Nothing is registered yet.
paymentMethod charges nothing. With card, Hostup holds back the e-mail with the unpaid invoice for a short while. That gives you time to pay before the account holder is asked to.
5. Pay the invoice
Section titled “5. Pay the invoice”Payment starts the registration. An order placed with an API key is never registered before its invoice is paid. When paymentStatus.status in the order answer is already paid, go to step 6.
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. - The account holder can pay in the client area:
https://cloud.hostup.sefollowed byinvoice.paymentUrlfrom the order.
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.
6. Find the domain ID
Section titled “6. 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.se" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "data": [ { "id": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "name": "example.se", "serviceStatus": "pending", "awaitingPayment": false, "actionRequired": false, "…": "…" } ], "hasMore": false, "nextCursor": null}export DOMAIN_ID="dom_…" # data[0].id aboveserviceStatus stays pending until the registry has registered the domain. awaitingPayment is true while the invoice is unpaid.
7. Wait for the registration
Section titled “7. Wait for the registration”GET /api/v2/domains/{id}/lifecycle shows where the registration stands.
curl "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/lifecycle" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "actionRequired": false, "registrationStatus": { "kind": "pending", "reason": "Registration is being processed.", "autoRetry": null, "…": "…" }, "…": "…"}Poll it while registrationStatus.kind is pending. Keep polling while registrationStatus.autoRetry.active is true too. The registry then refused an attempt and Hostup tries again by itself. kind shows failed during that wait.
while true; do BODY=$(curl -s "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/lifecycle" \ -H "Authorization: Bearer $HOSTUP_API_KEY") KIND=$(echo "$BODY" | jq -r '.registrationStatus.kind // empty') RETRYING=$(echo "$BODY" | jq -r '.registrationStatus.autoRetry.active // false') echo "${KIND:-no status in this answer, polling again}" case "$KIND" in active|unknown) break ;; failed) [ "$RETRYING" = "true" ] || break ;; esac sleep 60doneThe loop stops only at a final state. An answer without registrationStatus prints a line and the loop polls again. That happens when one call fails, for example with 429. If the line keeps coming, run the lifecycle request above and read detail.
At active the domain is registered. serviceStatus in the domain list is active too. At failed the registration needs you. Retry the registration says what to do. unknown means the domain is no longer being registered, for example because the order was cancelled.
Retry the registration
Section titled “Retry the registration”When the registry refuses the registration, registrationStatus.kind is failed and actionRequired is true. registrationStatus.reason says what is wrong. When details are missing from the account profile, registrationFailure.recovery.missing lists them. Each entry’s profileField names the profile field. For .se and .nu domains recovery is always null. There the retry below answers 422 registration_recovery_required and names what is missing. Fill the details in with Update your account profile or in the client area.
Then check registrationStatus.actions.canRetryRegistration.allowed in the lifecycle. When it is true, retry the registration with POST /api/v2/domains/{id}/actions/retry-registration. Send {"profileUpdated": true} after you changed the account details. Otherwise send {}. One exception: when a .se or .nu registration was refused over the identity number, allowed stays false even after you correct it. Send the retry with {"profileUpdated": true} once the number is corrected.
curl -X POST "https://cloud.hostup.se/api/v2/domains/$DOMAIN_ID/actions/retry-registration" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"profileUpdated": true}'{ "domainId": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3", "registrationStatus": "completed", "reason": "Registration completed.", "…": "…"}When the answer has operation, follow that job with wait-for-job.sh before you read the domain again:
export JOB_ID="dbj_…" # operation.jobId above./wait-for-job.sh "/api/jobs/$JOB_ID"Then run the polling loop from step 7 again.
Some extensions need extra details for the retry. The call then answers 422 registration_recovery_required. recovery.suggestedBody in that answer is the body to send. Fill in its empty values and send it back. Stop when a refused retry has registration.terminal set to true. Another retry will not help then.
Check several names at once
Section titled “Check several names at once”To compare names before you choose one, send up to 50 full names in names to POST /api/v2/domains/availability. Names that share the part before the extension (example in example.se) are checked with at most eight extensions. Further extensions are dropped.
curl -X POST "https://cloud.hostup.se/api/v2/domains/availability" \ -H "Authorization: Bearer $HOSTUP_API_KEY" \ -H "Content-Type: application/json" \ -d '{"names": ["example.se", "example-shop.se"]}'{ "operation": { "status": "queued", "jobId": "dcheck_01hxa3b4c5d6e7f8g9h0j1k2m3", "pollUrl": "/api/v2/domains/availability/dcheck_01hxa3b4c5d6e7f8g9h0j1k2m3" }}export JOB_ID="dcheck_…" # operation.jobId aboveA check of one name with three extensions or fewer usually answers at once. That answer is 200 with the rows in data, in the same shape as below. Larger checks answer 202 with a dcheck_… job, as above.
Poll the job every few seconds until status is completed or failed. This job has its own poll URL. The general job endpoint and wait-for-job.sh do not know dcheck_… IDs.
curl "https://cloud.hostup.se/api/v2/domains/availability/$JOB_ID" \ -H "Authorization: Bearer $HOSTUP_API_KEY"{ "status": "completed", "data": [ { "name": "example.se", "state": "available", "actions": { "canRegister": { "allowed": true, "reason": null }, "…": "…" }, "billing": { "amount": 79, "currencyCode": "SEK", "billingCycle": "annually" }, "…": "…" }, { "name": "example-shop.se", "state": "unknown", "unknownReason": "upstream_timeout", "…": "…" } ]}Each row has the shape of the single check in step 1. While status is processing, data may already hold the names that are done. A failed job has only a reason. Send the check again then.
A name missing from a completed job was not checked. That has one of three causes:
- Hostup does not sell its extension.
- It was past the eighth extension for the same name.
- Its check failed.
Check a missing name again in a request of its own. A name whose extension Hostup does not sell then answers 400.
An answer given at once can have two more states:
checking: the check of this name is still running.invalid: not a name Hostup can check, for example an extension Hostup does not sell. A job leaves such names out.
Every name and extension counts against the account’s lookup budget. Rate limits gives its size.
What happens next
Section titled “What happens next”- The account holder gets the invoice by e-mail.
- When the invoice is paid, Hostup sends the registration to the registry. Until then
registrationStatus.reasonsays “Waiting for the registration invoice to be paid.” - Temporary registry errors are retried automatically.
registrationStatus.autoRetry.nextRetryAtsays when the next attempt runs. - When the registry refuses the registration and the holder’s details are the problem, the account holder also gets an e-mail about it. Retry the registration says what to do.
- The registry may ask the holder to confirm the holder’s e-mail address. Get the holder e-mail verification shows whether a confirmation is pending and which address it went to. Until it is confirmed, the registry can suspend the domain.
Next: Point a domain at your server. 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. - 429
rate_limit_exceededfrom the availability check: the account’s lookup budget is used up. Wait the number of seconds inRetry-Afterbefore you check again. - 502
upstream_failedfrom the availability check: the check did not finish within about 20 seconds. Send it again. - 422
domain_not_eligiblefrom the preview: the holder may not hold the extension, for example because of the holder’s country. WithregistrantContactin the item, the preview checks that holder’s country and type. Without it, it checks the account’s. - 400
invalid_requestfrom the order, withdomainRequirementsin the body: the registry needs something the order lacks.missingAcceptedTermsnames a key foracceptedTerms.missingProfileFieldsnames profile fields to fill in or send inclientData.missingRegistrantContactFieldsnames fields forregistrantContact. - 409
domain_not_availablefrom the order: the order checks the name with the registry again. Someone registered it after your check. Choose another name. - 409
domain_conflictfrom the order: the name is already at Hostup. You may also have ordered it recently. - 409
domain_order_in_progressfrom the order: two orders for the same name arrived at once. This one is the second. - 503
domain_verification_unavailablefrom the order: the registry did not answer the order’s own check. Try again in a moment with the sameattemptKey. - 504 with the code
TIMEOUTfrom the order: the API stopped waiting for the answer. The order may still be placed. Send the same request with the sameattemptKeya little later. If the first request placed the order, you get that order back with200. - 400
payment_failedfrom the payment: the card was declined.detailsays why. Pay with another card or with account credit. - 409 from a retry: the registry did not accept it.
codenames the reason. Whenregistration.terminalistrue, another retry will not help.
No order is created when the order answers 409 or 503.
Related endpoints
Section titled “Related endpoints”- Check domain availability
- Check availability for several domains
- Get availability check results
- Get TLD pricing and registry requirements
- Validate order contact details
- Preview an order
- Create an order
- Pay an invoice with a saved card
- Pay an invoice with account credit
- Cancel an order
- List domains
- Get registration and transfer status
- Retry a failed domain registration
- Get the holder e-mail verification