Entrar Criar conta
Referência da API

Recebidos e inboxes

Inboxes para receber e-mails e os endpoints para ler, encaminhar e organizar as mensagens recebidas.

Receba e-mails na Postal: crie inboxes num domínio seu (com recebimento ativo) ou num endereço gerado em in.postal.so, leia as mensagens pela API e seja avisado na hora pelo webhook email.received. Veja como apontar o MX em Domínios e DNS. A Postal aceita mensagens de até 25 MB. Os endpoints desta página exigem uma chave de acesso total, e o plano define quantas inboxes, e quais recursos, o time tem (veja Limites do plano).

#Listar inboxes

GET/inboxes

Chave de acesso total

Lista as inboxes do time com a contagem de mensagens não lidas. Aceita limit, after e before.

Parâmetros de consulta

  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    Cursor da próxima página.
  • beforestring
    Cursor da página anterior.
GET /inboxes

cURL

curl https://api.postal.so/inboxes \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "inbox",
      "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "paused_by_plan": false,
      "name": "Suporte",
      "address": "[email protected]",
      "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
      "receiving_address": "[email protected]",
      "catch_all": false,
      "forward_to": ["[email protected]"],
      "can_forward": true,
      "unread_count": 3,
      "last_received_at": "2026-10-24T12:41:09.000000Z",
      "created_at": "2026-10-02T18:20:00.000000Z",
      "reply_name": "Ada do Suporte",
      "view": "list",
      "agent": {
        "enabled": false,
        "instructions": null,
        "actions": [],
        "tone": {
          "formality": 0.3,
          "length": 0.3
        }
      }
    }
  ]
}

#Criar uma inbox

POST/inboxes

Chave de acesso total

Com domain_id, a inbox recebe em local_part@seu-dominio (use * para receber qualquer endereço do domínio, um catch-all). Sem domain_id, a Postal gera um endereço em in.postal.so, como [email protected]. Inboxes no seu domínio estão disponíveis nos planos pagos.

Corpo da requisição

  • namestringobrigatório
    Nome da inbox, até 255 caracteres.
  • domain_idstring
    Domínio seu com recebimento ativo (capabilities.receiving = "enabled").
  • local_partstring
    Parte antes do @, até 64 caracteres: letras, números, ., +, - e _, ou *. Obrigatório com domain_id. Precisa começar com letra ou número (ou ser só *).
  • forward_tostring[]
    Até 5 endereços para onde cada mensagem é encaminhada automaticamente (não vale para catch-all nem para in.postal.so).

Erros

  • 422validation_error
    Campo inválido, domínio sem recebimento ativo ou endereço já em uso.
  • 403plan_limit_reached
    O plano não permite mais inboxes ou inboxes no seu domínio.
POST /inboxes

cURL

curl -X POST https://api.postal.so/inboxes \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Suporte",
    "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
    "local_part": "suporte",
    "forward_to": ["[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Suporte',
    domain_id: '0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f',
    local_part: 'suporte',
    forward_to: ['[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Suporte",
        "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
        "local_part": "suporte",
        "forward_to": ["[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Suporte',
        'domain_id' => '0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f',
        'local_part' => 'suporte',
        'forward_to' => ['[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]"],
  "can_forward": true,
  "unread_count": 0,
  "last_received_at": null,
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  }
}

#Obter uma inbox

GET/inboxes/{id}

Chave de acesso total

receiving_address é um endereço em in.postal.so que também entrega na inbox: encaminhe e-mails de qualquer provedor para ele, sem apontar o MX do seu domínio (nas inboxes em in.postal.so é o próprio address). Além dos dados da inbox, traz folders com quantas conversas há em cada pasta (inbox, sent, drafts, archive, spam e trash) e quantas estão não lidas.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]"],
  "can_forward": true,
  "unread_count": 3,
  "last_received_at": "2026-10-24T12:41:09.000000Z",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  },
  "folders": {
    "inbox": {
      "threads": 12,
      "unread": 3
    },
    "sent": {
      "threads": 5
    },
    "drafts": {
      "threads": 1
    },
    "archive": {
      "threads": 40,
      "unread": 0
    },
    "spam": {
      "threads": 2,
      "unread": 2
    },
    "trash": {
      "threads": 0,
      "unread": 0
    }
  }
}

#Atualizar uma inbox

PATCH/inboxes/{id}

Chave de acesso total

