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.
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",
"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
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ó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.open_trackingbooleanRastrear aberturas. Padrão:false.click_trackingbooleanRastrear cliques. Padrão:false.tracking_subdomainstringSubdomínio próprio para os links rastreados, comolinks. Gera um registro CNAME.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.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). - 403
plan_limit_reachedO plano não permite mais domínios.
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",
"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}
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ó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",
"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}
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ó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. - 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.
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"
}
#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.
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"
}