Referência da API

Domínios

Cadastro, registros DNS, verificação e reivindicação de domínios.

Cadastre os domínios de onde você envia (e, se quiser, onde recebe), veja os registros DNS que precisam ser criados e acompanhe a verificação. O passo a passo do DNS está em Domínios e DNS.

#Listar domínios

GET/domains

Chave de acesso total

Lista os domínios do time, do mais recente para o mais antigo. A lista não traz os registros DNS.

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.
  • searchstring
    Busca no nome.
  • statusstring
    Um ou mais status separados por vírgula: not_started, pending, verified, partially_verified, partially_failed, failed.
  • regionstring
    Só domínios de uma região.

Erros

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

cURL

curl 'https://api.postal.so/domains?status=verified' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains?status=verified', {
  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/domains",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "status": "verified",
    },
    timeout=10,
)

data = response.json()

PHP

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

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

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "domain",
      "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
      "name": "lojaexemplo.com.br",
      "status": "verified",
      "created_at": "2026-10-02T18:20:00.000000Z",
      "region": "sa-east-1",
      "dns_provider": "cloudflare",
      "open_tracking": false,
      "click_tracking": false,
      "tracking_subdomain": null,
      "tls": "opportunistic",
      "capabilities": {
        "sending": "enabled",
        "receiving": "disabled"
      }
    }
  ]
}

#Criar um domínio

POST/domains

Chave de acesso total

Cadastra o domínio, gera a chave DKIM e devolve os registros DNS para você criar no seu provedor. Depois, chame POST /domains/{id}/verify para começar a verificação. Se o domínio já estiver cadastrado em outra conta, use a reivindicação.

Corpo da requisição

  • namestringobrigatório
    Nome do domínio, como lojaexemplo.com.br ou mail.lojaexemplo.com.br. Precisa ser único na Postal.
  • regionstring
    Região de envio: sa-east-1 (São Paulo, padrão), us-east-1, us-east-2, eu-west-1 ou ap-northeast-1.
  • custom_return_pathstring
    Subdomínio do Return-Path (MAIL FROM). Padrão: postal.
  • open_trackingboolean
    Rastrear aberturas. Padrão: false.
  • click_trackingboolean
    Rastrear cliques. Padrão: false.
  • tracking_subdomainstring
    Subdomínio próprio para os links rastreados, como links. Gera um registro CNAME.
  • tlsstring
    opportunistic (padrão) usa TLS quando o servidor do destinatário aceita; enforced só entrega com TLS.
  • capabilitiesobject
    O que o domínio faz.
    • sendingstring
      enabled (padrão) ou disabled.
    • receivingstring
      enabled ou disabled (padrão). Recebimento no seu domínio é dos planos pagos.

Erros

  • 422validation_error
    Nome inválido ou já cadastrado, região inválida, ou o domínio já está configurado no provedor de envio por outro sistema (use um subdomínio).
  • 403plan_limit_reached
    O plano não permite mais domínios.
POST /domains

cURL

curl -X POST https://api.postal.so/domains \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lojaexemplo.com.br",
    "region": "sa-east-1"
  }'

Node.js

const response = await fetch('https://api.postal.so/domains', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'lojaexemplo.com.br',
    region: 'sa-east-1',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/domains",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "lojaexemplo.com.br",
        "region": "sa-east-1",
    },
    timeout=10,
)

data = response.json()

PHP

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

$response = $client->post('domains', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'lojaexemplo.com.br',
        'region' => 'sa-east-1',
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "domain",
  "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "name": "lojaexemplo.com.br",
  "status": "not_started",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "region": "sa-east-1",
  "dns_provider": null,
  "open_tracking": false,
  "click_tracking": false,
  "tracking_subdomain": null,
  "tls": "opportunistic",
  "capabilities": {
    "sending": "enabled",
    "receiving": "disabled"
  },
  "records": [
    {
      "record": "DKIM",
      "name": "postal._domainkey",
      "type": "TXT",
      "ttl": "Auto",
      "status": "not_started",
      "value": "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4k…IDAQAB",
      "diagnostic": null,
      "last_checked_at": null
    },
    {
      "record": "SPF",
      "name": "postal",
      "type": "MX",
      "ttl": "Auto",
      "status": "not_started",
      "value": "feedback-smtp.sa-east-1.amazonses.com",
      "priority": 10,
      "diagnostic": null,
      "last_checked_at": null
    },
    {
      "record": "SPF",
      "name": "postal",
      "type": "TXT",
      "ttl": "Auto",
      "status": "not_started",
      "value": "v=spf1 include:_spf.postal.so ~all",
      "diagnostic": null,
      "last_checked_at": null
    }
  ]
}

