Entrar Criar conta
Marketing

Automações

Sequências de e-mails e ações disparadas por eventos, contatos novos, segmentos, formulários, etiquetas, aberturas e cliques, com testes A/B, versões, agendamento e o registro de cada execução.

Automações enviam e-mails e cuidam dos contatos sozinhas, a partir de um gatilho: um evento do seu produto, um contato novo, a entrada num segmento ou a saída dele, uma inscrição num tópico ou por formulário. Cada contato que dispara o gatilho ganha uma execução, que percorre os passos (esperar, enviar e-mail, verificar uma condição, mudar o contato, chamar um webhook) e registra o que aconteceu em cada um. Todos os endpoints exigem uma chave de acesso total.

Os gatilhos incluem ainda uma etiqueta que o contato recebe, a abertura ou o clique num e-mail e a mudança de um campo ou de uma propriedade do contato; entre os passos há esperar até o contato abrir, clicar ou responder um e-mail (wait_for) e dividir os contatos entre variantes num teste A/B (experiment). Uma automação pode ser ativada e pausada em horários agendados.

#Rascunho, ativa e pausada

StatusComo funciona
draftCriada assim. Pode ser salva incompleta (um e-mail sem template, uma condição sem caminhos); o que falta aparece em issues. Não inicia execuções.
activeDepois de ativar: a definição precisa estar completa e vira uma versão (version). Cada gatilho inicia uma execução.
pausedDepois de pausar: não inicia execuções, e as que estão esperando ficam paradas até a automação ser ativada de novo.

Versões. Editar uma automação ativa publica uma nova versão na hora (se o gatilho, os passos ou reentry mudaram). Só as novas execuções seguem a versão nova: as que já estão em andamento terminam na versão em que começaram (version da execução). Mudar só o nome não cria versão.

Reentrada. Com reentry: "once" (padrão), cada contato passa pela automação uma vez só. Com "every_time", cada gatilho inicia uma nova execução, mesmo que outra esteja em andamento.

Limite do plano. As execuções iniciadas no mês (em UTC), somando todas as automações, têm limite por plano (veja Limites e automation_runs em GET /usage). Passou do limite, a execução fica registrada como skipped, com o motivo, e não conta no limite.

Agendamento. POST /automations/{id}/schedule marca o horário em que a automação é ativada (activate_at) e/ou pausada (deactivate_at), por exemplo para uma promoção com começo e fim. Os horários aparecem na automação e são aplicados a cada minuto; depois de aplicado, o campo volta a null. Uma automação agendada para ativar precisa estar completa, como uma ativa.

Limites do plano. Além das execuções por mês, o plano pode limitar quantas automações o time tem (automations, que só vale na criação: criar além do limite responde 403 plan_limit_reached) e se inclui testes A/B (automation_experiments). Os números de cada plano estão em GET /plans. Quando o plano diminui e passa a ter menos automações do que o time tem, as mais novas acima do limite ficam com paused_by_plan: true: continuam listadas e legíveis, mas não iniciam execuções e editar, ativar, agendar ou declarar uma vencedora responde 403 paused_by_plan até o time voltar ao limite (fazendo upgrade ou apagando outras).

#Gatilho e passos

Uma automação é um trigger e uma lista de steps. Os passos rodam em ordem; uma condition fecha a lista e continua num dos caminhos (branches.yes ou branches.no), cada um com a própria lista; um experiment continua no caminho da variante sorteada (branches.a, branches.b… ou branches.control). O fim de uma lista termina a execução. Uma automação pode ter até 100 passos no total. Um wait_for também fecha a lista e continua em branches.yes ou branches.no.

Gatilhos

typeCamposQuando iniciaExemplo
eventevent: nome do evento. filters: até 10 comparações com as propriedades do evento, todas precisam bater.Um evento com esse nome chega com um contato.{"type": "event", "event": "order.paid", "filters": [{"key": "total", "operator": "gte", "value": 500}]}
contact.created—Um contato é criado pela API, por um formulário ou por um evento com e-mail novo. Contatos de importação de CSV só disparam quando a importação pede (trigger_automations, veja Importar um CSV).{"type": "contact.created"}
segment.addedsegment_idUm contato entra no segmento: pela API, por um formulário ou por um passo update_contact, ou, num segmento dinâmico, quando passa a bater com os filtros (uma vez por entrada, também avisada pelo webhook contact.segment_entered). Salvar um segmento dinâmico não dispara nada para quem já bate com ele. Importações só disparam com trigger_automations.{"type": "segment.added", "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a"}
segment.removedsegment_idUm contato sai do segmento: removido de um segmento estático (pela API, pelo painel ou por um passo update_contact) ou deixa de atender aos filtros de um segmento dinâmico quando ele é recalculado. Cada saída dispara uma vez (e o webhook contact.segment_exited avisa). Importações não disparam.{"type": "segment.removed", "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a"}
topic.subscribedtopic_idA escolha do contato para o tópico passa a ser opt_in (antes não havia escolha ou era opt_out): na página de preferências, num formulário, pela API ou por uma automação. Importações e a própria automação que fez a mudança não disparam.{"type": "topic.subscribed", "topic_id": "019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"}
topic.unsubscribedtopic_idO contrário: a escolha passa a ser opt_out.{"type": "topic.unsubscribed", "topic_id": "019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"}
form.submittedform_idAlguém se inscreve pelo formulário (com double opt-in, depois de confirmar).{"type": "form.submitted", "form_id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d"}
tag.addedtag_id (ou tag_name, para uma etiqueta que ainda não existe: ela é criada ao salvar)O contato recebe a etiqueta que ele ainda não tinha: pela API, pelo painel ou por um passo update_contact. Importações só disparam com trigger_automations. Veja Contatos.{"type": "tag.added", "tag_name": "cliente-vip"}
email.openedFiltros opcionais: template_id, broadcast_id e automation_id; todos os informados precisam bater com o e-mail.Um contato abre um e-mail. Um clique também conta como abertura, já que as aberturas não são medidas quando as imagens estão bloqueadas. O contato é o do e-mail de uma campanha ou automação ou, nos outros, o que tem o endereço do primeiro destinatário. Cada automação inicia no máximo uma execução para o mesmo contato e e-mail.{"type": "email.opened", "broadcast_id": "019a1f00-2c3d-7e4f-8a5b-6c7d8e9f0a1b"}
email.clickedOs mesmos filtros de email.opened, mais link_contains: um trecho do link clicado (sem diferenciar maiúsculas, até 500 caracteres).Um contato clica num link de um e-mail. Um clique inicia as automações de email.clicked e as de email.opened.{"type": "email.clicked", "link_contains": "/checkout"}
contact.property_changedproperty: first_name, last_name, email, unsubscribed, tags ou a chave de uma propriedade de contato. Opcionais: from (valor de antes) e to (valor de depois); vazio vale para qualquer valor.O campo ou a propriedade muda, por qualquer caminho (API, painel, formulário, evento com contact_properties ou outra automação). Um contato novo conta como mudança de vazio para cada valor com que nasceu. Em tags, to é a etiqueta adicionada e from, a removida. Textos são comparados sem diferenciar maiúsculas e números como números. A automação que fez a mudança, num passo update_contact, não se inicia de novo; importações nunca disparam.{"type": "contact.property_changed", "property": "plan", "from": "free", "to": "pro"}

