## POST /api/v2/shared-hosting/{accountId}/actions/change-main-domain

**Change the main domain**

Change the main (primary) domain of a cPanel-backed shared-hosting account. Get `accountId` from `GET /api/v2/shared-hosting` `data[].id`, and check the change first with `GET` on this path. The control-panel username, files and databases stay as they are: the same website folder is served on the new domain, and mailboxes on the previous main domain get addresses on the new one. The previous domain is detached unless `keepPreviousDomain` is true, which attaches it again to the same website folder without changing its DNS. No domain registration is cancelled, renewed or transferred. If the new domain is delegated to HostUp's nameservers and has no website or email records elsewhere, its DNS is pointed to the hosting account; if it has such records, the request is refused with `dns_setup_choice_required` until `dnsSetupPreference` is supplied. A domain that is an addon domain on the account today (see `promotableDomains` on `GET`) can take over as main domain: its website folder becomes `public_html`, the previous main domain's website files are kept in a folder named after that domain, its subdomains, mailboxes, certificate and DKIM key are kept, its DNS is not changed, and `promotion` in the response says what was put back; the website can be unreachable for a couple of minutes while the web server switches over. The change can take up to a minute, longer for an addon domain with several subdomains. The request is safe to repeat: a request for a domain that is already the main domain completes whatever an interrupted earlier request left undone and returns 200 with `changed: false`.

### Related Endpoints

- `GET /api/v2/shared-hosting/{accountId}/actions/change-main-domain`: Check a main-domain change
- `GET /api/v2/shared-hosting/{accountId}/actions/sso`: Check control-panel SSO availability
- `POST /api/v2/shared-hosting/{accountId}/actions/sso`: Create hosting control-panel SSO link

### Headers

- `Accept`: application/json
- `Authorization`: Bearer YOUR_API_KEY
- Required API scopes: `write:hosting`, `console:services`
- `Content-Type`: application/json

### Parameters

- `accountId` (path, string, required): Public shared-hosting account ID. Get it from `GET /api/v2/shared-hosting` `data[].id`. Do not invent this value; use the exact ID returned by the referenced API response. Example: `acct_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Body

- `domain` (string, required): The new main domain, without `www.` (the www name is attached automatically). It must be a registered domain (not a subdomain) with public nameservers, must not be attached to any hosting account, and, when it is registered with HostUp or has a DNS zone at HostUp, must be on the caller's own account. Example: `example.net`
- `keepPreviousDomain` (boolean, optional): Keep the previous main domain attached to the same website folder so it keeps showing the site. Uses one addon domain slot; see `actions.canKeepPreviousDomain` on GET. The previous domain's DNS is not changed, and its old mailbox addresses are not recreated.
- `dnsSetupPreference` (string, optional): What to point at the hosting account for the NEW domain when it already has records elsewhere: `full` = website and email, `website-only` = website (existing email records are kept), `email-only` = email (existing website records are kept), `none` = change no DNS. Required when GET reports `target.dnsSetup.requiresChoice`.
  Allowed values: full, email-only, website-only, none

### Request Examples

#### Change the main domain

```bash
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.net"
  }'
```

```json
{
  "domain": "example.net"
}
```

#### Change the main domain and keep the previous one showing the site

```bash
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.net",
    "keepPreviousDomain": true
  }'
```

```json
{
  "domain": "example.net",
  "keepPreviousDomain": true
}
```

#### The new domain has email elsewhere that should keep working

```bash
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.net",
    "dnsSetupPreference": "website-only"
  }'
