Entrar Criar conta
Referência da API

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:

  1. Você faz o POST (por exemplo, POST /ai/subjects). A Postal valida os campos, confere se há créditos e responde na hora com 202 Accepted e uma tarefa: {"object": "ai_task", "id": "…", "status": "pending"}.
  2. Você consulta GET /ai/tasks/{id} a cada 1 ou 2 segundos. Enquanto a IA trabalha, o status é pending ou processing.
  3. Quando fica pronta, a mesma consulta devolve a resposta completa, com status: "completed", o task_id e o saldo de credits. Se a tarefa falhar, a consulta devolve o erro, com o status HTTP correspondente.
Pedido e consulta

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).

UsoQuanto custa
Escrever, melhorar, assuntos e diagnóstico1 crédito por resposta
Assistente de automações1 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 inbox1 crédito por resposta
Agente da inbox1 crédito por e-mail novo que ele lê

Em toda resposta pronta, credits traz o saldo do mês:

CampoO que é
usedCréditos usados neste mês (UTC).
limitCréditos do plano por mês. null: sem limite. 0: o plano não inclui IA, e todo pedido responde 429 ai_credits_exhausted.
remainingO que sobra (null sem limite).
resets_atQuando 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:

#Escrever um e-mail

POST/ai/write-email

Chave de acesso total

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ório
    O que o e-mail deve dizer, de 10 a 2.000 caracteres.
  • tonestring
    professional (padrão), friendly, formal, casual ou persuasive.
  • languagestring
    pt-BR (padrão), en ou es.
  • modestring
    blocks (padrão) ou html.

Erros

  • 422validation_error
    Campo ausente ou fora dos limites.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /ai/write-email

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);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "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"
  }
}
200Resposta final no modo blocks
{
  "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

Chave de acesso total

Reescreve um trecho mantendo links e variáveis. Aceita formatação simples (<b>, <i>, <u>, <a>, <br>).

Corpo da requisição

  • htmlstringobrigatório
    O texto, até 5.000 caracteres.
  • improvementstringobrigatório
    shorter (mais curto), formal, friendly ou grammar (só corrige gramática e ortografia).

Erros

  • 422validation_error
    Campo ausente ou fora dos limites.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /ai/improve-text

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);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "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

Chave de acesso total

Sugere até 5 assuntos diferentes para o conteúdo.

Corpo da requisição

  • contentstringobrigatório
    O conteúdo do e-mail, de 10 a 20.000 caracteres.
  • subjectstring
    O assunto atual, até 200 caracteres, para a IA levar em conta.

Erros

  • 422validation_error
    Campo ausente ou fora dos limites.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /ai/subjects

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);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "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

Chave de acesso total

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ório
    ID do e-mail.

Corpo da requisição (opcional)

  • languagestring
    Idioma do texto: pt-BR, en ou es. Padrão: o idioma da requisição (Accept-Language), em inglês sem o cabeçalho.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
  • 422validation_error
    language fora de pt-BR, en e es, ou o e-mail não tem verificações com problema.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /ai/emails/{id}/fix-insights

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);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "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

Chave de acesso total

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ório
    ID da automação.

Corpo da requisição

  • messagesobject[]obrigatório
    A 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ório
      user ou assistant.
    • contentstringobrigatório
      O texto, até 4.000 caracteres (2.000 na última mensagem, que é o pedido).
  • definitionobjectobrigatório
    O rascunho atual, no formato da automação. Até 64 KB.
    • namestring | null
      Nome, até 255 caracteres.
    • triggerobjectobrigatório
      O gatilho, com type.
    • reentrystring
      once ou every_time.
    • stepsobject[]obrigatório
      Os passos. Pode ser uma lista vazia, mas precisa ser enviada.
  • 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 respostaO que é
definitionO rascunho já com as mudanças, normalizado (os passos ganham id e todos os campos de config). Se nada mudou, é o que você mandou.
changedtrue se o definition é diferente do que você mandou.
operationsAs 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_actionsRecursos 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.
skippedMudanças que a IA propôs e não puderam ser aplicadas, com o motivo em message.
issuesO 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

  • 404not_found
    O recurso não existe ou é de outro time.
  • 422validation_error
    messages vazio, a última mensagem não é do usuário ou passa de 2.000 caracteres, definition sem trigger.type ou steps, ou maior que 64 KB.
  • 429ai_credits_exhausted
    Os créditos de IA do mês acabaram. Eles renovam no dia 1º (UTC).
  • 503ai_not_configured
    O assistente de IA não está disponível.
POST /ai/automations/{id}/assist

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);
202Resposta (tarefa criada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Resposta final em GET /ai/tasks/{id}
{
  "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}

Chave de acesso total

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ório
    O id devolvido pelo POST.

Erros

  • 404not_found
    A tarefa não existe, é de outro time ou passou de 24 horas.
  • 422ai_refused
    A IA recusou o pedido. Reescreva e tente de novo.
  • 422validation_error
    O pedido não pôde ser atendido (por exemplo, o e-mail não tem problemas para corrigir).
  • 429ai_credits_exhausted
    Os créditos acabaram antes de a tarefa rodar.
  • 502ai_error
    A IA não devolveu uma resposta válida ou demorou demais. Tente de novo.
  • 503ai_unavailable
    O serviço de IA está ocupado. Tente de novo em instantes.
GET /ai/tasks/{id}

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);
200Resposta (ainda processando)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "processing"
}
200Resposta (pronta)
{
  "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"
  }
}
503Resposta (tarefa falhou)
{
  "statusCode": 503,
  "name": "ai_unavailable",
  "message": "The AI assistant is busy right now. Try again in a moment."
}