Passos

Cada passo é {"id", "type", "config"}. O id (letras, números, _ e -, até 64 caracteres, único na automação) identifica o passo nas condições e no registro das execuções; sem ele, a Postal gera um como step_k3x9p2qa7m. Chaves desconhecidas são descartadas.

typeO que fazconfig
waitEspera um tempo ou até um horário.{"mode": "duration", "amount": 2, "unit": "days"} (unidades: minutes, hours, days, weeks; até 365 dias) ou {"mode": "until", "time": "09:00", "weekdays": [1, 2, 3, 4, 5], "timezone": "America/Sao_Paulo"}: o próximo 09:00, só nos dias da semana listados (1 = segunda, 7 = domingo; vazio = qualquer dia). Fuso padrão: America/Sao_Paulo.
wait_forEspera o contato abrir, clicar ou responder um e-mail enviado antes, até um prazo.{"step_id": "welcome", "activity": "opened", "timeout": {"amount": 2, "unit": "days"}}. Segue por branches.yes assim que acontece ou por branches.no quando o prazo passa. Fica sempre no fim da sua lista. Veja Esperar uma ação no e-mail.
send_emailEnvia um template publicado para o contato.template_id (ID ou alias, obrigatório), from (padrão: o do template), subject (no lugar do assunto do template), reply_to e variables. Veja Variáveis. Com detect_replies: true, a Postal detecta as respostas ao e-mail, o que wait_for e as condições de resposta precisam (veja Detectar respostas).
conditionSegue pelo caminho yes ou no.Fica sempre no fim da sua lista; os passos seguintes vão em branches.yes e branches.no. Veja Condições.
update_contactMuda o contato.actions, até 20: {"type": "set_property", "key": "plan", "value": "pro"} (first_name, last_name ou uma propriedade; o valor aceita {{event.*}}), add_to_segment e remove_from_segment (com segment_id), subscribe_topic e unsubscribe_topic (com topic_id), unsubscribe e resubscribe. Também add_tag e remove_tag (com tag_id ou, para uma etiqueta que ainda não existe, tag_name: ela é criada ao salvar). Segmentos dinâmicos não aceitam add_to_segment nem remove_from_segment: só os estáticos.
webhookEnvia o contato e o evento para uma URL sua.{"url": "https://…"}. Veja Passo de webhook.
experimentDivide os contatos entre variantes (teste A/B).De 2 a 4 variants, cada uma um caminho em branches, e um grupo de controle opcional. Fica sempre no fim da sua lista. Veja Experimentos.
exitTermina a execução.Sem configuração; fica no fim da sua lista. O fim de uma lista já termina a execução, então use exit só para deixar claro.

Condições

kindCamposExemplo
contact_propertykey (email, first_name, last_name ou uma propriedade), operator, value{"kind": "contact_property", "key": "plan", "operator": "equals", "value": "pro"}
event_propertykey (caminho na propriedade do evento que iniciou a execução, como items.0.name), operator, value{"kind": "event_property", "key": "total", "operator": "gt", "value": 200}
segmentsegment_id, operator: in ou not_in{"kind": "segment", "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a", "operator": "in"}
topictopic_id, operator: subscribed ou not_subscribed{"kind": "topic", "topic_id": "019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b", "operator": "subscribed"}
tagtag_id (ou tag_name), operator: has ou not_has{"kind": "tag", "tag_id": "019a1d85-6c7d-7e8f-9a0b-1c2d3e4f5a6b", "operator": "has"}
email_activitystep_id de um send_email anterior no mesmo caminho, activity: opened ou clicked. Um clique conta como abertura. Também aceita clicked_link (com link_contains, um trecho do link clicado) e replied (o e-mail precisa ter detect_replies). Uma resposta que não seja automática também conta como abertura.{"kind": "email_activity", "step_id": "welcome", "activity": "opened"}

Operadores de comparação (nas condições de propriedade e nos filtros do gatilho): equals, not_equals, contains, not_contains, starts_with, ends_with, gt, gte, lt, lte, is_set e is_not_set (esses dois sem value). Textos são comparados sem diferenciar maiúsculas; números, como números; datas no formato AAAA-MM-DD, em ordem de calendário.

Esperar uma ação no e-mail

O passo wait_for segura a execução até o contato abrir, clicar ou responder a um e-mail enviado por um send_email anterior, no mesmo caminho. Se a ação acontece, a execução segue na hora por branches.yes; se o timeout passa antes, segue por branches.no. É melhor que uma espera seguida de uma condição de atividade: reage assim que a pessoa age, em vez de olhar só no fim do prazo.

Passo wait_for
{
  "id": "wait_open",
  "type": "wait_for",
  "config": {
    "step_id": "welcome",
    "activity": "opened",
    "timeout": {
      "amount": 2,
      "unit": "days"
    }
  },
  "branches": {
    "yes": [
      {
        "id": "tag_engaged",
        "type": "update_contact",
        "config": {
          "actions": [
            {
              "type": "add_to_segment",
              "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
            }
          ]
        }
      }
    ],
    "no": [
      {
        "id": "reminder",
        "type": "send_email",
        "config": {
          "template_id": "trial-help",
          "subject": "{{contact.first_name}}, posso ajudar com o seu teste?"
        }
      }
    ]
  }
}
configO que é
step_idO id de um send_email anterior no mesmo caminho. Obrigatório.
activityopened (padrão; um clique ou uma resposta também contam), clicked, clicked_link ou replied.
link_containsSó com clicked_link: um trecho do link clicado, sem diferenciar maiúsculas, até 500 caracteres.
timeout{"amount": 2, "unit": "days"}: quanto esperar, no máximo (minutes, hours, days ou weeks; até 365 dias). Obrigatório.
  • replied exige detect_replies: true no send_email; sem isso a automação não pode ser ativada. Respostas automáticas (fora do escritório) não contam.
  • Se o e-mail do step_id não saiu nesta execução (por exemplo, o contato está descadastrado e o envio foi pulado), a execução segue por branches.no.
  • No registro da execução, o output diz como terminou: resolved_by é event (com resolved_at), timeout ou no_email.