Muda o nome, os endereços de encaminhamento, o nome de resposta, o layout e o agente de IA. O endereço da inbox não muda.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • namestring
    Novo nome.
  • forward_tostring[] | null
    Até 5 endereços. null ou [] desliga o encaminhamento.
  • reply_namestring | null
    Nome de quem responde: as respostas saem como "Ada do Suporte" <suporte@…>. Até 100 caracteres.
  • viewstring
    Layout da inbox no painel: list (padrão), compact, board (quadro por etiquetas), split (lista e leitura lado a lado) ou cards (cartões).
  • agentobject
    O agente de IA da inbox (veja Agente de IA).
    • enabledboolean
      Liga ou desliga o agente.
    • instructionsstring | null
      O que o agente deve fazer, até 5.000 caracteres.
    • actionsstring[]
      draft_reply, forward_thread, add_labels, assign_thread, mark_as_spam, archive e move_to_trash.
    • toneobject
      formality (0 profissional, 1 descontraído) e length (0 conciso, 1 detalhado), de 0 a 1.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 403plan_limit_reached
    O plano não inclui o agente de IA da inbox (ao ligar agent.enabled).
  • 403paused_by_plan
    A inbox está pausada porque passa do limite de inboxes do plano.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /inboxes/{id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "forward_to": ["[email protected]", "[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    forward_to: ['[email protected]', '[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "forward_to": ["[email protected]", "[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'forward_to' => ['[email protected]', '[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]", "[email protected]"],
  "can_forward": true,
  "unread_count": 3,
  "last_received_at": "2026-10-24T12:41:09.000000Z",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  }
}

#Excluir uma inbox

DELETE/inboxes/{id}

Chave de acesso total

Exclui a inbox e todas as mensagens recebidas nela.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /inboxes/{id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "deleted": true
}

#Listar e-mails recebidos

GET/emails/receiving

Chave de acesso total

Lista as mensagens recebidas, da mais recente para a mais antiga. A lista traz o resumo; o conteúdo vem em GET /emails/receiving/{id}.

Cada mensagem traz otp_token: {"code", "origin"} quando o e-mail tem um cabeçalho OTP-Token válido, ou null. Veja Cabeçalho OTP-Token.

Parâmetros de consulta

  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    Cursor da próxima página.
  • beforestring
    Cursor da página anterior.
  • inbox_idstring
    Só mensagens de uma inbox.
  • unreadboolean
    Com true, só as não lidas.
  • searchstring
    Busca em remetente, destinatários e assunto.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
GET /emails/receiving

cURL

curl 'https://api.postal.so/emails/receiving?inbox_id=0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f&unread=true' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving?inbox_id=0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f&unread=true', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
        "unread": "true",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'inbox_id' => '0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f',
        'unread' => 'true',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "received_email",
      "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "from": "Carla Souza <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "subject": "Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "read": false,
      "size": 18342,
      "has_attachments": true,
      "otp_token": null
    }
  ]
}

#Obter um e-mail recebido

GET/emails/receiving/{id}

Chave de acesso total

Devolve a mensagem com HTML, texto, cabeçalhos e anexos. Os links de download dos anexos valem por 1 hora.

otp_token é {"code", "origin"} quando a mensagem tem exatamente um cabeçalho OTP-Token válido na seção de cabeçalhos do topo, e null em qualquer outro caso (também nos e-mails recebidos antes deste recurso). O código não vai no webhook email.received. Veja Cabeçalho OTP-Token.

Parâmetros de caminho

  • idstringobrigatório
    ID do e-mail recebido.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /emails/receiving/{id}

cURL

curl https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "from": "Carla Souza <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "subject": "Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "read": false,
  "size": 18342,
  "has_attachments": true,
  "otp_token": null,
  "reply_to": [],
  "html": "<p>Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.</p>",
  "text": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "headers": {
    "date": "Fri, 24 Oct 2026 09:41:07 -0300",
    "mime-version": "1.0"
  },
  "attachments": [
    {
      "id": "019a1c00-3e50-7b6c-8d7e-9f0a1b2c3d4e",
      "filename": "label-photo.jpg",
      "content_type": "image/jpeg",
      "content_id": null,
      "size": 15620,
      "download_url": "https://api.postal.so/received-attachments/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/019a1c00-3e50-7b6c-8d7e-9f0a1b2c3d4e?expires=1761313269&signature=c0de…"
    }
  ]
}

#Marcar como lido ou não lido

PATCH/emails/receiving/{id}

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID do e-mail recebido.

Corpo da requisição

  • readbooleanobrigatório
    true marca como lido; false, como não lido.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /emails/receiving/{id}

cURL

curl -X PATCH https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "read": true
  }'

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    read: true,
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "read": True,
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'read' => true,
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "from": "Carla Souza <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "subject": "Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "read": true,
  "size": 18342,
  "has_attachments": true,
  "otp_token": null
}

#Excluir um e-mail recebido

