Documentação da API do Atlas

Referência completa da API REST do Atlas, o CRM da Skill-Hub. Descreve exatamente o que a API faz hoje em produção.

URL base

https://atlas.skill-hub.app/api/v1

API REST do Atlas, o CRM da Skill-Hub.

Esta especificação descreve exatamente o que a API faz hoje em produção. Onde existe uma inconsistência entre recursos, ela está documentada em vez de escondida.

Autenticação

Gere um token em Configurações → Tokens de API dentro do Atlas. O valor é exibido uma única vez, no momento da criação. São 40 caracteres.

curl 'https://atlas.skill-hub.app/api/v1/contacts' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

O cabeçalho Accept não é opcional

Sem Accept: application/json, um request sem token ou com token inválido recebe um 302 para a tela de login em HTML, em vez de um 401 em JSON. É a causa mais comum de integração que parece "não autenticar".

O acesso também exige que a licença do ambiente tenha a API habilitada e que o addon de acesso à API esteja ativo na conta. Sem isso, todo request responde 403.

Escopos do token

Cada token carrega um ou mais escopos, escolhidos na criação. Escopo não se edita: para mudar, revogue o token e crie outro. Cada endpoint abaixo mostra o escopo que exige.

EscopoDá direito a
escopo readLeitura. Todos os GET.
escopo writeCriar, alterar e apagar dados: contatos, conversas (status e atribuição), faturas, propostas, agendamentos, produtos e webhooks.
escopo sendDisparos que saem para o cliente ou para terceiros e consomem crédito: POST /messages/send, POST /conversations/{id}/messages, POST /wa-connector/send-template e POST /webhooks/{id}/test.

Por que send é separado

Um token só de leitura e escrita nunca dispara mensagem por acidente. write não implica send. Marque send apenas em integrações que precisam enviar.

Tokens criados antes dos escopos têm acesso total e continuam valendo para tudo. Sem o escopo exigido, a resposta é 403 com código de máquina:

{
  "error": "Este token não tem o escopo 'send'. Crie um token com esse escopo em Configurações → Tokens de API.",
  "code": "insufficient_scope",
  "required_scope": "send"
}

Convenções

Escopo de dados

O tenant_id nunca é aceito no corpo de um request. Ele vem do token. Um recurso que pertença a outra conta responde 404, e não 403, por decisão de segurança.

Limite de requisições

60 requisições por minuto, por token, em janela fixa de 60 segundos. Toda resposta traz X-RateLimit-Limit e X-RateLimit-Remaining. Ao estourar, a resposta é 429 com Retry-After: 60.

Paginação

A maior parte das listagens aceita page e per_page (padrão 50, teto 100) e devolve um envelope meta.

{
  "data": [],
  "meta": { "current_page": 1, "last_page": 10, "per_page": 50, "total": 500 }
}

