Listas de Contatos
As Listas são os agrupamentos de contatos da sua organização — as mesmas listas da aba Contatos → Listas e que as campanhas de e-mail usam como público. A aba aparece para administradores quando o CRM de e-mail está disponível. Esta seção cobre os endpoints para adicionar um contato à lista, ler os membros e descadastrar.
É por aqui que um sistema externo (formulário, checkout, CRM, n8n, Make) coloca gente dentro do SquadOS: o contato entra com e-mail válido, origem do consentimento e campos personalizados, e isso dispara as automações que usam o gatilho “Contato adicionado à lista”.
Todos os endpoints exigem o header Authorization: Bearer pk_.... Quando houver corpo JSON, inclua também Content-Type: application/json.
Base URL: https://api.squados.io/v1
Consulte Autenticação para obter sua chave, e Erros para a referência de códigos.
O modelo Lista
Section titled “O modelo Lista”{ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Newsletter", "public_name": "Novidades do produto", "description": "Quem assinou pelo rodapé do site", "kind": "marketing", "subscribed_count": 1284, "unsubscribed_count": 37, "created_at": "2026-08-01T12:00:00Z", "updated_at": "2026-08-19T09:31:00Z"}| Campo | Tipo | Descrição |
|---|---|---|
id | uuid | Identificador da lista. É o listId dos endpoints abaixo. |
name | string | Nome interno, o que sua equipe vê no painel. |
public_name | string | Nome exibido para o contato no centro de preferências. |
description | string ou null | Nota interna sobre a lista. |
kind | string | marketing ou transactional. |
subscribed_count | integer | Inscritos ativos. |
unsubscribed_count | integer | Quem descadastrou. |
created_at | date-time | Criação da lista em ISO 8601. |
updated_at | date-time | Última alteração da lista em ISO 8601. |
GET /lists
Section titled “GET /lists”Lista as listas da organização, da mais recente para a mais antiga.
Parâmetros de query
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
limit | integer | 50 | Máximo de 100. |
offset | integer | 0 | Deslocamento para paginar. |
Envie inteiros não negativos e use limit entre 1 e 100. A resposta não traz total, next nem has_more: some a quantidade recebida ao offset e encerre quando vierem menos itens que o limit.
curl -X GET "https://api.squados.io/v1/lists" \ -H "Authorization: Bearer pk_sua_chave_aqui"Resposta — 200 OK
{ "lists": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Newsletter", "public_name": "Novidades do produto", "description": null, "kind": "marketing", "subscribed_count": 1284, "unsubscribed_count": 37, "created_at": "2026-08-01T12:00:00Z", "updated_at": "2026-08-19T09:31:00Z" } ], "limit": 50, "offset": 0}GET /lists/{listId}
Section titled “GET /lists/{listId}”Detalha uma lista.
Resposta — 200 OK
{ "list": { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Newsletter", "public_name": "Novidades do produto", "description": null, "kind": "marketing", "subscribed_count": 1284, "unsubscribed_count": 37, "created_at": "2026-08-01T12:00:00Z", "updated_at": "2026-08-19T09:31:00Z" }}listId inválido responde 400 invalid_request. Uma lista interna, inexistente ou de outra organização responde 404 not_found — a chave só enxerga a organização que a gerou.
POST /lists/{listId}/contacts
Section titled “POST /lists/{listId}/contacts”O endpoint principal. Cria (ou reaproveita) o contato pelo e-mail e o inscreve na lista.
Parâmetros de caminho
| Nome | Tipo | Descrição |
|---|---|---|
listId | uuid | ID da lista. |
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
email | string | sim | Endereço do contato. É normalizado para minúsculas — [email protected] e [email protected] são o mesmo contato. |
consent_source | string | sim | Onde esta pessoa consentiu em receber contato. De 3 a 500 caracteres, em texto livre (ex.: "formulário de newsletter do rodapé", "checkout da loja em 12/08/2026"). |
name | string | não | Nome de exibição do contato, até 200 caracteres. Quando enviado preenchido, atualiza o nome atual. |
metadata | object | não | Campos personalizados do contato (veja abaixo). |
status | string | não | subscribed (padrão) ou pending. |
curl -X POST "https://api.squados.io/v1/lists/3fa85f64-5717-4562-b3fc-2c963f66afa6/contacts" \ -H "Authorization: Bearer pk_sua_chave_aqui" \ -H "Content-Type: application/json" \ -d '{ "email": "[email protected]", "name": "Maria Silva", "consent_source": "formulário de newsletter do rodapé do site", "metadata": { "plano": "pro", "mrr": 199, "origem": "google_ads" } }'Resposta — 201 Created
{ "contact": { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "Maria Silva", "metadata": { "plano": "pro", "mrr": 199, "origem": "google_ads" } }, "membership": { "id": "8a1b2c3d-4e5f-6789-abcd-ef0123456789", "list_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "status": "subscribed", "consent_source": "formulário de newsletter do rodapé do site" }, "created": true}A chamada é idempotente
Section titled “A chamada é idempotente”Repetir o mesmo e-mail com o mesmo status na mesma lista não duplica nada: a resposta vem 200 OK com "created": false. Mesmo nessa resposta, um name preenchido atualiza o nome e metadata é mesclado ao contato. O campo created descreve se a associação mudou, não se o contato nasceu: confirmar pending como subscribed devolve 201 e created: true, embora reutilize a mesma associação.
O metadata é mesclado, nunca substituído: mandar {"plano": "enterprise"} numa segunda chamada troca só o plano e preserva os outros campos.
name: null, string vazia e metadata: {} não apagam valores existentes. Para manter a chamada realmente idempotente, envie sempre o mesmo nome, metadata, status e origem de consentimento para o mesmo evento externo.
Campos personalizados (metadata)
Section titled “Campos personalizados (metadata)”Os campos que você envia ficam disponíveis nas automações como {{contact.metadata.campo}}.
Regras do objeto:
- Objeto plano. Valores só podem ser texto, número, booleano ou
null— nada de objeto aninhado ou array. - Chaves em
[A-Za-z0-9_], até 64 caracteres.valor_totalfunciona;valor-totalé recusado com400, porque a interpolação{{...}}não alcança chave com hífen — o campo existiria e nunca seria substituído. - No máximo 30 chaves e 8 KB no total.
Campanhas de e-mail não aceitam contact.metadata.* como personalização. O catálogo de campanhas é fechado em {{contact.first_name}}, {{contact.name}} e {{contact.email}}; uma tag diferente bloqueia o agendamento. Se precisar usar metadata num fluxo, faça isso em uma automação.
Double opt-in (status: "pending")
Section titled “Double opt-in (status: "pending")”Com "status": "pending", o contato entra na lista sem receber campanha e sem disparar a automação. Quando a pessoa confirmar, chame o mesmo endpoint sem o status (ou com subscribed): a associação passa a subscribed e é nesse momento que a automação dispara.
| Status | Código | Quando acontece |
|---|---|---|
400 | invalid_request | listId, JSON, corpo, email, name, consent_source, metadata ou status inválido. |
404 | not_found | A lista não existe nesta organização. |
409 | conflict | O endereço está na lista de supressão da organização (bounce definitivo ou reclamação de spam), ou o contato já descadastrou desta lista. |
500 | internal_error | Falha inesperada ao criar/atualizar o contato ou a associação. A operação pode ter sido parcial; repita com os mesmos dados. |
GET /lists/{listId}/contacts
Section titled “GET /lists/{listId}/contacts”Lista os membros, do mais recente para o mais antigo.
Parâmetros de query
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
status | string | — | Filtra por subscribed, unsubscribed ou pending. |
limit | integer | 50 | Máximo de 100. |
offset | integer | 0 | Deslocamento para paginar. |
Esta listagem também não traz total nem cursor. Use inteiros não negativos, avance o offset pelo número de itens recebidos e pare quando o lote vier menor que o limit.
curl -X GET "https://api.squados.io/v1/lists/3fa85f64-5717-4562-b3fc-2c963f66afa6/contacts?status=subscribed&limit=100" \ -H "Authorization: Bearer pk_sua_chave_aqui"Resposta — 200 OK
{ "contacts": [ { "membership_id": "8a1b2c3d-4e5f-6789-abcd-ef0123456789", "status": "subscribed", "consent_source": "formulário de newsletter do rodapé do site", "subscribed_at": "2026-08-20T14:02:00Z", "unsubscribed_at": null, "contact_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "Maria Silva", "metadata": { "plano": "pro", "mrr": 199 } } ], "limit": 50, "offset": 0}O contact_id é o mesmo identificador usado pelos endpoints de tags e pelo painel de contato.
Além dos erros globais, este endpoint devolve 400 invalid_request para listId ou status inválido, 404 not_found para lista indisponível ao token e 500 internal_error quando a consulta dos membros falha.
DELETE /lists/{listId}/contacts/{contactId}
Section titled “DELETE /lists/{listId}/contacts/{contactId}”Descadastra o contato da lista.
curl -X DELETE "https://api.squados.io/v1/lists/3fa85f64-.../contacts/7c9e6679-..." \ -H "Authorization: Bearer pk_sua_chave_aqui"Resposta — 200 OK
{ "membership_id": "8a1b2c3d-...", "status": "unsubscribed", "changed": true }A associação passa a unsubscribed e a trilha de consentimento é preservada — a linha não é apagada. Chamar de novo devolve 200 com "changed": false. O contato continua existindo na organização, com as conversas e tags dele; sai apenas desta lista.
listId ou contactId inválido devolve 400 invalid_request; lista ou associação inexistente devolve 404 not_found; falha ao persistir o descadastro devolve 500 internal_error. O endpoint aceita membros subscribed e pending: nos dois casos o estado final é unsubscribed.
Disparar automação quando o contato entra
Section titled “Disparar automação quando o contato entra”Esta é a razão de existir do endpoint. No editor de automações, use o gatilho “Contato adicionado à lista”:
- Escolha a lista (deixe vazio para valer para qualquer lista da organização).
- Monte o fluxo. Desde o primeiro passo você tem:
{{contact.first_name}},{{contact.display_name}},{{contact.identity_value}}(o e-mail) e{{contact.metadata.campo}}— os campos que você mandou noPOST;{{trigger.payload.list_name}},{{trigger.payload.list_id}},{{trigger.payload.consent_source}}e{{trigger.payload.member_id}}.
- Publique e ligue a automação.
A partir daí, uma associação nova em subscribed ou a transição de pending para subscribed tenta colocar um run na fila de cada automação ativa e publicada cujo gatilho corresponda à lista. Não disparam: entrada em pending, chamada que já encontra o mesmo status e descadastro. A resposta da API confirma a associação; ela não traz run_id nem confirma que a automação terminou.