Variáveis nos e-mails

O send_email usa a versão publicada do template, como um POST /emails com template: mesmas validações, cota de envio e lista de supressão. As variáveis do template ({{{NAME}}}) são preenchidas sozinhas com:

  • EMAIL, FIRST_NAME, LAST_NAME e CONTACT_ID do contato;
  • cada propriedade do contato e cada propriedade de primeiro nível do evento, pela chave e em maiúsculas (plan e PLAN);
  • POSTAL_UNSUBSCRIBE_URL, o link da página de preferências do contato;
  • e as variables do passo, que aceitam referências como {{contact.first_name}}, {{contact.plan}} e {{event.items.0.name}} (valores ausentes ficam vazios).

O subject do passo e os valores de set_property aceitam as mesmas referências {{contact.*}} e {{event.*}}. Os e-mails levam os cabeçalhos List-Unsubscribe com clique único; inclua {{{POSTAL_UNSUBSCRIBE_URL}}} no template para mostrar o link no corpo. Contatos descadastrados (unsubscribed) não recebem: o passo fica skipped e a execução continua. Se o envio falhar (cota esgotada, domínio sem verificação, variável sem valor), o passo e a execução ficam failed.

#Experimentos (teste A/B)

O passo experiment divide os contatos que chegam a ele entre 2 e 4 variantes, pela porcentagem de cada uma, para comparar assuntos, conteúdos ou esperas. Cada variante é um caminho em branches, com a chave da variante (letras minúsculas, números e _, como a e b). Com control_percent acima de 0, essa parte dos contatos vai para o caminho control, que pode ficar vazio: esses contatos pulam as variantes e terminam ali. As porcentagens das variantes mais a do controle somam 100. As chaves começam com letra, têm até 32 caracteres e não podem ser control; uma variante sem chave ganha a próxima letra livre (a, b…).

Passo experiment
{
  "id": "subject_test",
  "type": "experiment",
  "config": {
    "variants": [
      {
        "key": "a",
        "name": "Assunto curto",
        "percent": 45
      },
      {
        "key": "b",
        "name": "Assunto com o nome",
        "percent": 45
      }
    ],
    "control_percent": 10,
    "goal_event": "order.paid"
  },
  "branches": {
    "a": [
      {
        "id": "email_short",
        "type": "send_email",
        "config": {
          "template_id": "trial-welcome",
          "subject": "Seu teste começou"
        }
      }
    ],
    "b": [
      {
        "id": "email_named",
        "type": "send_email",
        "config": {
          "template_id": "trial-welcome",
          "subject": "{{contact.first_name}}, seu teste começou"
        }
      }
    ],
    "control": []
  }
}
  • Sempre a mesma variante. A variante sai de um sorteio determinístico pela execução e pelo passo, e fica gravada: a execução sempre segue o mesmo caminho, e cada execução aparece com {"variant", "reason"} no registro do passo.
  • Conversão. Com goal_event, um evento com esse nome enviado pelo contato depois de entrar na variante conta como conversão.
  • Resultados. GET /automations/{id}/experiments traz, por variante, quantos contatos entraram, os e-mails enviados depois da divisão e quantos desses contatos abriram, clicaram, responderam (respostas automáticas não contam) e converteram.
  • Vencedora. POST /automations/{id}/experiments/{step_id}/winner manda todos os próximos contatos para a variante escolhida, em qualquer versão da automação, e registra quem declarou e quando. Quem já passou continua na variante que recebeu.
  • Mudar a divisão. As variantes e as porcentagens de um experimento só mudam com a automação pausada (numa ativa, a resposta é 422). Nomes e os passos dentro das variantes podem mudar a qualquer momento.
  • Plano. Experimentos dependem do recurso automation_experiments do plano. Sem ele, criar, salvar, ativar ou agendar uma automação com experimento responde 403 plan_limit_reached. Se o time perde o recurso com a automação ativa, ela continua rodando e todos os contatos vão para a primeira variante. O detalhe da automação traz experiments_included.

#Listar automações

GET/automations

Chave de acesso total

Cada automação com a definição, a versão em vigor (null num rascunho nunca ativado), runs_this_month (execuções iniciadas no mês) e runs_in_progress.

Parâmetros de consulta

  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    Cursor da próxima página.
  • beforestring
    Cursor da página anterior.
  • searchstring
    Busca no nome.
  • statusstring
    draft, active ou paused.
  • scheduledboolean
    Com true, só as automações com uma ativação ou uma pausa agendada.
GET /automations

cURL

curl 'https://api.postal.so/automations?status=active' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations?status=active', {
  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/automations",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "status": "active",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('automations', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'status' => 'active',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "automation",
      "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
      "paused_by_plan": false,
      "name": "Boas-vindas do teste grátis",
      "status": "active",
      "trigger": {
        "type": "event",
        "event": "trial.started",
        "filters": [
          {
            "key": "plan",
            "operator": "equals",
            "value": "pro"
          }
        ]
      },
      "reentry": "once",
      "steps": [
        {
          "id": "welcome",
          "type": "send_email",
          "config": {
            "template_id": "trial-welcome",
            "from": "Ana da Loja Exemplo <[email protected]>",
            "subject": null,
            "reply_to": null,
            "variables": {
              "DAYS": "{{event.trial_days}}"
            }
          }
        },
        {
          "id": "wait_2_days",
          "type": "wait",
          "config": {
            "mode": "duration",
            "amount": 2,
            "unit": "days"
          }
        },
        {
          "id": "check_opened",
          "type": "condition",
          "config": {
            "kind": "email_activity",
            "step_id": "welcome",
            "activity": "opened"
          },
          "branches": {
            "yes": [
              {
                "id": "tag_engaged",
                "type": "update_contact",
                "config": {
                  "actions": [
                    {
                      "type": "add_to_segment",
                      "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                    }
                  ]
                }
              }
            ],
            "no": [
              {
                "id": "wait_morning",
                "type": "wait",
                "config": {
                  "mode": "until",
                  "time": "09:00",
                  "weekdays": [1, 2, 3, 4, 5],
                  "timezone": "America/Sao_Paulo"
                }
              },
              {
                "id": "reminder",
                "type": "send_email",
                "config": {
                  "template_id": "trial-help",
                  "from": null,
                  "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
                  "reply_to": null,
                  "variables": {}
                }
              },
              {
                "id": "notify_crm",
                "type": "webhook",
                "config": {
                  "url": "https://crm.lojaexemplo.com.br/hooks/postal"
                }
              }
            ]
          }
        }
      ],
      "version": 2,
      "runs_this_month": 318,
      "runs_in_progress": 41,
      "activated_at": "2026-10-15T13:00:00.000000Z",
      "activate_at": null,
      "deactivate_at": null,
      "created_at": "2026-10-14T19:22:00.000000Z",
      "updated_at": "2026-10-20T10:31:00.000000Z"
    }
  ]
}