DELETE/emails/receiving/{id}

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID do e-mail recebido.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /emails/receiving/{id}

cURL

curl -X DELETE https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "deleted": true
}

#Mensagem recebida original

GET/emails/receiving/{id}/raw

Chave de acesso total

A mensagem exatamente como chegou ao servidor (RFC 5322, text/plain).

Parâmetros de caminho

  • idstringobrigatório
    ID do e-mail recebido.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /emails/receiving/{id}/raw

cURL

curl https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const message = await response.text();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

message = response.text

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$message = (string) $response->getBody();
200Resposta (text/plain)
Received: from mail.cliente.com.br (mail.cliente.com.br [203.0.113.24])
From: Carla Souza <[email protected]>
To: [email protected]
Subject: Troca de tamanho do pedido #10482
Message-ID: <[email protected]>
…

#Encaminhar um e-mail recebido

POST/emails/receiving/{id}/forward

Chave de acesso total

Encaminha a mensagem para até 5 endereços, em segundo plano. Só funciona para inboxes num domínio seu verificado (não catch-all), porque o encaminhamento sai desse domínio.

Parâmetros de caminho

  • idstringobrigatório
    ID do e-mail recebido.

Corpo da requisição

  • tostring[]obrigatório
    De 1 a 5 endereços.

Erros

  • 422validation_error
    Endereço inválido, ou a inbox não pode encaminhar.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /emails/receiving/{id}/forward

cURL

curl -X POST https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: ['[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": ["[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => ['[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "forwarded": true
}

#Conversas, pastas e etiquetas

Cada e-mail recebido entra numa conversa (thread): ele se junta à conversa que responde, pelos cabeçalhos In-Reply-To e References, casados com os e-mails recebidos e com as respostas enviadas pela inbox. Sem referência, abre uma conversa nova.

  • Pastas: inbox, archive, spam e trash (campo folder) e sent (conversas com resposta enviada pela inbox, fora da lixeira). Uma mensagem nova numa conversa arquivada a traz de volta para a Entrada; em Spam e na Lixeira ela continua onde está.
  • Etiquetas são por inbox, com cor, e agrupam conversas. Uma conversa pode ter várias.
  • Atribuição: uma conversa pode ser atribuída a um membro do time (assigned_to, o id do usuário em sua conta).
  • Enviar: responder e abrir conversas novas passam pelo mesmo caminho do POST /emails: o domínio precisa estar verificado e o envio conta na cota.

#Anexos ao escrever pela inbox

Responder, enviar uma mensagem nova, criar e atualizar um rascunho e enviar um rascunho aceitam attachments: [{"filename", "content", "content_type"}], com o conteúdo em Base64.

  • Limites: até 10 arquivos por mensagem ou rascunho, filename de até 255 caracteres e 40 MB no total. A validação é a mesma do POST /emails: Base64 inválido ou total acima do limite devolvem 422 validation_error.
  • Rascunhos guardam os arquivos até serem enviados ou apagados (apagar o rascunho, ou a conversa de vez, apaga os arquivos). O conteúdo nunca volta na API: o rascunho lista attachments: [{"id", "filename", "content_type", "size"}].
  • Atualizar um rascunho: se o PATCH trouxer attachments, a lista é substituída. Para manter um anexo que o rascunho já tem, mande só o id dele ({"id": "…"}); itens com filename e content são adicionados e o que ficar de fora é removido. Sem o campo, os anexos não mudam; [] remove todos.
  • Mensagens enviadas listam os anexos em attachments (id, filename, content_type, content_id, size e download_url, um link assinado que vale por 1 hora), no mesmo formato dos anexos recebidos.

#Listar conversas

GET/inboxes/{id}/threads

Chave de acesso total

Lista as conversas de uma pasta, da atividade mais recente para a mais antiga.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Parâmetros de consulta

  • folderstring
    inbox (padrão), sent, archive, spam ou trash.
  • labelstring
    Só conversas com esta etiqueta.
  • unlabeledboolean
    Com true, só conversas sem etiqueta.
  • assigned_tostring
    ID de um membro, me (você) ou none (sem responsável).
  • unreadboolean
    Com true, só as não lidas.
  • searchstring
    Busca em assunto, trecho e remetentes.
  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    ID da conversa para a próxima página.
  • beforestring
    ID da conversa para a página anterior.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}/threads

cURL

