Pipeline (CRM)
Gerencie pipelines de vendas completos com estagios customizaveis, cards (deals), automacoes de workflow e lead scoring integrado.
Base URL
Todos os endpoints usam o prefixo /api/v1/accounts/{account_id}
Isolamento multi-tenant e seguranca
Toda a API de Pipeline e escopada por conta. Voce nunca consegue ler, alterar ou apagar dados de outra conta — nem por engano, nem de proposito. Entenda como o isolamento funciona antes de automatizar operacoes destrutivas.
O account_id vem da URL, nunca do corpo da requisicao
O {account_id} faz parte do caminho da URL e e validado contra o seu token: a API confirma que o usuario autenticado e membro daquela conta antes de qualquer operacao (caso contrario, 401). Nenhum endpoint le account_id do corpo (body) da requisicao. Por isso nao existe o cenario de "esquecer de setar o account_id" e atingir outra conta — o escopo e sempre derivado da rota autenticada.
IDs no caminho sao resolvidos dentro da sua conta
Todos os identificadores em {id}, display_id e conversation_display_id sao buscados dentro do escopo da conta da URL(Current.account.pipeline_cards.find(...)). Um ID que pertence a outra conta retorna 404 Not Found — nunca o registro alheio.
Atencao: display_id e conversation_display_id sao sequencias por conta(a conta A e a conta B podem ambas ter o card #42), nao identificadores globais. Use sempre o ID retornado pelos endpoints da sua propria conta; nao tente adivinhar/iterar IDs.
Delete nao cascateia entre contas — e e soft por padrao
DELETE /pipeline_cards/{id}e um soft delete (marcadiscarded_at), reversivel. O hard delete exige um passo separado e explicito (permanently_delete, restrito a administradores e so para cards ja descartados).DELETE /pipelines/{id}faz soft delete apenas dos cards daquela conta — nunca dispara uma cascata atingindo cards de outras contas.- Operacoes em lote (
bulk) ereorderfiltram os IDs recebidos pela sua conta antes de agir: qualquer ID de outra conta e silenciosamente ignorado (no-op), nunca apagado nem movido. Oreorderainda exigepipeline_idobrigatorio para reforcar o isolamento entre pipelines.
Pipelines
/api/v1/accounts/{account_id}/pipelinesLista todos os pipelines da conta.
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipelines" \
-H "api_access_token: YOUR_TOKEN" | jq .[
{
"id": 1,
"account_id": 1,
"name": "Vendas B2B",
"description": "Pipeline principal de vendas",
"active": true,
"stages": {
"1_prospeccao": { "id": "1_prospeccao", "name": "Prospeccao", "color": "#3b82f6", "position": 0 },
"1_proposta": { "id": "1_proposta", "name": "Proposta", "color": "#f59e0b", "position": 1 },
"1_ganho": { "id": "1_ganho", "name": "Ganho", "color": "#10b981", "position": 2, "is_won_stage": true },
"1_perdido": { "id": "1_perdido", "name": "Perdido", "color": "#ef4444", "position": 3, "is_lost_stage": true }
},
"settings": { "require_won_value": false, "require_loss_reason": false },
"inbox_id": null,
"default_for_appointments": false,
"created_at": "2025-06-01T00:00:00Z",
"updated_at": "2025-06-01T00:00:00Z"
}
]/api/v1/accounts/{account_id}/pipelinesCria um novo pipeline com estagios.
stages e obrigatorio
O campo stages e obrigatorio e deve ser um objeto (hash), nao um array. Envie pelo menos um estagio ou a API retorna 422. Use sempre Content-Type: application/json.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline.name | string | Sim | Nome do pipeline |
pipeline.description | string | Nao | Descricao |
pipeline.stages | object | Sim | Objeto com estagios. Chaves sao IDs temporarios (string). Apos criacao os IDs sao normalizados para {pipeline_id}_{slug} |
pipeline.stages.{key}.name | string | Sim | Nome do estagio |
pipeline.stages.{key}.color | string | Nao | Cor em hex (ex: "#3B82F6") |
pipeline.stages.{key}.position | integer | Nao | Ordem de exibicao (1 = primeiro) |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipelines" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline": {
"name": "Vendas B2B",
"stages": {
"stage_1": { "name": "Prospeccao", "color": "#6366f1", "position": 1 },
"stage_2": { "name": "Qualificacao", "color": "#3B82F6", "position": 2 },
"stage_3": { "name": "Proposta", "color": "#f59e0b", "position": 3 },
"stage_4": { "name": "Fechamento", "color": "#10b981", "position": 4 }
}
}
}'IDs dos estagios apos criacao
Apos criar o pipeline, os IDs dos estagios sao normalizados pelo servidor para o formato {pipeline_id}_{slug_do_nome} (ex: 1_prospeccao). Use GET /pipelines/{id}/stages para obter os IDs reais antes de criar cards.
/api/v1/accounts/{account_id}/pipelines/{id}Atualiza nome, descricao, estagios ou settings de um pipeline.
ATENCAO: formato do campo stages
Sempre envie stages como objeto (hash) com IDs reais como chaves, nao como array. Array com cada item contendo id tambem e aceito (desde v4.13.0.34), mas array sem id em cada item e rejeitado com 422 e os cards nao sao alterados (defesa contra payloads mal-formatados que poderiam mover todos os cards para a primeira stage nao-terminal).
A rota PATCH /pipelines/{id}/stages/{stage_id} nao existe e retorna 404. Para editar uma stage individual (descricao, cor, nome, posicao), envie PATCH /pipelines/{id} com o hashstagescompleto — o backend faz diff inteligente.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline.name | string | Nao | Nome do pipeline |
pipeline.description | string | Nao | Descricao |
pipeline.active | boolean | Nao | Status de ativacao |
pipeline.stages | object | Nao | Hash com stages keyed por ID real (ex: "1_qualificado"). Array tambem aceito desde que cada item tenha campo id. |
pipeline.settings | object | Nao | Configuracoes do pipeline (JSONB). Chaves desconhecidas sao descartadas. |
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/pipelines/36" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline": {
"stages": {
"36_qualificado": {
"name": "Qualificado",
"position": 1,
"description": "Lead qualificado para proxima etapa",
"color": "#10b981"
},
"36_entrada_de_lead": { "name": "Entrada de Lead", "position": 0 }
}
}
}'Comportamento ao remover stages
Quando uma stage existente e omitida do payload, ela e considerada removida e os cards que estavam nela sao automaticamente movidos para a primeira stage nao-terminal por ordem de position. Cada movimento e registrado em pipeline_stage_histories.
Limites: maximo de 50 stages por pipeline; settings JSON ate 64 KB.
/api/v1/accounts/{account_id}/pipelines/{id}Remove um pipeline. Os cards associados sao soft-deleted (discard), nao apagados.
Escopo de conta e cascata
O id e resolvido dentro da conta autenticada(Current.account.pipelines.find) — nao e um ID global. Tentar deletar um pipeline de outra conta retorna 404.
Ao destruir o pipeline, os cards associados sao soft-deleted(discard!, marcando discarded_at) — nao sao hard-deleted — e pipeline_stage_histories, follow_up_rules e pipeline_webhooks sao removidos em cascata. O proprio registro do pipeline e removido permanentemente; planeje com cuidado antes de chamar.
Estagios do Pipeline
/api/v1/accounts/{account_id}/pipelines/{pipeline_id}/stagesLista os estagios de um pipeline. O id de cada estagio e a string no formato {pipeline_id}_{slug} usada no campo pipeline_stage dos cards.
{
"pipeline_id": 8143,
"pipeline_name": "Clinica Medica - Jornada do Paciente",
"stages": [
{
"id": "8143_entrada_de_lead",
"name": "Entrada de Lead",
"color": "#64748b",
"icon": null,
"position": 0,
"description": "Entrada automatica de leads via inbox",
"is_entry_stage": true,
"is_won_stage": false,
"is_lost_stage": false
},
{
"id": "8143_ganho",
"name": "Ganho",
"color": "#10b981",
"position": 6,
"is_entry_stage": false,
"is_won_stage": true,
"is_lost_stage": false
}
]
}Renomear um estagio muda o id (pipeline_stage)
O id do estagio e derivado do nome ({pipeline_id}_{slug}). Ao renomear um estagio via PATCH /pipelines/{id}, o backend re-slugifica a chave e migra os cards daquele estagio na mesma transacao — ou seja, o valor de pipeline_stage muda. Integracoes que armazenam o id do estagio devem reconsultar GET /pipelines/{id}/stages apos mudancas de configuracao.
Cards (Deals)
Cards representam oportunidades de venda (deals) dentro de um pipeline. Cada card possui valor, contato associado, estagio atual e lead score.
/api/v1/accounts/{account_id}/pipeline_cardsLista cards do kanban com filtros. Existem duas rotas: /pipeline_cards (legacy, paginacao limit/offset) e /pipeline/cards (canonica, paginacao page/per_page). Ambas aceitam o mesmo conjunto completo de filtros avancados (labels, priority, valor, agente, datas, SLA, stages) via o concern compartilhado PipelineCardFilterable.
Filtros avancados (ambas as rotas, via concern compartilhado)
A rota /api/v1/accounts/{id}/pipeline_cards (legacy, paginacao por limit/offset) aceita o conjunto completo de filtros: alem de pipeline_id, pipeline_stage, conversation_display_id e contact_id, ela tambem filtra por labels[], priority[], value_min/value_max, agent_id, date_start/date_end, status, sla_exceeded e stages[]. Os mesmos filtros sao aplicados pelo endpoint de exportacao (GET /pipeline/cards/export).
A rota /api/v1/accounts/{id}/pipeline/cards (canonica, paginacao por page/per_page) aceita os mesmos filtros avancados (mesmo concern), e adicionalmente owner_id e lead_score_category (hot/warm/cold).
Importante: o filtro contact_id foi implementado na v4.12.3.0. Antes dessa versao, ele era silenciosamente ignorado e retornava todos os cards da conta. Se voce tem automacoes que dependem desse filtro, confirme que esta rodando v4.12.3.0 ou superior.
Parametros (rota legacy /pipeline_cards — conjunto completo)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline_id(query) | integer | Nao | Filtrar por pipeline (todos os cards desse funnel) |
pipeline_stage(query) | string | Nao | Filtrar por um estagio especifico (ex: "1_lead") |
conversation_display_id(query) | integer | Nao | Filtrar por conversa associada (display_id, nao ID global) |
contact_id(query) | integer | Nao | Filtrar por contato (v4.12.3.0+) |
labels[](query) | string[] | Nao | Titulos de labels da conversa vinculada ao card. Match OR (qualquer um dos titulos). |
priority[](query) | string[] | Nao | Prioridade do card: none, low, medium, high, urgent. Match OR. |
value_min(query) | number | Nao | Valor minimo do card (>=). Valor do card = expected_revenue ou item_details.value. Cards sem valor algum sao EXCLUIDOS quando value_min/value_max esta presente. |
value_max(query) | number | Nao | Valor maximo do card (<=). Cards sem valor algum sao excluidos (filtrar por valor implica ter valor). |
agent_id(query) | integer | Nao | ID do responsavel (owner) do card. Use -1 ou "unassigned" para cards sem responsavel. |
date_start(query) | string | Nao | Data inicial de criacao (created_at), formato YYYY-MM-DD, interpretada no fuso da conta. |
date_end(query) | string | Nao | Data final de criacao (created_at), formato YYYY-MM-DD, fuso da conta. |
status(query) | string | Nao | Estado do deal: open, won, lost ou closed. |
sla_exceeded(query) | boolean | Nao | true = apenas cards abertos com SLA vencido (sla_overdue). |
stages[](query) | string[] | Nao | Lista de estagios (pipeline_stage). Match OR — cards em qualquer um dos estagios informados. |
limit(query) | integer | Nao | Itens por pagina (default 50, max 500 — valores acima sao limitados a 500) |
offset(query) | integer | Nao | Offset para paginacao(default: 0) |
Parametros extras da rota canonica /pipeline/cards (alem dos filtros avancados acima)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline_id(query) | integer | Nao | Filtrar por pipeline |
pipeline_stage(query) | string | Nao | Filtrar por estagio (ex: "69_lead") |
owner_id(query) | integer | Nao | Filtrar por responsavel (alem de agent_id da tabela acima) |
contact_id(query) | integer | Nao | Filtrar por contato (v4.12.3.0+) |
lead_score_category(query) | string | Nao | hot, warm, cold (exclusivo da canonica) |
page(query) | integer | Nao | Pagina atual(default: 1) |
per_page(query) | integer | Nao | Itens por pagina(default: 25) |
# Filtros avancados na rota legacy: prioridade alta, valor minimo, SLA vencido
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards?pipeline_id=1&priority[]=high&priority[]=urgent&value_min=5000&sla_exceeded=true" \
-H "api_access_token: YOUR_TOKEN" | jq .
# Filtrar por labels, agente e intervalo de datas
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards?labels[]=vip&agent_id=5&date_start=2026-01-01&date_end=2026-06-30" \
-H "api_access_token: YOUR_TOKEN" | jq .
# Cards sem responsavel
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards?agent_id=unassigned&pipeline_id=1" \
-H "api_access_token: YOUR_TOKEN" | jq .
# Canonica com status
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards?status=open&pipeline_id=1" \
-H "api_access_token: YOUR_TOKEN" | jq .{
"data": [
{
"id": 1,
"title": "Contrato Empresa XYZ",
"pipeline_id": 1,
"pipeline_stage": "1_qualificacao",
"position": 1,
"status": "open",
"contact_id": 10,
"owner_id": 5,
"lead_score": 78,
"conversation_display_id": 42,
"item_details": { "value": 15000.00 },
"active_sequence": { "id": 9, "name": "Onboarding", "status": "active", "current_step": 1, "total_steps": 3, "progress_percentage": 33 },
"created_at": "2026-01-10T10:00:00Z"
}
],
"meta": {
"limit": 50,
"offset": 0,
"has_more": false,
"next_cursor": null,
"stage_counts": { "1_lead": 12, "1_qualificacao": 18, "1_ganho": 12 }
}
}Importar Cards (CSV)
Importe cards (deals) em lote a partir de um arquivo CSV. O processamento e assincrono: a API valida e enfileira o import, e o job cria os cards em background. Ambos os endpoints exigem permissao de administrador.
/api/v1/accounts/{account_id}/pipeline/cards/templateBaixa um CSV modelo com os cabecalhos esperados e uma linha de exemplo.
Formato do CSV modelo
A resposta e um arquivo text/csv (download direto — Content-Disposition: attachment, filename pipeline_cards_import_template.csv). A primeira linha contem os cabecalhos e a segunda linha traz um exemplo preenchido.
Colunas (nessa ordem):
title— obrigatoria, titulo do card.stage— estagio inicial do card. Aceita a chave completa (1_contato), o slug (contato) ou o nome legivel sem distincao de caixa/acento (Contato,Negociação). Nao reconhecido → primeira etapa do pipeline.description— descricao livre.expected_revenue— receita esperada (numerico).contact_identifier— identificador externo do contato.contact_email— email do contato.contact_phone— telefone do contato.
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/template" \
-H "api_access_token: YOUR_TOKEN" \
-o pipeline_cards_import_template.csv/api/v1/accounts/{account_id}/pipeline/cards/importEnvia um CSV para importar cards em lote (multipart/form-data). Processamento assincrono.
multipart/form-data — campos obrigatorios
Envie como multipart/form-data. O arquivo CSV vai no campo import_file e o pipeline alvo no campo pipeline_id. O pipeline_id e resolvido dentro da conta autenticada — um pipeline de outra conta (ou ausente) retorna 422.
Validacao do arquivo (limites)
O upload e validado antes de enfileirar: tamanho maximo de 10MB; content-type aceito text/csv, text/plain, application/csv ou application/vnd.ms-excel; e a primeira linha precisa conter o cabecalho do template (coluna title). Arquivo fora dessas regras retorna 422.
Body (multipart/form-data)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
import_file(body) | file | Sim | Arquivo CSV no formato do template (cabecalhos na 1a linha). Maximo 10MB. |
pipeline_id(body) | integer | Sim | ID do pipeline alvo onde os cards serao criados (escopado a conta). |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/import" \
-H "api_access_token: YOUR_TOKEN" \
-F "import_file=@pipeline_cards.csv" \
-F "pipeline_id=1"{
"message": "Importacao iniciada. Os cards serao criados em segundo plano.",
"import_id": 42
}{ "error": "Pipeline obrigatorio para importar cards." }Exportar Cards (CSV)
Exporta os cards da conta como um arquivo CSV, respeitando os mesmos filtros aceitos pela rota de listagem. O resultado e escopado a conta autenticada (a visibilidade por pipeline do usuario tambem se aplica).
/api/v1/accounts/{account_id}/pipeline/cards/exportExporta os cards filtrados como CSV (download direto). Aceita os mesmos query params da listagem.
Filtros aceitos
Os mesmos filtros da rota legacy de listagem se aplicam (query params): pipeline_id, pipeline_stage, conversation_display_id, contact_id, labels[], priority[], value_min/value_max, agent_id, date_start/date_end, status, sla_exceeded e stages[]. Sem filtros, exporta todos os cards visiveis da conta.
A resposta e um arquivo text/csv (Content-Disposition: attachment, filename pipeline_cards_export_<timestamp>.csv, com o timestamp no fuso da conta).
Limite de linhas e protecao anti-formula
O export sincrono e limitado a 20.000 cards. Acima disso a API retorna 422 — aplique filtros para reduzir o resultado.
Celulas de texto que comecam com =, @, + ou - (e nao sao numeros/telefones) sao prefixadas com apostrofo (') para neutralizar injecao de formula em planilhas (CSV injection).
Colunas do CSV exportado
id, title, pipeline_id, stage, status, priority, value, currency, owner_name, contact_name, contact_email, contact_phone, conversation_display_id, labels, lead_score, lead_score_category, created_at, won_at, lost_at, deadline. Datas em ISO 8601 no fuso da conta; labels sao separadas por |.
# Exportar todos os cards de um pipeline com status aberto
curl -s "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/export?pipeline_id=1&status=open" \
-H "api_access_token: YOUR_TOKEN" \
-o pipeline_cards_export.csv/api/v1/accounts/{account_id}/pipeline_cardsCria um novo card (deal) no pipeline.
Como obter o pipeline_stage
O campo pipeline_stage e uma string no formato {pipeline_id}_{slug} (ex: "1_prospeccao"). Use GET /pipelines/{id}/stages para listar os IDs reais dos estagios do seu pipeline antes de criar cards.
Body envolto em pipeline_card
Envie sempre os campos dentro do wrapper pipeline_card. A rota legacy /pipeline_cards tolera campos no nivel raiz (sao re-embrulhados automaticamente via wrap_parameters e o card e criado normalmente), mas a rota canonica POST /pipeline/cards e estrita: sem o wrapper ela retorna 422 (param is missing: pipeline_card). Use sempre o wrapper para um comportamento consistente entre as duas rotas.
Body (pipeline_card)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
title | string | Nao | Titulo do deal no nivel raiz. Opcional — se omitido, o card usa item_details.title (ou fica null). |
pipeline_id | integer | Sim | ID do pipeline |
pipeline_stage | string | Sim | ID do estagio no formato "{pipeline_id}_{slug}" (ex: "1_prospeccao"). Obtenha via GET /pipelines/{id}/stages |
contact_id | integer | Nao | ID do contato associado (resolvido dentro da conta autenticada) |
conversation_display_id | integer | Nao | Display ID da conversa associada (per-account, nao ID global) |
owner_id | integer | Nao | ID do responsavel |
expected_revenue | number | Nao | Receita esperada do deal |
deadline | string | Nao | Data prevista de fechamento (ISO 8601) |
item_details | object | Nao | JSONB freeform. title, value e currency do card vivem aqui. |
custom_attributes | object | Nao | Atributos personalizados |
# Primeiro obtenha os IDs dos estagios:
# curl -s ".../api/v1/accounts/1/pipelines/1/stages" -H "api_access_token: TOKEN"
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline_card": {
"title": "Contrato Empresa XYZ",
"pipeline_id": 1,
"pipeline_stage": "1_prospeccao",
"contact_id": 10,
"owner_id": 5,
"item_details": {
"title": "Contrato Empresa XYZ",
"value": 15000.00,
"currency": "BRL"
},
"deadline": "2026-03-15T00:00:00Z"
}
}'/api/v1/accounts/{account_id}/pipeline_cards/{id}Retorna detalhes completos de um card incluindo timeline e score.
/api/v1/accounts/{account_id}/pipeline_cards/{id}Atualiza dados de um card. Body envolto em pipeline_card.
Body (pipeline_card)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
title | string | Nao | Titulo do card |
pipeline_stage | string | Nao | Estagio (formato "{pipeline_id}_{slug}"). Para mover entre estagios prefira POST /move_to_stage. |
owner_id | integer | Nao | ID do responsavel |
contact_id | integer | Nao | ID do contato |
expected_revenue | number | Nao | Receita esperada (coluna do card) |
deadline | string | Nao | Data prevista de fechamento (ISO 8601) |
forecast_close_date | string | Nao | Data de previsao de fechamento (ISO 8601) |
item_details | object | Nao | JSONB freeform. value, currency e deadline_at vivem aqui. Faz deep-merge com o valor atual — envie apenas as chaves a alterar. |
custom_attributes | object | Nao | Atributos personalizados (JSONB) |
Campos de valor: expected_revenue vs item_details.value vs won_value
Existem tres representacoes de valor, independentes:
expected_revenue— coluna numerica canonica do card (receita esperada). Use este para o valor previsto do deal.item_details.value— campo legado (string, JSONB), alimentado pelo formulario do frontend. A rota legacy aceitavalueedeadline_atno nivel raiz por compatibilidade e os roteia paraitem_detailsautomaticamente (deadline_attambem grava a colunadeadline). Nao confie nele como fonte canonica de valor.won_value— preenchido apenas ao fechar o deal viaPOST .../deal_status/mark_won(valor fechado).
curl -X PATCH "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards/5" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline_card": {
"title": "Contrato Empresa XYZ - revisado",
"owner_id": 5,
"item_details": { "value": 18000 }
}
}'/api/v1/accounts/{account_id}/pipeline_cards/{id}Remove um card (soft delete).
Soft delete escopado a conta
O id e resolvido dentro da conta autenticada(Current.account.pipeline_cards.find). Nao e um ID global — tentar deletar um card de outra conta retorna 404. A operacao e um soft delete (discarded_at); o card pode ser visto em GET /pipeline/cards/discarded e restaurado via POST /pipeline/cards/{id}/restore. A remocao definitiva exige um passo separado e explicito (DELETE /pipeline/cards/{id}/permanently_delete, so para cards ja descartados).
Lixeira por pipeline: GET /pipeline/cards/discarded e admin-only e aceita o parametro opcional pipeline_id(filtra a lixeira daquele pipeline; pipeline_id invalido/de outra conta retorna 404). Sem pipeline_id, lista a lixeira da conta inteira. Cada card retorna discarded_at, discarded_by, discard_reason e days_until_permanent_deletion (contagem regressiva ate a exclusao definitiva, respeitando a retencao da conta — default 30 dias).
Contatos e Conversas adicionais (multi-vinculo)
Alem do contato e da conversa primarios do card (contact_id / conversation_display_id), um card pode ter contatos e conversas adicionais vinculados. Isso permite representar um deal com varios envolvidos (ex: decisor + influenciador) ou varias conversas relacionadas. Os vinculos primarios nao mudam — estes endpoints apenas adicionam/removem vinculos extras (join aditivo, many-to-many).
Aditivo e retrocompativel — o vinculo primario nao muda
O contato primario continua em contact_id/contact e a conversa primaria em conversation_display_id. Os vinculos adicionais aparecem em dois novos campos aditivos do recurso pipeline_card: additional_contacts e additional_conversations. O vinculo primario e deduplicado para fora desses arrays (nunca aparece duas vezes).
Novos campos no recurso do card
{
"id": 5,
"contact_id": 10,
"conversation_display_id": 42,
"additional_contacts": [
{
"id": 3,
"contact_id": 27,
"name": "Joao Comprador",
"email": "joao@empresa.com",
"phone_number": "+5511999999999",
"avatar_url": "",
"role": "decisor"
}
],
"additional_conversations": [
{ "id": 8, "conversation_display_id": 57 }
]
}id do vinculo != id do recurso
Em additional_contacts/additional_conversations, o campo id e o ID do registro de vinculo (join), nao o contact_id nem o conversation_display_id. Use esse id de vinculo nas rotas DELETE abaixo.
Contatos adicionais
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/contactsVincula um contato adicional (nao-primario) ao card. Requer permissao de update no card.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
contact_id | integer | Sim | ID do contato a vincular (resolvido dentro da conta autenticada) |
role | string | Nao | Rotulo livre do papel do contato no deal (ex: "decisor", "influenciador") |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/contacts" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "contact_id": 27, "role": "decisor" }'{
"data": {
"id": 3,
"contact_id": 27,
"name": "Joao Comprador",
"email": "joao@empresa.com",
"phone_number": "+5511999999999",
"avatar_url": "",
"role": "decisor"
}
}Erros
404 se o card ou o contato nao existir na conta; 422 se o contato ja estiver vinculado a este card (seja como contato primario ou como adicional ja existente). Entradas invalidas nunca retornam 500.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/contacts/{id}Remove um vinculo de contato adicional. Nao afeta o contato primario (contact_id) do card.
O {id} e o ID do vinculo
O {id} na rota e o id do registro de vinculo (o campo id de um item de additional_contacts), nao o contact_id. Retorna 204 No Content em sucesso; 404 se o vinculo nao existir neste card.
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/contacts/3" \
-H "api_access_token: YOUR_TOKEN"Conversas adicionais
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/conversationsVincula uma conversa adicional (nao-primaria) ao card. Requer permissao de update no card.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
conversation_display_id | integer | Sim | Display ID da conversa a vincular (per-account, nao ID global) |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/conversations" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "conversation_display_id": 57 }'{
"data": {
"id": 8,
"conversation_display_id": 57
}
}Erros
404 se o card ou a conversa nao existir na conta; 422 se a conversa ja estiver vinculada a este card (seja como conversa primaria ou como adicional ja existente). Entradas invalidas nunca retornam 500.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/conversations/{id}Remove um vinculo de conversa adicional. Nao afeta a conversa primaria (conversation_display_id) do card.
O {id} e o ID do vinculo
O {id} na rota e o id do registro de vinculo (o campo id de um item de additional_conversations), nao o conversation_display_id. Retorna 204 No Content em sucesso; 404 se o vinculo nao existir neste card.
curl -X DELETE "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/conversations/8" \
-H "api_access_token: YOUR_TOKEN"Mover Card entre Estagios
/api/v1/accounts/{account_id}/pipeline_cards/{id}/move_to_stageMove um card para outro estagio do pipeline.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline_stage | string | Sim | Identificador do estagio destino no formato "{pipeline_id}_{stage_slug}" (ex: "2_qualificao"). Use GET /pipelines/{id}/stages para obter os identificadores disponiveis. |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards/5/move_to_stage" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "pipeline_stage": "1_qualificacao" }'/api/v1/accounts/{account_id}/pipeline_cards/reorderReordena cards dentro de um estagio.
pipeline_id e obrigatorio (isolamento)
O campo pipeline_id e obrigatorio — sem ele a API retorna 400. Ele garante que o reorder so afeta cards daquele pipeline especifico da sua conta; cards descartados e cards de outros pipelines sao ignorados. Cada entrada de positions tambem pode carregar pipeline_id proprio.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
pipeline_id | integer | Sim | ID do pipeline cujos cards serao reordenados (escopo obrigatorio) |
positions | array | Sim | Array de objetos { id, position }. Apenas cards do pipeline_id informado e da conta autenticada sao afetados. |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline_cards/reorder" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline_id": 1,
"positions": [
{ "id": 5, "position": 0 },
{ "id": 9, "position": 1 }
]
}'Status do Deal
/api/v1/accounts/{account_id}/pipeline/cards/{id}/deal_status/mark_wonMarca o deal como ganho. Requer que o pipeline tenha etapas Ganho/Perdido habilitadas.
Body (opcional)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
won_value | number | Nao | Valor final fechado (numerico). Valor invalido retorna 422. |
won_note | string | Nao | Nota sobre o ganho |
winning_offer_index | integer | Nao | Indice da oferta vencedora (quando o card tem ofertas em item_details) |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/deal_status/mark_won" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "won_value": 15000, "won_note": "Fechado no plano anual" }'Resposta uniforme
mark_won, mark_lost e reopen retornam o mesmo shape: { id, status, pipeline_stage, won_at, won_value, won_note, lost_at, lost_reason, item_details }. Card ja ganho retorna 409.
/api/v1/accounts/{account_id}/pipeline/cards/{id}/deal_status/mark_lostMarca o deal como perdido.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
lost_reason | string | Nao | Motivo da perda |
/api/v1/accounts/{account_id}/pipeline/cards/{id}/deal_status/reopenReabre um deal marcado como ganho ou perdido.
Timeline do Card
/api/v1/accounts/{account_id}/pipeline/cards/{id}/timelineRetorna a timeline de atividades do card.
{
"timeline": [
{
"date": "2026-01-15",
"activities": [
{
"id": 1780508401,
"type": "history_event",
"event_type": "stage_changed",
"details": {
"user": { "id": 5, "name": "Maria Santos", "avatar_url": "" },
"old_stage": "1_prospeccao",
"new_stage": "1_qualificacao"
},
"user": { "id": 5, "name": "Maria Santos", "avatar_url": "" },
"created_at": "2026-01-15T14:30:00Z",
"timestamp": "2026-01-15T14:30:00Z"
}
]
}
]
}Anexos do Card
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/attachmentsLista anexos de um card.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/attachmentsAdiciona um anexo ao card.
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/5/attachments" \
-H "api_access_token: YOUR_TOKEN" \
-F "file=@/path/to/proposta.pdf"/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/attachments/{id}Remove um anexo.
Sequencias de Atividade no Card
Inscreve um card numa sequencia de atividades (cadencia automatizada de etapas, definida previamente em pipeline/activity_sequences). Sequencias estao incluidas em toda licenca NooviChat valida; o acesso depende das permissoes do usuario e da configuracao operacional da conta. Um card so pode ter uma sequencia ativa/pausada da mesma definicao por vez (422 em duplicidade).
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequencesLista as sequencias inscritas no card.
{
"data": [
{
"id": 14,
"status": "active",
"current_step": 1,
"total_steps": 4,
"progress_percentage": 25,
"definition": { "id": 3, "name": "Cadencia de prospeccao" },
"next_step_at": "2026-06-15T12:00:00-03:00",
"steps_log": [],
"created_at": "2026-06-13T09:00:00-03:00",
"updated_at": "2026-06-13T09:00:00-03:00"
}
]
}/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequencesInicia uma sequencia no card.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
definition_id | integer | Sim | ID de uma activity_sequence ATIVA da conta |
Respostas
201 com a sequencia criada;404 se a definicao nao existir/estiver inativa; 422 se ja houver uma sequencia ativa/pausada da mesma definicao no card.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequences/external_startPonto de entrada para integracoes externas (ex: n8n). Inicia uma sequencia e aceita um contexto livre.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
definition_id | integer | Sim | ID de uma activity_sequence ativa |
context | object | Nao | Objeto livre com dados da origem externa. DEVE ser um objeto — um valor nao-objeto retorna 400. |
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequences/{id}/pausePausa a execucao ativa da sequencia. 422 se nao houver execucao ativa.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequences/{id}/resumeRetoma uma sequencia pausada. 422 se nao houver execucao pausada.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequences/{id}/complete_stepAvanca manualmente para a proxima etapa da sequencia. 422 se nao houver execucao ativa ou se nao restarem etapas.
/api/v1/accounts/{account_id}/pipeline/cards/{card_id}/sequences/{id}Cancela a sequencia no card (marca como cancelled). Retorna 200.
Operacoes em Lote
Operacao em massa — escopo de conta
O bulk_assign opera apenas sobre cards da conta autenticada(Current.account.pipeline_cards.where(id: item_ids)). IDs de cards de outras contas sao silenciosamente ignorados — nao ha vazamento cross-tenant. O lote e limitado a 200 cards por requisicao; passar mais retorna 400. Esta operacao apenas reatribui responsavel — nao remove nem move cards.
/api/v1/accounts/{account_id}/pipeline/cards/bulk_assignAtribui responsavel para varios cards de uma vez (max 200).
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
item_ids | array | Sim | IDs dos cards (max 200, escopados a conta autenticada) |
owner_id | integer | Nao | ID do novo responsavel. Omita junto com distribution para usar round_robin/workload_balanced. |
distribution | string | Nao | round_robin ou workload_balanced — distribui automaticamente entre agentes online. Se omitido, usa owner_id. |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/cards/bulk_assign" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "item_ids": [5, 9, 12], "owner_id": 3 }'/api/v1/accounts/{account_id}/pipeline/cards/{id}/assignAtribui um responsavel a um card especifico.
Body
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
owner_id | integer | Nao | ID do responsavel. Omita para desatribuir o card. |
notify | boolean | Nao | Enviar notificacao ao novo responsavel |
Automacoes do Pipeline
Crie workflows automatizados que executam acoes quando cards mudam de estagio, atingem determinado score ou atendem condicoes especificas.
/api/v1/accounts/{account_id}/pipeline/automationsLista todas as automacoes do pipeline. Retorna um array de automacoes (cada uma com objeto stats agregado).
[
{
"id": 1,
"name": "Notificar ao entrar em Negociacao",
"description": "Avisa o responsavel quando um card chega na negociacao",
"active": true,
"trigger_type": "event",
"pipeline_id": 1,
"trigger": { "event": "stage_change", "to_stage": "1_negociacao" },
"conditions": [],
"actions": [{ "type": "send_notification", "to": "owner" }],
"flow": { "nodes": [], "connections": [], "viewport": { "x": 0, "y": 0, "zoom": 1 } },
"stats": {
"total_executions": 23,
"success_count": 22,
"failure_count": 1,
"success_rate": 96
}
}
]/api/v1/accounts/{account_id}/pipeline/automationsCria uma nova automacao. Body envolto em pipeline_automation.
Body envolto em pipeline_automation
Os campos devem estar dentro do wrapper pipeline_automation. trigger_type aceita: event, webhook, scheduled ou manual.
Body (pipeline_automation)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
name | string | Sim | Nome da automacao |
description | string | Nao | Descricao |
active | boolean | Nao | Se a automacao esta ativa |
trigger_type | string | Nao | event | webhook | scheduled | manual |
pipeline_id | integer | Nao | ID do pipeline alvo |
trigger | object | Nao | Configuracao do gatilho (ex: { event, to_stage }) |
conditions | array | Nao | Array de condicoes para acionar |
actions | array | Nao | Array de acoes a executar |
schedule_config | object | Nao | Configuracao de agendamento (quando trigger_type = scheduled) |
flow | object | Nao | Editor de fluxo visual: { nodes, connections, viewport } |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/automations" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline_automation": {
"name": "Notificar em Negociacao",
"active": true,
"trigger_type": "event",
"pipeline_id": 1,
"trigger": { "event": "stage_change", "to_stage": "1_negociacao" },
"conditions": [],
"actions": [{ "type": "send_notification", "to": "owner" }],
"flow": { "nodes": [], "connections": [], "viewport": { "x": 0, "y": 0, "zoom": 1 } }
}
}'/api/v1/accounts/{account_id}/pipeline/automations/{id}Atualiza uma automacao.
/api/v1/accounts/{account_id}/pipeline/automations/{id}Remove uma automacao.
/api/v1/accounts/{account_id}/pipeline/automations/{id}/duplicateClona uma automacao existente.
Executar e Testar
/api/v1/accounts/{account_id}/pipeline/automations/{id}/executeExecuta manualmente uma automacao.
/api/v1/accounts/{account_id}/pipeline/automations/{id}/dry_runExecuta simulacao sem efeito real (teste).
Dry Run
Permite testar a automacao sem executar as acoes reais. Retorna o resultado esperado.
/api/v1/accounts/{account_id}/pipeline/automations/{id}/validateValida a configuracao da automacao.
/api/v1/accounts/{account_id}/pipeline/automations/{id}/executionsHistorico de execucoes da automacao.
Estatisticas de Automacao
/api/v1/accounts/{account_id}/pipeline/automations/{id}/statsMetricas de uma automacao especifica.
/api/v1/accounts/{account_id}/pipeline/automations/dashboardDashboard de todas as automacoes.
/api/v1/accounts/{account_id}/pipeline/automations/{id}/audit_logsLogs de auditoria da automacao.
Webhooks do Pipeline
Receba eventos do Pipeline em tempo real numa URL externa. Gerenciar webhooks requer permissao de administrador.
A URL precisa ser publica
A URL do webhook deve resolver para um IP publico. Enderecos privados/internos (127.0.0.0/8, 10/8, 172.16/12, 192.168/16, 169.254/16, IPv6 ULA/link-local) sao bloqueados por protecao anti-SSRF e a criacao retorna 422 — a verificacao tambem roda no momento do envio.
/api/v1/accounts/{account_id}/pipeline/webhooksCria um webhook. Body envolto em pipeline_webhook.
Body (pipeline_webhook)
| Nome | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
name | string | Sim | Nome do webhook |
url | string | Sim | URL https publica que recebera os POSTs |
events | string[] | Sim | Subconjunto dos eventos disponiveis (ver abaixo). Evento invalido retorna 422. |
curl -X POST "https://chat.seudominio.com/api/v1/accounts/1/pipeline/webhooks" \
-H "api_access_token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pipeline_webhook": {
"name": "Integracao CRM",
"url": "https://example.com/hooks/pipeline",
"events": ["pipeline_card_won", "pipeline_card_lost"]
}
}'{ "id": 71, "name": "Integracao CRM", "url": "https://example.com/hooks/pipeline", "events": ["pipeline_card_won", "pipeline_card_lost"], "active": true, "secret": "<64 hex>" }/api/v1/accounts/{account_id}/pipeline/webhooksLista os webhooks (admin).
/api/v1/accounts/{account_id}/pipeline/webhooks/{id}Detalhes de um webhook.
/api/v1/accounts/{account_id}/pipeline/webhooks/{id}Atualiza name, url, events ou active.
/api/v1/accounts/{account_id}/pipeline/webhooks/{id}Remove o webhook.
/api/v1/accounts/{account_id}/pipeline/webhooks/{id}/testDispara um POST de teste sincrono para a URL configurada.
/api/v1/accounts/{account_id}/pipeline/webhooks/{id}/regenerate_secretGera um novo secret HMAC (invalida o anterior).
Eventos disponiveis
Sete eventos:
pipeline_card_created— card criadopipeline_card_updated— card atualizadopipeline_card_deleted— card removido (soft delete)pipeline_card_stage_changed— mudou de estagio (incluifrom_stage/to_stage)pipeline_card_won— marcado como ganho (incluiwon_at,pre_won_stage)pipeline_card_lost— marcado como perdido (incluilost_reason,pre_lost_stage)pipeline_card_owner_changed— responsavel alterado
Formato do payload
{
"event": "pipeline_card_won",
"timestamp": "2026-06-03T23:21:38Z",
"data": {
"card": { "id": 12473, "pipeline_id": 8143, "pipeline_stage": "8143_ganho", "won_at": "...", "contact_id": 21, "item_details": { ... } }
}
}Verificacao da assinatura (HMAC)
Cada POST inclui o header X-Pipeline-Webhook-Signature: sha256=<hex>, onde <hex> e o HMAC-SHA256 do corpo JSON cru usando o secret do webhook (64 hex). Recalcule e compare para garantir a autenticidade:
import crypto from "crypto";
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-pipeline-webhook-signature"]));