IA
El asistente de IA: escribir correos electrónicos, mejorar textos, sugerir asuntos, explicar problemas de entregabilidad y armar automatizaciones.
El asistente de IA escribe y mejora correos electrónicos y explica cómo resolver problemas de entregabilidad. Cada respuesta usa 1 crédito del plan (Gratis: 10, Profesional: 100, Escala: 1.000 al mes). Los créditos se renuevan el día 1 de cada mes (UTC).
Además de escribir correos electrónicos, la IA arma automatizaciones a partir de una petición en lenguaje natural, traduce mensajes de los buzones de entrada y, activada en un buzón, lee cada correo electrónico nuevo y prepara borradores de respuesta. Todos los endpoints exigen una clave de acceso total.
#Cómo funciona
Generar una respuesta puede tardar desde unos segundos hasta más de un minuto, por eso las solicitudes son asíncronas:
- Haces el
POST(por ejemplo,POST /ai/subjects). Postal valida los campos, comprueba que haya créditos y responde al instante con202 Acceptedy una tarea:{"object": "ai_task", "id": "…", "status": "pending"}. - Consultas
GET /ai/tasks/{id}cada 1 o 2 segundos. Mientras la IA trabaja, elstatusespendingoprocessing. - Cuando está lista, la misma consulta devuelve la respuesta completa, con
status: "completed", eltask_idy el saldo decredits. Si la tarea falla, la consulta devuelve el error, con el status HTTP correspondiente.
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);
}
Las tareas están disponibles durante 24 horas; pasado ese tiempo, la consulta responde 404.
#Créditos
El crédito se cuenta cuando la IA devuelve una respuesta válida. No consumen créditos las solicitudes rechazadas al instante (campos no válidos, sin créditos) ni las tareas que fallan antes de que la IA responda: servicio ocupado (ai_unavailable), solicitud rechazada por la IA (ai_refused), respuesta demasiado larga o fuera del formato (ai_error). El saldo también aparece en GET /usage (ai_credits).
| Uso | Cuánto cuesta |
|---|---|
| Escribir, mejorar, asuntos y diagnóstico | 1 crédito por respuesta |
| Asistente de automatizaciones | 1 crédito por mensaje, aunque la IA necesite una segunda pasada para corregir su propio borrador |
| Traducir un mensaje y mejorar las instrucciones del agente del buzón de entrada | 1 crédito por respuesta |
| Agente del buzón de entrada | 1 crédito por cada correo electrónico nuevo que lee |
En cada respuesta lista, credits trae el saldo del mes:
| Campo | Qué es |
|---|---|
used | Créditos usados este mes (UTC). |
limit | Créditos del plan por mes. null: sin límite. 0: el plan no incluye IA, y toda solicitud responde 429 ai_credits_exhausted. |
remaining | Lo que queda (null si no hay límite). |
resets_at | Cuándo se renuevan los créditos: el día 1 del mes siguiente, a la medianoche UTC. |
Sin pedir una respuesta, lee el mismo saldo en GET /usage: ai_credits tiene los mismos campos, y features.ai_assistant indica si el servidor y el plan permiten usar la IA. Los créditos de cada plan están en GET /plans (limits.ai_credits).
#La IA en los buzones de entrada
Estas funciones de IA están en la página de Recibidos y buzones de entrada. Usan el mismo flujo asíncrono (202 y GET /ai/tasks/{id}) y los mismos créditos:
POST /inboxes/{id}/threads/{thread_id}/messages/{message_id}/translatetraduce un mensaje de la conversación, recibido o enviado. El resultado esai_translation.POST /inboxes/{id}/agent/improve-instructionsreescribe las instrucciones del agente con más claridad, sin guardar. El resultado esagent_instructions.- El agente del buzón de entrada no tiene endpoint propio: activado con
PATCH /inboxes/{id}, lee cada correo electrónico nuevo y usa 1 crédito por correo. Solo crea borradores para que una persona los revise, nunca envía por sí solo.
#Escribir un correo electrónico
POST/ai/write-email
Escribe el asunto, el preencabezado y el cuerpo a partir de una descripción. En el modo blocks, el cuerpo viene en bloques del editor visual (heading, text, button, divider, spacer, footer); en el modo html, viene un documento HTML completo y la versión en texto. La respuesta final (al lado) llega por GET /ai/tasks/{id}.
En el modo blocks (el predeterminado), la respuesta trae subject, preheader y blocks, una lista de hasta 40 bloques sin id (quien los usa se los asigna): heading (html, level de 1 a 3, align), text y footer (html y align), button (label, url, align), divider y spacer (height en píxeles, de 4 a 160). El texto de los bloques solo tiene <b>, <i>, <u>, <a> y <br>, y las variables aparecen como {{{NAME}}}. En el modo html, los scripts, los formularios y los manejadores de eventos se eliminan del documento.
Cuerpo de la solicitud
briefstringobligatorioLo que debe decir el correo electrónico, de 10 a 2.000 caracteres.tonestringprofessional(predeterminado),friendly,formal,casualopersuasive.languagestringpt-BR(predeterminado),enoes.modestringblocks(predeterminado) ohtml.
Errores
- 422
validation_errorCampo ausente o fuera de los límites. - 429
ai_credits_exhaustedSe acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC). - 503
ai_not_configuredEl asistente de IA no está disponible.
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"
}
}
#Mejorar un texto
POST/ai/improve-text
Reescribe un fragmento manteniendo los enlaces y las variables. Acepta formato simple (<b>, <i>, <u>, <a>, <br>).
Cuerpo de la solicitud
htmlstringobligatorioEl texto, hasta 5.000 caracteres.improvementstringobligatorioshorter(más corto),formal,friendlyogrammar(solo corrige gramática y ortografía).
Errores
- 422
validation_errorCampo ausente o fuera de los límites. - 429
ai_credits_exhaustedSe acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC). - 503
ai_not_configuredEl asistente de IA no está disponible.
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 asuntos
POST/ai/subjects
Sugiere hasta 5 asuntos distintos para el contenido.
Cuerpo de la solicitud
contentstringobligatorioEl contenido del correo electrónico, de 10 a 20.000 caracteres.subjectstringEl asunto actual, hasta 200 caracteres, para que la IA lo tenga en cuenta.
Errores
- 422
validation_errorCampo ausente o fuera de los límites. - 429
ai_credits_exhaustedSe acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC). - 503
ai_not_configuredEl asistente de IA no está disponible.
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 el diagnóstico
POST/ai/emails/{id}/fix-insights
Explica cómo corregir las verificaciones que fallaron en el diagnóstico de entregabilidad de un correo electrónico enviado. El texto sale en el idioma del parámetro language (pt-BR, en o es); sin él, en el idioma de la solicitud: Accept-Language: pt-BR o en, y en inglés cuando la clave de API no envía el encabezado. Si el correo electrónico no tiene problemas, la tarea termina con 422 validation_error, sin usar créditos.
Parámetros de ruta
idstringobligatorioID del correo.
Cuerpo de la solicitud (opcional)
languagestringIdioma del texto:pt-BR,enoes. Predeterminado: el idioma de la solicitud (Accept-Language), en inglés sin el encabezado.
Errores
- 404
not_foundEl recurso no existe o es de otro equipo. - 422
validation_errorlanguagedistinto dept-BR,enyes, o el correo electrónico no tiene verificaciones con problemas. - 429
ai_credits_exhaustedSe acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC). - 503
ai_not_configuredEl asistente de IA no está disponible.
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"
}
}
#Armar una automatización con la IA
POST/ai/automations/{id}/assist
El asistente del constructor de automatizaciones: envías la conversación hasta ahora (terminando con la petición de la persona) y el borrador actual, y responde con una frase y los cambios en el borrador (disparador, pasos, nombre, reentrada), ya comprobados con las mismas reglas de POST /automations. No se guarda, activa ni envía nada: para que valga, aplica el definition devuelto en tu editor y guarda con PATCH /automations/{id}. La automatización de la ruta debe existir (puede ser un borrador); da el contexto, y el definition del cuerpo es el que vale. La respuesta final llega por GET /ai/tasks/{id}.
El asistente conoce lo que tiene el equipo (propiedades, segmentos, temas, plantillas, formularios, etiquetas, campañas, eventos recientes y dominios de envío). Si el borrador que arma rompe alguna regla, hace una segunda pasada para corregirlo; los cambios que siguen siendo no válidos se dejan fuera, en skipped. Las dos pasadas cuestan 1 crédito.
Las frases (reply y los summary) salen en portugués con Accept-Language: pt-BR; sin el encabezado, en inglés.
Parámetros de ruta
idstringobligatorioID de la automatización.
Cuerpo de la solicitud
messagesobject[]obligatorioLa conversación, de la más antigua a la más reciente, hasta 50 mensajes (solo los 12 últimos llegan a la IA). El último debe ser del usuario.rolestringobligatoriouseroassistant.contentstringobligatorioEl texto, hasta 4.000 caracteres (2.000 en el último mensaje, que es la petición).
definitionobjectobligatorioEl borrador actual, en el formato de la automatización. Hasta 64 KB.namestring | nullNombre, hasta 255 caracteres.triggerobjectobligatorioEl disparador, contype.reentrystringonceoevery_time.stepsobject[]obligatorioLos pasos. Puede ser una lista vacía, pero debe enviarse.
notesstring[]Hasta 10 avisos de hasta 500 caracteres sobre lo que la persona hizo en el editor desde la última respuesta, como “creé la propiedad plan” o “deshice los cambios”.
| Campo de la respuesta | Qué es |
|---|---|
definition | El borrador ya con los cambios, normalizado (los pasos reciben id y todos los campos de config). Si nada cambió, es lo que enviaste. |
changed | true si el definition es distinto de lo que enviaste. |
operations | Los cambios aplicados, en orden: op (set_trigger, add_step, update_step, remove_step, move_step, set_entry_filters, set_reentry o rename_automation), un summary para mostrar a la persona y el step_id del paso afectado (o null). |
pending_actions | Recursos que el flujo necesita y que aún no existen: create_contact_property, create_segment o create_topic, cada uno con id, summary y payload. La API no crea nada: después de que la persona confirme, crea el recurso en propiedades, segmentos o temas y cambia el ID provisional new:<ref> del definition por el ID creado. |
skipped | Cambios que la IA propuso y que no pudieron aplicarse, con el motivo en message. |
issues | Lo que aún falta o está mal en el definition (cada uno con step_id y message, como en GET /automations/{id}). Los pendientes solo impiden la activación. |
Errores
- 404
not_foundEl recurso no existe o es de otro equipo. - 422
validation_errormessagesvacío, el último mensaje no es del usuario o supera los 2.000 caracteres,definitionsintrigger.typeosteps, o mayor de 64 KB. - 429
ai_credits_exhaustedSe acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC). - 503
ai_not_configuredEl asistente de IA no está disponible.
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 una tarea
GET/ai/tasks/{id}
Mientras la IA trabaja, devuelve solo el status (pending o processing). Cuando termina, devuelve la respuesta de la solicitud (los mismos campos mostrados en cada endpoint anterior) con task_id, status: "completed" y credits. Si la tarea falló, la respuesta es el error, en el formato estándar y con el status HTTP del error. Consulta cada 1 o 2 segundos; la mayoría de las respuestas están listas en hasta un minuto.
Parámetros de ruta
idstringobligatorioEliddevuelto por elPOST.
Errores
- 404
not_foundLa tarea no existe, es de otro equipo o pasaron más de 24 horas. - 422
ai_refusedLa IA rechazó la solicitud. Reescríbela e inténtalo de nuevo. - 422
validation_errorNo se pudo atender la solicitud (por ejemplo, el correo electrónico no tiene problemas que corregir). - 429
ai_credits_exhaustedLos créditos se acabaron antes de que la tarea se ejecutara. - 502
ai_errorLa IA no devolvió una respuesta válida o tardó demasiado. Inténtalo de nuevo. - 503
ai_unavailableEl servicio de IA está ocupado. Inténtalo de nuevo en unos 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."
}