curl 'https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads?folder=inbox&unread=true' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads?folder=inbox&unread=true', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "folder": "inbox",
        "unread": "true",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'folder' => 'inbox',
        'unread' => 'true',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "thread",
      "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "subject": "Troca de tamanho do pedido #10482",
      "folder": "inbox",
      "read": false,
      "unread_count": 1,
      "message_count": 2,
      "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
      "participants": [
        {
          "name": "Carla Souza",
          "email": "[email protected]",
          "is_me": false
        },
        {
          "name": "Ada do Suporte",
          "email": "[email protected]",
          "is_me": true
        }
      ],
      "has_attachments": true,
      "has_draft": false,
      "has_sent": true,
      "last_message_at": "2026-10-24T13:02:11.000000Z",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "assigned_to": {
        "id": 12,
        "name": "Carlos Lima",
        "email": "[email protected]",
        "avatar_url": null
      },
      "labels": [
        {
          "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
          "name": "Importante",
          "color": "orange"
        }
      ]
    }
  ]
}

#Obter uma conversa

GET/inboxes/{id}/threads/{thread_id}

Chave de acesso total

Devolve a conversa com todas as mensagens, da mais antiga para a mais nova (recebidas e enviadas, no mesmo formato) e os rascunhos. Os links de download dos anexos, recebidos e enviados, valem por 1 hora.

As mensagens recebidas (direction: "received") trazem otp_token, com o código do cabeçalho OTP-Token ou null, como em GET /emails/receiving/{id}. Veja Cabeçalho OTP-Token.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • thread_idstringobrigatório
    ID da conversa.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}/threads/{thread_id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "subject": "Troca de tamanho do pedido #10482",
  "folder": "inbox",
  "read": false,
  "unread_count": 1,
  "message_count": 2,
  "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "participants": [
    {
      "name": "Carla Souza",
      "email": "[email protected]",
      "is_me": false
    },
    {
      "name": "Ada do Suporte",
      "email": "[email protected]",
      "is_me": true
    }
  ],
  "has_attachments": true,
  "has_draft": false,
  "has_sent": true,
  "last_message_at": "2026-10-24T13:02:11.000000Z",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "assigned_to": {
    "id": 12,
    "name": "Carlos Lima",
    "email": "[email protected]",
    "avatar_url": null
  },
  "labels": [
    {
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "name": "Importante",
      "color": "orange"
    }
  ],
  "messages": [
    {
      "object": "thread_message",
      "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "direction": "received",
      "from": "Carla Souza <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "read": false,
      "status": null,
      "html": "<p>Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.</p>",
      "text": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
      "otp_token": null,
      "attachments": []
    },
    {
      "object": "thread_message",
      "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "direction": "sent",
      "from": "\"Ada do Suporte\" <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Re: Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "created_at": "2026-10-24T13:02:11.000000Z",
      "read": true,
      "status": "queued",
      "html": null,
      "text": "Oi, Carla! Já separamos o tamanho M para você.",
      "attachments": [
        {
          "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
          "filename": "tabela-de-medidas.pdf",
          "content_type": "application/pdf",
          "content_id": null,
          "size": 48213,
          "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
        }
      ]
    }
  ],
  "drafts": []
}

#Atualizar uma conversa

PATCH/inboxes/{id}/threads/{thread_id}

Chave de acesso total

Move de pasta, marca como lida ou não lida, atribui a um membro e muda as etiquetas. Todos os campos são opcionais.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • thread_idstringobrigatório
    ID da conversa.

Corpo da requisição

  • folderstring
    inbox, archive, spam ou trash.
  • readboolean
    true marca todas as mensagens recebidas como lidas; false marca a última como não lida.
  • assigned_tointeger | null
    ID de um membro do time, ou null para tirar o responsável.
  • labelsstring[]
    Substitui as etiquetas da conversa.
  • add_labelsstring[]
    Acrescenta etiquetas.
  • remove_labelsstring[]
    Tira etiquetas.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /inboxes/{id}/threads/{thread_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "folder": "archive",
    "add_labels": ["019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f"],
    "read": true
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    folder: 'archive',
    add_labels: ['019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f'],
    read: true,
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "folder": "archive",
        "add_labels": ["019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f"],
        "read": True,
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'folder' => 'archive',
        'add_labels' => ['019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f'],
        'read' => true,
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "subject": "Troca de tamanho do pedido #10482",
  "folder": "archive",
  "read": true,
  "unread_count": 0,
  "message_count": 2,
  "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "participants": [
    {
      "name": "Carla Souza",
      "email": "[email protected]",
      "is_me": false
    },
    {
      "name": "Ada do Suporte",
      "email": "[email protected]",
      "is_me": true
    }
  ],
  "has_attachments": true,
  "has_draft": false,
  "has_sent": true,
  "last_message_at": "2026-10-24T13:02:11.000000Z",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "assigned_to": {
    "id": 12,
    "name": "Carlos Lima",
    "email": "[email protected]",
    "avatar_url": null
  },
  "labels": [
    {
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "name": "Importante",
      "color": "orange"
    }
  ]
}

