Audiência

Formulários

Formulários de inscrição hospedados ou no seu site, com double opt-in.

Formulários de inscrição colocam pessoas na sua audiência: cada envio cria (ou atualiza) um contato, coloca-o no segmento do formulário e o inscreve nos tópicos escolhidos. Você usa a página pronta, hospedada em https://forms.postal.so/{slug}, ou envia do seu próprio site (HTML ou JavaScript), como mostra Envio público. Com double opt-in, a pessoa recebe um e-mail de confirmação e só entra na audiência depois de clicar no link.

#Listar formulários

GET/forms

Chave de acesso total

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.
GET /forms

cURL

curl https://api.postal.so/forms \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/forms', {
  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/forms",
    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('forms', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "form",
      "id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
      "name": "Newsletter",
      "slug": "newsletter-k3x9p2qa",
      "status": "active",
      "public_url": "https://forms.postal.so/newsletter-k3x9p2qa",
      "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
      "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
      "fields": [
        {
          "key": "email",
          "label": "E-mail",
          "placeholder": "[email protected]",
          "required": true,
          "type": "email"
        },
        {
          "key": "first_name",
          "label": "Nome",
          "placeholder": null,
          "required": false,
          "type": "text"
        }
      ],
      "double_opt_in": true,
      "content": {
        "title": "Receba nossas novidades",
        "description": "Lançamentos e promoções, uma vez por semana.",
        "logo_url": null
      },
      "success": {
        "logo_url": null,
        "title": "Inscrição confirmada!",
        "message": "Obrigado por se inscrever. Em breve você recebe novidades no seu e-mail.",
        "button_label": null,
        "button_url": null
      },
      "redirect_url": null,
      "style": {
        "layout": "stacked",
        "font": "system",
        "accent_color": "#4f46e5",
        "text_color": "#18181b",
        "background_color": "#ffffff",
        "button_text": "Inscrever-se",
        "button_color": "#4f46e5",
        "button_text_color": "#ffffff",
        "rounded": true
      },
      "allowed_domains": ["lojaexemplo.com.br"],
      "submissions_count": 412,
      "shows_postal_branding": false,
      "created_at": "2026-10-21T12:00:00.000000Z",
      "updated_at": "2026-10-21T12:00:00.000000Z"
    }
  ]
}

#Criar um formulário

POST/forms

Chave de acesso total

Devolve o formulário completo, com o public_url e os valores padrão aplicados.

Corpo da requisição

  • namestringobrigatório
    Nome interno, até 255 caracteres.
  • segment_idstringobrigatório
    Segmento que recebe os contatos.
  • slugstring
    Endereço público, de 3 a 64 caracteres (letras minúsculas, números e hífens), único na Postal. Padrão: gerado a partir do nome, como newsletter-k3x9p2qa.
  • statusstring
    active (padrão) ou paused (não aceita envios).
  • topic_idsstring[]
    Até 20 tópicos em que o contato é inscrito (opt_in).
  • fieldsobject[]
    De 1 a 20 campos, na ordem de exibição. O campo email é obrigatório. Padrão: só o e-mail.
    • keystringobrigatório
      email, first_name, last_name ou a chave de uma propriedade. O tipo do campo segue o tipo da propriedade.
    • labelstring
      Rótulo, até 255 caracteres.
    • placeholderstring
      Texto de exemplo, até 255 caracteres.
    • requiredboolean
      Obrigatório. O e-mail é sempre obrigatório.
  • double_opt_inboolean
    Pede confirmação por e-mail antes de incluir o contato.
  • contentobject
    Textos da página hospedada: title, description (até 2.000 caracteres) e logo_url.
  • successobject
    Tela de sucesso: logo_url, title, message, button_label e button_url.
  • redirect_urlstring | null
    URL para onde mandar a pessoa depois do envio (ou da confirmação, com double opt-in), em vez da tela de sucesso.
  • styleobject
    Aparência: layout (stacked ou inline), font (system, serif, mono, rounded), cores em hexadecimal (accent_color, text_color, background_color, button_color, button_text_color), button_text e rounded.
  • allowed_domainsstring[]
    Até 50 domínios que podem enviar o formulário pelo navegador, como lojaexemplo.com.br (inclui subdomínios). Vazio: qualquer site.

