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
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
limitintegerDe 1 a 100. Padrão: 20.afterstringCursor da próxima página.beforestringCursor da página anterior.searchstringBusca no nome.statusstringUm ou mais status separados por vírgula:not_started,pending,verified,partially_verified,partially_failed,failed.regionstringSó domínios de uma região.
Erros
- 422
validation_errorAlgum campo é inválido. A mensagem diz qual.
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);
{
"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
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órioNome do domínio, comolojaexemplo.com.broumail.lojaexemplo.com.br. Precisa ser único na Postal.regionstringRegião de envio:sa-east-1(São Paulo, padrão),us-east-1,us-east-2,eu-west-1ouap-northeast-1.custom_return_pathstringSubdomínio do Return-Path (MAIL FROM). Padrão:postal. Escolher outro depende do recurso de registros próprios do plano.open_trackingbooleanRastrear aberturas. Padrão:false. Ligar depende do recurso de rastreamento do plano.click_trackingbooleanRastrear cliques. Padrão:false. Ligar depende do recurso de rastreamento do plano.tracking_subdomainstringSubdomínio próprio para os links rastreados, comolinks. Gera um registro CNAME. Depende do recurso de registros próprios do plano.tlsstringopportunistic(padrão) usa TLS quando o servidor do destinatário aceita;enforcedsó entrega com TLS.capabilitiesobjectO que o domínio faz.sendingstringenabled(padrão) oudisabled. Pelo menos uma das duas capacidades precisa ficar ligada.receivingstringenabledoudisabled(padrão). Recebimento no seu domínio é dos planos pagos.
Erros
- 422
validation_errorNome 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. - 403
plan_limit_reachedO 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.
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);
{
"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}
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órioID do domínio.
Erros
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"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}
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órioID do domínio.
Corpo da requisição
open_trackingbooleanRastrear aberturas.click_trackingbooleanRastrear cliques.tracking_subdomainstring | nullSubdomínio dos links rastreados.tlsstringopportunisticouenforced.capabilitiesobject{"sending": "enabled" | "disabled", "receiving": "enabled" | "disabled"}.
Erros
- 422
validation_errorAlgum campo é inválido. A mensagem diz qual. - 403
plan_limit_reachedRecebimento 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. - 403
paused_by_planO domínio está pausado porque passa do limite de domínios do plano. - 404
not_foundO recurso não existe ou é de outro time.
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);
{
"object": "domain",
"id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f"
}
#Excluir um domínio
DELETE/domains/{id}
Remove o domínio. Envios a partir dele passam a falhar. Dispara o webhook domain.deleted.
Parâmetros de caminho
idstringobrigatórioID do domínio.
Erros
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"object": "domain",
"id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
"deleted": true
}
#Verificar um domínio
POST/domains/{id}/verify
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órioID do domínio.
Erros
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"object": "domain",
"id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f"
}
#Conferir o MX antes de receber
GET/domains/{id}/receiving-check
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órioID do domínio.
mx_conflict:
| Campo | Descrição |
|---|---|
has_other_mx | true quando o domínio tem algum MX que não é o da Postal (o MX da Postal, mx.postal.so, nunca conta como conflito). |
records | Os 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_domain | O 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
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"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
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órioDomínio cadastrado em outra conta.…Os demais campos dePOST /domains, aplicados quando o domínio for transferido.
Erros
- 422
validation_errorO domínio não está em outra conta (crie comPOST /domains) ou já é seu. - 403
plan_limit_reachedO plano não permite mais domínios, ou não inclui o que o corpo pede (rastreamento, registros próprios).
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);
{
"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
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órioOdomain_iddevolvido na criação da reivindicação.
Erros
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"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
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órioOdomain_idda reivindicação.
Erros
- 404
not_foundO recurso não existe ou é de outro time.
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);
{
"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
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órioID do domínio.
Corpo da requisição
tostring[]obrigatórioDe 1 a 5 endereços de e-mail, com até 254 caracteres cada. Os repetidos contam uma vez.messagestringRecado que vai no e-mail, com até 500 caracteres e sem links (o e-mail já traz tudo o que a pessoa precisa).
Erros
- 422
validation_errortovazio, com mais de 5 endereços ou com um endereço inválido, oumessagelonga demais ou com link. - 429
rate_limit_exceededMais de 3 pedidos por minuto ou 10 por hora no time, ou um dos endereços já recebeu as instruções 5 vezes hoje. - 404
not_foundO recurso não existe ou é de outro time.
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);
{
"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.
| Campo | Descrição |
|---|---|
status | O 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_confirmation | true 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. |
capabilities | sending e receiving, cada um enabled ou disabled. Só um domínio verificado e com sending ligado envia. |
dns_provider | O provedor de DNS detectado (como cloudflare), ou null. |
records_missing | null, 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_plan | true 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.
| record | Registros | Para quê |
|---|---|---|
DKIM | TXT em postal._domainkey | A assinatura dos e-mails. Obrigatório para enviar. |
SPF | MX 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. |
Tracking | CNAME no tracking_subdomain | Links de rastreamento no seu domínio. Só existe com rastreamento ligado e um subdomínio de rastreamento; não bloqueia o envio. |
Receiving | MX 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:
| Tipo | Quando |
|---|---|
domain.created | O domínio foi cadastrado. |
domain.verification_started | A verificação começou ou recomeçou. |
domain.dns_verified | Todos os registros foram encontrados. |
domain.verified | O domínio foi verificado e pode enviar. |
domain.partially_failed, domain.failed | O prazo de verificação acabou com parte dos registros ou sem nenhum. |
domain.tracking_updated | O rastreamento ou o subdomínio de rastreamento mudou. |
domain.records_missing | Um registro obrigatório sumiu de um domínio que já funcionava. |
domain.records_restored | O registro que faltava voltou. |
domain.verification_lost | O registro não voltou dentro do prazo e o domínio deixou de estar verificado. |
domain.provider_error | Houve 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.
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