#Excluir uma conversa

DELETE/inboxes/{id}/threads/{thread_id}

Chave de acesso total

Move a conversa para a lixeira. Se ela já está na lixeira, apaga de vez, com as mensagens recebidas (as respostas enviadas continuam no registro de e-mails).

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • thread_idstringobrigatório
    ID da conversa.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /inboxes/{id}/threads/{thread_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "deleted": true
}

#Agir em várias conversas

POST/inboxes/{id}/threads/batch

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • idsstring[]obrigatório
    De 1 a 100 IDs de conversas.
  • actionstringobrigatório
    read, unread, archive, spam, trash, inbox, delete, add_label, remove_label ou assign.
  • label_idstring
    Obrigatório em add_label e remove_label.
  • assigned_tointeger | null
    Em assign: o membro, ou null.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /inboxes/{id}/threads/batch

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e"],
    "action": "archive"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    ids: ['019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e'],
    action: 'archive',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "ids": ["019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e"],
        "action": "archive",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'ids' => ['019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e'],
        'action' => 'archive',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "batch",
  "updated": 1
}

#Responder numa conversa

POST/inboxes/{id}/threads/{thread_id}/reply

Chave de acesso total

Envia uma resposta a partir do endereço da inbox (com o reply_name), na mesma conversa, com In-Reply-To e References corretos. Sem to, responde ao Reply-To (ou ao remetente) da última mensagem recebida; sem subject, usa Re: …. Precisa de text ou html e de um domínio verificado. Aceita anexos.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • thread_idstringobrigatório
    ID da conversa.

Corpo da requisição

  • textstring
    Texto da resposta.
  • htmlstring
    HTML da resposta.
  • tostring[]
    Destinatários.
  • ccstring[]
    Com cópia.
  • bccstring[]
    Com cópia oculta.
  • subjectstring
    Assunto.
  • draft_idstring
    Rascunho que esta resposta envia (ele é apagado).
  • attachmentsobject[]
    Anexos, até 10 arquivos e 40 MB no total, com as mesmas regras do POST /emails. Aqui só há arquivos enviados no próprio corpo (sem path nem content_id).
    • filenamestringobrigatório
      Nome do arquivo, até 255 caracteres.
    • contentstringobrigatório
      Conteúdo do arquivo em Base64.
    • content_typestring
      Tipo MIME, como application/pdf. Sem ele, é deduzido pela extensão.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
  • 403validation_error
    O domínio da inbox não está verificado.
  • 403sending_paused
    Os envios do time estão pausados por taxa alta de bounces ou reclamações.
  • 403paused_by_plan
    O domínio da inbox está pausado pelo plano.
  • 403plan_limit_reached
    Os anexos passam do limite do plano.
  • 429daily_quota_exceeded
    A cota de envio do plano acabou (também monthly_quota_exceeded).