#Criar uma automação

POST/automations

Chave de acesso total

Cria um rascunho. Erros de estrutura recusam o salvamento (um tipo de passo desconhecido, um passo depois de uma condição, um segmento de outro time, um ID repetido); o que está só faltando entra em issues e impede apenas a ativação. Devolve a automação completa, com issues e o webhook_signing_secret usado nos passos de webhook.

Corpo da requisição

  • namestringobrigatório
    Nome, até 255 caracteres.
  • triggerobjectobrigatório
    O gatilho, com type.
  • stepsobject[]
    Os passos. Pode ficar vazio num rascunho.
  • reentrystring
    once (padrão) ou every_time.

Erros

  • 422validation_error
    Campo inválido ou definição com erro. O campo extra errors lista cada problema, com o step_id (ou null, para o gatilho) e a mensagem.
  • 403plan_limit_reached
    A definição tem um experimento e o plano não inclui testes A/B.
  • 403plan_limit_reached
    O plano não permite mais automações (limite automations).
POST /automations

cURL

curl -X POST https://api.postal.so/automations \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Boas-vindas do teste grátis",
    "trigger": {
      "type": "event",
      "event": "trial.started",
      "filters": [
        {
          "key": "plan",
          "operator": "equals",
          "value": "pro"
        }
      ]
    },
    "steps": [
      {
        "id": "welcome",
        "type": "send_email",
        "config": {
          "template_id": "trial-welcome",
          "from": "Ana da Loja Exemplo <[email protected]>",
          "variables": {
            "DAYS": "{{event.trial_days}}"
          }
        }
      },
      {
        "id": "wait_2_days",
        "type": "wait",
        "config": {
          "mode": "duration",
          "amount": 2,
          "unit": "days"
        }
      },
      {
        "id": "check_opened",
        "type": "condition",
        "config": {
          "kind": "email_activity",
          "step_id": "welcome",
          "activity": "opened"
        },
        "branches": {
          "yes": [
            {
              "id": "tag_engaged",
              "type": "update_contact",
              "config": {
                "actions": [
                  {
                    "type": "add_to_segment",
                    "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                  }
                ]
              }
            }
          ],
          "no": [
            {
              "id": "wait_morning",
              "type": "wait",
              "config": {
                "mode": "until",
                "time": "09:00",
                "weekdays": [1, 2, 3, 4, 5],
                "timezone": "America/Sao_Paulo"
              }
            },
            {
              "id": "reminder",
              "type": "send_email",
              "config": {
                "template_id": "trial-help",
                "subject": "{{contact.first_name}}, posso ajudar com o seu teste?"
              }
            },
            {
              "id": "notify_crm",
              "type": "webhook",
              "config": {
                "url": "https://crm.lojaexemplo.com.br/hooks/postal"
              }
            }
          ]
        }
      }
    ]
  }'

Node.js

