Entrar Criar conta
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. Os campos do domínio estão em Status, registros e eventos.

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",
      "paused_by_plan": false,
      "name": "lojaexemplo.com.br",
      "status": "verified",
      "awaiting_confirmation": false,
      "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_missing": null
    }
  ]
}

#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. Os registros também seguem por e-mail para quem cadastrou o domínio (o usuário do painel ou o criador da chave), e o webhook domain.created é disparado. Para mandar os registros a outra pessoa, como o time de TI, use POST /domains/{id}/instructions.

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. Escolher outro depende do recurso de registros próprios do plano.
  • open_trackingboolean
    Rastrear aberturas. Padrão: false. Ligar depende do recurso de rastreamento do plano.
  • click_trackingboolean
    Rastrear cliques. Padrão: false. Ligar depende do recurso de rastreamento do plano.
  • tracking_subdomainstring
    Subdomínio próprio para os links rastreados, como links. Gera um registro CNAME. Depende do recurso de registros próprios do plano.
  • 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. Pelo menos uma das duas capacidades precisa ficar ligada.
    • 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). Também quando nenhuma capacidade fica ligada.
  • 403plan_limit_reached
    O plano não permite mais domínios. Também quando o plano não inclui o que foi pedido: rastreamento de aberturas e cliques, Return-Path ou subdomínio de rastreamento próprios, ou receber no seu domínio.
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",
  "paused_by_plan": false,
  "name": "lojaexemplo.com.br",
  "status": "not_started",
  "awaiting_confirmation": false,
  "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_missing": null,
  "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. O que cada status, registro e evento significa está em Status, registros e 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",
  "paused_by_plan": false,
  "name": "lojaexemplo.com.br",
  "status": "verified",
  "awaiting_confirmation": false,
  "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_missing": null,
  "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. Não é possível mudar o nome, a região nem o Return-Path de um domínio: crie outro. Um domínio pausado pelo plano não pode ser alterado.

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. Também quando o plano não inclui o rastreamento de aberturas e cliques ou o subdomínio de rastreamento próprio.
  • 403paused_by_plan
    O domínio está pausado porque passa do limite de domínios do 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. Num domínio já verificado que perdeu um registro, a chamada só pede uma conferência imediata, e o domínio continua enviando enquanto isso.

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"
}

#Conferir o MX antes de receber

GET/domains/{id}/receiving-check

Chave de acesso total

Antes de ligar capabilities.receiving, descubra se o domínio já tem registros MX de outro provedor (Google Workspace, Microsoft 365…). Eles passariam a competir com o MX da Postal, e parte dos e-mails continuaria indo para a caixa antiga. A Postal consulta o DNS agora (com tempo curto de espera, e guarda a resposta por cerca de 1 minuto) e devolve em mx_conflict os registros que não são da Postal e a sugestão de usar um subdomínio. É só informação: nada é bloqueado e PATCH /domains/{id} funciona do mesmo jeito. Use para mostrar um aviso antes de ativar.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

mx_conflict:

CampoDescrição
has_other_mxtrue quando o domínio tem algum MX que não é o da Postal (o MX da Postal, mx.postal.so, nunca conta como conflito).
recordsOs MX de outros provedores, do menor para o maior priority: {"priority", "host"}. Um MX nulo (RFC 7505) aparece com host ".".
suggestion"subdomain" quando há conflito (receba num subdomínio, sem tocar no MX do domínio principal); senão null.
suggested_domainO nome sugerido, como entrada.lojaexemplo.com.br (inbound.… em inglês, conforme o Accept-Language), ou null sem sugestão. Cadastre-o com POST /domains.

Se o DNS não puder ser consultado agora (falha ou tempo esgotado), a chamada continua dando 200 e mx_conflict vem null: não mostre aviso, e tente de novo depois. O campo receiving diz se o recebimento do domínio já está ligado.

Erros

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

cURL

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

Node.js

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

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "domain_receiving_check",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving": "disabled",
  "mx_conflict": {
    "has_other_mx": true,
    "records": [
      {
        "priority": 1,
        "host": "aspmx.l.google.com"
      },
      {
        "priority": 5,
        "host": "alt1.aspmx.l.google.com"
      }
    ],
    "suggestion": "subdomain",
    "suggested_domain": "entrada.lojaexemplo.com.br"
  }
}

#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, ou não inclui o que o corpo pede (rastreamento, registros próprios).
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"
}

#Enviar as instruções por e-mail

POST/domains/{id}/instructions

Chave de acesso total

Manda por e-mail os registros DNS do domínio e o passo a passo do provedor de DNS para até 5 endereços, como o time de TI ou a agência que cuida do seu DNS. Quem recebe não precisa de conta na Postal: o e-mail traz tudo, e diz quem pediu. É um e-mail do sistema, que não conta na cota de envio.

Parâmetros de caminho

  • idstringobrigatório
    ID do domínio.

Corpo da requisição

  • tostring[]obrigatório
    De 1 a 5 endereços de e-mail, com até 254 caracteres cada. Os repetidos contam uma vez.
  • messagestring
    Recado que vai no e-mail, com até 500 caracteres e sem links (o e-mail já traz tudo o que a pessoa precisa).

