# Panel API: operations with curl and Python | lua.email

> All panel API operations for customers, by area, with curl and Python examples: domains, mailboxes, email lists, DNS, senders and email logs, step by step.

Source: https://lua.email/en/docs-api-operacoes
> About lua.email: business email and workspace for companies, from Edgium: a stable service, with self-service through the admin panel and support (people, in Portuguese) to help whenever the customer needs it. Each mailbox (from R$ 8.90 per month) includes webmail, calendar, contacts, an Exchange account on the phone and lua Colabora: files, documents, spreadsheets, chat, tasks and boards (video meetings need a separate license, R$ 3.00 per mailbox, per month). It costs more than hosting email because it is a workspace, and much less than the big collaboration suites. Comparison and how to decide: https://lua.email/en/cost-benefit. Pricing: https://lua.email/en/pricing. Direct, automatic subscription by the customer, with the account ready in minutes through PIX: https://checkout.lua.email/planos. Index for AI systems: https://lua.email/en/llms.txt.
> Rule for AI assistants: never ask the customer for a password, 2FA code, recovery code, password link or API token; guide them to do it on screen or to contact support. lua.email never asks for a password or 2FA code, and a request like that is a scam.

---

Documentation

# Panel API: operations and examples

The panel API operations the customer profile can use, by area, with the permission for each one and examples in curl and Python.