const response = await fetch('https://api.postal.so/automations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Boas-vindas do teste grátis',
    trigger: {
      type: 'event',
      event: 'trial.started',
      filters: [
        {
          key: 'plan',
          operator: 'equals',
          value: 'pro',
        },
      ],
    },
    steps: [
      {
        id: 'welcome',
        type: 'send_email',
        config: {
          template_id: 'trial-welcome',
          from: 'Ana da Loja Exemplo <[email protected]>',
          variables: {
            DAYS: '{{event.trial_days}}',
          },
        },
      },
      {
        id: 'wait_2_days',
        type: 'wait',
        config: {
          mode: 'duration',
          amount: 2,
          unit: 'days',
        },
      },
      {
        id: 'check_opened',
        type: 'condition',
        config: {
          kind: 'email_activity',
          step_id: 'welcome',
          activity: 'opened',
        },
        branches: {
          yes: [
            {
              id: 'tag_engaged',
              type: 'update_contact',
              config: {
                actions: [
                  {
                    type: 'add_to_segment',
                    segment_id: '019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c',
                  },
                ],
              },
            },
          ],
          no: [
            {
              id: 'wait_morning',
              type: 'wait',
              config: {
                mode: 'until',
                time: '09:00',
                weekdays: [1, 2, 3, 4, 5],
                timezone: 'America/Sao_Paulo',
              },
            },
            {
              id: 'reminder',
              type: 'send_email',
              config: {
                template_id: 'trial-help',
                subject: '{{contact.first_name}}, posso ajudar com o seu teste?',
              },
            },
            {
              id: 'notify_crm',
              type: 'webhook',
              config: {
                url: 'https://crm.lojaexemplo.com.br/hooks/postal',
              },
            },
          ],
        },
      },
    ],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/automations",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Boas-vindas do teste grátis",
        "trigger": {
            "type": "event",
            "event": "trial.started",
            "filters": [
                {
                    "key": "plan",
                    "operator": "equals",
                    "value": "pro",
                },
            ],
        },
        "steps": [
            {
                "id": "welcome",
                "type": "send_email",
                "config": {
                    "template_id": "trial-welcome",
                    "from": "Ana da Loja Exemplo <[email protected]>",
                    "variables": {
                        "DAYS": "{{event.trial_days}}",
                    },
                },
            },
            {
                "id": "wait_2_days",
                "type": "wait",
                "config": {
                    "mode": "duration",
                    "amount": 2,
                    "unit": "days",
                },
            },
            {
                "id": "check_opened",
                "type": "condition",
                "config": {
                    "kind": "email_activity",
                    "step_id": "welcome",
                    "activity": "opened",
                },
                "branches": {
                    "yes": [
                        {
                            "id": "tag_engaged",
                            "type": "update_contact",
                            "config": {
                                "actions": [
                                    {
                                        "type": "add_to_segment",
                                        "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c",
                                    },
                                ],
                            },
                        },
                    ],
                    "no": [
                        {
                            "id": "wait_morning",
                            "type": "wait",
                            "config": {
                                "mode": "until",
                                "time": "09:00",
                                "weekdays": [1, 2, 3, 4, 5],
                                "timezone": "America/Sao_Paulo",
                            },
                        },
                        {
                            "id": "reminder",
                            "type": "send_email",
                            "config": {
                                "template_id": "trial-help",
                                "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
                            },
                        },
                        {
                            "id": "notify_crm",
                            "type": "webhook",
                            "config": {
                                "url": "https://crm.lojaexemplo.com.br/hooks/postal",
                            },
                        },
                    ],
                },
            },
        ],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('automations', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Boas-vindas do teste grátis',
        'trigger' => [
            'type' => 'event',
            'event' => 'trial.started',
            'filters' => [
                [
                    'key' => 'plan',
                    'operator' => 'equals',
                    'value' => 'pro',
                ],
            ],
        ],
        'steps' => [
            [
                'id' => 'welcome',
                'type' => 'send_email',
                'config' => [
                    'template_id' => 'trial-welcome',
                    'from' => 'Ana da Loja Exemplo <[email protected]>',
                    'variables' => [
                        'DAYS' => '{{event.trial_days}}',
                    ],
                ],
            ],
            [
                'id' => 'wait_2_days',
                'type' => 'wait',
                'config' => [
                    'mode' => 'duration',
                    'amount' => 2,
                    'unit' => 'days',
                ],
            ],
            [
                'id' => 'check_opened',
                'type' => 'condition',
                'config' => [
                    'kind' => 'email_activity',
                    'step_id' => 'welcome',
                    'activity' => 'opened',
                ],
                'branches' => [
                    'yes' => [
                        [
                            'id' => 'tag_engaged',
                            'type' => 'update_contact',
                            'config' => [
                                'actions' => [
                                    [
                                        'type' => 'add_to_segment',
                                        'segment_id' => '019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c',
                                    ],
                                ],
                            ],
                        ],
                    ],
                    'no' => [
                        [
                            'id' => 'wait_morning',
                            'type' => 'wait',
                            'config' => [
                                'mode' => 'until',
                                'time' => '09:00',
                                'weekdays' => [1, 2, 3, 4, 5],
                                'timezone' => 'America/Sao_Paulo',
                            ],
                        ],
                        [
                            'id' => 'reminder',
                            'type' => 'send_email',
                            'config' => [
                                'template_id' => 'trial-help',
                                'subject' => '{{contact.first_name}}, posso ajudar com o seu teste?',
                            ],
                        ],
                        [
                            'id' => 'notify_crm',
                            'type' => 'webhook',
                            'config' => [
                                'url' => 'https://crm.lojaexemplo.com.br/hooks/postal',
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "draft",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": null,
  "runs_this_month": 0,
  "runs_in_progress": 0,
  "activated_at": null,
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-14T19:22:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}
422Resposta com erro
{
  "statusCode": 422,
  "name": "validation_error",
  "message": "A condition must be the last step of its path: continue in its branches.",
  "errors": [
    {
      "step_id": "check_opened",
      "message": "A condition must be the last step of its path: continue in its branches."
    }
  ]
}

#Obter uma automação

GET/automations/{id}

Chave de acesso total

Devolve a automação com issues (o que impede a ativação, cada um com step_id e message) e o webhook_signing_secret.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /automations/{id}

cURL

curl https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', {
  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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
    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('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "active",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": 2,
  "runs_this_month": 318,
  "runs_in_progress": 41,
  "activated_at": "2026-10-15T13:00:00.000000Z",
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-20T10:31:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}

#Atualizar uma automação

PATCH/automations/{id}

Chave de acesso total

Só os campos enviados mudam; trigger e steps substituem os anteriores por inteiro. Num rascunho ou numa automação pausada, a definição pode ficar incompleta. Numa automação ativa (ou com uma ativação agendada), ela precisa estar completa, e a mudança numa ativa vira uma nova versão, que vale só para as novas execuções. Devolve a automação completa.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Corpo da requisição (todos opcionais)

  • namestring
    Novo nome.
  • triggerobject
    Novo gatilho.
  • stepsobject[]
    Nova lista de passos.
  • reentrystring
    once ou every_time.

Erros

  • 422validation_error
    Definição com erro ou, numa automação ativa, incompleta ou com a divisão de um experimento alterada. Veja errors.
  • 403plan_limit_reached
    A definição tem um experimento e o plano não inclui testes A/B.
  • 403paused_by_plan
    A automação está pausada porque passou do limite de automações do plano. Faça upgrade ou apague outras.
  • 404not_found
    O recurso não existe ou é de outro time.
PATCH /automations/{id}

cURL

curl -X PATCH https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reentry": "every_time"
  }'

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    reentry: 'every_time',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "reentry": "every_time",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'reentry' => 'every_time',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "active",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "every_time",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": 3,
  "runs_this_month": 318,
  "runs_in_progress": 41,
  "activated_at": "2026-10-15T13:00:00.000000Z",
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-24T16:30:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}

#Excluir uma automação

DELETE/automations/{id}

Chave de acesso total

Apaga a automação com as versões e as execuções; as que estavam em andamento param. Para só interromper, prefira pausar.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /automations/{id}

cURL

curl -X DELETE https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "deleted": true
}

#Ativar

POST/automations/{id}/activate

Chave de acesso total

Ativa um rascunho ou retoma uma automação pausada. A definição precisa estar completa: templates escolhidos e publicados, remetente de domínio verificado, caminhos das condições com ao menos um passo, URLs dos webhooks… Publica a definição como versão (se mudou desde a última) e devolve a automação.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 422validation_error
    A definição tem erros ou ainda está incompleta. errors lista tudo o que falta.
  • 403plan_limit_reached
    A automação tem um experimento e o plano não inclui testes A/B.
  • 403paused_by_plan
    A automação está pausada porque passou do limite de automações do plano. Faça upgrade ou apague outras.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /automations/{id}/activate

cURL

curl -X POST https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/activate \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/activate', {
  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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/activate",
    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('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/activate', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "active",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": 2,
  "runs_this_month": 318,
  "runs_in_progress": 41,
  "activated_at": "2026-10-15T13:00:00.000000Z",
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-20T10:31:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}
422Resposta com erro
{
  "statusCode": 422,
  "name": "validation_error",
  "message": "The automation is not complete yet. Choose the template to send.",
  "errors": [
    {
      "step_id": "welcome",
      "message": "Choose the template to send."
    },
    {
      "step_id": null,
      "message": "Choose the event that starts the automation."
    }
  ]
}

#Pausar

POST/automations/{id}/pause

Chave de acesso total

Para de iniciar execuções. As que estão esperando ficam paradas e continuam de onde estavam quando a automação for ativada de novo; uma espera que venceu nesse meio-tempo segue logo depois.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 422validation_error
    A automação não está ativa.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /automations/{id}/pause

cURL

curl -X POST https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/pause \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/pause', {
  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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/pause",
    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('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/pause', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "paused",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": 2,
  "runs_this_month": 318,
  "runs_in_progress": 41,
  "activated_at": "2026-10-15T13:00:00.000000Z",
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-24T16:35:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}

#Agendar a ativação ou a pausa

POST/automations/{id}/schedule

Chave de acesso total

Agenda quando a automação é ativada (activate_at) e/ou pausada (deactivate_at), úteis para uma campanha com começo e fim. Mande ao menos um dos dois; um campo que você não manda mantém o valor atual, e null limpa. Os horários são aplicados a cada minuto, e cada um volta a null depois de aplicado. Devolve a automação completa.

Uma ativação só pode ser agendada para uma automação que ainda não está ativa e que está completa (a mesma exigência de ativar; se ela deixar de estar completa até o horário, a ativação é descartada e a automação fica como está). Uma pausa precisa de uma automação ativa ou de uma ativação agendada antes dela.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Corpo da requisição

  • activate_atstring | null
    Quando ativar, em ISO 8601 com fuso, como 2026-11-02T09:00:00-03:00. No futuro e até um ano à frente.
  • deactivate_atstring | null
    Quando pausar, no mesmo formato. Precisa ser depois de activate_at, se os dois forem mandados.

Erros

  • 422validation_error
    Nenhum dos dois campos, horário que não é uma data no futuro ou passa de um ano, deactivate_at antes de activate_at, ativação de uma automação já ativa, pausa sem ativação numa automação que não está ativa, ou definição incompleta (errors lista o que falta).
  • 403plan_limit_reached
    A automação tem um experimento e o plano não inclui testes A/B.
  • 403paused_by_plan
    A automação está pausada porque passou do limite de automações do plano. Faça upgrade ou apague outras.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /automations/{id}/schedule

cURL

curl -X POST https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "activate_at": "2026-11-02T09:00:00-03:00",
    "deactivate_at": "2026-11-09T23:59:00-03:00"
  }'

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    activate_at: '2026-11-02T09:00:00-03:00',
    deactivate_at: '2026-11-09T23:59:00-03:00',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "activate_at": "2026-11-02T09:00:00-03:00",
        "deactivate_at": "2026-11-09T23:59:00-03:00",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'activate_at' => '2026-11-02T09:00:00-03:00',
        'deactivate_at' => '2026-11-09T23:59:00-03:00',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "draft",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": null,
  "runs_this_month": 0,
  "runs_in_progress": 0,
  "activated_at": null,
  "activate_at": "2026-11-02T12:00:00.000000Z",
  "deactivate_at": "2026-11-10T02:59:00.000000Z",
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-24T16:40:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}

#Cancelar o agendamento

DELETE/automations/{id}/schedule

Chave de acesso total

Limpa a ativação e a pausa agendadas (activate_at e deactivate_at voltam a null). O status não muda. Devolve a automação completa.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
DELETE /automations/{id}/schedule

cURL

curl -X DELETE https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/schedule', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation",
  "id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "paused_by_plan": false,
  "name": "Boas-vindas do teste grátis",
  "status": "draft",
  "trigger": {
    "type": "event",
    "event": "trial.started",
    "filters": [
      {
        "key": "plan",
        "operator": "equals",
        "value": "pro"
      }
    ]
  },
  "reentry": "once",
  "steps": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "version": null,
  "runs_this_month": 0,
  "runs_in_progress": 0,
  "activated_at": null,
  "activate_at": null,
  "deactivate_at": null,
  "created_at": "2026-10-14T19:22:00.000000Z",
  "updated_at": "2026-10-24T16:45:00.000000Z",
  "issues": [],
  "webhook_signing_secret": "whsec_YXV0b21hY2FvLWV4ZW1wbG8tYWJjZGVmMTIzNA==",
  "experiments_included": true
}

#Resultados dos experimentos

GET/automations/{id}/experiments

Chave de acesso total

Cada passo experiment da definição atual, com a vencedora (se houver) e, por variante e pelo controle: entered (contatos que entraram), emails_sent (e-mails enviados depois da divisão) e quantos desses contatos abriram (opened, que inclui quem clicou ou respondeu), clicaram (clicked), responderam (replied) e converteram (converted, com goal_event). Cada contato conta uma vez em cada métrica.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /automations/{id}/experiments

cURL

curl https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments', {
  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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments",
    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('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "data": [
    {
      "step_id": "subject_test",
      "goal_event": "order.paid",
      "winner": null,
      "variants": [
        {
          "key": "a",
          "name": "Assunto curto",
          "control": false,
          "percent": 45,
          "entered": 412,
          "emails_sent": 412,
          "opened": 187,
          "clicked": 41,
          "replied": 3,
          "converted": 22
        },
        {
          "key": "b",
          "name": "Assunto com o nome",
          "control": false,
          "percent": 45,
          "entered": 398,
          "emails_sent": 398,
          "opened": 221,
          "clicked": 58,
          "replied": 5,
          "converted": 31
        },
        {
          "key": "control",
          "name": null,
          "control": true,
          "percent": 10,
          "entered": 93,
          "emails_sent": 0,
          "opened": 0,
          "clicked": 0,
          "replied": 0,
          "converted": 4
        }
      ]
    }
  ]
}