#Obter um domínio

GET/domains/{id}

Chave de acesso total

Devolve o domínio com os registros DNS (cada um com o próprio status e um diagnostic quando algo está errado) e o histórico de eventos.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

Erros

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

cURL

curl https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', {
  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/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
    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('domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain",
  "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "name": "lojaexemplo.com.br",
  "status": "verified",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "region": "sa-east-1",
  "dns_provider": "cloudflare",
  "open_tracking": false,
  "click_tracking": false,
  "tracking_subdomain": null,
  "tls": "opportunistic",
  "capabilities": {
    "sending": "enabled",
    "receiving": "disabled"
  },
  "records": [
    {
      "record": "DKIM",
      "name": "postal._domainkey",
      "type": "TXT",
      "ttl": "Auto",
      "status": "verified",
      "value": "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4k…IDAQAB",
      "diagnostic": null,
      "last_checked_at": "2026-10-02T18:31:44.000000Z"
    },
    {
      "record": "SPF",
      "name": "postal",
      "type": "MX",
      "ttl": "Auto",
      "status": "verified",
      "value": "feedback-smtp.sa-east-1.amazonses.com",
      "priority": 10,
      "diagnostic": null,
      "last_checked_at": "2026-10-02T18:31:44.000000Z"
    },
    {
      "record": "SPF",
      "name": "postal",
      "type": "TXT",
      "ttl": "Auto",
      "status": "verified",
      "value": "v=spf1 include:_spf.postal.so ~all",
      "diagnostic": null,
      "last_checked_at": "2026-10-02T18:31:44.000000Z"
    }
  ],
  "events": [
    {
      "type": "domain.created",
      "occurred_at": "2026-10-02T18:20:00.000000Z",
      "data": null
    },
    {
      "type": "domain.verification_started",
      "occurred_at": "2026-10-02T18:22:10.000000Z",
      "data": null
    },
    {
      "type": "domain.verified",
      "occurred_at": "2026-10-02T18:31:44.000000Z",
      "data": null
    }
  ]
}

#Atualizar um domínio

PATCH/domains/{id}

Chave de acesso total

Liga ou desliga rastreamento, TLS obrigatório, envio e recebimento. Mudar as capacidades ou o subdomínio de rastreamento muda os registros DNS: consulte o domínio de novo para ver o que criar. Dispara o webhook domain.updated.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

Corpo da requisição

  • open_trackingboolean
    Rastrear aberturas.
  • click_trackingboolean
    Rastrear cliques.
  • tracking_subdomainstring | null
    Subdomínio dos links rastreados.
  • tlsstring
    opportunistic ou enforced.
  • capabilitiesobject
    {"sending": "enabled" | "disabled", "receiving": "enabled" | "disabled"}.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 403plan_limit_reached
    Recebimento no seu domínio não está no plano.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /domains/{id}

cURL

curl -X PATCH https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "open_tracking": true,
    "click_tracking": true,
    "capabilities": {
      "receiving": "enabled"
    }
  }'

Node.js

const response = await fetch('https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    open_tracking: true,
    click_tracking: true,
    capabilities: {
      receiving: 'enabled',
    },
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "open_tracking": True,
        "click_tracking": True,
        "capabilities": {
            "receiving": "enabled",
        },
    },
    timeout=10,
)

data = response.json()

PHP

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

$response = $client->patch('domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'open_tracking' => true,
        'click_tracking' => true,
        'capabilities' => [
            'receiving' => 'enabled',
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain",
  "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f"
}

#Excluir um domínio

DELETE/domains/{id}

Chave de acesso total

Remove o domínio. Envios a partir dele passam a falhar. Dispara o webhook domain.deleted.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

Erros

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

cURL

curl -X DELETE https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', {
  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/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
    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('domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain",
  "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "deleted": true
}

#Verificar um domínio

POST/domains/{id}/verify

Chave de acesso total

Inicia (ou reinicia) a verificação dos registros DNS em segundo plano. A resposta é imediata; acompanhe o resultado com GET /domains/{id} ou com o webhook domain.updated. A verificação continua sozinha por até 72 horas.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
POST /domains/{id}/verify

cURL

curl -X POST https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/verify \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/verify', {
  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/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/verify",
    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('domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/verify', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain",
  "id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f"
}

#Reivindicar um domínio

POST/domains/claim

Chave de acesso total

Quando o domínio já está cadastrado em outra conta da Postal (por exemplo, de um ex-funcionário), prove que você controla o DNS para transferi-lo. O corpo é o mesmo de POST /domains. Crie o registro TXT devolvido em record e chame a verificação. O pedido vale por 7 dias; repetir o mesmo pedido devolve o que já está aberto (status 200).

Corpo da requisição

  • namestringobrigatório
    Domínio cadastrado em outra conta.
  • …
    Os demais campos de POST /domains, aplicados quando o domínio for transferido.

Erros

  • 422validation_error
    O domínio não está em outra conta (crie com POST /domains) ou já é seu.
  • 403plan_limit_reached
    O plano não permite mais domínios.
POST /domains/claim

cURL

curl -X POST https://api.postal.so/domains/claim \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lojaexemplo.com.br"
  }'