Três exceções: /webhooks e /wa-connector/* não paginam, e /conversations/{id}/messages usa cursor (before_id, after_id, limit) com has_more no lugar de meta.

Datas e valores

Datas em ISO 8601 com offset, por exemplo 2026-09-03T19:57:10-03:00. As exceções são connected_at e last_synced_at do WhatsApp Connector, que vêm em UTC com sufixo Z. Datas puras usam YYYY-MM-DD. Valores monetários são devolvidos como número, não como texto.

Erros

A API usa dois formatos de erro, por razões históricas. Ambos estão documentados aqui porque ambos acontecem.

Formato geral

{ "error": "Contact not found." }

Validação, formato do framework

{
  "message": "The name field is required.",
  "errors": { "name": ["The name field is required."] }
}

Autenticação

{ "message": "Unauthenticated." }
CódigoSignificado
401Token ausente, inválido ou expirado.
403Token sem o escopo exigido: corpo { "error": "…", "code": "insufficient_scope", "required_scope": "send" }. Ou conta sem vínculo, licença sem API habilitada, ou addon de acesso à API inativo.
404Recurso inexistente ou de outra conta.
422Falha de validação, ou regra de negócio que impede a operação.
429Limite de requisições estourado.
500Erro interno.

Contatos

Pessoas e empresas, seus negócios e conversas.

GET/contactsescopo read

Listar contatos

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
typequerylead | customer | prospect | supplier

Igualdade exata.

person_typequeryindividual | company
searchquerystring

Busca parcial, sem diferenciar maiúsculas, em nome, nome fantasia, e-mail, telefone, celular e documento.

group_idqueryinteger
assigned_toqueryinteger
sourcequerystring

Igualdade exata e sensível a maiúsculas. A base contém grafias diferentes para a mesma origem, então prefira listar e filtrar do seu lado quando precisar de precisão.

is_activequeryboolean
tagsqueryarray<string>

Array ou lista separada por vírgula. As tags são combinadas com E, não com OU.

sort_byquerypadrão created_atname | email | type | created_at | updated_at | last_contact_at
sort_dirquerypadrão descasc | desc

Qualquer valor diferente de asc é tratado como desc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/contacts' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
{
  "data": [
    {
      "id": 1001,
      "name": "Maria Exemplo",
      "trading_name": null,
      "display_name": "Maria Exemplo",
      "person_type": "individual",
      "type": "lead",
      "email": "[email protected]",
      "phone": null,
      "mobile": "(11) 91234-5678",
      "document": null,
      "source": null,
      "tags": [],
      "is_active": true,
      "group": null,
      "assigned_to": null,
      "deals_count": 0,
      "contracts_count": 0,
      "created_at": "2026-09-03T19:57:10-03:00",
      "updated_at": "2026-09-03T19:57:10-03:00"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 12,
    "per_page": 50,
    "total": 580
  }
}
POST/contactsescopo write

Criar contato

Aplica os padrões type = lead e person_type = individual quando os campos não são enviados, e grava o usuário do token em created_by.

Campos de rastreamento (utm_*, captured_at) não são aceitos.

Corpo

CampoTipoObservação
nameobrigatóriomáx 255string
trading_namemáx 255string?
emailmáx 255string (email)?
phonemáx 50string?
mobilemáx 50string?
websitemáx 255string?
companymáx 255string?
positionmáx 255string?
documentmáx 50string?
state_registrationmáx 50string?
municipal_registrationmáx 50string?
person_typepadrão individualindividual | company
typepadrão leadlead | customer | prospect | supplier
sourcemáx 100string?
contact_group_idinteger?
assigned_tointeger?
parent_idinteger?
addressmáx 255string?
address_numbermáx 20string?
address_complementmáx 100string?
neighborhoodmáx 100string?
citymáx 100string?
statemáx 50string?
zip_codemáx 20string?
countrymáx 100string?
birth_datestring (date)?
tagsarray<string>
notesmáx 5000string?
custom_fieldsobject

{chave: valor} com as chaves do módulo de Campos Personalizados (GET /custom-fields). cf_chave também vale (dentro de custom_fields ou solto na raiz do corpo). Formato aceito por tipo: - text, textarea: texto (respeita tamanho mínimo e máximo do campo) - select, radio: o valor ou o rótulo de uma opção, sem diferenciar maiúsculas - multiselect: lista de valores ou rótulos das opções - checkbox: true/false ou lista de valores das opções - number, rating: número inteiro - decimal, currency: número (1234.56) ou texto "1.234,56" - date: AAAA-MM-DD - datetime: ISO 8601 (2026-09-30T14:00:00-03:00) - url: começando com http:// ou https:// - email, phone, color (#RRGGBB) - file, image: não gravados pela API (enviar pela tela)

Campos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas mudam e null apaga o valor do campo. Chave inexistente: 422 com unknown_keys (contas em modo compatível recebem warnings e a chave vai para legacy_custom_fields).

notification_settingsobject?

Respostas

201

Criado. Devolve a representação detalhada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

422

Validação, incluindo campos personalizados. Quando algum campo é inválido ou a chave não existe, nada do request é gravado.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/contacts' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Maria Exemplo", "email": "[email protected]", "mobile": "11912345678", "type": "lead", "person_type": "individual", "tags": ["campanha-setembro"]}'
GET/contacts/{contact}escopo read

Obter contato

Devolve a representação detalhada, incluindo employees quando o contato for uma empresa com pessoas vinculadas.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger

Respostas

200

OK

401

Token ausente, inválido ou expirado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/contacts/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/contacts/{contact}escopo write

Atualizar contato

Grava o usuário do token em updated_by. Aceita dois campos que a criação não aceita, is_active e bot_protected. PATCH faz o mesmo. Campos personalizados são parciais: só as chaves enviadas mudam e null apaga o valor do campo.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger

Corpo

CampoTipoObservação
nameobrigatóriomáx 255string
trading_namemáx 255string?
emailmáx 255string (email)?
phonemáx 50string?
mobilemáx 50string?
websitemáx 255string?
companymáx 255string?
positionmáx 255string?
documentmáx 50string?
state_registrationmáx 50string?
municipal_registrationmáx 50string?
person_typepadrão individualindividual | company
typepadrão leadlead | customer | prospect | supplier
sourcemáx 100string?
contact_group_idinteger?
assigned_tointeger?
parent_idinteger?
addressmáx 255string?
address_numbermáx 20string?
address_complementmáx 100string?
neighborhoodmáx 100string?
citymáx 100string?
statemáx 50string?
zip_codemáx 20string?
countrymáx 100string?
birth_datestring (date)?
tagsarray<string>
notesmáx 5000string?
custom_fieldsobject

{chave: valor} com as chaves do módulo de Campos Personalizados (GET /custom-fields). cf_chave também vale (dentro de custom_fields ou solto na raiz do corpo). Formato aceito por tipo: - text, textarea: texto (respeita tamanho mínimo e máximo do campo) - select, radio: o valor ou o rótulo de uma opção, sem diferenciar maiúsculas - multiselect: lista de valores ou rótulos das opções - checkbox: true/false ou lista de valores das opções - number, rating: número inteiro - decimal, currency: número (1234.56) ou texto "1.234,56" - date: AAAA-MM-DD - datetime: ISO 8601 (2026-09-30T14:00:00-03:00) - url: começando com http:// ou https:// - email, phone, color (#RRGGBB) - file, image: não gravados pela API (enviar pela tela)

Campos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas mudam e null apaga o valor do campo. Chave inexistente: 422 com unknown_keys (contas em modo compatível recebem warnings e a chave vai para legacy_custom_fields).

notification_settingsobject?
is_activeboolean?
bot_protectedboolean?

Respostas

200

Atualizado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Validação, incluindo campos personalizados. Quando algum campo é inválido ou a chave não existe, nada do request é gravado.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/contacts/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PATCH/contacts/{contact}escopo write

Atualizar contato (parcial)

Igual ao PUT. Só os campos enviados mudam; em custom_fields, null apaga o valor do campo.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger

Corpo

CampoTipoObservação
nameobrigatóriomáx 255string
trading_namemáx 255string?
emailmáx 255string (email)?
phonemáx 50string?
mobilemáx 50string?
websitemáx 255string?
companymáx 255string?
positionmáx 255string?
documentmáx 50string?
state_registrationmáx 50string?
municipal_registrationmáx 50string?
person_typepadrão individualindividual | company
typepadrão leadlead | customer | prospect | supplier
sourcemáx 100string?
contact_group_idinteger?
assigned_tointeger?
parent_idinteger?
addressmáx 255string?
address_numbermáx 20string?
address_complementmáx 100string?
neighborhoodmáx 100string?
citymáx 100string?
statemáx 50string?
zip_codemáx 20string?
countrymáx 100string?
birth_datestring (date)?
tagsarray<string>
notesmáx 5000string?
custom_fieldsobject

{chave: valor} com as chaves do módulo de Campos Personalizados (GET /custom-fields). cf_chave também vale (dentro de custom_fields ou solto na raiz do corpo). Formato aceito por tipo: - text, textarea: texto (respeita tamanho mínimo e máximo do campo) - select, radio: o valor ou o rótulo de uma opção, sem diferenciar maiúsculas - multiselect: lista de valores ou rótulos das opções - checkbox: true/false ou lista de valores das opções - number, rating: número inteiro - decimal, currency: número (1234.56) ou texto "1.234,56" - date: AAAA-MM-DD - datetime: ISO 8601 (2026-09-30T14:00:00-03:00) - url: começando com http:// ou https:// - email, phone, color (#RRGGBB) - file, image: não gravados pela API (enviar pela tela)

Campos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas mudam e null apaga o valor do campo. Chave inexistente: 422 com unknown_keys (contas em modo compatível recebem warnings e a chave vai para legacy_custom_fields).

notification_settingsobject?
is_activeboolean?
bot_protectedboolean?

Respostas

200

Atualizado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Validação, incluindo campos personalizados. Quando algum campo é inválido ou a chave não existe, nada do request é gravado.

Exemplo

curl -X PATCH 'https://atlas.skill-hub.app/api/v1/contacts/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"custom_fields": {"segmento": "clinica"}}'
DELETE/contacts/{contact}escopo write

Remover contato

Exclusão lógica. O registro deixa de aparecer nas listagens mas não é apagado do banco.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/contacts/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/contacts/{contact}/dealsescopo read

Negócios do contato

Ordenado por data de criação, do mais novo para o mais antigo. Sem filtros.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger
pagequerypadrão 1integer
per_pagequerypadrão 25integer

Respostas

200

Lista paginada.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/contacts/123/deals' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/contacts/{contact}/conversationsescopo read

Conversas do contato

Ordenado pela última mensagem, da mais recente para a mais antiga. Sem filtros.

Parâmetros

NomeTipoObservação
contactpathobrigatóriointeger
pagequerypadrão 1integer
per_pagequerypadrão 25integer

Respostas

200

Lista paginada.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/contacts/123/conversations' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Funis e etapas

Funis de venda e suas etapas. É aqui que se descobre o id da etapa de destino.

GET/pipelinesescopo read

Listar funis

Funis ativos da conta, em ordem alfabética, cada um com suas etapas já ordenadas (inclusive as de ganho e de perda). Sem paginação.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/pipelines' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/pipelines/{pipeline}/stagesescopo read

Listar etapas de um funil

Etapas do funil em sort_order. Por padrão exclui as etapas de ganho e de perda; passe include_won_lost=true para incluí-las. Funil de outra conta responde 404.

Parâmetros

NomeTipoObservação
pipelinepathobrigatóriointeger
include_won_lostquerypadrão Falseboolean

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/pipelines/123/stages' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Negócios

Negócios do Kanban. Mudança de etapa e fechamento só por /move, /win e /lose, com os dois interruptores.

GET/dealsescopo read

Listar negócios

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
pipeline_idqueryinteger
stage_idqueryinteger
assigned_toqueryinteger
contact_idqueryinteger
statusqueryopen | won | lost
created_fromquerystring (date)

Criação, a partir de (data ou data-hora).

created_toquerystring (date)
stage_changed_fromquerystring (date)

Entrada na etapa atual, a partir de.

stage_changed_toquerystring (date)
value_minquerynumber
value_maxquerynumber
searchquerystring

Busca parcial no título do negócio e no nome do contato.

sort_byquerypadrão created_atcreated_at | updated_at | value | title | expected_close_date | stage_changed_at
sort_dirquerypadrão descasc | desc

Qualquer valor diferente de asc é tratado como desc.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/deals' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/dealsescopo write

Criar negócio

Cria o negócio na etapa informada, que precisa pertencer ao funil informado (GET /pipelines). Contato, responsável, origem e campanha são verificados contra a sua conta. Se conversation_id vier, é vinculado após verificação de posse; se não vier e o contato tiver exatamente uma conversa, ela é vinculada.

Na criação o único efeito colateral existente é a automação deal_created, controlada por trigger_automations (padrão false, exige send). fire_pixel_events é aceito por consistência e não tem efeito aqui.

Corpo

CampoTipoObservação
titleobrigatóriomáx 255string
pipeline_idobrigatóriointeger
stage_idobrigatóriointeger

Etapa do funil informado.

contact_idinteger?
assigned_tointeger?
valuepadrão 0number
currencypadrão BRLmáx 10string
expected_close_datestring (date)?
notesmáx 5000string?
custom_fieldsobject

{chave: valor} com as chaves do módulo de Campos Personalizados (GET /custom-fields). cf_chave também vale (dentro de custom_fields ou solto na raiz do corpo). Formato aceito por tipo: - text, textarea: texto (respeita tamanho mínimo e máximo do campo) - select, radio: o valor ou o rótulo de uma opção, sem diferenciar maiúsculas - multiselect: lista de valores ou rótulos das opções - checkbox: true/false ou lista de valores das opções - number, rating: número inteiro - decimal, currency: número (1234.56) ou texto "1.234,56" - date: AAAA-MM-DD - datetime: ISO 8601 (2026-09-30T14:00:00-03:00) - url: começando com http:// ou https:// - email, phone, color (#RRGGBB) - file, image: não gravados pela API (enviar pela tela)

Campos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas mudam e null apaga o valor do campo. Chave inexistente: 422 com unknown_keys (contas em modo compatível recebem warnings e a chave vai para legacy_custom_fields).

source_idinteger?
campaign_idinteger?
conversation_idinteger?
trigger_automationspadrão Falseboolean
fire_pixel_eventspadrão Falseboolean

Respostas

201

Criado

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

422

Validação, incluindo campos personalizados. Quando algum campo é inválido ou a chave não existe, nada do request é gravado.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/deals' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/deals/{deal}escopo read

Ler negócio

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/deals/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PATCH/deals/{deal}escopo write

Alterar dados do negócio

Altera título, valor, moeda, responsável, contato, data prevista, notas, campos personalizados, origem, campanha e conversa vinculada.

Campos personalizados no PATCH são parciais: só as chaves enviadas mudam; as que não vierem ficam como estão; null apaga o valor daquele campo. Se qualquer campo for inválido, a resposta é 422 e nada do request é gravado.

Não muda etapa nem status. stage_id, status, pipeline_id, probability, closed_at, lost_at, loss_reason_id, won e lost respondem 422 mandando usar /move, /win ou /lose.

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Corpo

CampoTipoObservação
titlemáx 255string
contact_idinteger?
assigned_tointeger?
valuenumber
currencymáx 10string
expected_close_datestring (date)?
notesmáx 5000string?
custom_fieldsobject

{chave: valor} com as chaves do módulo de Campos Personalizados (GET /custom-fields). cf_chave também vale (dentro de custom_fields ou solto na raiz do corpo). Formato aceito por tipo: - text, textarea: texto (respeita tamanho mínimo e máximo do campo) - select, radio: o valor ou o rótulo de uma opção, sem diferenciar maiúsculas - multiselect: lista de valores ou rótulos das opções - checkbox: true/false ou lista de valores das opções - number, rating: número inteiro - decimal, currency: número (1234.56) ou texto "1.234,56" - date: AAAA-MM-DD - datetime: ISO 8601 (2026-09-30T14:00:00-03:00) - url: começando com http:// ou https:// - email, phone, color (#RRGGBB) - file, image: não gravados pela API (enviar pela tela)

Campos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas mudam e null apaga o valor do campo. Chave inexistente: 422 com unknown_keys (contas em modo compatível recebem warnings e a chave vai para legacy_custom_fields).

source_idinteger?
campaign_idinteger?
conversation_idinteger?

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Validação, campo personalizado inválido (ver CustomFieldValidationError), ou tentativa de mudar etapa/status por PATCH: {"message":"…","errors":{"stage_id":["Este campo não pode ser alterado por PATCH. Use POST /deals/{id}/move…"]}}.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X PATCH 'https://atlas.skill-hub.app/api/v1/deals/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"custom_fields": {"cargo_da_vaga": "SDR", "tipo_de_vaga": "SDR", "data_da_candidatura": "2026-09-30", "link_da_vaga": null}}'
GET/deals/{deal}/documentsescopo read

Listar documentos

Lista os arquivos da aba Documentos do negócio, do mais novo para o mais antigo. Cada item traz um link assinado novo em url, válido por 30 minutos (url_expires_at).

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Respostas

200

Lista de documentos.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/deals/123/documents' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/deals/{deal}/documentsescopo write

Anexar documento

Anexa um arquivo na aba Documentos do negócio. multipart/form-data, campo file. Mesmos limites da tela: até 50 MB; arquivos executáveis ou que o navegador interpreta (php, html, svg, js, exe…) são recusados. Mesmo limite de requisições por minuto dos outros endpoints.

O arquivo fica em área privada. A resposta traz url, um link assinado e temporário para baixar o arquivo sem token, válido por 30 minutos (url_expires_at). Depois disso o link responde 403; para baixar de novo, chame GET /deals/{deal}/documents, que devolve links novos. Não guarde a URL como endereço permanente do arquivo.

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Respostas

201

Anexado.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/deals/123/documents' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/deals/{deal}/moveescopo write

Mover de etapa

Move o negócio para a etapa informada, que pode ser de outro funil da mesma conta. Etapa de ganho ou de perda responde 422: use /win ou /lose. Registra a atividade com o nome do token.

Sem os interruptores: só a etapa muda. Com trigger_automations=true as automações da etapa disparam e a métrica de conversão é gravada; com fire_pixel_events=true o evento mapeado da etapa vai para a Meta CAPI. Qualquer um ligado exige o escopo send.

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Corpo

CampoTipoObservação
stage_idobrigatóriointeger
trigger_automationspadrão Falseboolean
fire_pixel_eventspadrão Falseboolean

Respostas

200

Movido

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/deals/123/move' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/deals/{deal}/winescopo write

Marcar como ganho

Status won, etapa de ganho do funil, closed_at hoje e o contato vira cliente. Negócio já ganho responde 422.

Sem os interruptores nada mais acontece. Com trigger_automations=true a automação deal_won dispara e a venda entra na métrica de conversão; com fire_pixel_events=true a Meta CAPI recebe Purchase (ou o evento mapeado). Purchase falso na conta de anúncios não se desfaz. Qualquer um ligado exige o escopo send.

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Corpo

CampoTipoObservação
trigger_automationspadrão Falseboolean
fire_pixel_eventspadrão Falseboolean

Respostas

200

Ganho

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Negócio já ganho.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/deals/123/win' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/deals/{deal}/loseescopo write

Marcar como perdido

Status lost, etapa de perda do funil, lost_at agora, motivo e notas de perda. O motivo precisa ser da sua conta. Negócio já perdido responde 422. Interruptores como em /win (deal_lost e evento mapeado).

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger

Corpo

CampoTipoObservação
loss_reason_idinteger?
loss_notesmáx 1000string?
trigger_automationspadrão Falseboolean
fire_pixel_eventspadrão Falseboolean

Respostas

200

Perdido

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/deals/123/lose' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/deals/{deal}/activitiesescopo read

Histórico do negócio

Atividades do mais recente para o mais antigo, com page e per_page (padrão 50, teto 100).

Parâmetros

NomeTipoObservação
dealpathobrigatóriointeger
pagequerypadrão 1integer
per_pagequerypadrão 50integer

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/deals/123/activities' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Campos personalizados

Definições dos campos personalizados de negócios e contatos (as chaves que custom_fields aceita).

GET/custom-fieldsescopo read

Listar campos personalizados

Definições dos campos personalizados da sua conta: é daqui que saem as chaves que custom_fields aceita em negócios e contatos. Por padrão só os ativos.

Parâmetros

NomeTipoObservação
entity_typequerydeal | contact

Sem ele, vêm os dois.

include_inactivequerypadrão Falseboolean

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

422

Falha de validação. Formato padrão do framework.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/custom-fields' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Conversas

Conversas de WhatsApp e envio de mensagens.

GET/conversationsescopo read

Listar conversas

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
statusqueryopen | pending | resolved
channel_idqueryinteger
assigned_toqueryinteger
contact_idqueryinteger
searchquerystring

Busca parcial em nome exibido, identificador remoto e prévia da última mensagem.

is_groupqueryboolean
sort_byquerypadrão last_message_atlast_message_at | created_at | unread_count
sort_dirquerypadrão descasc | desc

Qualquer valor diferente de asc é tratado como desc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/conversations' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/conversations/{conversation}escopo read

Obter conversa

Parâmetros

NomeTipoObservação
conversationpathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/conversations/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/conversations/{conversation}escopo write

Atualizar conversa

Enviar status: resolved executa a rotina de resolução da conversa, registrando quem resolveu.

Cuidado com dois comportamentos:

- assigned_user_id só atribui se o usuário pertencer à sua conta. Se não pertencer, o valor é ignorado em silêncio e a resposta é 200. - assigned_department_id é gravado sem essa verificação.

Envie null em assigned_user_id para desatribuir.

Parâmetros

NomeTipoObservação
conversationpathobrigatóriointeger

Corpo

CampoTipoObservação
statusopen | pending | resolved
assigned_user_idinteger?
assigned_department_idinteger?

Respostas

200

Atualizada.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/conversations/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/conversations/{conversation}/messagesescopo read

Listar mensagens

Paginação por cursor, e não por página. A resposta traz has_more no lugar de meta. As mensagens vêm da mais antiga para a mais nova. Mensagens apagadas não são incluídas.

Parâmetros

NomeTipoObservação
conversationpathobrigatóriointeger
before_idqueryinteger

Devolve mensagens anteriores a este id. Use para carregar histórico.

after_idqueryinteger

Devolve mensagens posteriores a este id. Use para buscar novidades.

limitquerypadrão 50integer

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/conversations/123/messages' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/conversations/{conversation}/messagesescopo send

Enviar mensagem em uma conversa

Envia para o canal já vinculado à conversa.

É obrigatório ao menos um entre content, media e media_url.

Efeitos colaterais, que acontecem sempre:

  • A mensagem é marcada com metadata.via = "api".
  • Se a conversa não tiver responsável, ela é atribuída ao usuário do token.
  • O status da conversa passa para open.

Áudio é enviado como mensagem de voz. Quando há mídia sem type, o padrão é document.

Parâmetros

NomeTipoObservação
conversationpathobrigatóriointeger

Corpo

CampoTipoObservação
contentmáx 10000string?
typetext | image | document | video | audio
media_urlstring (uri)?
filenamemáx 255string?

Aceita também multipart/form-data, com o arquivo no campo media.

Respostas

201

Enviada.

400

O canal recusou o envio. Ocorre com canal desconectado (WhatsApp channel is disconnected.), com erro devolvido pelo provedor, ou com falha genérica de envio.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação, ou nenhum conteúdo enviado.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/conversations/123/messages' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"content": "Olá! Seu atendimento foi retomado.", "type": "text"}'
POST/messages/sendescopo send

Enviar mensagem por telefone

Envia sem conhecer a conversa. Dois modos:

- Com conversation_id: usa a conversa existente e o canal dela. - Sem conversation_id: channel_id passa a ser obrigatório. O telefone é reduzido a dígitos e a conversa é criada se ainda não existir.

Embora phone seja declarado como opcional, ele é obrigatório na prática no segundo modo.

Corpo

CampoTipoObservação
contentmáx 10000string?
typetext | image | document | video | audio
media_urlstring (uri)?
filenamemáx 255string?
phonemáx 50string

Obrigatório quando não há conversation_id.

conversation_idinteger?
channel_idinteger

Obrigatório quando não há conversation_id.

Aceita também multipart/form-data, com o arquivo no campo media.

Respostas

201

Enviada.

400

Canal desconectado ou erro do provedor.

404

Conversa ou canal não encontrado. As mensagens possíveis são Conversation not found., Channel not found for this conversation. e Channel not found.

422

Falha de validação. Formato padrão do framework.

500

Não foi possível criar a conversa.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/messages/send' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"phone": "5511912345678", "channel_id": 12, "content": "Olá! Recebemos seu contato."}'

Faturas

Faturas, itens e pagamentos.

GET/invoicesescopo read

Listar faturas

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
statusquerydraft | pending | sent | paid | cancelled
contact_idqueryinteger
searchquerystring

Busca parcial em número e título.

due_fromquerystring (date)
due_toquerystring (date)
is_recurringqueryboolean
sort_byquerypadrão created_atcreated_at | due_date | total | number | status
sort_dirquerypadrão descasc | desc

Qualquer valor diferente de asc é tratado como desc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/invoices' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/invoicesescopo write

Criar fatura

Os totais são calculados pelo servidor a partir dos itens. Não envie subtotal nem total.

Padrões aplicados: issue_date igual a hoje, currency igual a BRL, discount_type igual a fixed, status igual a draft, unit igual a un, e o gateway de pagamento padrão da conta.

Atenção: o contact_id enviado não é verificado contra a sua conta.

Corpo

CampoTipoObservação
contact_idobrigatóriointeger
titlemáx 255string?
descriptionmáx 5000string?
issue_datestring (date)?
due_dateobrigatóriostring (date)
currencypadrão BRLmáx 10string?
discount_amountnumber?
discount_typepadrão fixedfixed | percent
notesmáx 5000string?
termsmáx 5000string?
itemsobrigatórioarray<object>

Respostas

201

Criada.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/invoices' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"contact_id": 1001, "due_date": "2026-10-15", "title": "Mensalidade de outubro", "items": [{"name": "Plano Pro", "quantity": 1, "unit_price": 499.9, "unit": "un"}]}'
GET/invoices/{invoice}escopo read

Obter fatura

Parâmetros

NomeTipoObservação
invoicepathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/invoices/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/invoices/{invoice}escopo write

Atualizar fatura

Não aceita itens e não recalcula os totais. Para mudar os itens de uma fatura, crie uma nova.

Parâmetros

NomeTipoObservação
invoicepathobrigatóriointeger

Corpo

CampoTipoObservação
contact_idinteger
titlemáx 255string?
descriptionmáx 5000string?
issue_datestring (date)?
due_datestring (date)
statusdraft | pending | sent | paid | cancelled
currencymáx 10string?
discount_amountnumber?
discount_typefixed | percent
notesmáx 5000string?
termsmáx 5000string?

Respostas

200

Atualizada.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/invoices/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
DELETE/invoices/{invoice}escopo write

Remover fatura

Parâmetros

NomeTipoObservação
invoicepathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/invoices/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/invoices/{invoice}/paymentsescopo read

Listar pagamentos da fatura

Parâmetros

NomeTipoObservação
invoicepathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/invoices/123/payments' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/invoices/{invoice}/paymentsescopo write

Registrar pagamento

Registra um pagamento e atualiza o status da fatura. Uma fatura em status que não aceita pagamento responde 422 antes mesmo da validação dos campos.

Padrões: method igual a manual e paid_at igual a agora.

Parâmetros

NomeTipoObservação
invoicepathobrigatóriointeger

Corpo

CampoTipoObservação
amountobrigatórionumber
methodpix | boleto | credit_card | debit_card | transfer | manual
notesmáx 1000string?
paid_atstring (date-time)?

Respostas

201

Registrado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Validação, ou a fatura não aceita pagamento no status atual.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/invoices/123/payments' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"amount": 499.9, "method": "pix"}'

Propostas

Propostas comerciais e itens.

GET/proposalsescopo read

Listar propostas

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
statusquerydraft | sent | accepted | rejected | expired
contact_idqueryinteger
deal_idqueryinteger
searchquerystring

Busca parcial em número, título e assunto.

sort_byquerypadrão created_atcreated_at | proposal_date | valid_until | total | number | status
sort_dirquerypadrão descasc | desc

Qualquer valor diferente de asc é tratado como desc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/proposals' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/proposalsescopo write

Criar proposta

Totais calculados pelo servidor.

Padrões: assigned_to igual ao usuário do token, proposal_date igual a hoje, valid_until igual a hoje mais 30 dias, currency igual a BRL e status igual a draft.

Atenção ao discount_type: aqui os valores são fixed e percentage, diferente das faturas, que usam percent.

Corpo

CampoTipoObservação
contact_idobrigatóriointeger
deal_idinteger?
assigned_tointeger?
titlemáx 255string?
subjectmáx 255string?
proposal_datestring (date)?
valid_untilstring (date)?
currencypadrão BRLmáx 10string?
discount_amountnumber?
discount_typefixed | percentage
contentstring?
notesmáx 5000string?
termsmáx 5000string?
itemsobrigatórioarray<object>

Respostas

201

Criada.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/proposals' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"contact_id": 1001, "title": "Proposta comercial", "items": [{"name": "Implantação", "quantity": 1, "unit_price": 3500}]}'
GET/proposals/{proposal}escopo read

Obter proposta

Parâmetros

NomeTipoObservação
proposalpathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/proposals/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/proposals/{proposal}escopo write

Atualizar proposta

Não aceita itens.

Parâmetros

NomeTipoObservação
proposalpathobrigatóriointeger

Corpo

CampoTipoObservação
contact_idinteger
deal_idinteger?
assigned_tointeger?
titlemáx 255string?
subjectmáx 255string?
proposal_datestring (date)?
valid_untilstring (date)?
statusdraft | sent | accepted | rejected | expired
currencymáx 10string?
discount_amountnumber?
discount_typefixed | percentage
contentstring?
notesmáx 5000string?
termsmáx 5000string?

Respostas

200

Atualizada.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/proposals/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
DELETE/proposals/{proposal}escopo write

Remover proposta

Parâmetros

NomeTipoObservação
proposalpathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/proposals/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Agendamentos

Compromissos da agenda.

GET/appointmentsescopo read

Listar agendamentos

A ordenação padrão aqui é crescente, diferente dos outros recursos.

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
statusqueryscheduled | confirmed | cancelled | completed
typequerymeeting | call | task | event | other
user_idqueryinteger
contact_idqueryinteger
deal_idqueryinteger
start_fromquerystring (date-time)
start_toquerystring (date-time)
searchquerystring

Busca parcial em título, descrição e local.

sort_byquerypadrão start_atstart_at | end_at | created_at | title | status
sort_dirquerypadrão ascasc | desc

Qualquer valor diferente de desc é tratado como asc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/appointments' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/appointmentsescopo write

Criar agendamento

Padrões: user_id igual ao usuário do token, type igual a meeting, status igual a scheduled e end_at igual a start_at mais uma hora.

Corpo

CampoTipoObservação
titleobrigatóriomáx 255string
descriptionmáx 5000string?
start_atobrigatóriostring (date-time)
end_atstring (date-time)?

Precisa ser igual ou posterior a start_at.

all_dayboolean?
locationmáx 255string?
typepadrão meetingmeeting | call | task | event | other
meeting_typein_person | online
statuspadrão scheduledscheduled | confirmed | cancelled | completed
colormáx 20string?
user_idinteger?
contact_idinteger?
deal_idinteger?
notesmáx 5000string?

Respostas

201

Criado.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/appointments' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"title": "Reunião de apresentação", "start_at": "2026-09-20T14:00:00-03:00", "end_at": "2026-09-20T15:00:00-03:00", "contact_id": 1001, "meeting_type": "online"}'
GET/appointments/{appointment}escopo read

Obter agendamento

Parâmetros

NomeTipoObservação
appointmentpathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/appointments/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/appointments/{appointment}escopo write

Atualizar agendamento

A validação de que end_at vem depois de start_at existe na criação, mas não na atualização. Garanta a coerência do seu lado.

Parâmetros

NomeTipoObservação
appointmentpathobrigatóriointeger

Corpo

CampoTipoObservação
titleobrigatóriomáx 255string
descriptionmáx 5000string?
start_atobrigatóriostring (date-time)
end_atstring (date-time)?

Precisa ser igual ou posterior a start_at.

all_dayboolean?
locationmáx 255string?
typepadrão meetingmeeting | call | task | event | other
meeting_typein_person | online
statuspadrão scheduledscheduled | confirmed | cancelled | completed
colormáx 20string?
user_idinteger?
contact_idinteger?
deal_idinteger?
notesmáx 5000string?

Respostas

200

Atualizado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/appointments/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
DELETE/appointments/{appointment}escopo write

Remover agendamento

Parâmetros

NomeTipoObservação
appointmentpathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/appointments/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Produtos

Produtos e serviços do catálogo.

GET/productsescopo read

Listar produtos

A ordenação padrão aqui é crescente por nome.

Parâmetros

NomeTipoObservação
pagequerypadrão 1integer
per_pagequerypadrão 50integer
typequeryproduct | service
category_idqueryinteger
is_activequeryboolean
has_variationsqueryboolean
searchquerystring

Busca parcial em nome, SKU e descrição.

sort_byquerypadrão namename | price | sku | created_at | stock_quantity | sort_order
sort_dirquerypadrão ascasc | desc

Qualquer valor diferente de desc é tratado como asc.

Respostas

200

Lista paginada.

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/products' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/productsescopo write

Criar produto

Padrões: type igual a product e is_active igual a verdadeiro.

Não existe endpoint para criar ou editar variações. Elas são apenas listadas na leitura de um produto.

Corpo

CampoTipoObservação
nameobrigatóriomáx 255string
skumáx 100string?
descriptionmáx 5000string?
short_descriptionmáx 500string?
typepadrão productproduct | service
categorymáx 255string?
category_idinteger?
priceobrigatórionumber
costnumber?
currencymáx 10string?
unitmáx 50string?
tax_ratenumber?
track_stockboolean?
stock_quantitynumber?
min_stocknumber?
is_activepadrão Trueboolean?
custom_fieldsobject?

Respostas

201

Criado.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/products' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Plano Pro", "type": "service", "price": 499.9, "unit": "mês"}'
GET/products/{product}escopo read

Obter produto

Parâmetros

NomeTipoObservação
productpathobrigatóriointeger

Respostas

200

OK

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/products/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/products/{product}escopo write

Atualizar produto

Parâmetros

NomeTipoObservação
productpathobrigatóriointeger

Corpo

CampoTipoObservação
nameobrigatóriomáx 255string
skumáx 100string?
descriptionmáx 5000string?
short_descriptionmáx 500string?
typepadrão productproduct | service
categorymáx 255string?
category_idinteger?
priceobrigatórionumber
costnumber?
currencymáx 10string?
unitmáx 50string?
tax_ratenumber?
track_stockboolean?
stock_quantitynumber?
min_stocknumber?
is_activepadrão Trueboolean?
custom_fieldsobject?

Respostas

200

Atualizado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/products/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
DELETE/products/{product}escopo write

Remover produto

Parâmetros

NomeTipoObservação
productpathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/products/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

WhatsApp Connector

Canais oficiais e envio por template aprovado na Meta.

GET/wa-connector/channelsescopo read

Listar canais oficiais

Devolve apenas canais da API Oficial da Meta que estejam ativos. Sem paginação, com envelope próprio { data, total }.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/wa-connector/channels' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
GET/wa-connector/templatesescopo read

Listar templates aprovados

Somente templates aprovados pela Meta, ordenados por nome. Sem paginação, com envelope próprio { data, total }.

Parâmetros

NomeTipoObservação
categoryqueryUTILITY | MARKETING | AUTHENTICATION

Convertido para maiúsculas automaticamente.

languagequerystring
channel_idqueryinteger

Filtra pelos templates da conta de WhatsApp Business daquele canal.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/wa-connector/templates' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/wa-connector/send-templateescopo send

Enviar template

Envia um template aprovado pela API Oficial. É o caminho para iniciar conversa fora da janela de 24 horas.

O telefone é reduzido a dígitos. O idioma padrão é pt_BR.

components segue o formato da Cloud API da Meta.

Registro no Atlas. Depois que a Meta aceita o envio, o Atlas registra a mensagem como um envio feito pela tela:

- Contato: procurado pelo telefone na conta (com e sem 55, com e sem o 9º dígito). Se não existir, é criado como lead, com o nome de contact_name (ou o próprio número). Contato existente sem nome (ou com o número no lugar do nome) recebe o contact_name; nome de verdade não é trocado. - Conversa: a deste número no mesmo canal é reaproveitada, pela mesma regra que a entrada da Meta usa. Por isso, quando o lead responde, a resposta cai nessa conversa. Se não existir, é criada; se estiver resolvida, é reaberta. Ela termina com status open. - Mensagem: gravada como mensagem de saída, com o texto do template já com as variáveis (ou [Template: nome] e as variáveis, quando o template não está sincronizado no Atlas), o message_id da Meta (os status entregue/lido atualizam por ele) e metadata.via = "api". A conversa aparece na hora no inbox. A IA não é acionada por essa mensagem.

Se a Meta recusar, nada é criado no Atlas e a resposta é 400, como antes.

Atenção: o serviço não verifica se o template existe na conta antes de enviar. Um nome errado só falha na resposta da Meta.

Corpo

CampoTipoObservação
channel_idobrigatóriointeger
toobrigatóriomáx 30string
template_nameobrigatóriomáx 255string
languagepadrão pt_BRmáx 10string
componentsarray<object>

Formato da Cloud API da Meta.

contact_namemáx 255string

Opcional. Nome do contato ao criar, ou para completar um contato existente sem nome.

Respostas

200

Enviado.

400

A Meta recusou o envio. A mensagem devolvida é a da própria Meta. Nada é criado no Atlas.

404

Canal não encontrado ou inativo para esta conta.

422

Validação, canal desconectado, ou canal sem token de acesso e identificador de telefone configurados.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/wa-connector/send-template' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"channel_id": 12, "to": "5511912345678", "template_name": "appointment_reminder", "contact_name": "Maria Souza", "language": "pt_BR", "components": [{"type": "body", "parameters": [{"type": "text", "text": "Maria"}, {"type": "text", "text": "Consulta"}, {"type": "text", "text": "20/09 às 14h"}, {"type": "text", "text": "Dra. Ana"}]}]}'

E-mail

Envio de e-mail pelas caixas do Atlas (pessoal, do time ou Geral).

GET/email-accountsescopo read

Listar caixas de e-mail

Caixas que o dono do token pode usar para enviar: a caixa pessoal dele, as caixas do time em que ele participa e a caixa Geral (E-mail SMTP das configurações) quando ele tem a permissão de configurações de e-mail. São as mesmas caixas da tela E-mail.

O campo account é a chave usada em POST /emails/send: o id numérico da caixa ou general. Sem paginação, com envelope { data }.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/email-accounts' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
{
  "data": [
    {
      "account": "3",
      "name": "Comercial",
      "from_email": "[email protected]",
      "from_name": "Comercial",
      "provider": "imap_smtp",
      "is_general": false,
      "is_personal": true,
      "is_active": true
    },
    {
      "account": "general",
      "name": "Geral",
      "from_email": "[email protected]",
      "from_name": "Sua Empresa",
      "provider": "smtp",
      "is_general": true,
      "is_personal": false,
      "is_active": false
    }
  ]
}
POST/emails/sendescopo send

Enviar e-mail por uma caixa

Envia um e-mail por uma caixa do Atlas, do mesmo jeito que a tela Novo e-mail. Funciona com caixas IMAP/SMTP e Microsoft. A mensagem fica na pasta Enviados da caixa, com o usuário do token como remetente interno, e o resultado aparece em Configurações, Logs de Integração.

account é a chave devolvida por GET /email-accounts. Sem ela, usa a caixa pessoal do dono do token; se ele não tiver, a primeira acessível. Caixa inativa (teste de SMTP falhou ou desligada) responde 422.

Destinatários (to, cc, bcc) aceitam três formas: uma string com um e-mail, uma lista de strings, ou uma lista de objetos { email, name }. Endereço inválido é descartado; sem nenhum destinatário válido em to, 422.

Corpo: body_html (apelido body) ou body_text. Mandando só um, o outro é derivado. Anexos vão em base64, até 10 arquivos e 10 MB somados.

O envio é síncrono: a resposta só volta depois que o servidor da caixa aceitou a mensagem. Com anexo grande ou servidor lento isso pode levar alguns segundos.

Corpo

CampoTipoObservação
accountmáx 20string

Id numérico da caixa ou general. Padrão: caixa pessoal do dono do token.

toobrigatórioobject

String, lista de strings ou lista de { email, name }.

ccobject

Mesmo formato de to.

bccobject

Mesmo formato de to. Cópia oculta.

subjectobrigatóriomáx 255string
body_htmlmáx 500000string

Corpo em HTML. Apelido: body.

body_textmáx 200000string

Corpo em texto puro. Obrigatório quando não há body_html.

reply_tostring (email)

Endereço de resposta (Reply-To).

in_reply_to_idinteger

Id de um e-mail recebido no Atlas, para responder na mesma conversa.

attachmentsarray<object>

Até 10 arquivos e 10 MB somados.

Respostas

201

Enviado.

403

Token sem o escopo send, ou caixa que o dono do token não pode usar (a caixa de outra pessoa responde 403 mesmo existindo).

422

Validação (sem destinatário válido, sem corpo, anexo inválido ou acima de 10 MB), caixa inativa ou nenhuma caixa disponível para o usuário.

502

O servidor da caixa (SMTP ou Microsoft) recusou ou não respondeu. A mensagem fica em Enviados com status failed e o detalhe em Logs de Integração.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/emails/send' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"account": "3", "to": [{"email": "[email protected]", "name": "Cliente"}], "cc": "[email protected]", "subject": "Proposta comercial", "body_html": "<p>Olá! Segue a proposta em anexo.</p>", "attachments": [{"filename": "proposta.pdf", "content_base64": "JVBERi0xLjQK...", "mime": "application/pdf"}]}'
GET/emails/{id}escopo read

Consultar e-mail enviado

Status e destinatários de um e-mail enviado pela API ou pela tela (só mensagens de saída da conta).

Parâmetros

NomeTipoObservação
idpathobrigatóriointeger

Respostas

200

OK

404

E-mail inexistente, de entrada, ou de outra conta.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/emails/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Webhooks

Notificações de saída para o seu sistema.

GET/webhooksescopo read

Listar webhooks

Sem paginação e sem filtros. O segredo não é exibido na listagem.

Respostas

200

OK

401

Token ausente, inválido ou expirado.

403

Quatro causas, distinguíveis pelo corpo:

- token sem o escopo exigido (code: insufficient_scope, com required_scope), ver a seção *Escopos do token*; - usuário sem conta vinculada; - licença sem API habilitada; - addon de acesso à API inativo.

429

Limite de 60 requisições por minuto estourado.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/webhooks' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/webhooksescopo write

Criar webhook

O segredo é gerado pelo servidor e devolvido apenas nesta resposta e nas leituras individuais. Use * em events para receber todos.

Corpo

CampoTipoObservação
urlobrigatóriomáx 500string (uri)
eventsobrigatórioarray<*|contact.created|contact.updated|contact.deleted|conversation.created|conversation.updated|message.received|message.sent|invoice.created|invoice.updated|invoice.paid|invoice.overdue|invoice.cancelled|product.created|product.updated|product.deleted>
is_activepadrão Trueboolean?

Respostas

201

Criado. Guarde o secret.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/webhooks' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://seusistema.com/hooks/atlas", "events": ["contact.created", "message.received"]}'
GET/webhooks/{webhook}escopo read

Obter webhook

Parâmetros

NomeTipoObservação
webhookpathobrigatóriointeger

Respostas

200

OK. Inclui o segredo.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X GET 'https://atlas.skill-hub.app/api/v1/webhooks/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
PUT/webhooks/{webhook}escopo write

Atualizar webhook

Parâmetros

NomeTipoObservação
webhookpathobrigatóriointeger

Corpo

CampoTipoObservação
urlmáx 500string (uri)
eventsarray<*|contact.created|contact.updated|contact.deleted|conversation.created|conversation.updated|message.received|message.sent|invoice.created|invoice.updated|invoice.paid|invoice.overdue|invoice.cancelled|product.created|product.updated|product.deleted>
is_activeboolean?

Respostas

200

Atualizado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

422

Falha de validação. Formato padrão do framework.

Exemplo

curl -X PUT 'https://atlas.skill-hub.app/api/v1/webhooks/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
DELETE/webhooks/{webhook}escopo write

Remover webhook

Parâmetros

NomeTipoObservação
webhookpathobrigatóriointeger

Respostas

200

Removido.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X DELETE 'https://atlas.skill-hub.app/api/v1/webhooks/123' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'
POST/webhooks/{webhook}/testescopo send

Disparar teste

Envia o evento webhook.test para a URL cadastrada, com o corpo {"message":"This is a test webhook event.","timestamp":"..."}.

Parâmetros

NomeTipoObservação
webhookpathobrigatóriointeger

Respostas

200

Teste despachado.

404

Recurso inexistente ou pertencente a outra conta. As duas situações respondem igual, por decisão de segurança.

Exemplo

curl -X POST 'https://atlas.skill-hub.app/api/v1/webhooks/123/test' \
  -H 'Authorization: Bearer SEU_TOKEN' \
  -H 'Accept: application/json'

Eventos de webhook

Cada entrega é um POST na URL cadastrada, com o corpo {"event":"...","timestamp":"...","data":{...}}.

Cabeçalhos enviados

Content-Type: application/json
User-Agent: Atlas/1.0
X-Webhook-Event: contact.created
X-Webhook-Id: 1
X-Webhook-Signature: <HMAC-SHA256 hex do corpo cru, com o seu secret>

Verifique a assinatura

Calcule o HMAC-SHA256 do corpo cru da requisição usando o secret do webhook e compare com X-Webhook-Signature. Use comparação em tempo constante.

Reentrega

Timeout de 15 segundos. Até 3 tentativas, com espera de 10, 30 e 60 segundos, apenas para erro 5xx ou falha de rede. Um 4xx conta como falha e não é repetido. Após 50 falhas consecutivas o webhook é desativado automaticamente.

Eventos disponíveis

Evento Quando disparaCampos em data
contact.createdQualquer criação de contato, pela API ou pela interface.id, name, email, phone, mobile, type, person_type, document, source, tags[]
contact.updatedQualquer atualização de contato, sem filtro de campo.igual ao anterior
contact.deletedExclusão lógica de contato.id, name
conversation.createdNova conversa.id, remote_jid, display_name, status, contact_id, channel_id, assigned_user_id
conversation.updatedSomente quando status, responsável, departamento ou contato mudam.igual ao anterior
message.receivedMensagem recebida. Exclui mensagens de sistema e ocultas.id, conversation_id, contact_id, direction, type, content, media_url, status, remote_message_id, created_at
message.sentMensagem enviada, inclusive por bot, campanha ou automação.igual ao anterior

Oito eventos nunca disparam hoje

invoice.created, invoice.updated, invoice.paid, invoice.overdue, invoice.cancelled, product.created, product.updated e product.deleted são aceitos no cadastro de um webhook, mas nenhum código os emite. Não construa integração que dependa deles.

Objetos de resposta

Os campos que cada recurso devolve.

CustomFieldErrorBody

CampoTipoObservação
messagestring
errorsobject
custom_field_errorsarray<object>
unknown_keysarray<string>

CustomFieldDefinition

CampoTipoObservação
entity_typedeal | contact
keystring

Fixa depois de criada; renomear o rótulo não muda a chave.

labelstring
field_typetext | textarea | number | decimal | date | datetime | select | multiselect | checkbox | radio | email | phone | url | file | image | color | rating | currency
optionsarray<object>
is_requiredboolean
is_activeboolean
orderinteger

DealDocument

CampoTipoObservação
idinteger
namestring
sizeinteger

Bytes.

mime_typestring?
urlstring (uri)

Link assinado e temporário para baixar o arquivo, sem token. Vale 30 minutos; vencido ou alterado, responde 403.

url_expires_atstring (date-time)

Quando o link de url deixa de valer.

created_atstring (date-time)

ErrorScope

CampoTipoObservação
errorobrigatóriostring

Mensagem legível.

codeobrigatórioinsufficient_scope
required_scopeobrigatórioread | write | send

Pipeline

CampoTipoObservação
idinteger
namestring
descriptionstring?
is_defaultboolean
is_activeboolean
sort_orderinteger
stagesarray<object>

Etapas do funil em ordem, inclusive as de ganho e de perda.

PipelineStage

CampoTipoObservação
idinteger
pipeline_idinteger
namestring
colorstring?
probabilityinteger

Probabilidade de fechamento atribuída à etapa, de 0 a 100.

rotting_daysinteger?

Dias parado na etapa a partir dos quais o negócio é sinalizado como esfriando.

is_wonboolean

Etapa de ganho.

is_lostboolean

Etapa de perda.

sort_orderinteger

DealRef

CampoTipoObservação
idinteger
namestring

Deal

CampoTipoObservação
idinteger
titlestring
valuenumber (float)
currencystring
statusopen | won | lost
probabilityinteger

Herdada da etapa, 0 a 100.

pipelineobject
stageobject?
contactobject
assigned_toobject
conversation_idinteger?

Conversa vinculada ao negócio, quando houver.

expected_close_datestring (date)?
stage_changed_atstring (date-time)?

Última entrada na etapa atual.

closed_atstring (date)?
created_atstring (date-time)
updated_atstring (date-time)
custom_fieldsobject

Valores do módulo de Campos Personalizados, os mesmos da tela, com as chaves do módulo. Só campos ativos e preenchidos. Tipos como em CustomFieldsInput (data em AAAA-MM-DD, data e hora em ISO 8601, número como número, múltipla seleção como lista).

DealDetailed

CampoTipoObservação
idinteger
titlestring
valuenumber (float)
currencystring
statusopen | won | lost
probabilityinteger

Herdada da etapa, 0 a 100.

pipelineobject
stageobject?
contactobject
assigned_toobject
conversation_idinteger?

Conversa vinculada ao negócio, quando houver.

expected_close_datestring (date)?
stage_changed_atstring (date-time)?

Última entrada na etapa atual.

closed_atstring (date)?
created_atstring (date-time)
updated_atstring (date-time)
custom_fieldsobject

Valores do módulo de Campos Personalizados, os mesmos da tela, com as chaves do módulo. Só campos ativos e preenchidos. Tipos como em CustomFieldsInput (data em AAAA-MM-DD, data e hora em ISO 8601, número como número, múltipla seleção como lista).

notesstring?
legacy_custom_fieldsobject

Somente leitura. Chaves antigas que não existem no módulo (contas em modo compatível e dados de importação). Não aparecem na tela nem para a IA.

source_idinteger?
campaign_idinteger?
loss_reasonobject
loss_notesstring?
lost_atstring (date-time)?

DealEffects

CampoTipoObservação
trigger_automationsboolean
fire_pixel_eventsboolean

DealActivity

CampoTipoObservação
idinteger
typestring

created, stage_change, status_change, note, call, email, whatsapp, meeting…

titlestring?
descriptionstring?
userobject
metadataobject?

Em movimentações: old_stage_id, new_stage_id, actor_label, via, trigger_automations, fire_pixel_events.

activity_atstring (date-time)?
created_atstring (date-time)

EmailAccount

CampoTipoObservação
accountstring

Chave para POST /emails/send: id numérico da caixa ou general.

namestring
from_emailstring?
from_namestring?
providerimap_smtp | microsoft | smtp

smtp é a caixa Geral das configurações.

is_generalboolean
is_personalboolean

Caixa pessoal do dono do token.

is_activeboolean

Só caixa ativa envia.

EmailRecipient

CampoTipoObservação
emailobrigatóriostring (email)
namestring?

EmailAttachment

CampoTipoObservação
filenameobrigatóriomáx 255string
content_base64obrigatóriostring

Conteúdo do arquivo em base64.

mimepadrão application/octet-streammáx 100string

EmailSent

CampoTipoObservação
idinteger
accountstring
message_idstring

Message-ID do cabeçalho.

statussent | failed | draft
from_emailstring?
from_namestring?
toarray<object>
ccarray<object>
bccarray<object>
subjectstring
in_reply_tostring?

Message-ID do e-mail respondido.

sent_atstring (date-time)?
sent_by_user_idinteger?

Usuário do token que enviou.

Contact

CampoTipoObservação
idinteger
namestring
trading_namestring?
display_namestring

Calculado a partir do nome e do nome fantasia.

person_typeindividual | company
typelead | customer | prospect | supplier
emailstring?
phonestring?
mobilestring?
documentstring?
sourcestring?

Texto livre. É um rótulo de origem, não um canal detectado automaticamente.

tagsarray<string>
is_activeboolean
groupobject?
assigned_toobject?
deals_countinteger

Preenchido na listagem. Na leitura individual vem zero.

contracts_countinteger

Preenchido na listagem. Na leitura individual vem zero.

created_atstring (date-time)
updated_atstring (date-time)
custom_fieldsobject

Valores do módulo de Campos Personalizados, os mesmos da tela, com as chaves do módulo. Só campos ativos e preenchidos. Tipos como em CustomFieldsInput (data em AAAA-MM-DD, data e hora em ISO 8601, número como número, múltipla seleção como lista).

ContactDetailed

CampoTipoObservação
idinteger
namestring
trading_namestring?
display_namestring

Calculado a partir do nome e do nome fantasia.

person_typeindividual | company
typelead | customer | prospect | supplier
emailstring?
phonestring?
mobilestring?
documentstring?
sourcestring?

Texto livre. É um rótulo de origem, não um canal detectado automaticamente.

tagsarray<string>
is_activeboolean
groupobject?
assigned_toobject?
deals_countinteger

Preenchido na listagem. Na leitura individual vem zero.

contracts_countinteger

Preenchido na listagem. Na leitura individual vem zero.

created_atstring (date-time)
updated_atstring (date-time)
custom_fieldsobject

Valores do módulo de Campos Personalizados, os mesmos da tela, com as chaves do módulo. Só campos ativos e preenchidos. Tipos como em CustomFieldsInput (data em AAAA-MM-DD, data e hora em ISO 8601, número como número, múltipla seleção como lista).

websitestring?
companystring?
positionstring?
state_registrationstring?
municipal_registrationstring?
addressstring?
address_numberstring?
address_complementstring?
neighborhoodstring?
citystring?
statestring?
zip_codestring?
countrystring?
birth_datestring (date)?
notesstring?
legacy_custom_fieldsobject

Somente leitura. Chaves antigas que não existem no módulo (contas em modo compatível e dados de importação). Não aparecem na tela nem para a IA.

notification_settingsobject
parent_idinteger?
bot_protectedboolean
last_contact_atstring (date-time)?
employeesarray<object>

Pessoas vinculadas a este contato. Só é carregado na leitura individual.

DealSummary

CampoTipoObservação
idinteger
titlestring
valuenumber
currencystring
statusstring
stageobject?
assigned_toobject?
expected_close_datestring (date)?
won_atstring (date-time)?
lost_atstring (date-time)?
created_atstring (date-time)

Conversation

CampoTipoObservação
idinteger
remote_jidstring

Identificador no WhatsApp, no formato [email protected].

display_namestring?
avatar_urlstring?
statusopen | pending | resolved
is_groupboolean
unread_countinteger
last_message_previewstring?
last_message_atstring (date-time)?
channelobject?
contactobject?
assigned_toobject?
assigned_departmentobject?
created_atstring (date-time)
updated_atstring (date-time)

Message

CampoTipoObservação
idinteger
conversation_idinteger
directionincoming | outgoing
typetext | image | document | video | audio
contentstring?
media_urlstring?
media_mimetypestring?
filenamestring?

Sempre responde null. O campo é montado a partir de um atributo que não existe na tabela. Use media_url para identificar o arquivo.

statusstring
remote_message_idstring?
sender_userobject?
metadataobject

Mensagens enviadas por esta API trazem via = "api".

created_atstring (date-time)

MessageSend

CampoTipoObservação
contentmáx 10000string?
typetext | image | document | video | audio
media_urlstring (uri)?
filenamemáx 255string?

MessageSendByPhone

CampoTipoObservação
contentmáx 10000string?
typetext | image | document | video | audio
media_urlstring (uri)?
filenamemáx 255string?
phonemáx 50string

Obrigatório quando não há conversation_id.

conversation_idinteger?
channel_idinteger

Obrigatório quando não há conversation_id.

Invoice

CampoTipoObservação
idinteger
numberstring
titlestring?
statusdraft | pending | sent | paid | cancelled
issue_datestring (date)
due_datestring (date)
subtotalnumber
discount_amountnumber
discount_typefixed | percent
tax_totalnumber
totalnumber
amount_paidnumber
amount_duenumber
currencystring
is_recurringboolean
contactobject?
created_byobject?
created_atstring (date-time)
updated_atstring (date-time)

InvoiceDetailed

CampoTipoObservação
idinteger
numberstring
titlestring?
statusdraft | pending | sent | paid | cancelled
issue_datestring (date)
due_datestring (date)
subtotalnumber
discount_amountnumber
discount_typefixed | percent
tax_totalnumber
totalnumber
amount_paidnumber
amount_duenumber
currencystring
is_recurringboolean
contactobject?
created_byobject?
created_atstring (date-time)
updated_atstring (date-time)
descriptionstring?
notesstring?
termsstring?
public_urlstring

Link público da fatura, para envio ao cliente.

sent_atstring (date-time)?
paid_atstring (date-time)?
viewed_atstring (date-time)?
itemsarray<object>
paymentsarray<object>

Preenchido na leitura individual. Na criação e na atualização vem vazio.

InvoiceItem

CampoTipoObservação
idinteger
namestring
descriptionstring?
quantitynumber
unitstring?
unit_pricenumber
discount_amountnumber
discount_typefixed | percent
tax_ratenumber
tax_amountnumber
totalnumber
product_idinteger?

PaymentSummary

CampoTipoObservação
idinteger
amountnumber
methodstring
statusstring
paid_atstring (date-time)?

Payment

CampoTipoObservação
idinteger
amountnumber
methodstring
statusstring
paid_atstring (date-time)?
feenumber
net_amountnumber
gatewaystring?
transaction_idstring?
notesstring?
created_atstring (date-time)

Proposal

CampoTipoObservação
idinteger
numberstring
titlestring?
subjectstring?
statusdraft | sent | accepted | rejected | expired
proposal_datestring (date)
valid_untilstring (date)?
subtotalnumber
discount_amountnumber
discount_typefixed | percentage
tax_amountnumber
totalnumber
currencystring
contactobject?
assigned_toobject?
created_byobject?
created_atstring (date-time)
updated_atstring (date-time)

ProposalDetailed

CampoTipoObservação
idinteger
numberstring
titlestring?
subjectstring?
statusdraft | sent | accepted | rejected | expired
proposal_datestring (date)
valid_untilstring (date)?
subtotalnumber
discount_amountnumber
discount_typefixed | percentage
tax_amountnumber
totalnumber
currencystring
contactobject?
assigned_toobject?
created_byobject?
created_atstring (date-time)
updated_atstring (date-time)
dealobject?
contentstring?
notesstring?
termsstring?
public_urlstring

Link público da proposta, para envio e assinatura.

sent_atstring (date-time)?
viewed_atstring (date-time)?
accepted_atstring (date-time)?
rejected_atstring (date-time)?
signed_atstring (date-time)?
signer_namestring?
itemsarray<object>

ProposalItem

CampoTipoObservação
idinteger
namestring
descriptionstring?
quantitynumber
unitstring?
unit_pricenumber
discount_amountnumber
discount_typefixed | percentage
tax_ratenumber
tax_amountnumber
totalnumber
is_optionalboolean
product_idinteger?

Appointment

CampoTipoObservação
idinteger
titlestring
typemeeting | call | task | event | other
statusscheduled | confirmed | cancelled | completed
start_atstring (date-time)
end_atstring (date-time)?
all_dayboolean
locationstring?
meeting_typein_person | online
colorstring?
userobject?
contactobject?
dealobject?
created_atstring (date-time)
updated_atstring (date-time)

AppointmentDetailed

CampoTipoObservação
idinteger
titlestring
typemeeting | call | task | event | other
statusscheduled | confirmed | cancelled | completed
start_atstring (date-time)
end_atstring (date-time)?
all_dayboolean
locationstring?
meeting_typein_person | online
colorstring?
userobject?
contactobject?
dealobject?
created_atstring (date-time)
updated_atstring (date-time)
descriptionstring?
notesstring?
google_meet_linkstring?
google_event_idstring?

Product

CampoTipoObservação
idinteger
namestring
skustring?
typeproduct | service
pricenumber
costnumber
tax_ratenumber
stock_quantitynumber
currencystring
unitstring?
is_activeboolean
track_stockboolean
has_variationsboolean
categoryobject?
imagestring?

Caminho relativo do arquivo, não uma URL completa.

created_atstring (date-time)
updated_atstring (date-time)

ProductDetailed

CampoTipoObservação
idinteger
namestring
skustring?
typeproduct | service
pricenumber
costnumber
tax_ratenumber
stock_quantitynumber
currencystring
unitstring?
is_activeboolean
track_stockboolean
has_variationsboolean
categoryobject?
imagestring?

Caminho relativo do arquivo, não uma URL completa.

created_atstring (date-time)
updated_atstring (date-time)
descriptionstring?
short_descriptionstring?
min_stocknumber
custom_fieldsobject
marginnumber

Margem calculada a partir de preço e custo.

variationsarray<object>

Somente na leitura individual. Não há endpoint para criar ou editar variações.

WaChannel

CampoTipoObservação
idinteger
namestring
phone_numberstring
profile_namestring?
profile_picturestring?
statusstring
is_connectedboolean
is_primaryboolean
is_defaultboolean
waba_idstring?

Identificador da conta de WhatsApp Business na Meta.

connected_atstring (date-time)?

Em UTC, com sufixo Z.

WaTemplate

CampoTipoObservação
idinteger
namestring
languagestring
categoryUTILITY | MARKETING | AUTHENTICATION
category_labelstring
statusstring
header_typestring
body_textstring

Corpo do template, com as variáveis no formato {{1}}, {{2}} e assim por diante.

footer_textstring?
buttonsarray<object>
dynamic_url_buttonsarray<object>
has_dynamic_url_buttonsboolean
variablesarray<string>

Nomes das variáveis, por exemplo ["1","2"].

variables_countinteger
waba_idstring?
last_synced_atstring (date-time)?

Em UTC, com sufixo Z.

Webhook

CampoTipoObservação
idinteger
urlstring (uri)
eventsarray<*|contact.created|contact.updated|contact.deleted|conversation.created|conversation.updated|message.received|message.sent|invoice.created|invoice.updated|invoice.paid|invoice.overdue|invoice.cancelled|product.created|product.updated|product.deleted>
is_activeboolean
failure_countinteger

Falhas consecutivas. Em 50, o webhook é desativado automaticamente.

last_triggered_atstring (date-time)?
created_atstring (date-time)

WebhookWithSecret

CampoTipoObservação
idinteger
urlstring (uri)
eventsarray<*|contact.created|contact.updated|contact.deleted|conversation.created|conversation.updated|message.received|message.sent|invoice.created|invoice.updated|invoice.paid|invoice.overdue|invoice.cancelled|product.created|product.updated|product.deleted>
is_activeboolean
failure_countinteger

Falhas consecutivas. Em 50, o webhook é desativado automaticamente.

last_triggered_atstring (date-time)?
created_atstring (date-time)
secretstring

64 caracteres. Usado para calcular a assinatura HMAC-SHA256 de cada entrega. Não aparece na listagem.

Armadilhas conhecidas

Comportamentos que existem hoje e surpreendem quem integra. Estão aqui porque são reais, não porque sejam desejáveis.

Accept: application/json é obrigatório

Sem ele, um erro de autenticação responde 302 em HTML em vez de 401 em JSON. É a causa mais comum de integração que "não autentica".

Recurso de outra conta responde 404

Nunca 403. Um 404 não significa que o registro não existe, significa que ele não é seu.

discount_type muda de nome entre recursos

Faturas usam percent. Propostas usam percentage.

A ordenação padrão não é a mesma em todo lugar

desc em contatos, conversas, faturas e propostas. asc em agendamentos e produtos.

filename de mensagem sempre responde null

Use media_url para identificar o arquivo.

deals_count e contracts_count só vêm na listagem

Na leitura individual de um contato eles respondem zero.

O filtro source é sensível a maiúsculas

E a base contém grafias diferentes para a mesma origem. Para precisão, liste e filtre do seu lado.

Atualizar fatura ou proposta não mexe nos itens

PUT ignora itens, e no caso da fatura também não recalcula os totais.

Atualizar agendamento não valida a ordem das datas

A verificação de que end_at vem depois de start_at só existe na criação.

Atribuir usuário de outra conta a uma conversa falha em silêncio

O valor é ignorado e a resposta ainda é 200. Confira o retorno.

Referências no corpo não são verificadas contra a sua conta

contact_id, deal_id, product_id e category_id são aceitos sem essa checagem.

Enviar mensagem muda o estado da conversa

Ela é atribuída ao usuário do token, se não tiver responsável, e o status passa para open.

send-template não verifica o template antes de enviar

Um nome errado só falha na resposta da Meta.

Oito eventos de webhook nunca disparam

Os cinco de fatura e os três de produto são aceitos no cadastro mas não têm código que os emita. Não construa integração sobre eles.

Downloads

A especificação completa, para importar em ferramentas ou colar dentro de um modelo de linguagem.