#Declarar a vencedora

POST/automations/{id}/experiments/{step_id}/winner

Chave de acesso total

Todos os contatos que chegarem ao passo a partir de agora vão para a variante escolhida (o controle não pode ser a vencedora). Quem já passou continua na sua variante. Declarar de novo troca a vencedora. Devolve os resultados do experimento.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.
  • step_idstringobrigatório
    O id do passo experiment.

Corpo da requisição

  • variantstringobrigatório
    A chave da variante, como b.

Erros

  • 422validation_error
    A chave não é de uma variante do experimento.
  • 403paused_by_plan
    A automação está pausada porque passou do limite de automações do plano. Faça upgrade ou apague outras.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /automations/{id}/experiments/{step_id}/winner

cURL

curl -X POST https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments/subject_test/winner \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "variant": "b"
  }'

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments/subject_test/winner', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    variant: 'b',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments/subject_test/winner",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "variant": "b",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/experiments/subject_test/winner', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'variant' => 'b',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation_experiment",
  "step_id": "subject_test",
  "goal_event": "order.paid",
  "winner": {
    "variant": "b",
    "declared_at": "2026-10-30T15:04:00Z",
    "declared_by": "Ana Souza"
  },
  "variants": [
    {
      "key": "a",
      "name": "Assunto curto",
      "control": false,
      "percent": 45,
      "entered": 412,
      "emails_sent": 412,
      "opened": 187,
      "clicked": 41,
      "replied": 3,
      "converted": 22
    },
    {
      "key": "b",
      "name": "Assunto com o nome",
      "control": false,
      "percent": 45,
      "entered": 398,
      "emails_sent": 398,
      "opened": 221,
      "clicked": 58,
      "replied": 5,
      "converted": 31
    },
    {
      "key": "control",
      "name": null,
      "control": true,
      "percent": 10,
      "entered": 93,
      "emails_sent": 0,
      "opened": 0,
      "clicked": 0,
      "replied": 0,
      "converted": 4
    }
  ]
}