POST /inboxes/{id}/threads/{thread_id}/reply

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Oi, Carla! Já separamos o tamanho M para você.",
    "attachments": [
      {
        "filename": "tabela-de-medidas.pdf",
        "content": "JVBERi0xLjQK…",
        "content_type": "application/pdf"
      }
    ]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'Oi, Carla! Já separamos o tamanho M para você.',
    attachments: [
      {
        filename: 'tabela-de-medidas.pdf',
        content: 'JVBERi0xLjQK…',
        content_type: 'application/pdf',
      },
    ],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "text": "Oi, Carla! Já separamos o tamanho M para você.",
        "attachments": [
            {
                "filename": "tabela-de-medidas.pdf",
                "content": "JVBERi0xLjQK…",
                "content_type": "application/pdf",
            },
        ],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'text' => 'Oi, Carla! Já separamos o tamanho M para você.',
        'attachments' => [
            [
                'filename' => 'tabela-de-medidas.pdf',
                'content' => 'JVBERi0xLjQK…',
                'content_type' => 'application/pdf',
            ],
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Enviar uma mensagem nova

POST/inboxes/{id}/messages

Chave de acesso total

Envia uma mensagem nova a partir da inbox. Ela abre uma conversa (thread_id na resposta), que aparece na pasta sent. Aceita anexos.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • tostring[]obrigatório
    Destinatários.
  • subjectstringobrigatório
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • ccstring[]
    Com cópia.
  • bccstring[]
    Com cópia oculta.
  • attachmentsobject[]
    Anexos, até 10 arquivos e 40 MB no total, com as mesmas regras do POST /emails. Aqui só há arquivos enviados no próprio corpo (sem path nem content_id).
    • filenamestringobrigatório
      Nome do arquivo, até 255 caracteres.
    • contentstringobrigatório
      Conteúdo do arquivo em Base64.
    • content_typestring
      Tipo MIME, como application/pdf. Sem ele, é deduzido pela extensão.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
  • 403validation_error
    O domínio da inbox não está verificado.
  • 403sending_paused
    Os envios do time estão pausados.
  • 403paused_by_plan
    O domínio da inbox está pausado pelo plano.
  • 429daily_quota_exceeded
    A cota de envio do plano acabou (também monthly_quota_exceeded).
POST /inboxes/{id}/messages

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["[email protected]"],
    "subject": "Seu pedido foi enviado",
    "text": "Oi, Carla! Seu pedido já saiu."
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: ['[email protected]'],
    subject: 'Seu pedido foi enviado',
    text: 'Oi, Carla! Seu pedido já saiu.',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": ["[email protected]"],
        "subject": "Seu pedido foi enviado",
        "text": "Oi, Carla! Seu pedido já saiu.",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => ['[email protected]'],
        'subject' => 'Seu pedido foi enviado',
        'text' => 'Oi, Carla! Seu pedido já saiu.',
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Traduzir uma mensagem

POST/inboxes/{id}/threads/{thread_id}/messages/{message_id}/translate

Chave de acesso total

Traduz uma mensagem da conversa (recebida ou enviada). É assíncrono, como os demais pedidos de IA: a resposta é uma tarefa; consulte GET /ai/tasks/{id} até ela ficar pronta. Usa 1 crédito de IA. O resultado é {"object": "ai_translation", "text", "subject", "from_language", "to_language", "credits"}.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • thread_idstringobrigatório
    ID da conversa.
  • message_idstringobrigatório
    ID da mensagem.

Corpo da requisição

  • tostringobrigatório
    Idioma de destino: pt-BR, en, es, fr, de, it, ja, zh, ko, ru, nl ou ar.
  • fromstring
    Idioma original. Sem ele, a IA detecta.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram.
POST /inboxes/{id}/threads/{thread_id}/messages/{message_id}/translate

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "en"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: 'en',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": "en",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => 'en',
    ],
]);

$data = json_decode($response->getBody(), true);
202Resposta
{
  "object": "ai_task",
  "id": "019a1c04-3e54-7b65-8c76-4d5e6f708192",
  "status": "pending"
}

#Listar etiquetas

GET/inboxes/{id}/labels

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}/labels

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "inbox_label",
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "name": "Importante",
      "color": "orange",
      "thread_count": 2
    }
  ]
}

#Criar uma etiqueta

POST/inboxes/{id}/labels

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • namestringobrigatório
    Até 50 caracteres, único na inbox.
  • colorstring
    cyan, teal, green, lime, yellow, orange, indigo, purple, pink, brown ou gray (padrão).

Erros

  • 422validation_error
    Nome repetido ou cor inválida.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /inboxes/{id}/labels

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Importante",
    "color": "orange"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Importante',
    color: 'orange',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Importante",
        "color": "orange",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Importante',
        'color' => 'orange',
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "name": "Importante",
  "color": "orange",
  "thread_count": 0
}

#Atualizar uma etiqueta

PATCH/inboxes/{id}/labels/{label_id}

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • label_idstringobrigatório
    ID da etiqueta.

Corpo da requisição

  • namestring
    Novo nome.
  • colorstring
    Nova cor.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /inboxes/{id}/labels/{label_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "color": "pink"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    color: 'pink',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "color": "pink",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'color' => 'pink',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "name": "Importante",
  "color": "pink",
  "thread_count": 2
}

#Excluir uma etiqueta

DELETE/inboxes/{id}/labels/{label_id}

Chave de acesso total

Tira a etiqueta de todas as conversas. As conversas continuam.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • label_idstringobrigatório
    ID da etiqueta.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /inboxes/{id}/labels/{label_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "deleted": true
}

#Listar rascunhos

GET/inboxes/{id}/drafts

Chave de acesso total

Rascunhos da inbox, do mais recente para o mais antigo, com a lista de attachments de cada um (sem o conteúdo). Os que o agente de IA sugere têm source: "agent".

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Parâmetros de consulta

  • thread_idstring
    Só rascunhos de uma conversa.
  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    Cursor da próxima página.
  • beforestring
    Cursor da página anterior.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}/drafts

cURL

