Documentação da API
Integre o Devskin CRM com seus sistemas usando nossa API REST poderosa e fácil de usar.
Autenticação
Todas as requisições à API devem incluir uma API Key válida no header Authorization.
Acesse o painel admin → API Keys → Criar nova chave
curl -X GET "https://crm-api.devskin.com/api/v1/leads" \
-H "Authorization: Bearer dsk_your_api_key_here"
Escopos de Permissão
| Escopo | Descrição |
|---|---|
| leads:read | Ler informações de leads |
| leads:write | Criar e atualizar leads |
| leads:delete | Deletar leads |
| messages:read | Ler mensagens |
| messages:write | Enviar mensagens |
| whatsapp:read | Ler templates aprovados do WhatsApp Cloud API |
| whatsapp:write | Criar templates e enviar mensagens pelo WhatsApp Cloud API |
| pipeline:read | Ler pipelines e etapas (listar pipelines, buscar por ID, listar estágios) |
| pipeline:write | Criar e editar etapas comerciais, mover leads no pipeline e gerenciar oportunidades (criar, atualizar, mover stage, alterar status) |
| campaigns:read | Ler campanhas |
| campaigns:write | Criar e gerenciar campanhas |
Idempotência
Nas criações de leads, oportunidades, etapas e mensagens, envie
Idempotency-Key com um valor único. A chave fica válida por 24 horas.
Repetições idênticas retornam a resposta original com
Idempotency-Replayed: true; reutilização com outro payload retorna HTTP 409.
-H "Idempotency-Key: cliente-123-operacao-456"
Telefones
Respostas que contêm phone também retornam
phoneE164 e phoneValid. Telefones brasileiros com DDD,
mas sem código do país, recebem +55.
Usuários e responsáveis
GET /v1/users retorna os membros ativos do projeto com
id, name e email. Leads aceitam
ownerId no POST/PUT. Para oportunidades, use
PUT /v1/opportunities/:id/owner com
{"ownerId":"USER_ID"}; o responsável é aplicado ao lead vinculado.
Webhooks de oportunidades
Eventos disponíveis: opportunity.created,
opportunity.updated, opportunity.moved,
opportunity.won e opportunity.lost.
Todos retornam leadId, opportunityId,
pipelineStageId, status, value,
lossReason e updatedAt.
Rate Limits
A API tem limite de 1000 requisições por hora por API Key.
Requisições que excederem o limite retornarão status HTTP 429 (Too Many Requests)
Headers de Rate Limit
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640000000
API de Leads
Lista todos os leads com paginação
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| page | integer | Opcional | Número da página (padrão: 1) |
| limit | integer | Opcional | Items por página (padrão: 20, máx: 100) |
| status | string | Opcional | Filtrar por status |
curl -X GET "https://crm-api.devskin.com/api/v1/leads?page=1&limit=10" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Resposta de Exemplo
{
"data": [
{
"id": "lead_123",
"name": "João Silva",
"email": "[email protected]",
"phone": "+5519996042828",
"status": "NEW",
"score": 85,
"createdAt": "2025-01-07T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 150,
"totalPages": 15
}
}
Busca leads por email, telefone ou nome
Parâmetros de Query
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| string | Opcional | Email do lead para buscar | |
| phone | string | Opcional | Telefone do lead (com ou sem formatação) |
| name | string | Opcional | Nome do lead (busca parcial) |
curl -X GET "https://crm-api.devskin.com/api/v1/leads/search?phone=%2B5519996042828" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Resposta de Exemplo
{
"success": true,
"data": {
"id": "lead_123",
"name": "João Silva",
"email": "[email protected]",
"phone": "+5519996042828",
"company": "Empresa XYZ",
"status": "NEW",
"score": 85,
"tags": ["vip", "high-priority"],
"createdAt": "2025-01-07T10:00:00Z",
"updatedAt": "2025-01-07T15:30:00Z"
}
}
Cria um novo lead
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Obrigatório | Nome completo do lead |
| string | Opcional | Email do lead | |
| phone | string | Opcional | Telefone do lead |
| company | string | Opcional | Empresa do lead |
| position | string | Opcional | Cargo do lead |
| revenue | number | Opcional | Faturamento do lead (valor numérico, ex: 50000) |
| source | string | Opcional | Origem do lead (ex: site, indicacao, linkedin) |
| value | number | Opcional | Valor estimado do lead |
| ownerId | string | Opcional | ID do responsável pelo lead (deve pertencer ao projeto) |
| pipelineStageId | string | Opcional | ID da etapa do pipeline onde o lead será inserido. Se não informado, o lead será automaticamente inserido na primeira etapa da pipeline padrão. Uma oportunidade também é criada automaticamente. Use GET /v1/pipeline para obter os IDs das pipelines e etapas disponíveis. |
| tags | array | Opcional | Array de IDs de tags para associar ao lead |
| observation | string | Opcional | Observação livre sobre o lead — campo de texto até 5000 caracteres. Aparece no formulário de edição do lead e nos templates via {{observation}}. Útil pra capturar mensagens de formulários de contato, pains, contexto qualitativo. |
| customFields | object | Opcional | Objeto com campos personalizados. As chaves devem corresponder ao key do campo configurado no painel admin. Veja a seção de Campos Personalizados para mais detalhes. |
Para enviar dados como CNPJ, UTM ou qualquer outro campo específico do seu negócio, primeiro crie o campo personalizado no painel admin (Configurações → Campos Personalizados), depois envie o valor no objeto
customFields usando a chave (key) do campo. Faturamento já é um campo nativo do lead (use o campo revenue). Consulte seus campos disponíveis via GET /v1/admin/custom-fields.
curl -X POST "https://crm-api.devskin.com/api/v1/leads" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Santos",
"email": "[email protected]",
"phone": "+5511888888888",
"company": "Empresa XYZ",
"position": "Diretor Comercial",
"revenue": 500000,
"source": "site",
"value": 15000,
"pipelineStageId": "stage_id_here",
"observation": "Lead veio do form de contato pedindo orçamento de planos enterprise.",
"customFields": {
"CNPJ": "12.345.678/0001-90"
}
}'
📝 Parâmetros do Teste
Atualiza um lead existente
curl -X PATCH "https://crm-api.devskin.com/api/v1/leads/lead_123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Santos Silva",
"status": "CONTACTED"
}'
📝 Parâmetros do Teste
Deleta um lead
curl -X DELETE "https://crm-api.devskin.com/api/v1/leads/lead_123" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
API de Mensagens
Envia uma mensagem para um lead
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| leadId | string | Obrigatório | ID do lead |
| channel | string | Obrigatório | Canal (whatsapp, email, telegram, instagram, sms) |
| message | string | Condicional | Conteúdo da mensagem (obrigatório se não usar template) |
| templateName | string | Opcional | Nome do template WhatsApp Cloud API (substitui message) |
| templateLanguage | string | Opcional | Idioma do template (ex: pt_BR, en_US). Padrão: pt_BR |
| templateComponents | array | Opcional | Componentes do template com variáveis substituídas |
curl -X POST "https://crm-api.devskin.com/api/v1/messages" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"leadId": "lead_123",
"channel": "whatsapp",
"message": "Olá! Como posso ajudar?"
}'
📝 Parâmetros do Teste
Enviar mensagem via Instagram — DM direta (recipientId) ou resposta privada a um comentário (commentId). Requer uma conta Instagram conectada no projeto.
Informe
recipientId ou commentId (um dos dois). DM livre só é permitida dentro da janela de 24h após a última mensagem do usuário; commentId permite 1 resposta privada por comentário.
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| message | string | Obrigatório | Texto da mensagem. |
| recipientId | string | Condicional | ID do usuário do Instagram (IGSID) para enviar DM. Obrigatório se não usar commentId. |
| commentId | string | Condicional | ID do comentário para responder de forma privada. Obrigatório se não usar recipientId. |
Exemplo
curl -X POST "https://crm-api.devskin.com/api/v1/instagram/send" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "recipientId": "17841400000000000", "message": "Olá! Como posso ajudar?" }'
Resposta de Exemplo
{
"success": true,
"messageId": "aWdEM...",
"type": "dm"
}
Enviar mensagem via Discord — DM ao usuário (userId) ou em um canal (channelId). Suporta texto, embed e botões. Requer um bot do Discord conectado no projeto.
Informe
userId (cria a DM automaticamente) ou channelId (canal/thread). Pelo menos um de message ou embed é obrigatório. Mensagens acima de 2000 caracteres são quebradas automaticamente.
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| userId | string | Condicional | ID do usuário do Discord para enviar DM. Obrigatório se não usar channelId. |
| channelId | string | Condicional | ID do canal/thread. Obrigatório se não usar userId. |
| message | string | Condicional | Texto da mensagem. Obrigatório se não enviar embed. |
| embed | object | Opcional | Embed no formato do Discord (title, description, color, fields…). |
| buttons | array | Opcional | Botões de ação (componentes do Discord). |
Exemplo
curl -X POST "https://crm-api.devskin.com/api/v1/discord/send" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "userId": "123456789012345678", "message": "Olá! Mensagem do CRM." }'
Resposta de Exemplo
{
"success": true,
"messageId": "1180000000000000000",
"channelId": "1170000000000000000"
}
📞 API de Telefonia
Inicie ligações automaticamente via API. O bot de voz atenderá e conversará com o lead utilizando inteligência artificial.
Para usar a API de telefonia, você precisa ter um número de telefone configurado e créditos de telefonia disponíveis na sua conta.
Inicia uma ligação de saída para um número de telefone
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
phoneNumberId |
string | Sim | ID do número de telefone de origem (seu número) |
toNumber |
string | Sim | Número de destino no formato E.164 (ex: +5519996042828) |
leadId |
string | Não | ID do lead para vincular a ligação (opcional) |
curl -X POST "https://crm-api.devskin.com/api/v1/telephony/calls/outbound" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-H "X-Project-Id: your_project_id" \
-d '{
"phoneNumberId": "phn_abc123",
"toNumber": "+5519996042828",
"leadId": "lead_xyz789"
}'
Resposta
{
"success": true,
"data": {
"callId": "call_abc123xyz",
"callSid": "CA1234567890abcdef",
"conversationId": "conv_abc123",
"from": "+5511888888888",
"to": "+5519996042828",
"status": "RINGING",
"leadId": "lead_xyz789"
},
"message": "Call initiated successfully"
}
Status da Ligação
| Status | Descrição |
|---|---|
RINGING | Ligação em andamento, tocando |
IN_PROGRESS | Ligação atendida, em progresso |
COMPLETED | Ligação finalizada com sucesso |
BUSY | Número ocupado |
NO_ANSWER | Não atendeu |
FAILED | Falha na ligação |
VOICEMAIL | Caixa postal detectada |
Testar Endpoint
Lista o histórico de ligações
Parâmetros Query
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
limit |
number | Não | Quantidade de registros (padrão: 50) |
offset |
number | Não | Offset para paginação |
status |
string | Não | Filtrar por status (COMPLETED, FAILED, etc) |
startDate |
string | Não | Data inicial (YYYY-MM-DD) |
endDate |
string | Não | Data final (YYYY-MM-DD) |
curl -X GET "https://crm-api.devskin.com/api/v1/telephony/calls?limit=20&status=COMPLETED" \
-H "Authorization: Bearer dsk_your_api_key_here"
Obtém os detalhes de uma ligação específica
curl -X GET "https://crm-api.devskin.com/api/v1/telephony/calls/call_abc123" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta
{
"id": "call_abc123xyz",
"direction": "OUTBOUND",
"fromNumber": "+5511888888888",
"toNumber": "+5519996042828",
"status": "COMPLETED",
"duration": 145,
"costBRL": 0.85,
"recordingUrl": "https://...",
"transcription": "Olá, tudo bem? ...",
"leadId": "lead_xyz789",
"lead": {
"id": "lead_xyz789",
"name": "João Silva",
"phone": "+5519996042828"
},
"createdAt": "2024-01-15T10:30:00Z",
"answeredAt": "2024-01-15T10:30:05Z",
"endedAt": "2024-01-15T10:32:30Z"
}
📝 Parâmetros do Teste
Lista seus números de telefone configurados
curl -X GET "https://crm-api.devskin.com/api/v1/telephony/numbers" \
-H "Authorization: Bearer dsk_your_api_key_here"
Consulta o saldo de créditos de telefonia
curl -X GET "https://crm-api.devskin.com/api/v1/telephony/credits" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta
{
"hasAccess": true,
"creditsCents": 15000,
"creditsBRL": 150.00,
"creditsFormatted": "R$ 150,00",
"usage": {
"minutesThisMonth": 45,
"callsThisMonth": 12,
"costThisMonth": 38.50,
"costThisMonthFormatted": "R$ 38,50"
},
"planName": "Pro"
}
Central de atendimento
Acima, quem atende é a IA de voz. Aqui, quem atende é uma pessoa: a ligação toca no ramal do operador e, quando ele atende, o cliente é chamado. É o mesmo motor da tela da central — a ligação feita por API aparece para o operador, é gravada, entra na linha do tempo do contato e conta nos relatórios.
calls:read para consultar · calls:write para discar, desligar e tabular · dialer:read e dialer:write para o discador. Sem o escopo, a resposta é 403 com a lista do que a chave tem.
é preciso uma rota de saída (tronco SIP próprio ou a saída pela plataforma), um ramal para quem vai atender, e o softphone conectado nesse ramal. Sem o softphone, a API recusa com
AGENT_NOT_REGISTERED em vez de deixar a ligação chamar o vazio.
Montando a central dentro do seu sistema
Tudo que a tela da central faz está aqui: registrar o softphone no navegador, colocar o operador disponível, discar, acompanhar a ligação, tabular e ler o resultado. Não existe endpoint privado — a nossa tela usa este mesmo caminho.
| Passo | O que a sua interface faz | Endpoint |
|---|---|---|
| 1 | Descobre quem são os operadores e qual ramal é de quem | GET /v1/telephony/agents |
| 2 | Pega as credenciais SIP e registra o webphone no navegador | GET /v1/telephony/agents/{id}/webphone |
| 3 | Coloca o operador disponível — sem isso o discador não entrega nada | POST /v1/telephony/agents/{id}/status |
| 4 | Disca: por contato do CRM, por número, ou deixa o discador puxar da lista | POST /v1/telephony/calls |
| 5 | Acompanha o que está acontecendo — por webhook, ou perguntando | call.* · GET /v1/telephony/calls/live |
| 6 | Mostra o roteiro na tela do operador e grava o que ele respondeu | GET /v1/telephony/scripts |
| 7 | Encerra e tabula o resultado da conversa | POST /v1/telephony/calls/{id}/disposition |
| 8 | Lê os resultados: histórico, indicadores, gravação e transcrição | GET /v1/telephony/calls · /summary · /dashboard |
a API comanda a ligação; a voz trafega direto entre o navegador do operador e a nossa central, por WebRTC. É por isso que existe o passo 2 — sem um softphone registrado no ramal, a ligação é recusada com
AGENT_NOT_REGISTERED antes de tocar em qualquer lugar. Se o seu operador usa telefone físico ou celular (deviceMode: EXTERNAL), o passo 2 não se aplica.
Webphone: o áudio no navegador
O operador atende dentro da sua interface, sem instalar nada. O endpoint abaixo devolve tudo que uma biblioteca SIP para navegador precisa — ramal, usuário e senha, o endereço do WebSocket e os servidores STUN/TURN que atravessam o NAT da rede dele.
Credenciais para registrar o softphone no navegador
a resposta contém a senha SIP do ramal, por isso o endpoint exige
calls:write. Chame do seu servidor e entregue ao navegador por uma sessão sua — nunca coloque a chave de API no front-end.
{
"agentId": "0b0f...",
"extension": "1001",
"username": "1001_a3f9",
"password": "•••••••••••",
"domain": "pbx.suaempresa.com",
"wsUrl": "wss://pbx.suaempresa.com/ws",
"iceServers": [
{ "urls": ["stun:stun.suaempresa.com:3478"] },
{ "urls": ["turn:turn.suaempresa.com:3478"], "username": "crm", "credential": "•••" }
],
"deviceMode": "WEBRTC",
"autoAnswer": false
}
Registrando com sip.js
import { UserAgent, Registerer, SessionState } from 'sip.js'
// As credenciais vêm do SEU servidor, que as buscou com a chave de API.
const c = await fetch('/api-interna/webphone').then(r => r.json())
const ua = new UserAgent({
uri: UserAgent.makeURI(`sip:${c.username}@${c.domain}`),
authorizationUsername: c.username,
authorizationPassword: c.password,
transportOptions: { server: c.wsUrl },
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers: c.iceServers,
// Com TURN disponível, forçar relay evita o caso mais comum de
// "chama mas não sai áudio": rede corporativa sem rota direta.
...(c.iceServers.some(s => String(s.urls).includes('turn')) ? { iceTransportPolicy: 'relay' } : {}),
},
constraints: { audio: true, video: false },
},
})
await ua.start()
await new Registerer(ua).register()
// A ligação chega como convite: é o resultado do POST /v1/telephony/calls
// (ou do discador). Atender é aceitar e plugar o áudio num elemento
o navegador só entrega microfone em página
https (ou localhost), e autoplay de áudio costuma exigir que a página já tenha recebido um clique. Se a ligação conecta e ninguém ouve nada, o problema quase sempre é ICE — comece checando se o TURN está acessível a partir da rede do operador.
Estado do operador
O discador só entrega chamada para quem está disponível, e as filas seguem a mesma regra. Se a sua interface não controlar isso, o operador registra o webphone e mesmo assim nunca recebe nada.
Entra, sai, pausa e volta
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
action |
string | Sim | login, logout, pause ou resume |
reason |
string | Não | Motivo da pausa — o que aparece no relatório de produtividade |
curl -X POST "https://crm-api.devskin.com/api/v1/telephony/agents/AGENT_ID/status" \
-H "X-API-Key: dsk_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{ "action": "pause", "reason": "Almoço" }'
Os motivos aceitos são os que a empresa configurou —
GET /v1/telephony/pause-reasons devolve a lista para você montar
o menu sem inventar texto solto que depois não agrupa em relatório nenhum.
Acompanhando a ligação em tempo real
Uma ligação muda de estado várias vezes em poucos segundos. Há dois caminhos, e o primeiro é melhor: webhook empurra assim que acontece; a consulta serve para montar a tela quando ela abre, ou para reconciliar depois de uma queda.
Eventos (webhook)
| Evento | Quando dispara | O que vem junto |
|---|---|---|
call.initiated |
A ligação começou e o ramal do operador está tocando | id, ref, direction, leadId, userId, campaignId, queueId |
call.answered |
Operador e cliente ficaram na mesma linha — é aqui que a conversa começa | answeredAt, waitSeconds |
call.completed |
Terminou depois de conversar | duration, talkSeconds, dispositionId, recordingUrl |
call.failed |
Terminou sem conversa: ninguém atendeu, ocupado, ou abandonou na fila | error, hangupCause, abandoned |
call.voicemail |
Caiu na caixa postal e a chamada não foi entregue a ninguém | detectedAt |
"tentamos e não deu" não é "conversamos". Chamada atendida que nunca chegou a um operador chega como
call.failed com abandoned: true — assim você não precisa reinterpretar status para saber se houve conversa. Os números só fecham quando essa diferença é respeitada.
Os webhooks são cadastrados em Configurações → Webhooks, ou pela API de webhooks,
assinando os eventos que interessam. O corpo chega como POST JSON.
Consulta direta
O que está acontecendo agora: ligações em andamento e o estado de cada operador
{
"calls": [
{
"id": "8f2c...", "ref": "8F2C1D", "status": "IN_PROGRESS",
"direction": "OUTBOUND", "startedAt": "2026-08-26T14:02:11.000Z",
"agent": { "id": "0b0f...", "name": "Marina", "extension": "1001" },
"lead": { "id": "11b3...", "name": "Padaria do Zé" }
}
],
"agents": [
{ "id": "0b0f...", "name": "Marina", "extension": "1001", "status": "ON_CALL" },
{ "id": "7c31...", "name": "Rafael", "extension": "1002", "status": "AVAILABLE" }
]
}
quando a empresa liga o sigilo do número, as respostas trazem o número mascarado e a ligação é identificada pelo
ref — seis caracteres que a pessoa consegue ler em voz alta. Para discar sem ver o número, use leadId: o telefone fica no servidor.
Inicia uma ligação atendida por um operador humano
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
leadId |
string | Um dos dois | Contato do CRM. O número sai do cadastro e não trafega pela API — é a forma recomendada |
toNumber |
string | Um dos dois | Número em E.164, para quem não está na base |
userId |
string | Não | Qual operador atende. Sem ele, o dono da chave |
trunkId |
string | Não | Forçar uma rota de saída específica |
hideNumber |
boolean | Não | Sobrepõe o sigilo do número configurado na empresa |
record |
boolean | Não | Sobrepõe a gravação configurada na empresa |
além de não expor o telefone, a ligação já nasce vinculada ao contato — entra na linha do tempo dele, a ficha aparece para o operador durante a conversa e o histórico do cliente fica correto.
curl -X POST "https://crm-api.devskin.com/api/v1/telephony/calls" \
-H "X-API-Key: dsk_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{
"leadId": "11b337c6-53b6-4c5f-b4db-e81256081ab1",
"userId": "994c5964-ca47-4c97-9aec-4b3762157955"
}'
Como a ligação acontece
1. o ramal do operador toca
2. ele atende → o cliente é chamado
3. cliente atende → os dois na mesma linha, gravação começa
Chamar o cliente primeiro faria alguém atender e ouvir silêncio enquanto o operador é procurado. Enquanto isso, o operador ouve o telefone tocando — não música de espera.
Erros específicos
| HTTP | Código | O que resolver |
|---|---|---|
| 409 | AGENT_NOT_REGISTERED | O softphone do operador não está conectado |
| 409 | NO_TRUNK | Não há rota de saída configurada |
| 409 | DNC_BLOCKED | Número na lista de não perturbe |
Encerra a ligação
Registra o resultado da ligação
{ "dispositionId": "...", "note": "Cliente pediu retorno na terça" }
As tabulações disponíveis vêm de GET /v1/telephony/dispositions.
Sem tabular, o painel não consegue medir o que deu certo.
Histórico de ligações, com filtros e paginação
Filtros
| Parâmetro | Descrição |
|---|---|
from, to | Período (ISO 8601) |
userId | Operador |
direction | INBOUND · OUTBOUND |
status | COMPLETED · NO_ANSWER · BUSY · FAILED · VOICEMAIL |
queueId, campaignId, dispositionId, leadId | Fila, campanha, resultado, contato |
onlyRecorded, onlyTranscribed, abandoned | Marcadores (booleanos) |
minTalkSeconds | Tempo mínimo de conversa |
search | Procura dentro da transcrição, do resumo e da observação da tabulação — não só no nome e no número |
Uma ligação, com eventos, roteiro e respostas coletadas
O que está acontecendo agora: em conversa, tocando, na fila
Ramais, estado e disponibilidade de cada operador
Discador automático
Para volume. Você sobe uma lista, escolhe o ritmo, e o motor disca sozinho — entregando aos operadores só o que atendeu.
Cria uma campanha
{
"name": "Reativação — agosto",
"mode": "POWER",
"pacingRatio": 1.5,
"maxConcurrentCalls": 20,
"amdEnabled": true,
"amdAction": "HANGUP",
"respectDnc": true,
"recordCalls": true,
"callHoursStart": "09:00",
"callHoursEnd": "18:00",
"weekdays": [1, 2, 3, 4, 5],
"maxAttempts": 3,
"retryIntervalMin": 120,
"retryOnBusy": true,
"retryOnNoAnswer": true
}
Os quatro modos
| Modo | Como funciona | Quando usar |
|---|---|---|
PREVIEW | O operador vê o contato e decide quando ligar | Venda consultiva, ticket alto |
PROGRESSIVE | Uma chamada por operador livre | Operação padrão |
POWER | pacingRatio chamadas por operador livre | Volume, lista fria |
PREDICTIVE | Ajusta o ritmo sozinho pela taxa de atendimento | Equipe grande, lista muito grande |
quando o abandono passa de
maxAbandonRate, o motor reduz o ritmo sozinho. É regra de call center, não enfeite — abandono alto é o que gera reclamação e multa.
Faixas aceitas
Valor fora da faixa é ajustado para o limite, não recusado:
pacingRatio 1–5 · maxAbandonRate 0–20 ·
targetOccupancy 30–100 · maxConcurrentCalls 1–500 ·
maxAttempts 1–20 · retryIntervalMin 1–10080 (minutos).
Sobe a lista. Quatro formas, combináveis na mesma chamada
// contatos do CRM, por id
{ "leadIds": ["...", "..."] }
// por filtro — o mais prático para reativação
{ "filter": { "status": "LEAD", "tagIds": ["..."], "createdAfter": "2026-01-01", "limit": 5000 } }
// lista solta
{ "contacts": [{ "name": "Maria", "phoneNumber": "+5511999998888", "extraData": { "plano": "ouro" } }] }
// CSV — a coluna do telefone pode se chamar telefone, phone, numero, number ou celular
{ "csv": "nome,telefone\nMaria,+5511999998888\nJoão,11988887777" }
Quem entra por leadIds ou filter já vem vinculado ao
contato — a ligação cai na linha do tempo dele.
Controla a campanha: start, pause, stop ou schedule
Discadas, atendidas, abandono e conversão da campanha
O motor agora: linhas ativas, operadores livres e ritmo
Caixa postal
Com amdEnabled: true, a secretária eletrônica é detectada antes de
passar ao operador. O amdAction decide o destino:
HANGUP encerra (padrão), VOICEMAIL toca um áudio e desliga,
AGENT passa mesmo assim. Sem isso, cada caixa postal queima o tempo de
um operador.
Não perturbe
Bloqueia um ou vários números
{ "phoneNumbers": ["+5511999998888", "11988887777"], "reason": "Pediu na ligação" }
A comparação é pelos últimos dígitos, então +55 11 99999-8888 e
11999998888 são o mesmo número. Com respectDnc: true na
campanha, o discador nunca chama quem está aqui.
Resultados das ligações
Discar é metade. A outra metade é saber o que aconteceu — por operador, por campanha, por período — e conseguir ouvir a ligação quando o número não explica o que houve.
Histórico com filtros — é a consulta que responde quase tudo
| Parâmetro | Tipo | Descrição |
|---|---|---|
from · to | date | Período. Use o dia inteiro: from às 00:00:00 e to às 23:59:59 |
userId | string | Ligações de um operador |
campaignId | string | Ligações de uma campanha do discador |
queueId | string | Ligações de uma fila |
direction | string | INBOUND · OUTBOUND |
status | string | Situação final da ligação |
dispositionId | string | Como o operador tabulou |
abandoned | boolean | Só as que desistiram antes de falar com alguém |
onlyRecorded | boolean | Só as que têm gravação |
onlyTranscribed | boolean | Só as que já foram transcritas |
minTalkSeconds | number | Descarta as conversas curtas demais para valer análise |
search | string | Procura no nome do contato, na anotação e dentro da transcrição |
page · limit | number | Paginação (padrão 25 por página) |
é o que permite responder "quais ligações desta semana falaram em cancelamento" sem ouvir gravação por gravação —
?search=cancelamento&from=2026-08-19.
curl "https://crm-api.devskin.com/api/v1/telephony/calls?from=2026-08-01&to=2026-08-31&userId=USER_ID&limit=50" \
-H "X-API-Key: dsk_sua_chave_aqui"
Os números do período, já somados — aceita os mesmos filtros do histórico
{
"total": 412,
"answered": 268,
"abandoned": 31,
"totalTalkSeconds": 74210,
"avgTalkSeconds": 277,
"avgWaitSeconds": 14,
"recorded": 268,
"byDisposition": [
{ "id": "a1...", "name": "Interessado", "count": 84 },
{ "id": "b2...", "name": "Sem interesse", "count": 121 }
]
}
Indicadores prontos para painel: volume por hora, desempenho por operador e por fila
Uma ligação inteira: tempos, tabulação, respostas do roteiro, transcrição e resumo
O áudio da ligação
Devolve o áudio direto, ou redireciona para um link assinado quando a gravação
já está no armazenamento de arquivos. Dá para apontar uma tag <audio>
para cá — desde que a autenticação vá junto. Ligação sem gravação responde
404 dizendo isso, em vez de devolver um arquivo vazio.
Tabulação: o resultado que o operador registra
A tabulação é o que transforma ligação em dado. Sem ela o relatório sabe quantas
ligações houve e quanto tempo duraram, mas não sabe quantas viraram alguma coisa.
GET /v1/telephony/dispositions lista as opções configuradas;
POST /v1/telephony/calls/{id}/disposition grava a escolha e a anotação.
curl -X POST "https://crm-api.devskin.com/api/v1/telephony/calls/CALL_ID/disposition" \
-H "X-API-Key: dsk_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{
"dispositionId": "a1b2c3d4-...",
"note": "Pediu para retornar depois do dia 10",
"scheduledAt": "2026-09-11T14:00:00.000Z"
}'
Quando a ligação não sai
Recusar cedo é melhor que deixar a chamada tocar no vazio. Estes são os motivos que aparecem, e o que cada um pede da sua interface.
| HTTP | Resposta | O que significa e o que fazer |
|---|---|---|
| 401 | API Key not provided · Invalid API Key · API Key inactive | Chave ausente, malformada, revogada ou de outra empresa |
| 403 | Insufficient permissions | Falta escopo. A resposta traz requiredScopes e availableScopes — dá para mostrar ao usuário exatamente o que pedir |
| 409 | code: "AGENT_NOT_REGISTERED" | O softphone do operador não está conectado. Registre o webphone (passo 2) antes de discar — recusamos aqui em vez de deixar a chamada tocar no vazio |
| 409 | code: "NO_TRUNK" | Não há rota de saída ativa. Configure um tronco SIP próprio ou ative a saída pela plataforma |
| 409 | code: "DNC_BLOCKED" | O número está na lista de não perturbe — e isso é para respeitar, não para contornar |
| 404 | Contato não encontrado | O leadId não existe nesta empresa |
| 400 | Este contato não tem telefone cadastrado | O contato existe, mas não há para onde ligar |
| 400 | Informe o contato ou o número de destino | Faltou leadId ou toNumber |
as três recusas que a sua interface precisa distinguir —
AGENT_NOT_REGISTERED, NO_TRUNK e DNC_BLOCKED — vêm com um campo code estável, além da mensagem em português. A mensagem serve para mostrar; o code, para decidir.
ela existe para a sua proteção também.
POST /v1/telephony/dnc adiciona um número, e campanhas com respectDnc: true nunca o chamam. A comparação é pelos últimos dígitos, então o mesmo número escrito de formas diferentes continua sendo o mesmo número.
Limites de simultaneidade
| Camada | Padrão | Onde muda |
|---|---|---|
| Simultâneas na empresa | 60 | Telefonia → Visão geral |
| Canais do tronco | 30 | Cadastro do tronco |
| Por operador | 1 | Fixo |
isso não é configurável, e é assim de propósito — ninguém fala com dois clientes ao mesmo tempo. O paralelismo vem de ter mais operadores, ou do discador, que mantém várias chamadas em andamento e entrega só as que atenderam.
API de Pipeline
Lista todas as pipelines do projeto com suas etapas
curl -X GET "https://crm-api.devskin.com/api/v1/pipeline" \
-H "Authorization: Bearer dsk_your_api_key_here"
stages com as etapas ordenadas. A pipeline padrão é identificada por isDefault: true.
Exemplo de Resposta
[
{
"id": "pipeline_abc123",
"name": "Pipeline de Vendas",
"isDefault": true,
"order": 0,
"stages": [
{
"id": "stage_001",
"name": "Novo Lead",
"order": 0,
"probability": 10,
"color": "#3B82F6",
"finalStageType": null
},
{
"id": "stage_002",
"name": "Qualificação",
"order": 1,
"probability": 30,
"color": "#8B5CF6",
"finalStageType": null
}
]
},
{
"id": "pipeline_def456",
"name": "Prospecção",
"isDefault": false,
"order": 1,
"stages": [...]
}
]
Busca uma pipeline específica por ID, incluindo todas as suas etapas
curl -X GET "https://crm-api.devskin.com/api/v1/pipeline/PIPELINE_ID_HERE" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Lista todos os estágios do pipeline
?pipelineId=ID para filtrar etapas de uma pipeline específica. Sem o parâmetro, retorna etapas da pipeline padrão.
curl -X GET "https://crm-api.devskin.com/api/v1/pipeline/stages" \
-H "Authorization: Bearer dsk_your_api_key_here"
Cria uma etapa comercial na pipeline padrão ou na pipeline indicada
curl -X POST "https://crm-api.devskin.com/api/v1/pipeline/stages" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Novo lead",
"pipelineId": "pipeline_123",
"order": 0,
"probability": 10,
"color": "#3B82F6"
}'
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Sim | Nome da etapa |
| pipelineId | string | Não | Sem este campo, usa a pipeline padrão |
| order | integer | Não | Posição da coluna, iniciando em 0 |
| probability | integer | Não | Probabilidade percentual da etapa |
| color | string | Não | Cor hexadecimal da coluna |
| daysUntilStale | integer | Não | Dias até a oportunidade ser considerada parada |
| finalStageType | string|null | Não | Tipo final da etapa, quando aplicável |
Edita uma etapa comercial existente
curl -X PUT "https://crm-api.devskin.com/api/v1/pipeline/stages/STAGE_ID" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Primeiro contato",
"order": 1,
"probability": 20,
"color": "#6366F1"
}'
Reordena etapas comerciais
curl -X PUT "https://crm-api.devskin.com/api/v1/pipeline/stages/reorder" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"stages":[{"id":"stage_1","order":0},{"id":"stage_2","order":1}]}'
Exclui uma etapa e migra seus leads e oportunidades para outra etapa
curl -X DELETE "https://crm-api.devskin.com/api/v1/pipeline/stages/STAGE_ID" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"destinationStageId":"DESTINATION_STAGE_ID"}'
Move um lead (e suas oportunidades abertas) para outro estágio do pipeline
curl -X POST "https://crm-api.devskin.com/api/v1/pipeline/move" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"leadId": "lead_123",
"toStageId": "stage_456"
}'
📝 Parâmetros do Teste
Move múltiplas oportunidades entre stages em massa (migração)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| toStageId | string | ✅ Sim | ID do estágio de destino |
| opportunityIds | string[] | Modo 1 | Array de IDs de oportunidades específicas para mover |
| fromStageId | string | Modo 2 | Move todas as oportunidades OPEN deste stage de origem |
| fromPipelineId | string | Modo 3 | Move todas as oportunidades OPEN de toda a pipeline |
# Modo 1: Mover oportunidades específicas por ID
curl -X POST "https://crm-api.devskin.com/api/v1/pipeline/move-opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"opportunityIds": ["opp_id_1", "opp_id_2", "opp_id_3"],
"toStageId": "stage_destino"
}'
# Modo 2: Mover todas de um stage para outro
curl -X POST "https://crm-api.devskin.com/api/v1/pipeline/move-opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"fromStageId": "stage_origem",
"toStageId": "stage_destino"
}'
# Modo 3: Mover todas de uma pipeline inteira
curl -X POST "https://crm-api.devskin.com/api/v1/pipeline/move-opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"fromPipelineId": "pipeline_id",
"toStageId": "stage_destino"
}'
Exemplo de Resposta
{
"moved": 15,
"leadsUpdated": 12,
"toStage": {
"id": "stage_destino",
"name": "Qualificação"
},
"opportunityIds": ["opp_1", "opp_2", "..."]
}
📝 Parâmetros do Teste
API de Oportunidades
Lista todas as oportunidades do projeto
curl -X GET "https://crm-api.devskin.com/api/v1/opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here"
Exemplo de Resposta
{
"opportunities": [
{
"id": "opp_abc123",
"leadId": "lead_123",
"title": "Projeto Website",
"value": "5000",
"status": "OPEN",
"pipelineStageId": "stage_456",
"pipelineStage": {
"id": "stage_456",
"name": "Proposta Enviada"
},
"lead": {
"id": "lead_123",
"name": "João Silva"
},
"createdAt": "2026-03-20T10:00:00.000Z"
}
],
"total": 1,
"page": 1,
"totalPages": 1
}
Busca uma oportunidade por ID
curl -X GET "https://crm-api.devskin.com/api/v1/opportunities/opp_abc123" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Cria uma oportunidade vinculada a um lead existente
pipelineStageId, a oportunidade é criada automaticamente.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| leadId | string | ✅ Sim | ID do lead ao qual vincular a oportunidade |
| title | string | Não | Título da oportunidade (ex: "Projeto Website") |
| value | number | Não | Valor da oportunidade (ex: 5000) |
| pipelineStageId | string | Não | ID do estágio do pipeline. Se não informado, usa a primeira etapa da pipeline padrão |
| description | string | Não | Descrição da oportunidade |
curl -X POST "https://crm-api.devskin.com/api/v1/opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"leadId": "lead_123",
"title": "Projeto Website",
"value": 5000,
"pipelineStageId": "stage_456"
}'
📝 Parâmetros do Teste
Atualiza uma oportunidade (mover de stage, alterar valor, título, etc.)
pipelineStageId do novo estágio. Você pode atualizar qualquer campo individualmente.
| Campo | Tipo | Descrição |
|---|---|---|
| pipelineStageId | string | Novo estágio do pipeline (move a oportunidade no kanban) |
| title | string | Título da oportunidade |
| value | number | Valor da oportunidade |
| description | string | Descrição |
# Mover oportunidade para outro stage
curl -X PUT "https://crm-api.devskin.com/api/v1/opportunities/opp_abc123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"pipelineStageId": "stage_789"
}'
# Atualizar valor e título
curl -X PUT "https://crm-api.devskin.com/api/v1/opportunities/opp_abc123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Projeto Website Premium",
"value": 8000
}'
📝 Parâmetros do Teste
Altera o status da oportunidade (OPEN, WON, LOST)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| status | string | ✅ Sim | Novo status: OPEN, WON ou LOST |
| lossReason | string | Não | Motivo da perda (apenas quando status = LOST) |
# Marcar como ganha
curl -X PATCH "https://crm-api.devskin.com/api/v1/opportunities/opp_abc123/status" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"status": "WON"
}'
# Marcar como perdida com motivo
curl -X PATCH "https://crm-api.devskin.com/api/v1/opportunities/opp_abc123/status" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"status": "LOST",
"lossReason": "Cliente escolheu concorrente"
}'
📝 Parâmetros do Teste
API de Campanhas
Lista todas as campanhas
curl -X GET "https://crm-api.devskin.com/api/v1/campaigns" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Obtém estatísticas de uma campanha
curl -X GET "https://crm-api.devskin.com/api/v1/campaigns/campaign_123/stats" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
API de Tags
Lista todas as tags do projeto
curl -X GET "https://crm-api.devskin.com/api/v1/tags" \
-H "Authorization: Bearer dsk_your_api_key_here"
Adiciona tags a um lead
curl -X POST "https://crm-api.devskin.com/api/v1/leads/lead_123/tags" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"tags": ["vip", "high-priority"]
}'
📝 Parâmetros do Teste
Remove uma tag de um lead
curl -X DELETE "https://crm-api.devskin.com/api/v1/leads/lead_123/tags/tag_456" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Tickets
Gerencie tickets de suporte, categorias e acompanhe SLA.
Lista todos os tickets do projeto
Parâmetros de Query
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| status | string | Opcional | OPEN, IN_PROGRESS, WAITING_CUSTOMER, WAITING_INTERNAL, RESOLVED, CLOSED |
| priority | string | Opcional | LOW, MEDIUM, HIGH, URGENT |
| categoryId | string | Opcional | Filtrar por categoria |
| page | integer | Opcional | Número da página (padrão: 1) |
| limit | integer | Opcional | Items por página (padrão: 20) |
curl -X GET "https://crm-api.devskin.com/api/v1/tickets?status=OPEN&limit=10" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Resposta de Exemplo
{
"tickets": [
{
"id": "ticket_123",
"number": 1,
"subject": "Problema com login",
"description": "Não consigo acessar minha conta",
"status": "OPEN",
"priority": "HIGH",
"contactName": "João Silva",
"contactEmail": "[email protected]",
"createdAt": "2025-01-07T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"totalPages": 3
}
}
Busca um ticket específico pelo ID
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/ticket_123" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Cria um novo ticket de suporte
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| subject | string | Obrigatório | Assunto do ticket |
| description | string | Obrigatório | Descrição detalhada |
| priority | string | Opcional | LOW, MEDIUM (padrão), HIGH, URGENT |
| categoryId | string | Opcional | ID da categoria |
| contactName | string | Opcional | Nome do contato |
| contactEmail | string | Opcional | Email do contato |
| contactPhone | string | Opcional | Telefone do contato |
| leadId | string | Opcional | ID do lead vinculado |
curl -X POST "https://crm-api.devskin.com/api/v1/tickets" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"subject": "Problema com login",
"description": "Não consigo acessar minha conta desde ontem",
"priority": "HIGH",
"contactName": "João Silva",
"contactEmail": "[email protected]"
}'
📝 Parâmetros do Teste
Atualiza um ticket existente
curl -X PUT "https://crm-api.devskin.com/api/v1/tickets/ticket_123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"status": "IN_PROGRESS",
"priority": "URGENT"
}'
📝 Parâmetros do Teste
Marca o ticket como resolvido
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/ticket_123/resolve" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Reabre um ticket resolvido ou fechado
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/ticket_123/reopen" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Fecha o ticket definitivamente
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/ticket_123/close" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Lista os comentários de um ticket
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/ticket_123/comments" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Adiciona um comentário ao ticket
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| content | string | Obrigatório | Conteúdo do comentário |
| isInternal | boolean | Opcional | Se true, comentário interno (não visível para cliente) |
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/ticket_123/comments" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"content": "Comentário de teste via API",
"isInternal": false
}'
📝 Parâmetros do Teste
Lista as categorias de tickets
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/categories" \
-H "Authorization: Bearer dsk_your_api_key_here"
Cria uma nova categoria
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Obrigatório | Nome da categoria |
| color | string | Opcional | Cor em hex (ex: #3B82F6) |
| icon | string | Opcional | Emoji ou nome do ícone |
| slaFirstResponse | integer | Opcional | SLA primeira resposta em horas |
| slaResolution | integer | Opcional | SLA resolução em horas |
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/categories" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Suporte Técnico",
"color": "#3B82F6",
"slaFirstResponse": 4,
"slaResolution": 24
}'
📝 Parâmetros do Teste
Retorna métricas de tickets (total, por status, SLA violados)
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/metrics" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
{
"total": 150,
"open": 25,
"inProgress": 30,
"waitingCustomer": 10,
"waitingInternal": 5,
"resolved": 60,
"closed": 20,
"slaBreached": 3
}
Exclui um ticket permanentemente
curl -X DELETE "https://crm-api.devskin.com/api/v1/tickets/ticket_123" \
-H "Authorization: Bearer dsk_your_api_key_here"
⚠️ Zona de Perigo
Atribui o ticket a um usuário
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| userId | string | Obrigatório | ID do usuário para atribuir |
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/ticket_123/assign" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_456"
}'
📝 Parâmetros do Teste
Lista o histórico de atividades de um ticket
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/ticket_123/activities" \
-H "Authorization: Bearer dsk_your_api_key_here"
📝 Parâmetros do Teste
Resposta de Exemplo
[
{
"id": "act_123",
"type": "STATUS_CHANGED",
"description": "alterou o status",
"oldValue": "OPEN",
"newValue": "IN_PROGRESS",
"user": {
"id": "user_456",
"name": "João Silva"
},
"createdAt": "2025-01-07T14:30:00Z"
}
]
🎯 Etapas do Kanban (Stages)
Configure as etapas do Kanban para organizar seus tickets visualmente.
Lista todas as etapas do Kanban de tickets
curl -X GET "https://crm-api.devskin.com/api/v1/tickets/stages" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
[
{
"id": "stage_123",
"name": "Novos",
"status": "OPEN",
"color": "#3B82F6",
"icon": "circle",
"order": 1,
"isDefault": true
},
{
"id": "stage_456",
"name": "Em Análise",
"status": "IN_PROGRESS",
"color": "#F59E0B",
"icon": "clock",
"order": 2,
"isDefault": false
},
{
"id": "stage_789",
"name": "Resolvidos",
"status": "RESOLVED",
"color": "#10B981",
"icon": "check-circle",
"order": 3,
"isDefault": false
}
]
Cria uma nova etapa no Kanban
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Obrigatório | Nome da etapa |
| status | string | Obrigatório | OPEN, IN_PROGRESS, WAITING_CUSTOMER, WAITING_INTERNAL, RESOLVED, CLOSED |
| color | string | Opcional | Cor em hex (ex: #3B82F6) |
| icon | string | Opcional | Nome do ícone (circle, clock, check-circle, etc.) |
curl -X POST "https://crm-api.devskin.com/api/v1/tickets/stages" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Aguardando Aprovação",
"status": "WAITING_INTERNAL",
"color": "#8B5CF6",
"icon": "pause"
}'
📝 Parâmetros do Teste
Atualiza uma etapa existente
curl -X PUT "https://crm-api.devskin.com/api/v1/tickets/stages/stage_123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Em Revisão",
"color": "#EC4899"
}'
📝 Parâmetros do Teste
Exclui uma etapa (tickets serão movidos para etapa padrão)
curl -X DELETE "https://crm-api.devskin.com/api/v1/tickets/stages/stage_123" \
-H "Authorization: Bearer dsk_your_api_key_here"
⚠️ Zona de Perigo
Reordena as etapas do Kanban
Body (JSON)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| stageIds | array | Obrigatório | Array de IDs das etapas na nova ordem |
curl -X PUT "https://crm-api.devskin.com/api/v1/tickets/stages/reorder" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"stageIds": ["stage_456", "stage_123", "stage_789"]
}'
📝 Parâmetros do Teste
📁 Gerenciamento de Categorias
Atualiza uma categoria existente
curl -X PUT "https://crm-api.devskin.com/api/v1/tickets/categories/cat_123" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Suporte Premium",
"slaFirstResponse": 2,
"slaResolution": 24
}'
📝 Parâmetros do Teste
Exclui uma categoria (tickets serão movidos para "sem categoria")
curl -X DELETE "https://crm-api.devskin.com/api/v1/tickets/categories/cat_123" \
-H "Authorization: Bearer dsk_your_api_key_here"
⚠️ Zona de Perigo
API de Mensagens Agendadas
Agende mensagens para serem enviadas automaticamente em uma data/hora específica via WhatsApp, Email ou Ligação.
Criar uma nova mensagem agendada
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| channel | string | Obrigatório | Canal: WHATSAPP, EMAIL, CALL ou SMS |
| content | string | Obrigatório | Conteúdo da mensagem (não obrigatório para templates) |
| scheduledFor | string | Obrigatório | Data/hora no formato ISO: 2025-01-15T14:30:00 |
| leadId | string | Opcional | ID do lead (extrai telefone/email automaticamente) |
| toPhone | string | Opcional | Número de telefone destino |
| toEmail | string | Opcional | Email destino (para canal EMAIL) |
| subject | string | Opcional | Assunto (obrigatório para canal EMAIL) |
| templateName | string | Opcional | Nome do template WhatsApp (Cloud API) |
| templateLanguage | string | Opcional | Idioma do template (ex: pt_BR) |
| templateComponents | array | Opcional | Componentes do template com variáveis preenchidas |
| timezone | string | Opcional | Timezone (padrão: America/Sao_Paulo) |
# Mensagem simples
curl -X POST "https://crm-api.devskin.com/api/v1/scheduled-messages" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"channel": "WHATSAPP",
"content": "Olá {{name}}, lembrando da nossa reunião amanhã às 10h!",
"scheduledFor": "2025-01-15T09:00:00",
"leadId": "lead_abc123"
}'
# Com template WhatsApp Cloud API
curl -X POST "https://crm-api.devskin.com/api/v1/scheduled-messages" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"channel": "WHATSAPP",
"scheduledFor": "2025-01-15T09:00:00",
"leadId": "lead_abc123",
"templateName": "follow_up",
"templateLanguage": "pt_BR",
"templateComponents": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "João" },
{ "type": "text", "text": "Empresa XYZ" }
]
}
]
}'
Resposta de Exemplo
{
"id": "sm_abc123",
"channel": "WHATSAPP",
"content": "Olá João, lembrando da nossa reunião amanhã às 10h!",
"scheduledFor": "2025-01-15T09:00:00.000Z",
"status": "PENDING",
"toPhone": "+5519996042828",
"toName": "João Silva",
"createdAt": "2025-01-14T15:30:00.000Z"
}
📝 Parâmetros do Teste
Listar mensagens agendadas
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| status | string | Opcional | Filtrar por status: PENDING, SENT, FAILED, CANCELLED |
| channel | string | Opcional | Filtrar por canal: WHATSAPP, EMAIL, CALL |
| leadId | string | Opcional | Filtrar por lead específico |
curl -X GET "https://crm-api.devskin.com/api/v1/scheduled-messages?status=PENDING" \
-H "Authorization: Bearer dsk_your_api_key_here"
Cancelar mensagem agendada (apenas PENDING)
curl -X DELETE "https://crm-api.devskin.com/api/v1/scheduled-messages/sm_abc123" \
-H "Authorization: Bearer dsk_your_api_key_here"
⚠️ Zona de Perigo
API de Templates WhatsApp
Gerencie templates de mensagem do WhatsApp Cloud API. Templates são necessários para iniciar conversas após 24h sem resposta do contato.
O WhatsApp Cloud API exige que mensagens enviadas após 24h sem resposta do contato utilizem templates aprovados pela Meta. Mensagens de texto livre só podem ser enviadas dentro da janela de 24h após a última mensagem recebida do contato.
Listar templates aprovados do WhatsApp Cloud API
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| status | string | Opcional | Filtrar por status: APPROVED, PENDING, REJECTED |
curl -X GET "https://crm-api.devskin.com/api/v1/whatsapp/templates" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
{
"templates": [
{
"id": "tpl_abc123",
"name": "follow_up",
"language": "pt_BR",
"status": "APPROVED",
"category": "MARKETING",
"components": [
{
"type": "BODY",
"text": "Olá {{1}}, tudo bem? Gostaria de conversar sobre {{2}}."
}
]
}
]
}
Enviar mensagem no WhatsApp Business (Cloud API oficial). Use um template aprovado para iniciar conversa ou enviar fora da janela de 24h. Requer uma instância WhatsApp do tipo Cloud API conectada no projeto (templates só existem na API oficial da Meta).
1) Liste os templates aprovados em
GET /v1/whatsapp/templates e anote o name, o language e as variáveis ({{1}}, {{2}}…).2) Envie com
POST /v1/whatsapp/send passando templateName, languageCode e os components com os valores das variáveis na ordem.
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| to | string | Obrigatório | Número do destinatário em formato internacional, só dígitos. Ex.: 5519999990000. |
| templateName | string | Obrigatório* | Nome exato do template aprovado (de GET /v1/whatsapp/templates). *Obrigatório para enviar template. |
| languageCode | string | Opcional | Idioma do template (default pt_BR). Deve casar com o idioma do template aprovado na Meta. |
| components | array | Opcional | Variáveis do template no formato da Cloud API. Omita se o template não tiver variáveis. |
| message | string | Opcional | Texto livre (alternativa ao template). Só funciona dentro da janela de 24h após o contato responder. |
Exemplo — enviar template com variáveis
curl -X POST "https://crm-api.devskin.com/api/v1/whatsapp/send" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"to": "5519999990000",
"templateName": "follow_up",
"languageCode": "pt_BR",
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "João" },
{ "type": "text", "text": "sua proposta" }
]
}
]
}'
Resposta de Exemplo
{
"success": true,
"type": "template",
"messageId": "wamid.HBgM..."
}
components:Cada variável
{{1}}, {{2}}… do corpo do template vira um item em parameters, na mesma ordem. Se o template tiver cabeçalho ou botões com variável, adicione blocos { "type": "header", ... } / { "type": "button", "sub_type": "url", "index": "0", ... } conforme a documentação da Cloud API da Meta. Templates sem variáveis dispensam components.
Para enviar
message (texto livre) em vez de template, o contato precisa ter respondido nas últimas 24h (regra da Meta). Fora disso, use sempre um template aprovado.
Criar novo template (envia para aprovação da Meta)
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| name | string | Obrigatório | Nome único (lowercase, underscores, sem espaços) |
| category | string | Obrigatório | MARKETING, UTILITY ou AUTHENTICATION |
| language | string | Obrigatório | Código do idioma (pt_BR, en_US, etc.) |
| body | string | Obrigatório | Texto do corpo (use {{1}}, {{2}} para variáveis) |
| header | string | Opcional | Texto do cabeçalho |
| footer | string | Opcional | Texto do rodapé |
curl -X POST "https://crm-api.devskin.com/api/v1/whatsapp/cloud/templates" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "follow_up_call",
"category": "UTILITY",
"language": "pt_BR",
"body": "Olá {{1}}, tudo bem? Notamos que você se interessou por {{2}}. Podemos agendar uma conversa para esclarecer suas dúvidas?"
}'
Templates enviados passam por revisão da Meta e podem levar de algumas horas a alguns dias para serem aprovados. Use a API de listagem para verificar o status.
📝 Parâmetros do Teste
Deletar template
curl -X DELETE "https://crm-api.devskin.com/api/v1/whatsapp/cloud/templates/follow_up" \
-H "Authorization: Bearer dsk_your_api_key_here"
⚠️ Zona de Perigo
Enviar mensagem de template para um lead
Corpo da Requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| leadId | string | Obrigatório | ID do lead |
| templateName | string | Obrigatório | Nome do template aprovado |
| templateLanguage | string | Obrigatório | Idioma do template (pt_BR) |
| templateComponents | array | Opcional | Valores das variáveis do template |
curl -X POST "https://crm-api.devskin.com/api/v1/timeline/whatsapp" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"leadId": "lead_abc123",
"templateName": "follow_up",
"templateLanguage": "pt_BR",
"templateComponents": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "João" },
{ "type": "text", "text": "nosso produto XYZ" }
]
}
]
}'
Resposta de Exemplo
{
"success": true,
"messageId": "wamid.HBgLNTUxMTk5OTk5OTkVAgASGCA2N...",
"template": "follow_up",
"status": "sent"
}
📝 Parâmetros do Teste
Variáveis de Template
Templates do WhatsApp Cloud API usam variáveis no formato {{1}}, {{2}}, etc.
Ao enviar uma mensagem, você deve fornecer os valores na ordem correta através do templateComponents.
Mapeamento de Variáveis Sugerido
| Variável | Uso Comum | Campo do Lead |
|---|---|---|
{{1}} |
Nome do contato | lead.name ou lead.firstName |
{{2}} |
Empresa | lead.company |
{{3}} |
Produto/Serviço | Texto customizado |
{{4}} |
Data/Hora | Data formatada |
Na interface do CRM, o mapeamento de variáveis é feito automaticamente através de um seletor visual que conecta as variáveis do template aos campos do lead.
API de Campos Personalizados
Configure campos personalizados para leads e oportunidades através do painel admin. Use a API para consultar as configurações e incluir valores nos seus leads/oportunidades.
Os campos personalizados são configurados no painel admin → Campos Personalizados. Esta API permite consultar as configurações e usar os campos na criação/atualização de leads e oportunidades.
Lista todas as configurações de campos personalizados
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| entityType | string | Opcional | Filtrar por tipo: LEAD, OPPORTUNITY, ou BOTH |
curl -X GET "https://crm-api.devskin.com/api/v1/admin/custom-fields?entityType=LEAD" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
{
"fields": [
{
"id": "cf_123",
"name": "CNPJ",
"key": "CNPJ",
"type": "TEXT",
"entityType": "BOTH",
"required": true,
"defaultValue": null,
"description": "CNPJ da empresa",
"showInQuote": true,
"order": 1
},
{
"id": "cf_124",
"name": "Contrato",
"key": "CONTRATO",
"type": "FILE",
"entityType": "OPPORTUNITY",
"required": false,
"defaultValue": null,
"description": "Arquivo do contrato assinado",
"showInQuote": false,
"order": 2
}
]
}
Tipos de Campo Suportados
| Tipo | Descrição | Exemplo de Valor |
|---|---|---|
| TEXT | Texto curto (até 255 caracteres) | "12.345.678/0001-90" |
| TEXTAREA | Texto longo (múltiplas linhas) | "Observações detalhadas..." |
| NUMBER | Número inteiro ou decimal | 1500.50 |
| DATE | Data no formato ISO | "2025-01-15" |
| SELECT | Seleção de opções predefinidas | "Opção A" |
| FILE | Arquivo (PDF, imagem, etc.) | {"url": "...", "name": "contrato.pdf"} |
Criar lead com campos personalizados
curl -X POST "https://crm-api.devskin.com/api/v1/leads" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "João Silva",
"email": "[email protected]",
"phone": "+5519996042828",
"company": "Empresa XYZ",
"customFields": {
"CNPJ": "12.345.678/0001-90",
"NUMERO_FUNCIONARIOS": 50,
"DATA_FUNDACAO": "2020-05-15",
"SETOR": "Tecnologia"
}
}'
Resposta de Exemplo
{
"id": "lead_abc123",
"name": "João Silva",
"email": "[email protected]",
"phone": "+5519996042828",
"company": "Empresa XYZ",
"customFields": {
"CNPJ": "12.345.678/0001-90",
"NUMERO_FUNCIONARIOS": 50,
"DATA_FUNDACAO": "2020-05-15",
"SETOR": "Tecnologia"
},
"createdAt": "2025-01-15T10:30:00Z"
}
📝 Parâmetros do Teste
Criar oportunidade com campos personalizados
curl -X POST "https://crm-api.devskin.com/api/v1/opportunities" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Proposta Comercial - Empresa XYZ",
"leadId": "lead_abc123",
"value": 50000,
"customFields": {
"PRAZO_ENTREGA": "30 dias",
"CONDICAO_PAGAMENTO": "30/60/90",
"GARANTIA": "12 meses"
}
}'
📝 Parâmetros do Teste
Upload de arquivo para campo personalizado do tipo FILE
Corpo da Requisição (multipart/form-data)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| file | file | Obrigatório | Arquivo a ser enviado (máx 10MB) |
| fieldKey | string | Obrigatório | Chave do campo personalizado |
| entityType | string | Opcional | Tipo de entidade (LEAD ou OPPORTUNITY) |
curl -X POST "https://crm-api.devskin.com/api/v1/attachments/custom-field" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-F "file=@/path/to/contrato.pdf" \
-F "fieldKey=CONTRATO" \
-F "entityType=OPPORTUNITY"
Resposta de Exemplo
{
"url": "https://s3.amazonaws.com/.../contrato-123456.pdf",
"name": "contrato.pdf",
"size": 245678,
"mimeType": "application/pdf"
}
Após o upload, use a resposta como valor do campo personalizado ao criar/atualizar o lead ou oportunidade. Armazene o objeto JSON completo no campo.
Obtém os valores padrão configurados para campos personalizados
Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| entityType | string | Opcional | Filtrar por tipo: LEAD ou OPPORTUNITY |
curl -X GET "https://crm-api.devskin.com/api/v1/admin/custom-fields/defaults?entityType=LEAD" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
{
"defaults": {
"PRAZO_PAGAMENTO": "30 dias",
"GARANTIA": "12 meses",
"FRETE": "FOB"
}
}
API de Projetos
Liste os projetos da empresa vinculada à API Key. Útil para integrações que precisam selecionar em qual projeto operar.
Você pode enviar o header
x-project-id em qualquer requisição para especificar em qual projeto a operação deve ser executada. Se não informado, será usado o primeiro projeto da empresa.
Lista todos os projetos da empresa
curl -X GET "https://crm-api.devskin.com/api/v1/projects" \
-H "Authorization: Bearer dsk_your_api_key_here"
Resposta de Exemplo
{
"success": true,
"data": [
{
"id": "proj_abc123",
"name": "Projeto Principal"
},
{
"id": "proj_def456",
"name": "Projeto Secundário"
}
]
}
Após obter o ID do projeto, envie-o no header de todas as requisições subsequentes:
curl -X POST "https://crm-api.devskin.com/api/v1/leads" \
-H "Authorization: Bearer dsk_your_api_key_here" \
-H "x-project-id: proj_abc123" \
-H "Content-Type: application/json" \
-d '{"name": "Novo Lead", "email": "[email protected]"}'
Webhooks
Webhooks permitem que você receba notificações em tempo real quando eventos ocorrem no sistema.
Acesse o painel admin → Webhooks → Criar novo webhook e selecione os eventos que deseja receber.
Eventos Disponíveis
🎫 Eventos de Tickets
📞 Eventos de Ligações
Segurança dos Webhooks
Todos os webhooks são assinados com HMAC-SHA256 para garantir autenticidade.
Validando Assinatura
const crypto = require('crypto');
function validateWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return signature === expectedSignature;
}
// Uso
app.post('/webhook', (req, res) => {
const signature = req.headers['x-devskin-signature'];
const secret = 'your_webhook_secret';
if (!validateWebhookSignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Processar webhook
console.log('Evento:', req.body.event);
console.log('Dados:', req.body.data);
res.status(200).send('OK');
});
Lógica de Retry
Se seu endpoint retornar um erro (status 4xx ou 5xx), tentaremos reenviar automaticamente:
- 1ª tentativa: imediata
- 2ª tentativa: após 1 minuto
- 3ª tentativa: após 5 minutos