IA
O assistente de IA: escrever e-mails, melhorar textos, sugerir assuntos e explicar problemas de entregabilidade.
O assistente de IA escreve e melhora e-mails e explica como resolver problemas de entregabilidade. Cada resposta usa 1 crédito do plano (Free: 10, Pro: 100, Scale: 1.000 por mês). Os créditos renovam no dia 1º de cada mês (UTC).
#Como funciona
Gerar uma resposta pode levar de alguns segundos a mais de um minuto, então os pedidos são assíncronos:
- Você faz o
POST(por exemplo,POST /ai/subjects). A Postal valida os campos, confere se há créditos e responde na hora com202 Acceptede uma tarefa:{"object": "ai_task", "id": "…", "status": "pending"}. - Você consulta
GET /ai/tasks/{id}a cada 1 ou 2 segundos. Enquanto a IA trabalha, ostatusépendingouprocessing. - Quando fica pronta, a mesma consulta devolve a resposta completa, com
status: "completed", otask_ide o saldo decredits. Se a tarefa falhar, a consulta devolve o erro, com o status HTTP correspondente.
cURL
# 1. Pede a tarefa (responde 202 com o ID)
curl -X POST https://api.postal.so/ai/subjects \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis."}'
# 2. Consulta até "status" ser "completed" (ou vir um erro)
curl https://api.postal.so/ai/tasks/019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const headers = {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
'Content-Type': 'application/json',
};
// 1. Pede a tarefa: a resposta chega na hora, com status 202.
const request = await fetch('https://api.postal.so/ai/subjects', {
method: 'POST',
headers,
body: JSON.stringify({ content: 'Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis.' }),
});
const task = await request.json(); // { object: 'ai_task', id, status: 'pending' }
// 2. Consulta a cada 2 segundos até a resposta ficar pronta (ou dar erro).
async function waitFor(id) {
for (let attempt = 0; attempt < 90; attempt++) {
const response = await fetch(`https://api.postal.so/ai/tasks/${id}`, { headers });
const body = await response.json();
if (!response.ok) {
throw new Error(`${body.name}: ${body.message}`); // a tarefa falhou
}
if (body.status === 'completed') {
return body;
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error('A IA demorou demais para responder.');
}
const result = await waitFor(task.id);
console.log(result.subjects);
Python
import os
import time
import requests
HEADERS = {"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"}
# 1. Pede a tarefa: a resposta chega na hora, com status 202.
task = requests.post(
"https://api.postal.so/ai/subjects",
headers=HEADERS,
json={"content": "Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis."},
timeout=10,
).json()
# 2. Consulta a cada 2 segundos até a resposta ficar pronta (ou dar erro).
def wait_for(task_id, limit=90):
for _ in range(limit):
response = requests.get(f"https://api.postal.so/ai/tasks/{task_id}", headers=HEADERS, timeout=10)
body = response.json()
if not response.ok:
raise RuntimeError(f"{body['name']}: {body['message']}") # a tarefa falhou
if body["status"] == "completed":
return body
time.sleep(2)
raise TimeoutError("A IA demorou demais para responder.")
result = wait_for(task["id"])
print(result["subjects"])
PHP
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.postal.so',
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'http_errors' => false,
]);
// 1. Pede a tarefa: a resposta chega na hora, com status 202.
$task = json_decode($client->post('ai/subjects', [
'json' => ['content' => 'Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis.'],
])->getBody(), true);
// 2. Consulta a cada 2 segundos até a resposta ficar pronta (ou dar erro).
for ($attempt = 0; $attempt < 90; $attempt++) {
$response = $client->get("ai/tasks/{$task['id']}");
$body = json_decode($response->getBody(), true);
if ($response->getStatusCode() >= 400) {
throw new RuntimeException("{$body['name']}: {$body['message']}"); // a tarefa falhou
}
if ($body['status'] === 'completed') {
print_r($body['subjects']);
break;
}
sleep(2);
}
As tarefas ficam disponíveis por 24 horas; depois disso, a consulta responde 404.
#Créditos
O crédito é contado quando a IA devolve uma resposta válida. Não usam créditos os pedidos recusados na hora (campos inválidos, sem créditos) nem as tarefas que falham antes de a IA responder: serviço ocupado (ai_unavailable), pedido recusado pela IA (ai_refused), resposta longa demais ou fora do formato (ai_error). O saldo também aparece em GET /usage (ai_credits).
#Escrever um e-mail
POST/ai/write-email
Escreve assunto, pré-cabeçalho e corpo a partir de uma descrição. No modo blocks, o corpo vem em blocos do editor visual (heading, text, button, divider, spacer, footer); no modo html, vem um documento HTML completo e a versão em texto. A resposta final (ao lado) chega por GET /ai/tasks/{id}.
Corpo da requisição
briefstringobrigatórioO que o e-mail deve dizer, de 10 a 2.000 caracteres.tonestringprofessional(padrão),friendly,formal,casualoupersuasive.languagestringpt-BR(padrão),enoues.modestringblocks(padrão) ouhtml.
Erros
- 422
validation_errorCampo ausente ou fora dos limites. - 429
ai_credits_exhaustedOs créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC). - 503
ai_not_configuredO assistente de IA não está disponível.
cURL
curl -X POST https://api.postal.so/ai/write-email \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brief": "Avisar que o pedido saiu para entrega, com o link de rastreio e o prazo de 3 dias úteis.",
"tone": "friendly",
"mode": "html"
}'
Node.js
const response = await fetch('https://api.postal.so/ai/write-email', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
brief: 'Avisar que o pedido saiu para entrega, com o link de rastreio e o prazo de 3 dias úteis.',
tone: 'friendly',
mode: 'html',
}),
});
const data = await response.json();
Python
import os
import requests
response = requests.post(
"https://api.postal.so/ai/write-email",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
json={
"brief": "Avisar que o pedido saiu para entrega, com o link de rastreio e o prazo de 3 dias úteis.",
"tone": "friendly",
"mode": "html",
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->post('ai/write-email', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'json' => [
'brief' => 'Avisar que o pedido saiu para entrega, com o link de rastreio e o prazo de 3 dias úteis.',
'tone' => 'friendly',
'mode' => 'html',
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "pending"
}
{
"object": "ai_email",
"subject": "Seu pedido está a caminho",
"preheader": "Acompanhe a entrega pelo link de rastreio.",
"html": "<!DOCTYPE html><html lang=\"pt-BR\">…</html>",
"text": "Olá, {{{NAME}}}! Seu pedido saiu para entrega…",
"task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "completed",
"credits": {
"used": 13,
"limit": 100,
"remaining": 87,
"resets_at": "2026-11-01T00:00:00Z"
}
}
#Melhorar um texto
POST/ai/improve-text
Reescreve um trecho mantendo links e variáveis. Aceita formatação simples (<b>, <i>, <u>, <a>, <br>).
Corpo da requisição
htmlstringobrigatórioO texto, até 5.000 caracteres.improvementstringobrigatórioshorter(mais curto),formal,friendlyougrammar(só corrige gramática e ortografia).
Erros
- 422
validation_errorCampo ausente ou fora dos limites. - 429
ai_credits_exhaustedOs créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC). - 503
ai_not_configuredO assistente de IA não está disponível.
cURL
curl -X POST https://api.postal.so/ai/improve-text \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "Oi, seu pedido foi enviado e logo logo vai chegar ai na sua casa, qualquer coisa fala com a gente.",
"improvement": "grammar"
}'
Node.js
const response = await fetch('https://api.postal.so/ai/improve-text', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: 'Oi, seu pedido foi enviado e logo logo vai chegar ai na sua casa, qualquer coisa fala com a gente.',
improvement: 'grammar',
}),
});
const data = await response.json();
Python
import os
import requests
response = requests.post(
"https://api.postal.so/ai/improve-text",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
json={
"html": "Oi, seu pedido foi enviado e logo logo vai chegar ai na sua casa, qualquer coisa fala com a gente.",
"improvement": "grammar",
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->post('ai/improve-text', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'json' => [
'html' => 'Oi, seu pedido foi enviado e logo logo vai chegar ai na sua casa, qualquer coisa fala com a gente.',
'improvement' => 'grammar',
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "pending"
}
{
"object": "ai_text",
"html": "Oi! Seu pedido foi enviado e logo vai chegar aí na sua casa. Qualquer dúvida, fale com a gente.",
"task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "completed",
"credits": {
"used": 13,
"limit": 100,
"remaining": 87,
"resets_at": "2026-11-01T00:00:00Z"
}
}
#Sugerir assuntos
POST/ai/subjects
Sugere até 5 assuntos diferentes para o conteúdo.
Corpo da requisição
contentstringobrigatórioO conteúdo do e-mail, de 10 a 20.000 caracteres.subjectstringO assunto atual, até 200 caracteres, para a IA levar em conta.
Erros
- 422
validation_errorCampo ausente ou fora dos limites. - 429
ai_credits_exhaustedOs créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC). - 503
ai_not_configuredO assistente de IA não está disponível.
cURL
curl -X POST https://api.postal.so/ai/subjects \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis. Acompanhe pelo link de rastreio."
}'
Node.js
const response = await fetch('https://api.postal.so/ai/subjects', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: 'Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis. Acompanhe pelo link de rastreio.',
}),
});
const data = await response.json();
Python
import os
import requests
response = requests.post(
"https://api.postal.so/ai/subjects",
headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
json={
"content": "Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis. Acompanhe pelo link de rastreio.",
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->post('ai/subjects', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
'json' => [
'content' => 'Seu pedido #10482 saiu para entrega e chega em até 3 dias úteis. Acompanhe pelo link de rastreio.',
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "pending"
}
{
"object": "ai_subjects",
"subjects": [
"Seu pedido #10482 saiu para entrega",
"Chega em até 3 dias úteis",
"Acompanhe seu pedido em tempo real",
"Boa notícia: seu pedido está a caminho",
"Quer saber onde está seu pedido?"
],
"task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "completed",
"credits": {
"used": 13,
"limit": 100,
"remaining": 87,
"resets_at": "2026-11-01T00:00:00Z"
}
}
#Explicar o diagnóstico
POST/ai/emails/{id}/fix-insights
Explica, em português, como corrigir as verificações que falharam no diagnóstico de entregabilidade de um e-mail enviado. Se o e-mail não tiver problemas, a tarefa termina com 422 validation_error, sem usar créditos.
Parâmetros de caminho
idstringobrigatórioID do e-mail.
Erros
- 404
not_foundO recurso não existe ou é de outro time. - 429
ai_credits_exhaustedOs créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC). - 503
ai_not_configuredO assistente de IA não está disponível.
cURL
curl -X POST https://api.postal.so/ai/emails/019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b/fix-insights \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/ai/emails/019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b/fix-insights', {
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/ai/emails/019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b/fix-insights",
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('ai/emails/019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b/fix-insights', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "pending"
}
{
"object": "ai_insights",
"summary": "Um link sem HTTPS pode fazer o e-mail parecer inseguro para os filtros de spam.",
"fixes": [
{
"code": "https_links",
"title": "Use HTTPS em todos os links",
"steps": [
"Troque http:// por https:// no link do rastreio.",
"Confirme que o site abre com certificado válido."
]
}
],
"task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "completed",
"credits": {
"used": 13,
"limit": 100,
"remaining": 87,
"resets_at": "2026-11-01T00:00:00Z"
}
}
#Consultar uma tarefa
GET/ai/tasks/{id}
Enquanto a IA trabalha, devolve só o status (pending ou processing). Quando termina, devolve a resposta do pedido (os mesmos campos mostrados em cada endpoint acima) com task_id, status: "completed" e credits. Se a tarefa falhou, a resposta é o erro, no formato padrão e com o status HTTP do erro. Consulte a cada 1 ou 2 segundos; a maioria das respostas fica pronta em até um minuto.
Parâmetros de caminho
idstringobrigatórioOiddevolvido peloPOST.
Erros
- 404
not_foundA tarefa não existe, é de outro time ou passou de 24 horas. - 422
ai_refusedA IA recusou o pedido. Reescreva e tente de novo. - 422
validation_errorO pedido não pôde ser atendido (por exemplo, o e-mail não tem problemas para corrigir). - 429
ai_credits_exhaustedOs créditos acabaram antes de a tarefa rodar. - 502
ai_errorA IA não devolveu uma resposta válida ou demorou demais. Tente de novo. - 503
ai_unavailableO serviço de IA está ocupado. Tente de novo em instantes.
cURL
curl https://api.postal.so/ai/tasks/019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b \
-H "Authorization: Bearer $POSTAL_API_KEY"
Node.js
const response = await fetch('https://api.postal.so/ai/tasks/019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b', {
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/ai/tasks/019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
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('ai/tasks/019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b', [
'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "processing"
}
{
"object": "ai_subjects",
"subjects": [
"Seu pedido #10482 saiu para entrega",
"Chega em até 3 dias úteis"
],
"task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "completed",
"credits": {
"used": 13,
"limit": 100,
"remaining": 87,
"resets_at": "2026-11-01T00:00:00Z"
}
}
{
"statusCode": 503,
"name": "ai_unavailable",
"message": "The AI assistant is busy right now. Try again in a moment."
}