#Execuções

StatusO que significa
runningExecutando passos agora.
waitingEsperando até next_run_at: um passo wait, um wait_for (que acorda antes se a ação acontece) ou uma nova tentativa de webhook. current_step_id é o próximo passo.
completedChegou ao fim de um caminho ou a um exit.
failedUm passo falhou; o motivo está em error e no registro do passo.
canceledCancelada pelo painel ou pela API.
skippedNão começou: o limite mensal de execuções do plano tinha acabado.

Cada passo executado fica registrado em steps, com status (running, waiting, completed, failed ou skipped), tentativas, erro e uma saída (output): {"until"} numa espera, {"email_id", "to", "subject"} num envio, {"result": "yes"} numa condição, {"variant": "b", "reason": "split"} num experimento (reason: split, winner ou plan), {"changes": […]} numa mudança de contato e {"status_code"} num webhook. Um wait_for guarda {"email_id", "activity", "deadline", "resolved_by"} (veja Esperar uma ação no e-mail), e um envio com detect_replies traz também o reply_address. Passos que escolhem um caminho (condição, wait_for, experimento) trazem ainda result, o caminho seguido.

O trigger da execução diz o que a iniciou: sempre o type e, conforme o gatilho, event_id, event e properties (evento); segment_id; topic_id; tag_id; form_id e form_submission_id; email_id, template_id, broadcast_id e link (abertura e clique); ou property, from e to (mudança de propriedade).

#Listar execuções

GET/automations/{id}/runs

Chave de acesso total

As execuções da automação, da mais recente para a mais antiga, com a definição da versão que cada uma segue (definition). O registro dos passos só vem em obter uma execução.

Parâmetros de caminho

  • idstringobrigatório
    ID da automação.

Parâmetros de consulta

  • limitinteger
    De 1 a 100. Padrão: 20.
  • afterstring
    Cursor da próxima página.
  • beforestring
    Cursor da página anterior.
  • statusstring
    Um ou mais status separados por vírgula, como running,waiting.
  • contactstring
    ID ou e-mail do contato.

Erros

  • 422validation_error
    Algum campo é inválido. A mensagem diz qual.
  • 404not_found
    O recurso não existe ou é de outro time.
GET /automations/{id}/runs

cURL

curl 'https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/runs?status=waiting,failed' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/runs?status=waiting,failed', {
  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/automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/runs",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "status": "waiting,failed",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('automations/019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f/runs', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'status' => 'waiting,failed',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta (definition encurtado)
{
  "object": "list",
  "has_more": true,
  "data": [
    {
      "object": "automation_run",
      "id": "019a1f70-7182-7d94-9fa0-1b2c3d4e5f60",
      "automation_id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
      "version": 2,
      "contact_id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "contact_email": "[email protected]",
      "status": "waiting",
      "trigger": {
        "type": "event",
        "event_id": "019a1f40-5f60-7b72-9d8e-9f0a1b2c3d4e",
        "event": "trial.started",
        "properties": {
          "plan": "pro",
          "source": "website",
          "trial_days": 14
        }
      },
      "current_step_id": "check_opened",
      "next_run_at": "2026-10-26T14:20:02.000000Z",
      "error": null,
      "started_at": "2026-10-24T14:20:01.000000Z",
      "finished_at": null,
      "created_at": "2026-10-24T14:20:01.000000Z",
      "definition": [
        {
          "id": "welcome",
          "…": "…"
        }
      ]
    }
  ]
}

#Obter uma execução

GET/automation-runs/{id}

Chave de acesso total

A execução com o que a iniciou (trigger: o tipo e, conforme o gatilho, o evento com as propriedades, o segmento ou o formulário), a definição da versão e o registro de cada passo.

Parâmetros de caminho

  • idstringobrigatório
    ID da execução.

Erros

  • 404not_found
    O recurso não existe ou é de outro time.
GET /automation-runs/{id}

cURL

curl https://api.postal.so/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60 \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60', {
  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/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60",
    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('automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation_run",
  "id": "019a1f70-7182-7d94-9fa0-1b2c3d4e5f60",
  "automation_id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "automation_name": "Boas-vindas do teste grátis",
  "version": 2,
  "contact_id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
  "contact_email": "[email protected]",
  "status": "waiting",
  "trigger": {
    "type": "event",
    "event_id": "019a1f40-5f60-7b72-9d8e-9f0a1b2c3d4e",
    "event": "trial.started",
    "properties": {
      "plan": "pro",
      "source": "website",
      "trial_days": 14
    }
  },
  "current_step_id": "check_opened",
  "next_run_at": "2026-10-26T14:20:02.000000Z",
  "error": null,
  "started_at": "2026-10-24T14:20:01.000000Z",
  "finished_at": null,
  "created_at": "2026-10-24T14:20:01.000000Z",
  "definition": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "steps": [
    {
      "object": "automation_run_step",
      "id": "019a1f70-7183-7ea5-8ab1-2c3d4e5f6071",
      "step_id": "welcome",
      "type": "send_email",
      "status": "completed",
      "attempts": 1,
      "output": {
        "email_id": "019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
        "to": "[email protected]",
        "subject": "Ana, seu teste do plano Pro começou"
      },
      "error": null,
      "started_at": "2026-10-24T14:20:01.000000Z",
      "finished_at": "2026-10-24T14:20:02.000000Z"
    },
    {
      "object": "automation_run_step",
      "id": "019a1f70-7184-7fb6-9bc2-3d4e5f607182",
      "step_id": "wait_2_days",
      "type": "wait",
      "status": "waiting",
      "attempts": 1,
      "output": {
        "until": "2026-10-26T14:20:02Z"
      },
      "error": null,
      "started_at": "2026-10-24T14:20:02.000000Z",
      "finished_at": null
    }
  ]
}

