# Suporte

Abrir, responder e fechar tickets, e ler os artigos da Base de Conhecimento e os anúncios pela API.

Actualizado em 05/11/2026

A API lê os tickets da sua conta, abre tickets novos, responde-lhes e fecha-os. Também lê os artigos da Base de Conhecimento e os anúncios publicados. Os tickets ficam sempre em nome da sua conta.

## O que precisa

Uma chave com a permissão `support:read` para ler, e `support:write` para abrir, responder e fechar. Veja [Permissões](https://docs.interweb.ao/pt/permissions.md). Com uma chave `sandbox_`, ou enquanto a API está em preparação, as alterações são simuladas e nenhum ticket é criado. Veja [Sandbox e Produção](https://docs.interweb.ao/pt/api-rules.md).

## A lista de tickets

[`GET /v1/tickets`](https://docs.interweb.ao/pt/support.md) devolve os tickets da conta, primeiro os que têm a resposta mais recente. Pode filtrar com `status`, por exemplo `?status=open`. A lista vem em [páginas](https://docs.interweb.ao/pt/api-rules.md).

**curl**

```bash
curl "https://api.interweb.ao/v1/tickets" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/tickets", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


## O detalhe de um ticket

[`GET /v1/tickets/{id}`](https://docs.interweb.ao/pt/support.md) usa o número do ticket e devolve todas as mensagens, primeiro as mais antigas. Dos anexos, vem só o nome.

## Departamentos

[`GET /v1/support/departments`](https://docs.interweb.ao/pt/support.md) devolve os departamentos que podem receber tickets.

## Tickets novos

[`POST /v1/tickets`](https://docs.interweb.ao/pt/support.md) abre um ticket no departamento que escolher. Pode ligá-lo a um serviço com `service_id` ou a um domínio com `domain_id`.

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/tickets" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
        '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": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}
r = requests.post("https://api.interweb.ao/v1/tickets", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/tickets", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}),
});
console.log(response.status);
console.log(await response.text());
```


- Precisa do cabeçalho `Idempotency-Key`: repetir o pedido com a mesma chave não abre outro ticket. Veja [Repetições](https://docs.interweb.ao/pt/api-rules.md).
- Pode abrir até 10 tickets por hora, por conta.
- Um departamento que peça dados extra responde `422` com `department_portal_only`. Nesse caso, abra o ticket no [Meu Interweb](https://www.meu.interweb.ao/clientarea.php?language=portuguese&currency=1).
- Com uma chave `sandbox_`, `id` vem a `null`, porque nenhum ticket é criado.

## Respostas

[`POST /v1/tickets/{id}/replies`](https://docs.interweb.ao/pt/support.md) acrescenta a sua resposta. Como no portal, responder a um ticket fechado volta a abri-lo, e `reopened` vem a `true`. O limite é de 30 respostas por hora, por conta. Envie `Idempotency-Key` para que repetir o pedido não responda duas vezes.

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"message": "Thank you, it works now."}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"message": "Thank you, it works now."}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
        '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": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"message": "Thank you, it works now."}
r = requests.post("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"message": "Thank you, it works now."}),
});
console.log(response.status);
console.log(await response.text());
```


## Fecho de tickets

[`POST /v1/tickets/{id}/close`](https://docs.interweb.ao/pt/support.md) fecha o ticket. Alguns só podem ser fechados pela nossa equipa: nesses, responde `409` com `closure_not_allowed`. Abrir, responder e fechar enviam os mesmos e-mails que no portal, incluindo o pedido de avaliação ao fechar.

## Base de Conhecimento

Só saem os artigos publicados, na língua do cabeçalho `Accept-Language`, quando houver tradução.

- [`GET /v1/kb/categories`](https://docs.interweb.ao/pt/support.md) devolve as categorias.
- [`GET /v1/kb/articles`](https://docs.interweb.ao/pt/support.md) lista os artigos. Use `q` para pesquisar e `category` para escolher uma categoria.
- [`GET /v1/kb/articles/{id}`](https://docs.interweb.ao/pt/support.md) devolve o artigo, com o texto em HTML.

**curl**

```bash
curl "https://api.interweb.ao/v1/kb/articles" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/kb/articles');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/kb/articles", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


## Anúncios

[`GET /v1/announcements`](https://docs.interweb.ao/pt/support.md) lista os anúncios publicados, primeiro os mais recentes, e [`GET /v1/announcements/{id}`](https://docs.interweb.ao/pt/support.md) devolve o texto de um deles.

## O que está previsto

- Enviar anexos. Por agora, envie-os no [Meu Interweb](https://www.meu.interweb.ao/clientarea.php?language=portuguese&currency=1).


## Operações

11 operações nesta área. Clique numa operação para ver os parâmetros, os exemplos e as respostas, ou para a experimentar no Playground, sem sair da página.

### Listar os tickets

`GET https://api.interweb.ao/v1/tickets`

Precisa de `support:read`. Primeiro os que têm a resposta mais recente. Os valores de `status` são os nomes dos estados, como `open`, `answered` e `closed`.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `status` | consulta | string | não | Só os tickets neste estado. |
| `page` | consulta | integer | não | O número da página. A primeira é a 1. |
| `per_page` | consulta | integer | não | Resultados por página, de 1 a 100. Por omissão, 10. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/tickets" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/tickets", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Listar os tickets.

```json
{
  "data": [
    {
      "id": "482913",
      "subject": "Dúvida sobre o e-mail",
      "status": "answered",
      "priority": "medium",
      "department": {
        "id": 2,
        "name": "Departamento Técnico"
      },
      "unread": false,
      "created_at": "2026-11-03T08:00:00+02:00",
      "last_reply_at": "2026-11-03T09:00:00+02:00"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 10,
    "total": 1,
    "total_pages": 1
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `422`

Alguns dados não são válidos.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Alguns dados não são válidos.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 422 | `validation_failed` | Alguns dados não são válidos. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Abrir um ticket

`POST https://api.interweb.ao/v1/tickets`

Precisa de `support:write`. O ticket fica em nome da conta. Precisa do cabeçalho `Idempotency-Key`. Limite de 10 por hora por conta. Um departamento que peça dados extra responde `422` com `department_portal_only`: use o portal. Em Sandbox, `id` é `null`.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:write`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `Idempotency-Key` | cabeçalho | string | não | Um valor único qualquer, até 255 caracteres visíveis. Vale 24 horas por chave. Repetir o mesmo pedido devolve a resposta guardada; outro corpo, ou um primeiro pedido ainda em curso, devolve 409. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Corpo do pedido

| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `department_id` | integer | sim |  |
| `subject` | string | sim |  |
| `message` | string | sim |  |
| `priority` | string: low, medium, high | não |  |
| `service_id` | integer | não | Um serviço da conta a que o ticket diz respeito. |
| `domain_id` | integer | não | Um domínio da conta. Use-o em vez de `service_id`. |

#### Exemplos

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/tickets" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
        '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": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}
r = requests.post("https://api.interweb.ao/v1/tickets", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/tickets", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"department_id": 1, "subject": "Help with the plan", "message": "I need help with my plan."}),
});
console.log(response.status);
console.log(await response.text());
```


#### Respostas

##### `201`

Abrir um ticket.

```json
{
  "data": {
    "id": "731942",
    "subject": "Help with the plan",
    "status": "open",
    "priority": "medium",
    "department": {
      "id": 1,
      "name": "Technical Department"
    },
    "unread": false,
    "created_at": "2026-11-05T09:30:00+02:00",
    "last_reply_at": "2026-11-05T09:30:00+02:00",
    "simulated": false
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `400`

O pedido não é válido, por exemplo o corpo não é JSON ou a ligação não é HTTPS.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "O pedido não é válido.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `422`

Alguns dados não são válidos.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Alguns dados não são válidos.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

O serviço por trás da API não respondeu como esperado.

```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`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 400 | `invalid_request` | O pedido não é válido, por exemplo o corpo não é JSON ou a ligação não é HTTPS. |
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 422 | `validation_failed` | Alguns dados não são válidos. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 502 | `upstream_error` | O serviço por trás da API não respondeu como esperado. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Ver um ticket

`GET https://api.interweb.ao/v1/tickets/{id}`

Precisa de `support:read`. Com todas as mensagens, primeiro as mais antigas. Dos anexos, vem só o nome.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `id` | caminho | string | sim | O número do ticket. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000", {
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Respostas

##### `200`

Ver um ticket.

```json
{
  "data": {
    "id": "482913",
    "subject": "Dúvida sobre o e-mail",
    "status": "answered",
    "priority": "medium",
    "department": {
      "id": 2,
      "name": "Departamento Técnico"
    },
    "unread": false,
    "created_at": "2026-11-03T08:00:00+02:00",
    "last_reply_at": "2026-11-03T09:00:00+02:00",
    "messages": [
      {
        "id": "ticket",
        "from": "client",
        "author": "Ana Silva",
        "message": "Bom dia. Como configuro o e-mail?",
        "attachments": [],
        "created_at": "2026-11-03T08:00:00+02:00"
      }
    ]
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Não encontrado.

```json
{
  "error": {
    "code": "not_found",
    "message": "Não encontrado.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 404 | `not_found` | Não encontrado. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Listar os departamentos

`GET https://api.interweb.ao/v1/support/departments`

Precisa de `support:read`. Os departamentos que podem receber tickets.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/support/departments" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/support/departments');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/support/departments", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Listar os departamentos.

```json
{
  "data": [
    {
      "id": 2,
      "name": "Departamento Técnico",
      "description": ""
    }
  ],
  "meta": {
    "total": 1
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Responder a um ticket

`POST https://api.interweb.ao/v1/tickets/{id}/replies`

Precisa de `support:write`. A resposta fica em nome da conta. Responder a um ticket fechado volta a abri-lo. Limite de 30 por hora por conta. Envie `Idempotency-Key` para que repetir o pedido não responda duas vezes.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:write`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `id` | caminho | string | sim | O número do ticket. |
| `Idempotency-Key` | cabeçalho | string | não | Um valor único qualquer, até 255 caracteres visíveis. Vale 24 horas por chave. Repetir o mesmo pedido devolve a resposta guardada; outro corpo, ou um primeiro pedido ainda em curso, devolve 409. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Corpo do pedido

| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `message` | string | sim |  |

#### Exemplos

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt" \
  -H "Idempotency-Key: 9f1c2d3e-example" \
  -H "Content-Type: application/json" \
  -d '{"message": "Thank you, it works now."}'
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_POSTFIELDS => '{"message": "Thank you, it works now."}',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
        '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": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
}
payload = {"message": "Thank you, it works now."}
r = requests.post("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies", headers=headers, json=payload, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/replies", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"message": "Thank you, it works now."}),
});
console.log(response.status);
console.log(await response.text());
```


#### Respostas

##### `201`

Responder a um ticket.

```json
{
  "data": {
    "ticket_id": "731942",
    "reply_id": "392",
    "status": "customer_reply",
    "reopened": false,
    "simulated": false
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Não encontrado.

```json
{
  "error": {
    "code": "not_found",
    "message": "Não encontrado.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `422`

Alguns dados não são válidos.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Alguns dados não são válidos.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

O serviço por trás da API não respondeu como esperado.

```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`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 404 | `not_found` | Não encontrado. |
| 422 | `validation_failed` | Alguns dados não são válidos. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 502 | `upstream_error` | O serviço por trás da API não respondeu como esperado. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Fechar um ticket

`POST https://api.interweb.ao/v1/tickets/{id}/close`

Precisa de `support:write`. Em `conflict`, `details.reason` pode ser `already_closed` ou `closure_not_allowed`.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:write`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `id` | caminho | string | sim | O número do ticket. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |
| `Idempotency-Key` | cabeçalho | string | não | Identificador único deste pedido, para o repetir sem o duplicar. |

#### Exemplos

**curl**

```bash
curl -X POST "https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/close" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt" \
  -H "Idempotency-Key: 9f1c2d3e-example"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/close');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
        '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": "pt",
    "Idempotency-Key": "9f1c2d3e-example",
}
r = requests.post("https://api.interweb.ao/v1/tickets/00000000-0000-4000-8000-000000000000/close", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Fechar um ticket.

```json
{
  "data": {
    "ticket_id": "731942",
    "status": "closed",
    "simulated": false
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Não encontrado.

```json
{
  "error": {
    "code": "not_found",
    "message": "Não encontrado.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `409`

A operação não se pode fazer no estado actual. Veja `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`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `502`

O serviço por trás da API não respondeu como esperado.

```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`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 404 | `not_found` | Não encontrado. |
| 409 | `conflict` | A operação não se pode fazer no estado actual. Veja `details.reason`. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 502 | `upstream_error` | O serviço por trás da API não respondeu como esperado. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Listar as categorias da Base de Conhecimento

`GET https://api.interweb.ao/v1/kb/categories`

Precisa de `support:read`. Na língua do `Accept-Language`, quando houver tradução.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/kb/categories" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/kb/categories');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/kb/categories", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Listar as categorias da Base de Conhecimento.

```json
{
  "data": [
    {
      "id": 9,
      "name": "Billing and payments",
      "description": "",
      "articles": 40
    }
  ],
  "meta": {
    "total": 1
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Listar ou pesquisar artigos da Base de Conhecimento

`GET https://api.interweb.ao/v1/kb/articles`

Precisa de `support:read`. Só os artigos publicados. As pesquisas têm um limite de 30 por minuto.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `q` | consulta | string | não | As palavras a procurar, de 2 a 100 caracteres. |
| `category` | consulta | string | não | O `id` de uma categoria. |
| `page` | consulta | integer | não | O número da página. A primeira é a 1. |
| `per_page` | consulta | integer | não | Resultados por página, de 1 a 100. Por omissão, 10. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/kb/articles" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/kb/articles');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/kb/articles", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Listar ou pesquisar artigos da Base de Conhecimento.

```json
{
  "data": [
    {
      "id": 28,
      "title": "How to access cPanel",
      "categories": [
        {
          "id": 1,
          "name": "cPanel and WHM"
        }
      ],
      "views": 120,
      "translated": false,
      "url": "https://my.example.co.mz/knowledgebase/28?language=english&currency=1",
      "excerpt": "First words of the article."
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 10,
    "total": 1,
    "total_pages": 1
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `422`

Alguns dados não são válidos.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Alguns dados não são válidos.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 422 | `validation_failed` | Alguns dados não são válidos. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Ler um artigo da Base de Conhecimento

`GET https://api.interweb.ao/v1/kb/articles/{id}`

Precisa de `support:read`. O texto vem em HTML. Na língua do `Accept-Language`, quando houver tradução; `translated` diz se houve.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `id` | caminho | string | sim | O `id` do artigo. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/kb/articles/00000000-0000-4000-8000-000000000000" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/kb/articles/00000000-0000-4000-8000-000000000000');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/kb/articles/00000000-0000-4000-8000-000000000000", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/kb/articles/00000000-0000-4000-8000-000000000000", {
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Respostas

##### `200`

Ler um artigo da Base de Conhecimento.

```json
{
  "data": {
    "id": 28,
    "title": "How to access cPanel",
    "categories": [
      {
        "id": 1,
        "name": "cPanel and WHM"
      }
    ],
    "views": 120,
    "translated": false,
    "url": "https://my.example.co.mz/knowledgebase/28?language=english&currency=1",
    "body_html": "<p>Article text.</p>"
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Não encontrado.

```json
{
  "error": {
    "code": "not_found",
    "message": "Não encontrado.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 404 | `not_found` | Não encontrado. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Listar os anúncios

`GET https://api.interweb.ao/v1/announcements`

Precisa de `support:read`. Só os anúncios publicados, primeiro os mais recentes.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `page` | consulta | integer | não | O número da página. A primeira é a 1. |
| `per_page` | consulta | integer | não | Resultados por página, de 1 a 100. Por omissão, 10. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/announcements" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/announcements');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/announcements", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

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


#### Respostas

##### `200`

Listar os anúncios.

```json
{
  "data": [
    {
      "id": 7,
      "title": "Scheduled maintenance",
      "published_at": "2026-11-01T08:00:00+02:00",
      "translated": false,
      "url": "https://my.example.co.mz/announcements/7/scheduled-maintenance.html?language=english&currency=1"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 10,
    "total": 1,
    "total_pages": 1
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `422`

Alguns dados não são válidos.

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Alguns dados não são válidos.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 422 | `validation_failed` | Alguns dados não são válidos. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).


### Ler um anúncio

`GET https://api.interweb.ao/v1/announcements/{id}`

Precisa de `support:read`. O texto vem em HTML.

#### Autenticação

Precisa de uma chave válida no cabeçalho `Authorization: Bearer`. Permissão necessária: `support:read`.

#### Parâmetros

| Nome | Onde | Tipo | Obrigatório | Descrição |
|---|---|---|---|---|
| `id` | caminho | string | sim | O `id` do anúncio. |
| `X-API-Mode` | cabeçalho | string: sandbox, production | não | Opcional. Se não coincidir com o modo da chave, o pedido é recusado com `invalid_request` e a razão `mode_mismatch`. Serve para garantir que um programa nunca corre com a chave errada. |

#### Exemplos

**curl**

```bash
curl "https://api.interweb.ao/v1/announcements/00000000-0000-4000-8000-000000000000" \
  -H "Authorization: Bearer $RLX_API_KEY" \
  -H "Accept-Language: pt"
```

**PHP**

```php
<?php
$ch = curl_init('https://api.interweb.ao/v1/announcements/00000000-0000-4000-8000-000000000000');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('RLX_API_KEY'),
        'Accept-Language: pt',
    ],
]);
$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": "pt",
}
r = requests.get("https://api.interweb.ao/v1/announcements/00000000-0000-4000-8000-000000000000", headers=headers, timeout=30)
print(r.status_code)
print(r.text)
```

**JavaScript**

```javascript
const response = await fetch("https://api.interweb.ao/v1/announcements/00000000-0000-4000-8000-000000000000", {
  headers: {
    Authorization: `Bearer ${process.env.RLX_API_KEY}`,
    "Accept-Language": "pt",
  },
});
console.log(response.status);
console.log(await response.text());
```


#### Respostas

##### `200`

Ler um anúncio.

```json
{
  "data": {
    "id": 7,
    "title": "Scheduled maintenance",
    "published_at": "2026-11-01T08:00:00+02:00",
    "translated": false,
    "url": "https://my.example.co.mz/announcements/7/scheduled-maintenance.html?language=english&currency=1",
    "body_html": "<p>Announcement text.</p>"
  },
  "meta": {}
}
```

| Cabeçalho | Descrição |
|---|---|
| `X-Correlation-ID` | Identificador deste pedido. Indique-o ao suporte se comunicar um problema. |
| `X-RateLimit-Limit` | Pedidos permitidos por minuto. |
| `X-RateLimit-Remaining` | Pedidos que restam no minuto actual. |
| `X-RateLimit-Reset` | Quando acaba o minuto, em segundos Unix. |
| `X-API-Mode` | O modo da chave que fez o pedido: `sandbox` ou `production`. |

##### `401`

A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Chave da API em falta ou inválida.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `403`

A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`).

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Esta chave não tem a permissão necessária.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "required": "dns:read"
    }
  }
}
```

##### `404`

Não encontrado.

```json
{
  "error": {
    "code": "not_found",
    "message": "Não encontrado.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `429`

Demasiados pedidos. Espere o tempo indicado em `Retry-After`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Demasiados pedidos. Tente de novo daqui a pouco.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {
      "retry_after": 42
    }
  }
}
```

| Cabeçalho | Descrição |
|---|---|
| `Retry-After` | Segundos a esperar antes de tentar de novo. |

##### `500`

Ocorreu um erro interno. Indique o `correlation_id` ao suporte.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Indique o correlation_id ao suporte.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```

##### `503`

A API está desligada.

```json
{
  "error": {
    "code": "disabled",
    "message": "A API está desligada de momento.",
    "correlation_id": "0f8fad5b-d9cb-469f-a165-70867728950e",
    "details": {}
  }
}
```


#### Erros

| Estado | Código | Significado |
|---|---|---|
| 401 | `unauthorized` | A chave está em falta, não é válida, foi revogada ou passou da validade, ou quem a criou já não tem acesso à conta. |
| 403 | `forbidden`, `insufficient_scope`, `ip_not_allowed` | A conta não pode usar a API (`forbidden`), a chave não tem a permissão necessária (`insufficient_scope`), ou o endereço IP não está na lista da chave (`ip_not_allowed`). |
| 404 | `not_found` | Não encontrado. |
| 429 | `rate_limited` | Demasiados pedidos. Espere o tempo indicado em `Retry-After`. |
| 500 | `internal_error` | Ocorreu um erro interno. Indique o `correlation_id` ao suporte. |
| 503 | `disabled` | A API está desligada. |

O formato dos erros está explicado em [Erros](https://docs.interweb.ao/pt/api-rules.md).