Node.js

const response = await fetch('https://api.postal.so/domains/claim', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'lojaexemplo.com.br',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/domains/claim",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "lojaexemplo.com.br",
    },
    timeout=10,
)

data = response.json()

PHP

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

$response = $client->post('domains/claim', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'lojaexemplo.com.br',
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "domain_claim",
  "id": "019a0a10-5c6d-7e7f-8a9b-0c1d2e3f4a5b",
  "name": "lojaexemplo.com.br",
  "status": "pending",
  "domain_id": "019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b",
  "region": "sa-east-1",
  "record": {
    "type": "TXT",
    "name": "lojaexemplo.com.br",
    "value": "postal-domain-verification=Qm8rT2xKp4Vn7sWd1Yc5Hf3Lz9Ag6Ej0",
    "ttl": "Auto"
  },
  "blocked_reason": null,
  "failure_reason": null,
  "created_at": "2026-10-24T13:10:00.000000Z",
  "expires_at": "2026-10-31T13:10:00.000000Z"
}

#Obter uma reivindicação

GET/domains/{domain_id}/claim

Chave de acesso total

Mostra o andamento. status pode ser pending, verified, completed (o domínio já é seu, com o ID domain_id), blocked (por exemplo, há e-mails agendados na outra conta: blocked_reason explica), expired, superseded, canceled ou failed.

Parâmetros de caminho

  • domain_idstringobrigatório
    O domain_id devolvido na criação da reivindicação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /domains/{domain_id}/claim

cURL

curl https://api.postal.so/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim', {
  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/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim",
    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('domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain_claim",
  "id": "019a0a10-5c6d-7e7f-8a9b-0c1d2e3f4a5b",
  "name": "lojaexemplo.com.br",
  "status": "pending",
  "domain_id": "019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b",
  "region": "sa-east-1",
  "record": {
    "type": "TXT",
    "name": "lojaexemplo.com.br",
    "value": "postal-domain-verification=Qm8rT2xKp4Vn7sWd1Yc5Hf3Lz9Ag6Ej0",
    "ttl": "Auto"
  },
  "blocked_reason": null,
  "failure_reason": null,
  "created_at": "2026-10-24T13:10:00.000000Z",
  "expires_at": "2026-10-31T13:10:00.000000Z"
}

#Verificar uma reivindicação

POST/domains/{domain_id}/claim/verify

Chave de acesso total

Procura o registro TXT e, se ele estiver lá, transfere o domínio em segundo plano. Consulte a reivindicação para ver o resultado; enquanto o TXT não aparece, failure_reason diz isso.

Parâmetros de caminho

  • domain_idstringobrigatório
    O domain_id da reivindicação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
POST /domains/{domain_id}/claim/verify

cURL

curl -X POST https://api.postal.so/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim/verify \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim/verify', {
  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/domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim/verify",
    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('domains/019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b/claim/verify', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain_claim",
  "id": "019a0a10-5c6d-7e7f-8a9b-0c1d2e3f4a5b",
  "name": "lojaexemplo.com.br",
  "status": "pending",
  "domain_id": "019a0a10-5c6e-7f80-9a1b-2c3d4e5f6a7b",
  "region": "sa-east-1",
  "record": {
    "type": "TXT",
    "name": "lojaexemplo.com.br",
    "value": "postal-domain-verification=Qm8rT2xKp4Vn7sWd1Yc5Hf3Lz9Ag6Ej0",
    "ttl": "Auto"
  },
  "blocked_reason": null,
  "failure_reason": null,
  "created_at": "2026-10-24T13:10:00.000000Z",
  "expires_at": "2026-10-31T13:10:00.000000Z"
}