IA
O assistente de IA: escrever e-mails, melhorar textos, sugerir assuntos, explicar problemas de entregabilidade e montar automações.
O assistente de IA escreve e melhora e-mails e explica como resolver problemas de entregabilidade. Cada resposta usa 1 crédito do plano (Grátis: 10, Profissional: 100, Escala: 1.000 por mês). Os créditos renovam no dia 1º de cada mês (UTC).
Além de escrever e-mails, a IA monta automações a partir de um pedido em linguagem natural, traduz mensagens das inboxes e, ligada numa inbox, lê cada e-mail novo e prepara rascunhos de resposta. Todos os endpoints exigem uma chave de acesso total.
#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).
| Uso | Quanto custa |
|---|---|
| Escrever, melhorar, assuntos e diagnóstico | 1 crédito por resposta |
| Assistente de automações | 1 crédito por mensagem, mesmo que a IA precise de uma segunda passada para corrigir o próprio rascunho |
| Traduzir uma mensagem e melhorar as instruções do agente da inbox | 1 crédito por resposta |
| Agente da inbox | 1 crédito por e-mail novo que ele lê |
Em toda resposta pronta, credits traz o saldo do mês:
| Campo | O que é |
|---|---|
used | Créditos usados neste mês (UTC). |
limit | Créditos do plano por mês. null: sem limite. 0: o plano não inclui IA, e todo pedido responde 429 ai_credits_exhausted. |
remaining | O que sobra (null sem limite). |
resets_at | Quando os créditos renovam: o dia 1º do mês seguinte, à meia-noite UTC. |
Sem pedir uma resposta, leia o mesmo saldo em GET /usage: ai_credits tem os mesmos campos, e features.ai_assistant diz se o servidor e o plano permitem usar a IA. Os créditos de cada plano estão em GET /plans (limits.ai_credits).
#A IA nas inboxes
Estes recursos de IA ficam na página de Recebidos e inboxes. Usam o mesmo fluxo assíncrono (202 e GET /ai/tasks/{id}) e os mesmos créditos:
POST /inboxes/{id}/threads/{thread_id}/messages/{message_id}/translatetraduz uma mensagem da conversa, recebida ou enviada. O resultado éai_translation.POST /inboxes/{id}/agent/improve-instructionsreescreve as instruções do agente com mais clareza, sem salvar. O resultado éagent_instructions.- O agente da inbox não tem endpoint próprio: ligado em
PATCH /inboxes/{id}, lê cada e-mail novo e usa 1 crédito por e-mail. Ele só cria rascunhos para uma pessoa revisar, nunca envia sozinho.
#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}.
No modo blocks (o padrão), a resposta traz subject, preheader e blocks, uma lista de até 40 blocos sem id (quem usa os atribui): heading (html, level de 1 a 3, align), text e footer (html e align), button (label, url, align), divider e spacer (height em pixels, de 4 a 160). O texto dos blocos só tem <b>, <i>, <u>, <a> e <br>, e as variáveis aparecem como {{{NAME}}}. No modo html, scripts, formulários e manipuladores de evento são removidos do documento.
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"
}
}
{
"object": "ai_email",
"subject": "Seu pedido está a caminho",
"preheader": "Acompanhe a entrega pelo link de rastreio.",
"blocks": [
{
"type": "heading",
"html": "Seu pedido está a caminho",
"level": 1,
"align": "left"
},
{
"type": "text",
"html": "Olá, {{{NAME}}}! O pedido #10482 saiu para entrega e chega em até 3 dias úteis.",
"align": "left"
},
{
"type": "button",
"label": "Rastrear pedido",
"url": "{{{TRACKING_URL}}}",
"align": "left"
},
{
"type": "footer",
"html": "Você recebe este e-mail porque fez um pedido na Loja Exemplo.",
"align": "center"
}
],
"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 como corrigir as verificações que falharam no diagnóstico de entregabilidade de um e-mail enviado. O texto sai no idioma do parâmetro language (pt-BR, en ou es); sem ele, no idioma da requisição: Accept-Language: pt-BR ou en, e em inglês quando a chave de API não envia o cabeçalho. 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.
Corpo da requisição (opcional)
languagestringIdioma do texto:pt-BR,enoues. Padrão: o idioma da requisição (Accept-Language), em inglês sem o cabeçalho.
Erros
- 404
not_foundO recurso não existe ou é de outro time. - 422
validation_errorlanguagefora dept-BR,enees, ou o e-mail não tem verificações com problema. - 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" \
-H "Content-Type: application/json" \
-d '{
"language": "pt-BR"
}'
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}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
language: 'pt-BR',
}),
});
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']}"},
json={
"language": "pt-BR",
},
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')],
'json' => [
'language' => 'pt-BR',
],
]);
$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"
}
}
#Montar uma automação com a IA
POST/ai/automations/{id}/assist
O assistente do construtor de automações: você manda a conversa até agora (terminando com o pedido da pessoa) e o rascunho atual, e ele responde com uma frase e as mudanças no rascunho (gatilho, passos, nome, reentrada), já conferidas pelas mesmas regras de POST /automations. Nada é salvo, ativado ou enviado: para valer, aplique o definition devolvido ao seu editor e salve com PATCH /automations/{id}. A automação do caminho precisa existir (pode ser um rascunho); ela dá o contexto, e o definition do corpo é o que vale. A resposta final chega por GET /ai/tasks/{id}.
O assistente conhece o que o time tem (propriedades, segmentos, tópicos, templates, formulários, etiquetas, campanhas, eventos recentes e domínios de envio). Se o rascunho que ele monta quebra alguma regra, ele faz uma segunda passada para corrigir; as mudanças que continuam inválidas ficam de fora, em skipped. As duas passadas custam 1 crédito.
As frases (reply e os summary) saem em português com Accept-Language: pt-BR; sem o cabeçalho, em inglês.
Parâmetros de caminho
idstringobrigatórioID da automação.
Corpo da requisição
messagesobject[]obrigatórioA conversa, da mais antiga para a mais recente, até 50 mensagens (só as 12 últimas chegam à IA). A última precisa ser do usuário.rolestringobrigatóriouserouassistant.contentstringobrigatórioO texto, até 4.000 caracteres (2.000 na última mensagem, que é o pedido).
definitionobjectobrigatórioO rascunho atual, no formato da automação. Até 64 KB.notesstring[]Até 10 avisos de até 500 caracteres sobre o que a pessoa fez no editor desde a última resposta, como “criei a propriedade plan” ou “desfiz as mudanças”.
| Campo da resposta | O que é |
|---|---|
definition | O rascunho já com as mudanças, normalizado (os passos ganham id e todos os campos de config). Se nada mudou, é o que você mandou. |
changed | true se o definition é diferente do que você mandou. |
operations | As mudanças aplicadas, em ordem: op (set_trigger, add_step, update_step, remove_step, move_step, set_entry_filters, set_reentry ou rename_automation), um summary para mostrar à pessoa e o step_id do passo afetado (ou null). |
pending_actions | Recursos que o fluxo precisa e ainda não existem: create_contact_property, create_segment ou create_topic, cada um com id, summary e payload. A API não cria nada: depois de a pessoa confirmar, crie o recurso em propriedades, segmentos ou tópicos e troque o ID provisório new:<ref> do definition pelo ID criado. |
skipped | Mudanças que a IA propôs e não puderam ser aplicadas, com o motivo em message. |
issues | O que ainda falta ou está errado no definition (cada um com step_id e message, como em GET /automations/{id}). As pendências só impedem a ativação. |
Erros
- 404
not_foundO recurso não existe ou é de outro time. - 422
validation_errormessagesvazio, a última mensagem não é do usuário ou passa de 2.000 caracteres,definitionsemtrigger.typeousteps, ou maior que 64 KB. - 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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/assist \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Accept-Language: pt-BR" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Quando alguém começar o teste grátis, mande as boas-vindas. Se abrir em até 2 dias, ponha num segmento Engajados; se não, mande um lembrete."
}
],
"definition": {
"name": "Boas-vindas do teste grátis",
"trigger": {
"type": "event",
"event": "trial.started"
},
"reentry": "once",
"steps": []
}
}'
Node.js
const response = await fetch('https://api.postal.so/ai/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/assist', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
'Content-Type': 'application/json',
'Accept-Language': 'pt-BR',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: 'Quando alguém começar o teste grátis, mande as boas-vindas. Se abrir em até 2 dias, ponha num segmento Engajados; se não, mande um lembrete.',
},
],
definition: {
name: 'Boas-vindas do teste grátis',
trigger: {
type: 'event',
event: 'trial.started',
},
reentry: 'once',
steps: [],
},
}),
});
const data = await response.json();
Python
import os
import requests
response = requests.post(
"https://api.postal.so/ai/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/assist",
headers={
"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}",
"Accept-Language": "pt-BR",
},
json={
"messages": [
{
"role": "user",
"content": "Quando alguém começar o teste grátis, mande as boas-vindas. Se abrir em até 2 dias, ponha num segmento Engajados; se não, mande um lembrete.",
},
],
"definition": {
"name": "Boas-vindas do teste grátis",
"trigger": {
"type": "event",
"event": "trial.started",
},
"reentry": "once",
"steps": [],
},
},
timeout=10,
)
data = response.json()
PHP
$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);
$response = $client->post('ai/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/assist', [
'headers' => [
'Authorization' => 'Bearer '.getenv('POSTAL_API_KEY'),
'Accept-Language' => 'pt-BR',
],
'json' => [
'messages' => [
[
'role' => 'user',
'content' => 'Quando alguém começar o teste grátis, mande as boas-vindas. Se abrir em até 2 dias, ponha num segmento Engajados; se não, mande um lembrete.',
],
],
'definition' => [
'name' => 'Boas-vindas do teste grátis',
'trigger' => [
'type' => 'event',
'event' => 'trial.started',
],
'reentry' => 'once',
'steps' => [],
],
],
]);
$data = json_decode($response->getBody(), true);
{
"object": "ai_task",
"id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"status": "pending"
}
{
"object": "ai_automation_assist",
"reply": "Montei o fluxo: manda as boas-vindas, espera até 2 dias pela abertura e, se abriu, coloca o contato no segmento Engajados; se não abriu, manda um lembrete. Falta escolher o template do lembrete, e o segmento Engajados ainda precisa ser criado.",
"definition": {
"name": "Boas-vindas do teste grátis",
"trigger": {
"type": "event",
"event": "trial.started",
"filters": []
},
"reentry": "once",
"steps": [
{
"id": "welcome",
"type": "send_email",
"config": {
"template_id": "0199f3a0-1b2c-7d3e-9f4a-5b6c7d8e9f0a",
"from": null,
"subject": null,
"reply_to": null,
"detect_replies": false,
"variables": {}
}
},
{
"id": "wait_open",
"type": "wait_for",
"config": {
"step_id": "welcome",
"activity": "opened",
"link_contains": null,
"timeout": {
"amount": 2,
"unit": "days"
}
},
"branches": {
"yes": [
{
"id": "tag_engaged",
"type": "update_contact",
"config": {
"actions": [
{
"type": "add_to_segment",
"segment_id": "new:engaged"
}
]
}
}
],
"no": [
{
"id": "reminder",
"type": "send_email",
"config": {
"template_id": null,
"from": null,
"subject": null,
"reply_to": null,
"detect_replies": false,
"variables": {}
}
}
]
}
}
]
},
"changed": true,
"operations": [
{
"op": "set_trigger",
"summary": "Gatilho definido: Evento recebido (trial.started).",
"step_id": null
},
{
"op": "add_step",
"summary": "Passo adicionado: Enviar e-mail (Boas-vindas do teste).",
"step_id": "welcome"
},
{
"op": "add_step",
"summary": "Passo adicionado: Esperar até (abriu, no máximo 2 dias).",
"step_id": "wait_open"
},
{
"op": "add_step",
"summary": "Passo adicionado: Atualizar contato.",
"step_id": "tag_engaged"
},
{
"op": "add_step",
"summary": "Passo adicionado: Enviar e-mail.",
"step_id": "reminder"
}
],
"pending_actions": [
{
"id": "action_1",
"type": "create_segment",
"summary": "Criar o segmento “Engajados”.",
"payload": {
"ref": "new:engaged",
"name": "Engajados",
"description": "Abriram o e-mail de boas-vindas"
}
}
],
"skipped": [],
"issues": [
{
"step_id": "reminder",
"message": "Escolha o template a enviar."
}
],
"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."
}