Erros

  • 422validation_error
    Campo inválido, slug em uso, formulário sem o campo email ou campo que não é contato nem propriedade.
  • 404not_found
    O segmento ou algum tópico não existe.
POST /forms

cURL

curl -X POST https://api.postal.so/forms \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Newsletter",
    "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
    "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
    "double_opt_in": true,
    "fields": [
      {
        "key": "email",
        "label": "E-mail",
        "placeholder": "[email protected]"
      },
      {
        "key": "first_name",
        "label": "Nome"
      }
    ],
    "content": {
      "title": "Receba nossas novidades"
    },
    "allowed_domains": ["lojaexemplo.com.br"]
  }'

Node.js

const response = await fetch('https://api.postal.so/forms', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Newsletter',
    segment_id: '019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a',
    topic_ids: ['019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b'],
    double_opt_in: true,
    fields: [
      {
        key: 'email',
        label: 'E-mail',
        placeholder: '[email protected]',
      },
      {
        key: 'first_name',
        label: 'Nome',
      },
    ],
    content: {
      title: 'Receba nossas novidades',
    },
    allowed_domains: ['lojaexemplo.com.br'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/forms",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Newsletter",
        "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
        "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
        "double_opt_in": True,
        "fields": [
            {
                "key": "email",
                "label": "E-mail",
                "placeholder": "[email protected]",
            },
            {
                "key": "first_name",
                "label": "Nome",
            },
        ],
        "content": {
            "title": "Receba nossas novidades",
        },
        "allowed_domains": ["lojaexemplo.com.br"],
    },
    timeout=10,
)

data = response.json()

PHP

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

$response = $client->post('forms', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Newsletter',
        'segment_id' => '019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a',
        'topic_ids' => ['019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b'],
        'double_opt_in' => true,
        'fields' => [
            [
                'key' => 'email',
                'label' => 'E-mail',
                'placeholder' => '[email protected]',
            ],
            [
                'key' => 'first_name',
                'label' => 'Nome',
            ],
        ],
        'content' => [
            'title' => 'Receba nossas novidades',
        ],
        'allowed_domains' => ['lojaexemplo.com.br'],
    ],
]);

$data = json_decode($response->getBody(), true);
201Resposta
{
  "object": "form",
  "id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
  "name": "Newsletter",
  "slug": "newsletter-k3x9p2qa",
  "status": "active",
  "public_url": "https://forms.postal.so/newsletter-k3x9p2qa",
  "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
  "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
  "fields": [
    {
      "key": "email",
      "label": "E-mail",
      "placeholder": "[email protected]",
      "required": true,
      "type": "email"
    },
    {
      "key": "first_name",
      "label": "Nome",
      "placeholder": null,
      "required": false,
      "type": "text"
    }
  ],
  "double_opt_in": true,
  "content": {
    "title": "Receba nossas novidades",
    "description": "Lançamentos e promoções, uma vez por semana.",
    "logo_url": null
  },
  "success": {
    "logo_url": null,
    "title": "Inscrição confirmada!",
    "message": "Obrigado por se inscrever. Em breve você recebe novidades no seu e-mail.",
    "button_label": null,
    "button_url": null
  },
  "redirect_url": null,
  "style": {
    "layout": "stacked",
    "font": "system",
    "accent_color": "#4f46e5",
    "text_color": "#18181b",
    "background_color": "#ffffff",
    "button_text": "Inscrever-se",
    "button_color": "#4f46e5",
    "button_text_color": "#ffffff",
    "rounded": true
  },
  "allowed_domains": ["lojaexemplo.com.br"],
  "submissions_count": 0,
  "shows_postal_branding": false,
  "created_at": "2026-10-21T12:00:00.000000Z",
  "updated_at": "2026-10-21T12:00:00.000000Z"
}

#Obter um formulário

GET/forms/{id}

Chave de acesso total

Parâmetros de caminho

  • idstringobrigatório
    ID do formulário.

Erros

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

cURL