```

```json
{
  "domain": "example.net",
  "dnsSetupPreference": "website-only"
}
```

### Response Schema

- `mainDomain` (string, required) Example: `example.net`
- `previousMainDomain` (string, required, nullable): The main domain before the change. `null` when the requested domain was already the main domain and nothing remained to finish. Example: `example.com`
- `changed` (boolean, required): True when this request changed the main domain. False when it was already in place and only remaining steps were completed.
- `emailAccounts` (array<any>, required, nullable): Mailboxes on the new main domain after the change. `null` when they could not be read.
- `previousDomain` (object, required, nullable): What happened to the previous main domain. `null` when `previousMainDomain` is null.
- `certificate` (object, required)
- `certificate.status` (string, required): `requested` = issuance of an SSL certificate for the new domain was started and normally completes within minutes. `request_failed` = it could not be started now and is issued at the next automatic check.
  Allowed values: requested, request_failed
- `dns` (object, required, nullable): DNS handling for the new main domain. `null` when the domain was already the main domain or its DNS could not be inspected.
- `promotion` (object, required, nullable): Outcome of the parts an addon-domain-to-main-domain change puts back. `null` when the new main domain was not an addon domain on the account, and on a repeated request for a domain that is already the main domain.

### Responses

#### 200 - The main domain is the requested domain.
```json
{
  "mainDomain": "example.net",
  "previousMainDomain": "example.com",
  "changed": true,
  "emailAccounts": [
    "info@example.net"
  ],
  "previousDomain": {
    "domain": "example.com",
    "status": "detached",
    "reason": null
  },
  "certificate": {
    "status": "requested"
  },
  "dns": {
    "usesHostupNameservers": true,
    "appliedPreference": "full"
  },
  "promotion": null
}
```

#### 400 - The request body is invalid.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "`domain` must be a full domain name such as \"example.com\".",
  "instance": "/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain",
  "code": "invalid_request",
  "errors": [
    {
      "pointer": "/body/domain",
      "detail": "`domain` must be a full domain name such as \"example.com\".",
      "code": "invalid_request"
    }
  ]
}
```

#### 401 - Unauthorized. Authentication is required.
```json
{
  "type": "https://developer.hostup.se/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Authentication is required.",
  "code": "unauthorized",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 403 - The domain is registered to another customer account, or has a DNS zone with HostUp that is not on the caller's account.
```json
{
  "type": "https://developer.hostup.se/errors/domain_not_owned",
  "title": "Domain cannot become the main domain",
  "status": 403,
  "detail": "This domain belongs to another customer account.",
  "instance": "/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain",
  "code": "domain_not_owned"
}
```

#### 404 - Not found. The resource does not exist or is not owned by the caller.
```json
{
  "type": "https://developer.hostup.se/errors/not_found",
  "title": "Not found",
  "status": 404,
  "detail": "The requested resource could not be found.",
  "code": "not_found",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 409 - The main domain cannot be changed in the current state. Nothing was changed.
```json
{
  "type": "https://developer.hostup.se/errors/main_domain_change_unavailable",
  "title": "Main domain cannot be changed",
  "status": 409,
  "detail": "The hosting account has other domains attached, so the main domain cannot be changed here. Contact support and we will change it for you.",
  "instance": "/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain",
  "code": "main_domain_change_unavailable"
}
```

#### 429 - Rate limited. Retry after the limit resets. 429 responses include `Retry-After` seconds plus `X-RateLimit-*` headers.
```json
{
  "type": "https://developer.hostup.se/errors/rate_limit_exceeded",
  "title": "Too many requests",
  "status": 429,
  "detail": "Too many requests. Retry after the limit resets.",
  "code": "rate_limit_exceeded",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 500 - Internal error. Retry later or contact support if the issue persists.
```json
{
  "type": "https://developer.hostup.se/errors/internal_error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. Retry later or contact support if the issue persists.",
  "code": "internal_error",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 502 - The change could not be completed or confirmed. Sending the same request again is safe. `mainDomainChanged: true` means the main domain was changed and only the final step is missing; repeat the request to finish it. For an addon domain, a change that stopped part-way and could not be put back automatically is reported here with a detail asking to contact support.
```json
{
  "type": "https://developer.hostup.se/errors/upstream_failed",
  "title": "Upstream request failed",
  "status": 502,
  "detail": "The main domain could not be changed right now. The account still uses its previous main domain.",
  "instance": "/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain",
  "code": "upstream_failed"
}
```

#### 503 - The domain's ownership could not be verified right now. Retry later.
```json
{
  "type": "https://developer.hostup.se/errors/upstream_unavailable",
  "title": "Domain could not be verified",
  "status": 503,
  "detail": "The domain's ownership could not be verified right now. Try again shortly.",
  "instance": "/api/v2/shared-hosting/acct_01hxa3b4c5d6e7f8g9h0j1k2m3/actions/change-main-domain",
  "code": "upstream_unavailable"
}
```