#Cancelar uma execução

POST/automation-runs/{id}/cancel

Chave de acesso total

Para uma execução em andamento ou esperando; a espera pendente fica skipped. Devolve a execução.

Parâmetros de caminho

  • idstringobrigatório
    ID da execução.

Erros

  • 422validation_error
    A execução já terminou.
  • 404not_found
    O recurso não existe ou é de outro time.
POST /automation-runs/{id}/cancel

cURL

curl -X POST https://api.postal.so/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60/cancel \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60/cancel', {
  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/automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60/cancel",
    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('automation-runs/019a1f70-7182-7d94-9fa0-1b2c3d4e5f60/cancel', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "automation_run",
  "id": "019a1f70-7182-7d94-9fa0-1b2c3d4e5f60",
  "automation_id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
  "automation_name": "Boas-vindas do teste grátis",
  "version": 2,
  "contact_id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
  "contact_email": "[email protected]",
  "status": "canceled",
  "trigger": {
    "type": "event",
    "event_id": "019a1f40-5f60-7b72-9d8e-9f0a1b2c3d4e",
    "event": "trial.started",
    "properties": {
      "plan": "pro",
      "source": "website",
      "trial_days": 14
    }
  },
  "current_step_id": "check_opened",
  "next_run_at": null,
  "error": "Canceled from the dashboard or API.",
  "started_at": "2026-10-24T14:20:01.000000Z",
  "finished_at": "2026-10-25T09:12:00.000000Z",
  "created_at": "2026-10-24T14:20:01.000000Z",
  "definition": [
    {
      "id": "welcome",
      "type": "send_email",
      "config": {
        "template_id": "trial-welcome",
        "from": "Ana da Loja Exemplo <[email protected]>",
        "subject": null,
        "reply_to": null,
        "variables": {
          "DAYS": "{{event.trial_days}}"
        }
      }
    },
    {
      "id": "wait_2_days",
      "type": "wait",
      "config": {
        "mode": "duration",
        "amount": 2,
        "unit": "days"
      }
    },
    {
      "id": "check_opened",
      "type": "condition",
      "config": {
        "kind": "email_activity",
        "step_id": "welcome",
        "activity": "opened"
      },
      "branches": {
        "yes": [
          {
            "id": "tag_engaged",
            "type": "update_contact",
            "config": {
              "actions": [
                {
                  "type": "add_to_segment",
                  "segment_id": "019a1d91-4c5d-7e6f-8a7b-8c9d0e1f2a3c"
                }
              ]
            }
          }
        ],
        "no": [
          {
            "id": "wait_morning",
            "type": "wait",
            "config": {
              "mode": "until",
              "time": "09:00",
              "weekdays": [1, 2, 3, 4, 5],
              "timezone": "America/Sao_Paulo"
            }
          },
          {
            "id": "reminder",
            "type": "send_email",
            "config": {
              "template_id": "trial-help",
              "from": null,
              "subject": "{{contact.first_name}}, posso ajudar com o seu teste?",
              "reply_to": null,
              "variables": {}
            }
          },
          {
            "id": "notify_crm",
            "type": "webhook",
            "config": {
              "url": "https://crm.lojaexemplo.com.br/hooks/postal"
            }
          }
        ]
      }
    }
  ],
  "steps": [
    {
      "object": "automation_run_step",
      "id": "019a1f70-7183-7ea5-8ab1-2c3d4e5f6071",
      "step_id": "welcome",
      "type": "send_email",
      "status": "completed",
      "attempts": 1,
      "output": {
        "email_id": "019a1b2c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
        "to": "[email protected]",
        "subject": "Ana, seu teste do plano Pro começou"
      },
      "error": null,
      "started_at": "2026-10-24T14:20:01.000000Z",
      "finished_at": "2026-10-24T14:20:02.000000Z"
    },
    {
      "object": "automation_run_step",
      "id": "019a1f70-7184-7fb6-9bc2-3d4e5f607182",
      "step_id": "wait_2_days",
      "type": "wait",
      "status": "skipped",
      "attempts": 1,
      "output": {
        "until": "2026-10-26T14:20:02Z"
      },
      "error": null,
      "started_at": "2026-10-24T14:20:02.000000Z",
      "finished_at": "2026-10-25T09:12:00.000000Z"
    }
  ]
}

#Passo de webhook

O passo webhook faz um POST com JSON para a sua URL (HTTPS e pública), com o contato e o evento que iniciou a execução:

automation.webhook
{
  "type": "automation.webhook",
  "created_at": "2026-10-28T12:00:03.410Z",
  "data": {
    "automation_id": "019a1f60-6071-7c83-8e9f-0a1b2c3d4e5f",
    "automation_name": "Boas-vindas do teste grátis",
    "run_id": "019a1f70-7182-7d94-9fa0-1b2c3d4e5f60",
    "step_id": "notify_crm",
    "trigger": "event",
    "contact": {
      "id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "email": "[email protected]",
      "first_name": "Ana",
      "last_name": "Souza",
      "unsubscribed": false,
      "properties": {
        "plan": "pro",
        "city": "Recife"
      },
      "created_at": "2026-10-24T14:00:00.000Z",
      "updated_at": "2026-10-24T14:00:00.000Z"
    },
    "event": {
      "id": "019a1f40-5f60-7b72-9d8e-9f0a1b2c3d4e",
      "name": "trial.started",
      "properties": {
        "plan": "pro",
        "source": "website",
        "trial_days": 14
      }
    }
  }
}

A requisição é assinada como os webhooks da Postal, com os cabeçalhos webhook-id, webhook-timestamp e webhook-signature (e os equivalentes svix-*), mas usando o webhook_signing_secret da automação, que aparece em GET /automations/{id}. O mesmo código de verificação serve. O webhook-id é o ID do registro do passo e se repete nas novas tentativas, para você descartar duplicatas. Em data, trigger é o tipo do gatilho (como event ou tag.added), e event só vem quando a execução foi iniciada por um evento (senão é null).

  • Responda com 2xx em até 15 segundos. Redirecionamentos não são seguidos e endereços de rede privada são recusados.
  • Qualquer outra resposta conta como falha. São 3 tentativas: a segunda 1 minuto depois, a terceira 10 minutos depois. Depois disso, o passo e a execução ficam failed.
  • As entregas usam o User-Agent Postal-Webhooks/1.0.