curl https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', {
  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/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
    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('forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "form",
  "id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
  "name": "Newsletter",
  "slug": "newsletter-k3x9p2qa",
  "status": "active",
  "public_url": "https://forms.postal.so/newsletter-k3x9p2qa",
  "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
  "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
  "fields": [
    {
      "key": "email",
      "label": "E-mail",
      "placeholder": "[email protected]",
      "required": true,
      "type": "email"
    },
    {
      "key": "first_name",
      "label": "Nome",
      "placeholder": null,
      "required": false,
      "type": "text"
    }
  ],
  "double_opt_in": true,
  "content": {
    "title": "Receba nossas novidades",
    "description": "Lançamentos e promoções, uma vez por semana.",
    "logo_url": null
  },
  "success": {
    "logo_url": null,
    "title": "Inscrição confirmada!",
    "message": "Obrigado por se inscrever. Em breve você recebe novidades no seu e-mail.",
    "button_label": null,
    "button_url": null
  },
  "redirect_url": null,
  "style": {
    "layout": "stacked",
    "font": "system",
    "accent_color": "#4f46e5",
    "text_color": "#18181b",
    "background_color": "#ffffff",
    "button_text": "Inscrever-se",
    "button_color": "#4f46e5",
    "button_text_color": "#ffffff",
    "rounded": true
  },
  "allowed_domains": ["lojaexemplo.com.br"],
  "submissions_count": 412,
  "shows_postal_branding": false,
  "created_at": "2026-10-21T12:00:00.000000Z",
  "updated_at": "2026-10-21T12:00:00.000000Z"
}

#Atualizar um formulário

PATCH/forms/{id}

Chave de acesso total

Todos os campos de criação são aceitos e opcionais. Em content, success e style, o objeto enviado substitui o anterior. Devolve o formulário atualizado.

Parâmetros de caminho

  • idstringobrigatório
    ID do formulário.

Erros

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

cURL

curl -X PATCH https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "paused"
  }'

Node.js

const response = await fetch('https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    status: 'paused',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "status": "paused",
    },
    timeout=10,
)

data = response.json()

PHP

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

$response = $client->patch('forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'status' => 'paused',
    ],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "form",
  "id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
  "name": "Newsletter",
  "slug": "newsletter-k3x9p2qa",
  "status": "paused",
  "public_url": "https://forms.postal.so/newsletter-k3x9p2qa",
  "segment_id": "019a1d90-3b4c-7d5e-9f6a-7b8c9d0e1f2a",
  "topic_ids": ["019a1d95-4c5d-7e6f-8a7b-8c9d0e1f2a3b"],
  "fields": [
    {
      "key": "email",
      "label": "E-mail",
      "placeholder": "[email protected]",
      "required": true,
      "type": "email"
    },
    {
      "key": "first_name",
      "label": "Nome",
      "placeholder": null,
      "required": false,
      "type": "text"
    }
  ],
  "double_opt_in": true,
  "content": {
    "title": "Receba nossas novidades",
    "description": "Lançamentos e promoções, uma vez por semana.",
    "logo_url": null
  },
  "success": {
    "logo_url": null,
    "title": "Inscrição confirmada!",
    "message": "Obrigado por se inscrever. Em breve você recebe novidades no seu e-mail.",
    "button_label": null,
    "button_url": null
  },
  "redirect_url": null,
  "style": {
    "layout": "stacked",
    "font": "system",
    "accent_color": "#4f46e5",
    "text_color": "#18181b",
    "background_color": "#ffffff",
    "button_text": "Inscrever-se",
    "button_color": "#4f46e5",
    "button_text_color": "#ffffff",
    "rounded": true
  },
  "allowed_domains": ["lojaexemplo.com.br"],
  "submissions_count": 412,
  "shows_postal_branding": false,
  "created_at": "2026-10-21T12:00:00.000000Z",
  "updated_at": "2026-10-21T12:00:00.000000Z"
}

#Excluir um formulário

DELETE/forms/{id}

Chave de acesso total

O endereço público deixa de funcionar. Os contatos já inscritos continuam na audiência.

Parâmetros de caminho

  • idstringobrigatório
    ID do formulário.

Erros

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

cURL

curl -X DELETE https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', {
  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/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
    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('forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "form",
  "id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
  "deleted": true
}

#Listar envios de um formulário

GET/forms/{id}/submissions

Chave de acesso total