Erros

  • 422validation_error
    to vazio, com mais de 5 endereços ou com um endereço inválido, ou message longa demais ou com link.
  • 429rate_limit_exceeded
    Mais de 3 pedidos por minuto ou 10 por hora no time, ou um dos endereços já recebeu as instruções 5 vezes hoje.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /domains/{id}/instructions

cURL

curl -X POST https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/instructions \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["[email protected]"],
    "message": "Oi! Pode criar esses registros no DNS do domínio?"
  }'

Node.js

const response = await fetch('https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/instructions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: ['[email protected]'],
    message: 'Oi! Pode criar esses registros no DNS do domínio?',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/domains/0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f/instructions",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": ["[email protected]"],
        "message": "Oi! Pode criar esses registros no DNS do domínio?",
    },
    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/instructions', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => ['[email protected]'],
        'message' => 'Oi! Pode criar esses registros no DNS do domínio?',
    ],
]);

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

#Status, registros e eventos

O objeto domínio traz, além do nome e da região, o estado da verificação e dos registros DNS.

CampoDescrição
statusO estado da verificação: not_started (criado, ainda sem verificação), pending (em verificação, por até 72 horas), partially_verified (só parte dos registros foi encontrada), verified (pode enviar), partially_failed (o prazo acabou com parte dos registros encontrada) e failed (o prazo acabou sem nenhum registro).
awaiting_confirmationtrue quando todos os registros necessários para enviar já foram encontrados e falta só a confirmação final do provedor de envio, que costuma levar de 5 a 30 minutos.
capabilitiessending e receiving, cada um enabled ou disabled. Só um domínio verificado e com sending ligado envia.
dns_providerO provedor de DNS detectado (como cloudflare), ou null.
records_missingnull, ou, num domínio que já funcionava e perdeu um registro obrigatório, {"since", "deadline_at", "records"}: desde quando falta, o prazo e os registros (record, type, name). O domínio continua enviando até o prazo, de 72 horas, e a Postal procura o registro de hora em hora; se ele voltar, o aviso some, e se o prazo passar, o domínio deixa de estar verificado.
paused_by_plantrue quando o domínio passa do limite de domínios do plano. Ele continua na lista, mas não pode ser alterado e não envia: veja Limites do plano.

Registros DNS

Cada item de records tem record (o tipo de registro da Postal), type (o tipo de DNS), name, ttl, value, priority (só nos MX), status, diagnostic e last_checked_at. O diagnostic é um texto para pessoas que explica o que a última checagem encontrou (null quando o registro está certo) e vem no idioma da requisição: Accept-Language: pt-BR ou en, em inglês sem o cabeçalho. Não compare com o texto: decida pelo status.

recordRegistrosPara quê
DKIMTXT em postal._domainkeyA assinatura dos e-mails. Obrigatório para enviar.
SPFMX e TXT no subdomínio do Return-Path (padrão postal)Recebe bounces e reclamações e autoriza a Postal a enviar. Obrigatório para enviar.
TrackingCNAME no tracking_subdomainLinks de rastreamento no seu domínio. Só existe com rastreamento ligado e um subdomínio de rastreamento; não bloqueia o envio.
ReceivingMX em @Entrega os e-mails recebidos nas inboxes. Só com capabilities.receiving ligado.

O status de um registro é not_started, pending, verified, failed ou temporary_failure (a consulta ao DNS falhou; a Postal tenta de novo). Um domínio verificado é conferido de novo todos os dias.

Eventos do domínio

O events de GET /domains/{id} conta a história do domínio, do mais antigo para o mais novo:

TipoQuando
domain.createdO domínio foi cadastrado.
domain.verification_startedA verificação começou ou recomeçou.
domain.dns_verifiedTodos os registros foram encontrados.
domain.verifiedO domínio foi verificado e pode enviar.
domain.partially_failed, domain.failedO prazo de verificação acabou com parte dos registros ou sem nenhum.
domain.tracking_updatedO rastreamento ou o subdomínio de rastreamento mudou.
domain.records_missingUm registro obrigatório sumiu de um domínio que já funcionava.
domain.records_restoredO registro que faltava voltou.
domain.verification_lostO registro não voltou dentro do prazo e o domínio deixou de estar verificado.
domain.provider_errorHouve um erro ao falar com o provedor de envio (data.stage e data.code); a Postal tenta de novo.

As mudanças de status também chegam por webhook: domain.created, domain.updated e domain.deleted (veja Webhooks).

#Com os SDKs

Os SDKs oficiais têm domains.list, domains.get, domains.create e domains.verify, com os campos da API.

Criar e verificar um domínio

Node.js

import { Postal } from 'postal-so'

const postal = new Postal() // lê POSTAL_API_KEY

const domain = await postal.domains.create({ name: 'lojaexemplo.com.br', region: 'sa-east-1' })
console.log(domain.records) // os registros DNS para criar

await postal.domains.verify(domain.id)
const current = await postal.domains.get(domain.id) // status e registros

PHP

use Postal\Client;

$postal = new Client(); // lê POSTAL_API_KEY

$domain = $postal->domains->create(['name' => 'lojaexemplo.com.br', 'region' => 'sa-east-1']);
$records = $domain['records']; // os registros DNS para criar

$postal->domains->verify($domain['id']);
$current = $postal->domains->get($domain['id']); // status e registros