curl 'https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts?thread_id=019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts?thread_id=019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'thread_id' => '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "draft",
      "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "source": "user",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Re: Troca de tamanho do pedido #10482",
      "text": "Oi, Carla! Já separamos o tamanho M para você.",
      "html": null,
      "attachments": [
        {
          "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
          "filename": "tabela-de-medidas.pdf",
          "content_type": "application/pdf",
          "size": 48213
        }
      ],
      "created_by": {
        "id": 12,
        "name": "Carlos Lima"
      },
      "created_at": "2026-10-24T13:00:00.000000Z",
      "updated_at": "2026-10-24T13:01:30.000000Z"
    }
  ]
}

#Criar um rascunho

POST/inboxes/{id}/drafts

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • thread_idstring
    Conversa a que o rascunho responde. Sem ela, é uma mensagem nova.
  • tostring[]
    Destinatários.
  • ccstring[]
    Com cópia.
  • bccstring[]
    Com cópia oculta.
  • subjectstring
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • attachmentsobject[]
    Anexos do rascunho, até 10 arquivos e 40 MB no total. Veja Anexos.
    • filenamestring
      Nome do arquivo, até 255 caracteres. Obrigatório sem id.
    • contentstring
      Conteúdo do arquivo em Base64. Obrigatório sem id.
    • content_typestring
      Tipo MIME. Sem ele, é deduzido pela extensão.
    • idstring
      Só na atualização: ID de um anexo que o rascunho já tem, para mantê-lo (sem reenviar o conteúdo).

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /inboxes/{id}/drafts

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    "to": ["[email protected]"],
    "text": "Oi, Carla!"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    thread_id: '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
    to: ['[email protected]'],
    text: 'Oi, Carla!',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
        "to": ["[email protected]"],
        "text": "Oi, Carla!",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'thread_id' => '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
        'to' => ['[email protected]'],
        'text' => 'Oi, Carla!',
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Obter um rascunho

GET/inboxes/{id}/drafts/{draft_id}

Chave de acesso total

Devolve o rascunho com o texto, o HTML e a lista de attachments (sem o conteúdo dos arquivos). created_by é a pessoa que o escreveu, ou null quando foi o agente de IA (source: "agent").

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • draft_idstringobrigatório
    ID do rascunho.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /inboxes/{id}/drafts/{draft_id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Atualizar um rascunho

PATCH/inboxes/{id}/drafts/{draft_id}

Chave de acesso total

Aceita os mesmos campos da criação, menos thread_id. Com attachments, a lista de anexos é substituída: mande {"id"} para manter um anexo que o rascunho já tem e filename com content para adicionar outro (veja Anexos).

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • draft_idstringobrigatório
    ID do rascunho.

Corpo da requisição

  • tostring[]
    Destinatários.
  • ccstring[]
    Com cópia.
  • bccstring[]
    Com cópia oculta.
  • subjectstring
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • attachmentsobject[]
    Anexos do rascunho, até 10 arquivos e 40 MB no total. Veja Anexos.
    • filenamestring
      Nome do arquivo, até 255 caracteres. Obrigatório sem id.
    • contentstring
      Conteúdo do arquivo em Base64. Obrigatório sem id.
    • content_typestring
      Tipo MIME. Sem ele, é deduzido pela extensão.
    • idstring
      Só na atualização: ID de um anexo que o rascunho já tem, para mantê-lo (sem reenviar o conteúdo).

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /inboxes/{id}/drafts/{draft_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Oi, Carla! Já separamos o tamanho M."
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'Oi, Carla! Já separamos o tamanho M.',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "text": "Oi, Carla! Já separamos o tamanho M.",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'text' => 'Oi, Carla! Já separamos o tamanho M.',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Enviar um rascunho

POST/inboxes/{id}/drafts/{draft_id}/send

Chave de acesso total

Envia o rascunho (como resposta, se ele pertence a uma conversa; como mensagem nova, se não) com os anexos do rascunho e o apaga, junto com os arquivos dele.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • draft_idstringobrigatório
    ID do rascunho.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
  • 403validation_error
    O domínio da inbox não está verificado.
  • 403sending_paused
    Os envios do time estão pausados.
  • 429daily_quota_exceeded
    A cota de envio do plano acabou (também monthly_quota_exceeded).
POST /inboxes/{id}/drafts/{draft_id}/send

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Excluir um rascunho

DELETE/inboxes/{id}/drafts/{draft_id}

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.
  • draft_idstringobrigatório
    ID do rascunho.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /inboxes/{id}/drafts/{draft_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "deleted": true
}

#Agente de IA

Com o agente ligado (agent.enabled em PATCH /inboxes/{id}), cada e-mail novo é lido por uma IA que segue as suas instructions e o tone, e só pode fazer o que está em actions: sugerir uma resposta (draft_reply), aplicar etiquetas e criar as que faltam (add_labels), atribuir (assign_thread), arquivar (archive), marcar como spam (mark_as_spam), mover para a lixeira (move_to_trash) e encaminhar (forward_thread).

  • Nunca envia nada sozinho: as respostas viram rascunhos (source: "agent") para uma pessoa revisar e enviar.
  • Só encaminha para endereços escritos nas instruções.
  • Usa 1 crédito de IA por e-mail. Não faz nada sem créditos, com a inbox ou o agente desligados, em e-mails automáticos (Auto-Submitted) ou enviados pela própria inbox.
  • Para escrever boas instructions, use POST /inboxes/{id}/agent/improve-instructions, que reescreve o seu rascunho com mais clareza.

#Melhorar as instruções do agente

POST/inboxes/{id}/agent/improve-instructions

Chave de acesso total

Reescreve o rascunho das instruções do agente para ficar mais claro e específico, no mesmo idioma e com a mesma intenção, sem inventar fatos: endereços de e-mail, preços, horários e regras ficam como você escreveu. Não salva nada: guarde o resultado com PATCH /inboxes/{id} (agent.instructions) depois de revisar.

É assíncrono, como os demais pedidos de IA: a resposta é uma tarefa (ai_task) e o resultado chega por GET /ai/tasks/{id} como {"object": "agent_instructions", "instructions", "task_id", "status": "completed", "credits"}. Usa 1 crédito de IA.

Parâmetros de caminho

  • idstringobrigatório
    ID da inbox.

Corpo da requisição

  • instructionsstringobrigatório
    O rascunho das instruções, de 1 a 4.000 caracteres.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
  • 422validation_error
    Campo ausente ou fora dos limites.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /inboxes/{id}/agent/improve-instructions

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "responde as duvidas dos clientes e manda cobranca pro [email protected]"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    instructions: 'responde as duvidas dos clientes e manda cobranca pro [email protected]',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "instructions": "responde as duvidas dos clientes e manda cobranca pro [email protected]",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'instructions' => 'responde as duvidas dos clientes e manda cobranca pro [email protected]',
    ],
]);

