# DNS

Read and change the DNS zones of your domains through the API, with a copy before each change.

Updated on 5 November 2026

The API reads and changes the DNS zone of the active domains on your account that have DNS on. To turn DNS on, use [Meu Interweb](https://www.meu.interweb.ao/clientarea.php?language=english&currency=2).

## What you need

A key with `dns:read` to read, and `dns:write` to change. With a `sandbox_` key, or while the API is in preparation, changes are simulated. See [Sandbox and Production](https://docs.interweb.ao/en/api-rules.md).

## Read the zone

[`GET /v1/domains/{domain}/dns`](https://docs.interweb.ao/en/dns.md) returns the records. Those with `read_only` set to `true`, such as the root NS records, cannot be changed here. Nameservers are changed in [Domains](https://docs.interweb.ao/en/domains.md).

## Add a record

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/domains/VALUE/dns" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Idempotency-Key: 9f1c2d3e-example',
        'Content-Type: application/json',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}
r = requests.post("https://api.interweb.ao/v1/domains/VALUE/dns", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}),
});
console.log(response.status);
console.log(await response.text());
```


Use `@` as the name for the domain itself. In a `TXT` record, quotes around the whole value are removed; quotes or backslashes inside the text answer `422`, because the DNS server would save a different value. To check a record without saving, use [`POST /v1/domains/{domain}/dns/validate`](https://docs.interweb.ao/en/dns.md).

## Change and delete

- [`PUT /v1/domains/{domain}/dns/{record_id}`](https://docs.interweb.ao/en/dns.md) changes a record.
- [`DELETE /v1/domains/{domain}/dns/{record_id}`](https://docs.interweb.ao/en/dns.md) deletes a record.

The `id` of a record changes when its name, type, value or priority changes; changing only the `ttl` keeps the same `id`. Always use the `id` from the latest list.

The response shows what stayed in the zone. An `AAAA` address comes in its short form. A CNAME cannot share its name with other records: it answers `409` with `cname_conflict`.

## The domain of the account email

The email used to log in to the account receives the password reset and the login code. If that email, or the email of a user of the account, is on this domain or on a subdomain of it, the API refuses whatever decides where the mail goes:

- the MX records of that name;
- the A, AAAA, CNAME and NS records of the name the MX records point to or, without MX records, of the name itself;
- resetting the zone, and restoring a backup that touches these records.

These requests answer `409` with the code `account_email_domain`, also in Sandbox. Make these changes in the [Meu Interweb](https://www.meu.interweb.ao/clientarea.php?language=english&currency=2). The other records can still be changed through the API.

## Backups and reset

A copy of the zone is kept before each change. The last 20 are kept for each domain.

- [`GET /v1/domains/{domain}/dns/backups`](https://docs.interweb.ao/en/dns.md) lists the copies.
- [`POST /v1/domains/{domain}/dns/backups/{id}/restore`](https://docs.interweb.ao/en/dns.md) goes back to a copy. If `complete` is `false`, a record of the copy could not stay exactly as it was: check the list.
- [`POST /v1/domains/{domain}/dns/reset`](https://docs.interweb.ao/en/dns.md) leaves the zone without your records. It needs the domain name in `confirm`.

## Limit

Changes are limited to 20 per minute, per account, on top of the [general limit](https://docs.interweb.ao/en/api-rules.md). Zone reads have a limit of 30 per minute, per account. The list can come from a copy up to 60 seconds old, which does not count towards that limit. What you change through the API shows at once; what you change in the portal can take that long.


## Operations

8 operations in this area. Click an operation to see its parameters, examples and responses, or to try it in the Playground, without leaving the page.

### List the DNS records

`GET https://api.interweb.ao/v1/domains/{domain}/dns`