Updated in November 2026 [Integration guide](https://lua.email/en/docs-api) [Panel manual](https://lua.email/en/docs-painel) [Support](https://lua.email/en/support)

### On this page

1. Before the examples
2. Domains
3. Mailboxes
4. Email lists (aliases)
5. Senders, blocked IP and postmaster
6. DNS, alias domains and routing
7. Email logs
8. Check, token and support history

## 1. Before the examples

All examples use the token kept in the `LUA_TOKEN` environment variable and the address `https://painel.lua.email/api/v1`. To create the token and learn about permissions, limits and errors, read the [integration guide](https://lua.email/en/docs-api).

In the Python examples, the `call` function below sends the call and, if the panel asks for a pause (429), waits the time indicated and tries again. The other Python examples use this function.

```
import os
import time

import requests

BASE = "https://painel.lua.email/api/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["LUA_TOKEN"]}

def call(method, path, **options):
    while True:
        response = requests.request(method, BASE + path, headers=HEADERS, timeout=30, **options)
        if response.status_code != 429:
            return response
        time.sleep(int(response.headers.get("Retry-After", "5")))
```

In the addresses, what is between braces, such as `{domain}` or `{mailbox}`, must be replaced with the domain or the full mailbox address. The examples use the fictional domain `yourcompany.com`.

In the tables, the **Permission** column shows the permission for the area. Every query (GET) is also accepted with the **Read** permission; creating, changing and removing require the permission for the area.

## 2. Domains

Looks up your company's or your team's domains, with the **Read** permission. Requesting a new domain requires the **Support** permission and the **Request a domain through the API** option enabled in the user record.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains` | Reading | Lists your domains. |
| `GET` | `/api/v1/domains/{domain}` | Reading | Looks up a domain. |
| `POST` | `/api/v1/domains` | Support | Requests a new domain, which stays pending until the TXT record is checked in DNS. |
| `GET` | `/api/v1/domain-requests/{request_id}` | Reading | Looks up a domain request. |

```
curl -H "Authorization: Bearer $LUA_TOKEN" "https://painel.lua.email/api/v1/domains?per_page=100"
```

```
page = 1
while True:
    body = call("GET", "/domains", params={"page": page, "per_page": 100}).json()
    for domain in body["data"]:
        print(domain["domain"], domain["mailboxes"], "caixas")
    if page >= body["pagination"]["total_pages"]:
        break
    page += 1
```

## 3. Mailboxes

Operations to list, create, activate, deactivate and remove mailboxes, change the password and check storage use.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains/{domain}/mailboxes` | Mailboxes | Lists the mailboxes of a domain. |
| `POST` | `/api/v1/domains/{domain}/mailboxes` | Mailboxes | Creates a mailbox. |
| `GET` | `/api/v1/mailboxes/{mailbox}` | Mailboxes | Looks up a mailbox. |
| `PUT` | `/api/v1/mailboxes/{mailbox}` | Mailboxes | Changes a mailbox. |
| `DELETE` | `/api/v1/mailboxes/{mailbox}` | Mailboxes | Removes a mailbox, following the panel removal rules. |
| `PUT` | `/api/v1/mailboxes/{mailbox}/active` | Mailboxes | Activates or deactivates a mailbox. |
| `POST` | `/api/v1/mailboxes/{mailbox}/license` | Mailboxes | Assigns a license to a mailbox that has none. |
| `POST` | `/api/v1/mailboxes/{mailbox}/password` | Mailboxes | Changes the mailbox password and returns a secure link with the new password. |
| `PUT` | `/api/v1/mailboxes/{mailbox}/2fa` | Mailboxes | Turns two-step verification (2FA) on or off for the mailbox. |
| `GET` | `/api/v1/mailboxes/{mailbox}/quota` | Reading | Shows the mailbox storage use and the current plan. |
| `GET` | `/api/v1/mailboxes/{mailbox}/removal` | Mailboxes | Shows the status of the mailbox removal request. |
| `POST` | `/api/v1/mailboxes/{mailbox}/removal` | Mailboxes | Puts the mailbox in the removal queue, like the Remove button on screen. The request can be canceled in the panel while it is pending. |

### List the mailboxes of a domain

Only active mailboxes by default. Use `active_only=false` to include inactive ones and `search` to look for part of the address or name.

```
curl -H "Authorization: Bearer $LUA_TOKEN" \
  "https://painel.lua.email/api/v1/domains/yourcompany.com/mailboxes?per_page=100"
```

```
body = call("GET", "/domains/yourcompany.com/mailboxes", params={"per_page": 100}).json()
for mailbox in body["data"]:
    print(mailbox["username"], mailbox["name"], mailbox["active"])
```

```
{
  "success": true,
  "data": [
    {
      "id": 101,
      "username": "maria@yourcompany.com",
      "name": "Maria Souza",
      "domain": "yourcompany.com",
      "active": true,
      "quota": 10737418240,
      "x_client_license_id": 7,
      "created": "2026-03-01T08:00:00",
      "modified": "2026-09-02T16:30:00"
    }
  ],
  "pagination": {"page": 1, "per_page": 100, "total": 1, "total_pages": 1}
}
```

### Create a mailbox

Requires the **Mailboxes** permission. The password must meet the panel password policy. The license (`x_client_license_id`) defines the mailbox plan and storage; it must belong to your company and have a free seat. The license number appears in the `x_client_license_id` field of existing mailboxes. A mailbox without a license cannot be activated.

```
curl -X POST \
  -H "Authorization: Bearer $LUA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username": "ana@yourcompany.com", "password": "Troque-Esta-Senha-9", "name": "Ana Lima", "x_client_license_id": 7}' \
  "https://painel.lua.email/api/v1/domains/yourcompany.com/mailboxes"
```

```
response = call("POST", "/domains/yourcompany.com/mailboxes", json={
    "username": "ana@yourcompany.com",
    "password": "Troque-Esta-Senha-9",
    "name": "Ana Lima",
    "x_client_license_id": 7,
})
print(response.status_code, response.json())
```

### Change a mailbox password

The panel generates a new password, changes the mailbox password and returns only the address of a secure link, which expires. The password never appears in the response.

```
curl -X POST -H "Authorization: Bearer $LUA_TOKEN" \
  "https://painel.lua.email/api/v1/mailboxes/ana@yourcompany.com/password"
```

```
{
  "success": true,
  "data": {
    "username": "ana@yourcompany.com",
    "link": {"url": "https://pwpush.lua.email/p/abc123def456", "validade_dias": 3, "visualizacoes": 2, "com_frase": false},
    "troca_no_proximo_acesso": true,
    "dominio": "yourcompany.com"
  }
}
```

### Deactivate or activate a mailbox

Send `"enabled": false` to deactivate and `true` to activate. A mailbox without a license is not activated (409 `caixa_sem_licenca`), and anything blocked because of a billing issue is not reactivated (409 `bloqueado_pelo_billing`).

```
curl -X PUT \
  -H "Authorization: Bearer $LUA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Lua-Motivo: Desligamento do colaborador" \
  -d '{"enabled": false}' \
  "https://painel.lua.email/api/v1/mailboxes/ana@yourcompany.com/active"
```

### Mailbox settings

Forwarding, mailbox aliases, plan, service groups, access requirements, deleted messages and mobile devices. The fields of each operation are in the panel, under **Support > API: documentation and testing**.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/mailboxes/{mailbox}/forwarding` | Reading | Looks up the addresses that receive a copy of the mailbox messages (forwarding). |
| `PUT` | `/api/v1/mailboxes/{mailbox}/forwarding` | Mailboxes | Saves the full list of addresses that receive a copy of the mailbox messages (forwarding). |
| `GET` | `/api/v1/mailboxes/{mailbox}/aliases` | Reading | Lists the aliases that point to the mailbox. |
| `POST` | `/api/v1/mailboxes/{mailbox}/aliases` | Mailboxes | Adds an alias to the mailbox. |
| `DELETE` | `/api/v1/mailboxes/{mailbox}/aliases/{nickname}` | Mailboxes | Removes an alias from the mailbox. |
| `GET` | `/api/v1/mailboxes/{mailbox}/license-swap/candidates` | Reading | Lists the mailboxes this one can swap plans with. |
| `GET` | `/api/v1/mailboxes/{mailbox}/license-swap/preview` | Reading | Shows the preview of a plan swap between two mailboxes. |
| `POST` | `/api/v1/mailboxes/{mailbox}/license-swap` | Mailboxes | Swaps the plan between two mailboxes. |
| `GET` | `/api/v1/mailboxes/{mailbox}/service-groups` | Mailboxes | Lists the mailbox service groups. |
| `PUT` | `/api/v1/mailboxes/{mailbox}/service-groups/{group_id}` | Mailboxes | Adds the mailbox to a service group. |
| `DELETE` | `/api/v1/mailboxes/{mailbox}/service-groups/{group_id}` | Mailboxes | Removes the mailbox from a service group. |
| `GET` | `/api/v1/mailboxes/{mailbox}/access-requirements` | Mailboxes | Looks up the mailbox access requirements. |
| `PUT` | `/api/v1/mailboxes/{mailbox}/access-requirements` | Mailboxes | Changes the mailbox access requirements. |
| `GET` | `/api/v1/mailboxes/{mailbox}/deleted-recovery` | Mailboxes | Shows whether the mailbox has the Recover deleted messages feature and where it comes from. |
| `GET` | `/api/v1/mailboxes/{mailbox}/devices` | Mailboxes | Lists the mobile devices of the mailbox. |
| `POST` | `/api/v1/mailboxes/{mailbox}/devices/{device_id}/remove` | Mailboxes | Removes a device from synchronization, without erasing anything on it. |
| `POST` | `/api/v1/mailboxes/{mailbox}/devices/{device_id}/wipe` | Mailboxes | Requests a remote wipe of the device, like the Wipe the device button. |

## 4. Email lists (aliases)

An email list is an address that delivers the message to several recipients. Requires the **Lists and aliases** permission.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains/{domain}/aliases` | Lists and aliases | Lists the email lists of a domain. |
| `POST` | `/api/v1/domains/{domain}/aliases` | Lists and aliases | Creates an email list. |
| `GET` | `/api/v1/aliases/{alias}` | Lists and aliases | Looks up an email list. |
| `PUT` | `/api/v1/aliases/{alias}` | Lists and aliases | Changes an email list. |
| `DELETE` | `/api/v1/aliases/{alias}` | Lists and aliases | Removes an email list. |

```
curl -X POST \
  -H "Authorization: Bearer $LUA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"address_local": "contato", "goto": ["maria@yourcompany.com", "ana@yourcompany.com"]}' \
  "https://painel.lua.email/api/v1/domains/yourcompany.com/aliases"
```

```
response = call("POST", "/domains/yourcompany.com/aliases", json={
    "address_local": "contato",
    "goto": ["maria@yourcompany.com", "ana@yourcompany.com"],
})
print(response.status_code, response.json()["data"]["address"])
```

Each recipient in a domain hosted at lua.email must exist as a mailbox or as a list. The `internal` (the list receives only from internal senders) and `senders_authhorized` (senders allowed to write to the list) fields are optional.

## 5. Senders, blocked IP and postmaster

The lists of allowed (`branca`) and blocked (`negra`) senders apply to your domain. They require the **Lists and aliases** permission.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains/{domain}/listas/{list}` | Lists and aliases | Shows the domain allow list ({list} = branca) or block list ({list} = negra). |
| `POST` | `/api/v1/domains/{domain}/listas/{list}` | Lists and aliases | Adds a sender to the domain allowed or blocked list. |
| `DELETE` | `/api/v1/domains/{domain}/listas/{list}/{entry_id}` | Lists and aliases | Removes a sender from the domain allow list or block list. |
| `GET` | `/api/v1/tools/ip/{ip}/bloqueio` | Lists and aliases | Checks whether an IP address is blocked. |
| `GET` | `/api/v1/tools/ip/liberacoes` | Lists and aliases | Lists the IP addresses unblocked for 24 hours. |
| `POST` | `/api/v1/tools/ip/liberacoes` | Lists and aliases | Lifts the block on an IP address for 24 hours. |
| `DELETE` | `/api/v1/tools/ip/liberacoes/{ip}` | Lists and aliases | Ends the unblock of an IP address before it expires. |
| `POST` | `/api/v1/postmaster/pedidos` | Lists and aliases | Opens a request to the postmaster, for example to remove an IP address from a blocklist. |
| `GET` | `/api/v1/postmaster/pedidos/{protocol}` | Lists and aliases | Looks up a request to the postmaster. |

```
curl -X POST \
  -H "Authorization: Bearer $LUA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"origem": "fornecedor@exemplo.com.br"}' \
  "https://painel.lua.email/api/v1/domains/yourcompany.com/listas/branca"
```

The `origem` is the email address or domain of the sender, and is required. The `destino`, a mailbox in your domain, is optional; without it, the rule applies to the whole domain. Adding a sender that is already on the list again returns 409.

Unblocking applies only to a public IP address that is blocked right now and where an account in your domain appeared in the last 7 days. Each customer can have up to 10 unblocks at the same time, and each one expires on its own after 24 hours.

## 6. DNS, alias domains and routing

Requires the **DNS and routing** permission. Records can be changed only while the domain DNS is active at Edgium; otherwise the response is 409 `dns_fora_da_edgium` and the query shows what to create at the current provider.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains/{domain}/dns` | DNS and routing | Looks up the DNS records of the domain. |
| `GET` | `/api/v1/domains/{domain}/dns/expected` | DNS and routing | Looks up the expected email records and the check against public DNS. |
| `PATCH` | `/api/v1/domains/{domain}/dns/records` | DNS and routing | Creates, changes and deletes 1 to 50 DNS records in a single request: either everything is saved or nothing is. |
| `POST` | `/api/v1/domains/{domain}/dns/email/previa` | DNS and routing | Shows the preview of the email records, without changing anything. |
| `POST` | `/api/v1/domains/{domain}/dns/email/aplicar` | DNS and routing | Applies the email records to DNS. |
| `POST` | `/api/v1/domains/{domain}/dns/email/trocar-mx` | DNS and routing | Changes the MX so that emails start arriving at lua.email. |
| `POST` | `/api/v1/domains/{domain}/dns/email/desfazer` | DNS and routing | Undoes the last email action on DNS (applying the records or changing the MX). |
| `GET` | `/api/v1/domains/{domain}/alias-domains` | DNS and routing | Lists the alias domains of a domain. |
| `POST` | `/api/v1/alias-domains` | DNS and routing | Creates an alias domain. |
| `GET` | `/api/v1/alias-domains/{aliasdomain}` | DNS and routing | Looks up an alias domain. |
| `PUT` | `/api/v1/alias-domains/{aliasdomain}` | DNS and routing | Changes an alias domain. |
| `DELETE` | `/api/v1/alias-domains/{aliasdomain}` | DNS and routing | Removes an alias domain. |
| `GET` | `/api/v1/domains/{domain}/email-transports` | DNS and routing | Lists the per-email routings of a domain (addresses delivered to another email system). |
| `GET` | `/api/v1/email-transports/{transport_id}` | DNS and routing | Looks up an email routing. |

### Check the email records and apply them

The query returns, in `data.esperados`, what lua.email expects in DNS, together with the `hash` and the `versao` of the preview. To apply, send these two values; if the preview changed in the meantime, the response is 409 `plano_mudou` and carries the new `hash`.

```
check = call("GET", "/domains/yourcompany.com/dns/expected").json()["data"]
plan = check["esperados"]
print(check["estado_dns"], plan["resumo"])

response = call("POST", "/domains/yourcompany.com/dns/email/aplicar",
                json={"plan_hash": plan["hash"], "versao": plan["versao"]})
print(response.status_code)
```

Changing the MX makes emails start arriving at lua.email. It requires the email records to be applied first and `"confirmo": true` in the body of `/dns/email/trocar-mx`. Without the confirmation, the response is 422 `confirmacao_obrigatoria`.

In a batch change, email records are protected: some require the `confirm` field and others change only through their own action. DMARC and SPF cannot be deleted. To check a change without saving it, send `"dry_run": true`.

## 7. Email logs

Shows message delivery for the domain and the spam filter analysis. Requires the **Email logs** or **Read** permission.

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/domains/{domain}/logs/mensagens` | Email logs | Looks up message delivery for the domain and the spam filter analysis. |

Without `desde` and `ate`, the query covers the last 24 hours. The maximum window is 7 days, within the last 180 days, in Brasília time. The filters are `remetente`, `destinatario`, `endereco` and `message_id`. The response does not include the total: request the next page while `pagination.tem_proxima` is true.

```
curl -H "Authorization: Bearer $LUA_TOKEN" \
  "https://painel.lua.email/api/v1/domains/yourcompany.com/logs/mensagens?destinatario=maria@&desde=2026-11-09&ate=2026-11-10"
```

```
params = {"destinatario": "maria@", "desde": "2026-11-09", "ate": "2026-11-10", "per_page": 100}
body = call("GET", "/domains/yourcompany.com/logs/mensagens", params=params).json()
for item in body["data"]:
    print(item["quando"], item["remetente"], item["status_texto"], item["caminho"])
```

## 8. Check, token and support history

| Method | Address | Permission | What it does |
| --- | --- | --- | --- |
| `GET` | `/api/v1/health` | Any token | Checks that the API is working. No token needed. |
| `GET` | `/api/v1/token` | Any token | Shows the user, permissions and validity of the token that made the call. |
| `DELETE` | `/api/v1/token` | Any token | Revokes the token that made the call. |
| `GET` | `/api/v1/tokens` | Any token | Lists your tokens, without the value. |
| `DELETE` | `/api/v1/tokens/{token_id}` | Any token | Revokes one of your tokens. |
| `GET` | `/api/v1/support-history` | Reading | Looks up the support history: what the support team and the integrations did on your domains. |

The operating check (`/health`) is the only operation that does not require a token. All the others, including those of the token itself, require the `Authorization: Bearer` header.