$data = json_decode($response->getBody(), true);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "object": "agent_instructions",
  "instructions": "Responda com educação às dúvidas dos clientes sobre pedidos, trocas e entregas.\nEncaminhe os assuntos de cobrança para [email protected].",
  "task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "completed",
  "credits": {
    "used": 13,
    "limit": 100,
    "remaining": 87,
    "resets_at": "2026-11-01T00:00:00Z"
  }
}

#Webhooks da inbox

Além de email.received, quatro eventos acompanham as conversas. Assine-os em POST /webhooks; os formatos estão em Webhooks.

EventoQuando
inbox.thread.createdUma conversa nova foi aberta, por um e-mail recebido ou por uma mensagem enviada pela inbox.
inbox.thread.updatedA pasta, o estado de lida, o responsável ou as etiquetas mudaram (por PATCH, ação em lote, exclusão para a lixeira ou pelo agente), ou um e-mail novo tirou a conversa do arquivo. Um evento por mudança, e uma por conversa alterada nas ações em lote.
inbox.thread.deletedA conversa foi apagada de vez.
inbox.draft.createdO agente de IA criou um rascunho (source: "agent"). Rascunhos criados por pessoas não geram evento.

#Limites do plano

O plano define quantas inboxes o time tem e quais recursos delas pode usar. Passar de um limite responde 403 plan_limit_reached, com uma mensagem que diz o que falta. Os valores de cada plano estão em Limites e em GET /plans.

Recurso do planoChaveO que muda na API
InboxesinboxesQuantas inboxes o time pode ter. Acima disso, POST /inboxes responde 403.
Inboxes no seu domíniocustom_inbound_domainsSem ele, só há inboxes em in.postal.so: criar uma com domain_id responde 403, e o domínio não pode ligar o recebimento.
Agente de IA da inboxinbox_agentLigar agent.enabled em PATCH /inboxes/{id} responde 403 sem ele.
Créditos de IAai_creditsO agente e a tradução usam créditos por mês; sem créditos, a tradução responde 429 ai_credits_exhausted e o agente não age.

Se o plano diminui e o time fica com mais inboxes do que o novo limite, a Postal não apaga nenhuma: as mais antigas, até o limite, continuam ativas e as mais novas ficam pausadas (paused_by_plan: true na inbox). Uma inbox pausada continua na lista e pode ser lida, mas PATCH /inboxes/{id} responde 403 paused_by_plan e o agente de IA dela não age. Ela volta ao normal quando o plano permite de novo ou quando você apaga outras inboxes. Veja também Limites do plano em E-mails.