Needs `dns:read`. `read_only` marks the records that cannot be changed. Limit: 30 per minute, per account. The answer can come from a copy up to 60 seconds old, which does not count towards the limit.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:read`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |

#### Examples

**curl**

```bash
curl "https://api.interweb.ao/v1/domains/VALUE/dns" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
}
r = requests.get("https://api.interweb.ao/v1/domains/VALUE/dns", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns", {
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

List the DNS records.

```json
{
  "data": [
    {
      "id": "3f2a91c07d11e4b6",
      "name": "www",
      "type": "A",
      "value": "192.0.2.10",
      "ttl": 14400,
      "priority": 0,
      "read_only": false
    }
  ],
  "meta": {
    "domain": "example.co.mz",
    "dns_enabled": true,
    "total": 1
  }
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Add a DNS record

`POST https://api.interweb.ao/v1/domains/{domain}/dns`

Needs `dns:write`. A copy of the zone is kept before each change. In a `TXT` record, quotes around the whole value are removed, and quotes or backslashes inside it are refused with `422`. The response shows what stayed in the zone: an `AAAA` address comes in its short form. A CNAME cannot share its name with other records: it answers `409` with `cname_conflict`. If it touches the MX records, or the names they point to, of the domain of the account email or of the email of a user of the account, it answers `409` with the code `account_email_domain`: make the change in the account portal.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:write`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |
| `Idempotency-Key` | header | string | no | A unique identifier for this request, so it can be repeated without duplicating it. |

#### Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | The name, such as `www`. Use `@` for the domain itself. |
| `type` | string: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA | yes |  |
| `value` | string | yes | SRV: `weight port target`. CAA: `tag value`. |
| `ttl` | integer | no |  |
| `priority` | integer | no | For MX and SRV. On CAA it is the flag. |

#### Examples

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/domains/VALUE/dns" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Idempotency-Key: 9f1c2d3e-example',
        'Content-Type: application/json',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}
r = requests.post("https://api.interweb.ao/v1/domains/VALUE/dns", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}),
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `201`

Add a DNS record.

```json
{
  "data": {
    "id": "3f2a91c07d11e4b6",
    "name": "www",
    "type": "A",
    "value": "192.0.2.10",
    "ttl": 14400,
    "priority": 0,
    "read_only": false,
    "simulated": true
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `422`

Some of the data is not valid.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Some of the data is not valid.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

The service behind the API did not respond as expected.

```json
{
  "error": {
    "code": "upstream_error",
    "message": "The service did not respond as expected. Please try again later.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 422 | `validation_failed` | Some of the data is not valid. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 502 | `upstream_error` | The service behind the API did not respond as expected. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Check a DNS record

`POST https://api.interweb.ao/v1/domains/{domain}/dns/validate`

Needs `dns:read`. Checks the record without saving anything.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:read`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |
| `Idempotency-Key` | header | string | no | A unique identifier for this request, so it can be repeated without duplicating it. |

#### Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | The name, such as `www`. Use `@` for the domain itself. |
| `type` | string: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA | yes |  |
| `value` | string | yes | SRV: `weight port target`. CAA: `tag value`. |
| `ttl` | integer | no |  |
| `priority` | integer | no | For MX and SRV. On CAA it is the flag. |

#### Examples

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/domains/VALUE/dns/validate" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/validate');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Idempotency-Key: 9f1c2d3e-example',
        'Content-Type: application/json',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}
r = requests.post("https://api.interweb.ao/v1/domains/VALUE/dns/validate", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/validate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}),
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

Check a DNS record.

```json
{
  "data": {
    "valid": true,
    "record": {
      "name": "www",
      "type": "A",
      "value": "192.0.2.10",
      "ttl": 14400,
      "priority": 0
    }
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `422`

Some of the data is not valid.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Some of the data is not valid.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 422 | `validation_failed` | Some of the data is not valid. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Change a DNS record

`PUT https://api.interweb.ao/v1/domains/{domain}/dns/{record_id}`

Needs `dns:write`. The answer brings the new `id`. In a `TXT` record, quotes around the whole value are removed, and quotes or backslashes inside it are refused with `422`. The response shows what stayed in the zone: an `AAAA` address comes in its short form. A CNAME cannot share its name with other records: it answers `409` with `cname_conflict`. If it touches the MX records, or the names they point to, of the domain of the account email or of the email of a user of the account, it answers `409` with the code `account_email_domain`: make the change in the account portal.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:write`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `record_id` | path | string | yes | The `id` of the record, from the list. It changes when the record changes. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |

#### Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | The name, such as `www`. Use `@` for the domain itself. |
| `type` | string: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA | yes |  |
| `value` | string | yes | SRV: `weight port target`. CAA: `tag value`. |
| `ttl` | integer | no |  |
| `priority` | integer | no | For MX and SRV. On CAA it is the flag. |

#### Examples

**curl**

```bash
curl -X PUT "https://api.interweb.ao/v1/domains/VALUE/dns/VALUE" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Content-Type: application/json" \
  -d '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/VALUE');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_POSTFIELDS => '{"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Content-Type: application/json',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
}
payload = {"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}
r = requests.put("https://api.interweb.ao/v1/domains/VALUE/dns/VALUE", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/VALUE", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name": "www", "type": "A", "value": "192.0.2.10", "ttl": 14400}),
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

Change a DNS record.

```json
{
  "data": {
    "id": "3f2a91c07d11e4b6",
    "name": "www",
    "type": "A",
    "value": "192.0.2.10",
    "ttl": 14400,
    "priority": 0,
    "read_only": false,
    "simulated": true
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `422`

Some of the data is not valid.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Some of the data is not valid.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

The service behind the API did not respond as expected.

```json
{
  "error": {
    "code": "upstream_error",
    "message": "The service did not respond as expected. Please try again later.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 422 | `validation_failed` | Some of the data is not valid. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 502 | `upstream_error` | The service behind the API did not respond as expected. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Delete a DNS record

`DELETE https://api.interweb.ao/v1/domains/{domain}/dns/{record_id}`

Needs `dns:write`. A copy of the zone is kept first. If it touches the MX records, or the names they point to, of the domain of the account email or of the email of a user of the account, it answers `409` with the code `account_email_domain`: make the change in the account portal.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:write`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `record_id` | path | string | yes | The `id` of the record, from the list. It changes when the record changes. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |

#### Examples

**curl**

```bash
curl -X DELETE "https://api.interweb.ao/v1/domains/VALUE/dns/VALUE" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/VALUE');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
}
r = requests.delete("https://api.interweb.ao/v1/domains/VALUE/dns/VALUE", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/VALUE", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

Delete a DNS record.

```json
{
  "data": {
    "id": "3f2a91c07d11e4b6",
    "deleted": true,
    "simulated": true
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

The service behind the API did not respond as expected.

```json
{
  "error": {
    "code": "upstream_error",
    "message": "The service did not respond as expected. Please try again later.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 502 | `upstream_error` | The service behind the API did not respond as expected. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Reset the DNS zone

`POST https://api.interweb.ao/v1/domains/{domain}/dns/reset`

Needs `dns:write`. Leaves the zone without your records. A copy of the zone is kept first. If it touches the MX records, or the names they point to, of the domain of the account email or of the email of a user of the account, it answers `409` with the code `account_email_domain`: make the change in the account portal.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:write`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |
| `Idempotency-Key` | header | string | no | A unique identifier for this request, so it can be repeated without duplicating it. |

#### Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `confirm` | string | yes | The domain name, to confirm. |

#### Examples

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/domains/VALUE/dns/reset" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"confirm": "example.co.mz"}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/reset');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"confirm": "example.co.mz"}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Idempotency-Key: 9f1c2d3e-example',
        'Content-Type: application/json',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"confirm": "example.co.mz"}
r = requests.post("https://api.interweb.ao/v1/domains/VALUE/dns/reset", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/reset", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"confirm": "example.co.mz"}),
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

Reset the DNS zone.

```json
{
  "data": {
    "domain": "example.co.mz",
    "reset": true,
    "simulated": true
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `422`

Some of the data is not valid.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Some of the data is not valid.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

The service behind the API did not respond as expected.

```json
{
  "error": {
    "code": "upstream_error",
    "message": "The service did not respond as expected. Please try again later.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 422 | `validation_failed` | Some of the data is not valid. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 502 | `upstream_error` | The service behind the API did not respond as expected. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### List the zone backups

`GET https://api.interweb.ao/v1/domains/{domain}/dns/backups`

Needs `dns:read`. The last 20 copies, the newest first.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:read`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |

#### Examples

**curl**

```bash
curl "https://api.interweb.ao/v1/domains/VALUE/dns/backups" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/backups');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
}
r = requests.get("https://api.interweb.ao/v1/domains/VALUE/dns/backups", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/backups", {
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

List the zone backups.

```json
{
  "data": [
    {
      "id": 12,
      "reason": "update",
      "records": 8,
      "created_at": "2026-11-05T09:00:00+02:00"
    }
  ],
  "meta": {
    "total": 1
  }
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).


### Restore a zone backup

`POST https://api.interweb.ao/v1/domains/{domain}/dns/backups/{id}/restore`

Needs `dns:write`. Removes what was not in the copy and adds what was missing. `complete` is `false` when a record of the copy could not stay exactly as it was. If it touches the MX records, or the names they point to, of the domain of the account email or of the email of a user of the account, it answers `409` with the code `account_email_domain`: make the change in the account portal.

#### Authentication

Needs a valid key in the `Authorization: Bearer` header. Required permission: `dns:write`.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain name or its `id`. |
| `id` | path | string | yes | The `id` of the backup. |
| `X-API-Mode` | header | string: sandbox, production | no | Optional. If it does not match the mode of the key, the request is refused with `invalid_request` and reason `mode_mismatch`. Use it to make sure a program never runs with the wrong key. |
| `Idempotency-Key` | header | string | no | A unique identifier for this request, so it can be repeated without duplicating it. |

#### Examples

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/domains/VALUE/dns/backups/00000000-0000-4000-8000-000000000000/restore" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: en" \
  -H "Idempotency-Key: 9f1c2d3e-example"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/domains/VALUE/dns/backups/00000000-0000-4000-8000-000000000000/restore');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: en',
        'Idempotency-Key: 9f1c2d3e-example',
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status, PHP_EOL, $body, PHP_EOL;
```

**Python**

```python
import os
import requests

headers = {
    "Authorization": "Bearer " + os.environ["RLX_API_KEY"],
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
}
r = requests.post("https://api.interweb.ao/v1/domains/VALUE/dns/backups/00000000-0000-4000-8000-000000000000/restore", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/domains/VALUE/dns/backups/00000000-0000-4000-8000-000000000000/restore", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "en",
    "Idempotency-Key": "9f1c2d3e-example",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Responses

##### `200`

Restore a zone backup.

```json
{
  "data": {
    "domain": "example.co.mz",
    "removed": 1,
    "added": 2,
    "simulated": true,
    "complete": true
  },
  "meta": {}
}
```

| Header | Description |
|---|---|
| `X-Correlation-ID` | Identifier of this request. Give it to support when you report a problem. |
| `X-RateLimit-Limit` | Requests allowed per minute. |
| `X-RateLimit-Remaining` | Requests left in the current minute. |
| `X-RateLimit-Reset` | When the minute ends, as a Unix timestamp in seconds. |
| `X-API-Mode` | The mode of the key that made the request: `sandbox` or `production`. |

##### `401`

The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the required permission.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Not found.

```json
{
  "error": {
    "code": "not_found",
    "message": "Not found.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

The operation cannot be done in the current state. See `details.reason`.

```json
{
  "error": {
    "code": "conflict",
    "message": "This operation only works on an active domain.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "reason": "domain_not_active"
    }
  }
}
```

##### `429`

Too many requests. Wait for the time in Retry-After.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Please try again shortly.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Header | Description |
|---|---|
| `Retry-After` | Seconds to wait before trying again. |

##### `500`

An internal error occurred. Give the correlation_id to support.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An internal error occurred. Please give the correlation_id to support.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

The service behind the API did not respond as expected.

```json
{
  "error": {
    "code": "upstream_error",
    "message": "The service did not respond as expected. Please try again later.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

The API is turned off.

```json
{
  "error": {
    "code": "disabled",
    "message": "The API is currently turned off.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Errors

| Status | Code | Meaning |
|---|---|---|
| 401 | `unauthorized` | The key is missing, invalid, revoked or expired, or the user who created it no longer has access to the account. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | The account cannot use the API (`forbidden`), the key lacks the permission (`insufficient_scope`), or the IP address is not on the list of the key (`ip_not_allowed`). |
| 404 | `not_found` | Not found. |
| 409 | `conflict` | The operation cannot be done in the current state. See `details.reason`. |
| 429 | `rate_limited` | Too many requests. Wait for the time in Retry-After. |
| 500 | `internal_error` | An internal error occurred. Give the correlation_id to support. |
| 502 | `upstream_error` | The service behind the API did not respond as expected. |
| 503 | `disabled` | The API is turned off. |

The error format is explained in [Errors](https://docs.interweb.ao/en/api-rules.md).
