## POST /api/v2/domains/{id}/redirects

**Create domain redirect**

Create a URL redirect for one owned domain. Get `{id}` from `GET /api/v2/domains` `data[].id`; the `sourceUrl` host must be the domain or one of its subdomains. Use this endpoint instead of manually adding DNS records for URL forwarding. The API creates or reuses the needed DNS anchor records for the source hostname. If an existing A/AAAA/CNAME/ALIAS record conflicts with the redirect anchor, the route returns `409 dns_conflict` instead of silently overwriting a live website. Only `sourceUrl` and `targetUrl` are required. Defaults are `httpStatusCode: 301`, `scope: "exact"`, `preserveQuery: false`, `includeSubdomains: false`, and `preservePath: false`. The domain can have up to 10 redirect rules. A newly created rule may return `status: "pending_dns"` until the domain's DNS is active for redirects.

### Related Endpoints

- `GET /api/v2/domains/{id}/redirects`: List domain redirects
- `PATCH /api/v2/domains/{id}/redirects/{ruleId}`: Update domain redirect
- `DELETE /api/v2/domains/{id}/redirects/{ruleId}`: Delete domain redirect

### Headers

- `Accept`: application/json
- `Authorization`: Bearer YOUR_API_KEY
- Required API scope: `write:domains`
- `Content-Type`: application/json

### Parameters

- `id` (path, string, required): Public domain ID from `GET /api/v2/domains` `data[].id`. Do not invent this value; use the exact ID returned by the referenced API response. Example: `dom_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Body

- `sourceUrl` (string, required): Source URL on the owned domain or one of its subdomains. Example: `https://www.example.com`
- `targetUrl` (string, required): Absolute destination URL. Example: `https://example.com`
- `httpStatusCode` (integer, optional) Example: `301`
  Allowed values: 301, 302, 307, 308
- `scope` (string, optional): `root_www` prepares both the apex and `www`; `subdomains` prepares wildcard subdomains; `all` prepares apex plus wildcard. Example: `root_www`
  Allowed values: exact, root_www, subdomains, all
- `preserveQuery` (boolean, optional): When true, keep the original query string on the redirected request. Example: `false`
- `includeSubdomains` (boolean, optional): When true, broaden matching to subdomains. Prefer selecting the intended `scope` explicitly. Example: `false`
- `preservePath` (boolean, optional): When true, append the source path suffix to the target URL. Example: `false`
- `description` (string,null, optional) Example: `Redirect www to apex`

### Request Examples

#### Redirect www to apex

```bash
curl -X POST "https://cloud.hostup.se/api/v2/domains/dom_01hxa3b4c5d6e7f8g9h0j1k2m3/redirects" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceUrl": "https://www.example.com",
    "targetUrl": "https://example.com",
    "httpStatusCode": 301,
    "scope": "root_www",
    "description": "Redirect www to apex"
  }'
```

```json
{
  "sourceUrl": "https://www.example.com",
  "targetUrl": "https://example.com",
  "httpStatusCode": 301,
  "scope": "root_www",
  "description": "Redirect www to apex"
}
```

#### Preserve path suffix

```bash
curl -X POST "https://cloud.hostup.se/api/v2/domains/dom_01hxa3b4c5d6e7f8g9h0j1k2m3/redirects" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceUrl": "https://old.example.com/docs",
    "targetUrl": "https://example.com/help",
    "httpStatusCode": 308,
    "preservePath": true,
    "preserveQuery": true
  }'
```

```json
{
  "sourceUrl": "https://old.example.com/docs",
  "targetUrl": "https://example.com/help",
  "httpStatusCode": 308,
  "preservePath": true,
  "preserveQuery": true
}
```

### Response Schema

- `id` (string, optional) Example: `redir_01hxa3b4c5d6e7f8g9h0j1k2m3`
- `domainId` (string, optional) Example: `dom_01hxa3b4c5d6e7f8g9h0j1k2m3`
- `sourceHost` (string, optional) Example: `www.example.com`
- `sourcePath` (string,null, optional) Example: `null`
- `sourceUrl` (string, optional) Example: `https://www.example.com`
- `targetUrl` (string, optional) Example: `https://example.com`
- `httpStatusCode` (integer, optional) Example: `301`
  Allowed values: 301, 302, 307, 308
- `scope` (string, optional) Example: `root_www`
  Allowed values: exact, root_www, subdomains, all
- `preservePath` (boolean, optional) Example: `false`
- `preserveQuery` (boolean, optional) Example: `false`
- `includeSubdomains` (boolean, optional) Example: `false`
- `status` (string, optional) Example: `active`
  Allowed values: active, pending_dns, error, disabled
- `reason` (string,null, optional) Example: `null`
- `description` (string,null, optional) Example: `Redirect www to apex`
- `createdAt` (string,null, optional) Example: `2026-04-27T12:00:00.000Z`
- `updatedAt` (string,null, optional) Example: `2026-04-27T12:00:00.000Z`

### Responses

#### 201 - Created redirect rule.
```json
{
  "id": "redir_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "domainId": "dom_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "sourceHost": "www.example.com",
  "sourcePath": null,
  "sourceUrl": "https://www.example.com",
  "targetUrl": "https://example.com",
  "httpStatusCode": 301,
  "scope": "root_www",
  "preservePath": false,
  "preserveQuery": false,
  "includeSubdomains": false,
  "status": "active",
  "reason": null,
  "description": "Redirect www to apex",
  "createdAt": "2026-04-27T12:00:00.000Z",
  "updatedAt": "2026-04-27T12:00:00.000Z"
}
```

#### 400 - Invalid request. The response body is an RFC 7807 Problem Details document.
```json
{
  "type": "https://developer.hostup.se/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body failed validation.",
  "code": "invalid_request",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z",
  "errors": [
    {
      "pointer": "/items/0/domainName",
      "detail": "`domainName` is required.",
      "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 - Forbidden. The caller lacks a required scope or does not own the resource.
```json
{
  "type": "https://developer.hostup.se/errors/forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "The caller lacks a required scope or does not own the resource.",
  "code": "forbidden",
  "instance": "/api/v2/resource",
  "requestId": "req_01hxa3b4c5d6e7f8g9h0j1k2m3",
  "timestamp": "2026-04-27T12:34:56.000Z"
}
```

#### 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"
}
```

#### 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"
}
```