Cada envio recebido, do mais recente para o mais antigo. status: pending (na fila), pending_confirmation (e-mail de confirmação enviado), subscribed (contato na audiência) ou failed (por exemplo, o limite de contatos do plano foi atingido).

Parâmetros de caminho

  • idstringobrigatório
    ID do formulário.

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.

Erros

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

cURL

curl https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d/submissions \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d/submissions', {
  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/forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d/submissions",
    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('forms/019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d/submissions', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Resposta
{
  "object": "list",
  "has_more": true,
  "data": [
    {
      "object": "form_submission",
      "id": "019a1e20-8091-7ca2-9d1e-3f4a5b6c7d8e",
      "form_id": "019a1da0-7f80-7b91-8c0d-2e3f4a5b6c7d",
      "contact_id": "019a1e00-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
      "email": "[email protected]",
      "data": {
        "email": "[email protected]",
        "first_name": "Ana",
        "properties": {}
      },
      "status": "subscribed",
      "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
      "referrer": "https://lojaexemplo.com.br/blog",
      "created_at": "2026-10-24T15:00:00.000000Z",
      "confirmed_at": "2026-10-24T15:03:12.000000Z"
    }
  ]
}

#Envio público

Os envios vão para o host dos formulários, sem chave de API: POST https://forms.postal.so/{slug}. O corpo pode ser um formulário HTML comum (application/x-www-form-urlencoded ou multipart/form-data) ou JSON. Os nomes dos campos são as chaves de fields.

RegraComo funciona
RobôsO campo _website é uma armadilha: deixe-o escondido e vazio. Se ele vier preenchido, a resposta é de sucesso, mas nada é guardado.
Limite5 envios por minuto por IP em cada formulário. Depois disso, 429.
OrigemCom allowed_domains, só páginas desses domínios (e subdomínios) podem enviar pelo navegador; as demais recebem 403 origin_not_allowed. A página hospedada sempre pode.
CORSResponde a OPTIONS e libera POST com os cabeçalhos Content-Type e Accept: para qualquer origem, ou só para os allowed_domains. Não usa cookies.
Formulário pausado ou inexistente404.

Formulário HTML

Um envio de formulário comum termina num redirecionamento 303: para o redirect_url, se houver (e sem double opt-in), ou para a tela de sucesso da Postal (/{slug}/obrigado). Se algum campo estiver errado, a página hospedada aparece com os erros.

HTML no seu site
<form action="https://forms.postal.so/newsletter-k3x9p2qa" method="post">
  <input type="email" name="email" placeholder="[email protected]" required>
  <input type="text" name="first_name" placeholder="Seu nome">

  <!-- Armadilha para robôs: fica escondida e precisa chegar vazia -->
  <input type="text" name="_website" tabindex="-1" autocomplete="off" hidden>

  <button type="submit">Inscrever-se</button>
</form>

JavaScript (JSON)

Com Accept: application/json ou corpo JSON, a resposta é JSON: 201 com os textos para mostrar na sua página, ou um erro no formato padrão. No 422, o campo extra errors traz as mensagens de cada campo, em português.

JavaScript no seu site
const response = await fetch('https://forms.postal.so/newsletter-k3x9p2qa', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  body: JSON.stringify({ email: '[email protected]', first_name: 'Ana', _website: '' }),
});

const body = await response.json();

if (response.ok) {
  showMessage(body.title, body.message); // "Confirme seu e-mail" com double opt-in
} else {
  showError(body.message);              // 422: body.errors traz o erro de cada campo
}
201Resposta
{
  "object": "form_submission",
  "id": "019a1e20-8091-7ca2-9d1e-3f4a5b6c7d8e",
  "requires_confirmation": true,
  "title": "Confirme seu e-mail",
  "message": "Enviamos um link para o seu e-mail. Clique nele para confirmar a inscrição.",
  "redirect_url": null
}
422Resposta com erro
{
  "statusCode": 422,
  "name": "validation_error",
  "message": "Informe um e-mail válido.",
  "errors": {
    "email": ["Informe um e-mail válido."]
  }
}

Quando o contato entra na audiência (na hora ou depois da confirmação), a Postal dispara o webhook form.submitted, além de contact.created ou contact.updated. Quem se inscreve por um formulário volta a receber envios de marketing mesmo que tenha se descadastrado antes.