Nuntis documentação Referência da API

Operação

Números e pareamento

O número é a linha por onde se fala, e o canal é propriedade dele. Você usa o numberId para enviar, e o estado de conexão para saber se aquela linha está de pé.

Listar os seus números

GET /v1/numbers

bash
curl -H "x-api-key: $NUNTIS_API_KEY" \
  https://api.nuntis.com.br/v1/numbers
200 OK
{
  "data": [
    {
      "id": "num_4711",
      "channel": "whatsapp-unofficial",
      "label": "Comercial",
      "phoneE164": "+5511999990000",
      "active": true,
      "sendingEnabled": true,
      "status": "connected",
      "since": "2026-08-13T13:30:00.000Z",
      "sinceUnix": 1786533000,
      "lastCheckedAt": "2026-08-13T13:44:30.000Z",
      "lastCheckedAtUnix": 1786534070
    },
    {
      "id": "num_4712",
      "channel": "telegram",
      "label": "Bot de atendimento",
      "phoneE164": null,
      "active": true,
      "sendingEnabled": false,
      "status": "connected",
      "since": null,
      "sinceUnix": null,
      "lastCheckedAt": null,
      "lastCheckedAtUnix": null
    }
  ]
}

A resposta traz todos os seus números e nunca é truncada em silêncio — não há paginação nesta rota. Se um dia houver, ela entra de forma aditiva, sem mudar o que já existe.

Se você ainda não tem número nenhum, a resposta é 200 com data: [] — nunca 404.

A lista traz também os números desativados, com active: false — é o que permite você ver o que desativou.

Cada item traz ainda sendingEnabled: se aquele número pode enviar agora. É uma pergunta diferente de active e de status, e é a que você precisa antes de enfileirar — veja abaixo.

active não é status

active é a sua decisão sobre o cadastro (ligado ou desligado); status é o fato observado do canal (conectado ou não). Um número ativo pode estar desconectado, e um recém-desativado pode aparecer conectado por alguns segundos até o estado convergir.

Cadastrar e desativar número é feito por você, pelo contrato — veja Provisionamento. Esta rota só lê, e só o que é seu.

Pode enviar? sendingEnabled

active e status não respondem à pergunta que importa na hora de enviar. Um número pode estar ativo, conectado — e mesmo assim não enviar. Quem responde isso é sendingEnabled, presente em GET /v1/numbers e em GET /v1/numbers/{id}/status.

CampoRespondeQuem decide
activeO cadastro está ligado?Você, por DELETE /v1/numbers/{id}.
statusO canal está conectado?O fato observado — o telefone, a rede, o canal.
sendingEnabledEste número pode enviar agora?A operação da plataforma Nuntis. Não há rota para você — nem para ligar, nem para desligar.

O ciclo de vida de um número

cadastrado    active: true   status: disconnected   sendingEnabled: true
pareado       active: true   status: connected      sendingEnabled: true
(desligado)   active: true   status: connected      sendingEnabled: false

O número já nasce podendo enviar — cadastrar e parear não deixam nenhuma liberação pendente. O que falta, entre o cadastro e a primeira mensagem, é o pareamento: sem sessão conectada não há por onde sair.

A terceira linha não é uma etapa do ciclo, e sim um estado que pode aparecer a qualquer momento: a operação do Nuntis desliga o envio de um número para proteger a linha, e há também o desligamento geral do serviço, que zera o envio de todos de uma vez. Não existe ordem garantida aqui — por isso o campo se lê, não se deduz do ponto do fluxo em que você está. Veja Provisionamento para o cadastro.

Enviar com sendingEnabled: false não dá erro

A mensagem é aceita com 202 e fica em queued por tempo indeterminado — sem falha, sem recusa, sem aviso. Quando o envio voltar, tudo o que ficou retido sai de uma vez. Uma mensagem escrita há três dias chegando ao lead como se fosse nova é falha visível do seu produto, não do Nuntis.

Leia o campo antes de enfileirar e bloqueie a ação na sua interface enquanto ele for false.

Leia o campo; não espelhe no seu banco

Guardar uma cópia local (“nasceu ligado, marco a flag aqui”) começa correta e fica errada silenciosamente: desligar e religar o envio acontece do lado do Nuntis, e o seu banco não fica sabendo. Esta rota é a fonte de verdade — consulte-a.

Na dúvida o Nuntis responde false. O campo nunca diz true por omissão: se a informação faltar, a resposta é a conservadora.

Estados de conexão

EstadoSignifica
connectedTransporte ligado e sessão pareada. Pronto para enviar.
disconnectedTransporte desligado.
qr_requiredTransporte ligado, sessão não pareada. Precisa escanear o código.
connectingConexão pedida, ainda sem um estado conclusivo.
unreachableO canal parou de responder. É veredito do Nuntis, não do canal.
bannedO número foi bloqueado pelo canal.
Trate estado desconhecido como disconnected

O conjunto é aditivo: valores novos podem aparecer. Um switch exaustivo que hoje compila vai encontrar um valor que não conhece amanhã, em produção. O default seguro é disconnected — ele leva você a verificar, em vez de assumir que está tudo bem. A mesma regra vale para channel.

Não há ordem entre os estados

Ao contrário do status de mensagem, aqui não existe escada. Um número vai e volta livremente entre conectado, desconectado e pareamento pendente — um telefone sai do ar e volta, e um “estado que só avança” faria o contrato mentir sobre a realidade física.

Consultar o estado de um número

GET /v1/numbers/{id}/status

200 OK
{
  "status": "connected",
  "since": "2026-08-13T13:30:00.000Z",
  "sinceUnix": 1786533000,
  "lastCheckedAt": "2026-08-13T13:44:30.000Z",
  "lastCheckedAtUnix": 1786534070,
  "reason": null,
  "sendingEnabled": true
}

O reason é sempre presente e null na esmagadora maioria das transições — queda de canal, encerramento pedido por você e pareamento normal não têm motivo declarado. Quando ele vem preenchido, é porque o telefone que pareou não bate com o que você cadastrou: os dois motivos e o que fazer.

Leia o lastCheckedAt

A resposta vem do estado que o Nuntis mantém, por verificação periódica — não de uma consulta ao canal feita agora. lastCheckedAt é a idade da informação, e um valor antigo significa que o próprio canal pode estar inalcançável.

disconnected com lastCheckedAt: null quer dizer “nunca verifiquei”, que é diferente de “verifiquei e está fora”.

Você não precisa consultar em laço: mudanças de estado chegam sozinhas pelo evento connection.updated, e o numberId de lá é o mesmo dos eventos de mensagem.

Parear um número

POST /v1/numbers/{id}/pairing

bash
curl -X POST -H "x-api-key: $NUNTIS_API_KEY" \
  https://api.nuntis.com.br/v1/numbers/num_4711/pairing
200 OK
{
  "status": "qr_required",
  "qr": {
    "code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
    "format": "png-data-uri",
    "obtainedAt": "2026-08-13T13:45:12.000Z",
    "obtainedAtUnix": 1786534112
  },
  "hint": { "refreshAfterSeconds": 20 }
}

O qr.code é um data URI completo, pronto para usar direto:

html
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="Código de pareamento">
O código é perecível (~20 s) e a rota é reentrante

Chame de novo para obter um código fresco enquanto o operador não escaneia — use o hint.refreshAfterSeconds como intervalo. Não existe “sessão de pareamento” para você manter do seu lado, e não há nada para encerrar se o operador desistir.

Se o número já estiver conectado, a resposta é 200 com status: "connected" e qr: null. Pedir pareamento do que já está pareado não é erro.

RespostaQuando
404 NOT_FOUNDNúmero inexistente — ou de outro produto.
422 CHANNEL_CAPABILITY_UNSUPPORTEDO canal não usa pareamento por código.
422 NUMBER_DEACTIVATEDO número tem active: false. Reativar não é self-service.
503 CHANNEL_UNREACHABLECanal indisponível. retryable: true.
504 PAIRING_QR_TIMEOUTO canal não produziu um código a tempo. retryable: true.

Fluxo de pareamento

  1. Chame POST /v1/numbers/{id}/pairing.
  2. Se vier status: "connected", acabou.
  3. Exiba o qr.code ao operador.
  4. Repita a chamada a cada ~20 s e substitua a imagem, enquanto ninguém escanear.
  5. Pare quando chegar connection.updated com state: "connected", ou quando a chamada devolver connected.

Encerrar a sessão

DELETE /v1/numbers/{id}/session

bash
curl -X DELETE -H "x-api-key: $NUNTIS_API_KEY" \
  https://api.nuntis.com.br/v1/numbers/num_4711/session

Destrói a sessão pareada: o número volta a exigir pareamento. É idempotente — sessão já encerrada devolve o mesmo resultado.

O número continua cadastrado e ativo: o que morre é a sessão.

Não confunda com DELETE /v1/numbers/{id}

Sem o /session no final, a chamada desativa o cadastro — o número para de parear, de sinalizar presença e de enviar, e reativar não é self-service. Veja Desativar.

Sinalizar presença

POST /v1/numbers/{id}/presence

bash
curl -X POST https://api.nuntis.com.br/v1/numbers/num_4711/presence \
  -H "x-api-key: $NUNTIS_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "conversationId": "conv_991", "state": "typing" }'

Estados aceitos: typing, recording, paused e seen. O último marca como lidas as mensagens recebidas daquela conversa. A resposta é 204, sem corpo.

Presença é efêmera e não é enfileirada

Ao contrário do envio de mensagem, presença vale agora ou não vale. “Digitando…” entregue 40 segundos depois não é atraso, é mentira — o operador já parou de digitar. Por isso não há garantia de entrega, não há registro no histórico da conversa, e a proteção contra abuso é um teto por minuto que devolve 429 RATE_LIMITED.

Capacidade do canal

GET /v1/channels

Consulte uma vez, no boot da sua aplicação, e guarde. A declaração diz o que cada canal sabe fazer — e é a resposta certa para quase toda pergunta que começa com “mas neste canal…”. O id daqui é o mesmo valor que aparece em channel na listagem de números: é por ele que você liga um número à sua capacidade.

A resposta é completa — cada canal traz todos os campos, sem omissão. Abaixo ela está transcrita inteira, como está no ar hoje:

200 OK
{
  "data": [
    {
      "id": "whatsapp-unofficial",
      "texto": true,
      "midia": ["image", "audio", "video", "document"],
      "midiaRecebimento": ["image", "audio", "video", "document", "sticker"],
      "reply": true,
      "botoes": false,
      "audioPtt": true,
      "deliveryReceipts": "read",
      "pairing": "qr",
      "janelaResposta": { "exige": false },
      "template": { "exige": false },
      "presence": true,
      "presenceStates": ["typing", "recording", "paused", "seen"],
      "grupos": false,
      "echoDeSaida": true,
      "idDeMensagemReservavel": true,
      "avatar": true,
      "provisioning": "self-service"
    },
    {
      "id": "telegram",
      "texto": true,
      "midia": ["image", "audio", "video", "document"],
      "midiaRecebimento": ["image", "audio", "video", "document", "sticker"],
      "reply": true,
      "botoes": false,
      "audioPtt": true,
      "deliveryReceipts": "none",
      "pairing": "none",
      "janelaResposta": { "exige": true },
      "template": { "exige": false },
      "presence": true,
      "presenceStates": ["typing", "recording"],
      "grupos": false,
      "echoDeSaida": false,
      "idDeMensagemReservavel": false,
      "avatar": false,
      "provisioning": "managed"
    }
  ]
}

Os valores acima são o retrato de hoje, não uma promessa: eles mudam quando um canal ganha capacidade, e canal novo entra de forma aditiva. Por isso a regra é sempre a mesma — leia o campo, não deduza pelo nome do canal.

O que cada campo decide na sua interface

CampoO que decide na sua interface
idIdentificador do canal. Cruze com channel do número para saber qual declaração vale para ele.
textoSe aceita mensagem de texto. Todo canal aceita — é true sempre.
midiaTipos de mídia aceitos no envio. Tipo fora da lista é 422 CHANNEL_CAPABILITY_UNSUPPORTED. Este campo não descreve o que o canal recebe.
midiaRecebimentoTipos de mídia que o canal sabe receber e entregar a você. É deliberadamente mais largo que midia: sticker chega e não é enviável por rota nenhuma. Use midia para o clipe de anexo e este para o que a sua interface precisa exibir.
replySe aceita responder citando outra mensagem.
botoesSe aceita botões interativos. Hoje false em todos os canais — não construa a tela contando com isso.
audioPttSe aceita áudio como nota de voz (PTT), em vez de arquivo anexado.
deliveryReceipts none · delivered · read — até onde vão as confirmações. Com none, sent é o estado final de sucesso: delivered e read nunca chegam, e um laço esperando por eles não termina.
pairing qr · none — como o número ganha sessão. Com qr, use POST /v1/numbers/{id}/pairing e mostre o código. Com none não há o que parear (a credencial é configurada conosco) e pedir pareamento devolve 422.
janelaResposta Se o canal exige que o contato tenha falado primeiro — e por quanto tempo esse consentimento vale. Veja a ressalva logo abaixo: exige sem horas não é o mesmo que exige com horas.
templateSe o canal exige template pré-aprovado para iniciar conversa.
presenceSe aceita sinalização de presença (“digitando…”).
presenceStates Quais estados de presença este canal aceita. A lista varia por canal — mandar um estado que não está nela é 422. Vazia quando presence é false.
grupos Se o canal entrega conversa de grupo: a mensagem chega, a thread aparece em GET /v1/messages e o recurso leva o bloco group. Não diz nada sobre enviar — para isso veja gruposEnvio.
gruposEnvio Se o canal aceita enviar para grupo. Com true, use a mesma rota POST /v1/messages com o conversationId da conversa de grupo — não há rota nem payload próprios. Com false, essa chamada responde 422 CHANNEL_CAPABILITY_UNSUPPORTED. Consulte por canal: os dois campos são eixos separados e não têm o mesmo valor em todos.
gruposParticipantes Se o canal entrega a lista de participantes de uma conversa de grupo (GET /v1/conversations/{id}/participants). Com false, essa rota responde 422 CHANNEL_CAPABILITY_UNSUPPORTED. É um terceiro eixo, independente de grupos e gruposEnvio.
echoDeSaida Se o canal devolve um eco do que saiu por ele. Com false, o evento message.echo nunca chega naquele canal.
idDeMensagemReservavel Se o canal aceita um id de mensagem escolhido pelo Nuntis antes do envio. Junto com echoDeSaida, é o que decide se um DISPATCH_AMBIGUOUS tem reconciliação — veja a ressalva abaixo.
avatar Se o canal sabe entregar a foto de perfil de um contato. Com false, aquela rota responde 422 — não reserve o espaço da foto na interface. ⚠️ true não promete que a foto existe: promete que o canal sabe responder a pergunta.
provisioning self-service · managed — se você mesmo pode cadastrar um número deste canal. Valor desconhecido: trate como managed.
janelaResposta: com horas e sem horas são regras diferentes

{ "exige": false } — o canal não exige nada. Você inicia conversa com quem quiser.

{ "exige": true, "horas": 24 } — há uma janela que expira: só dá para enviar dentro de N horas depois da última mensagem recebida naquela conversa. Passou, o envio é recusado com 422 OUTSIDE_MESSAGING_WINDOW, e o remédio é template.

{ "exige": true } sem horas — não é janela, é consentimento sem prazo: o canal não deixa iniciar conversa com quem nunca falou com você, e é só isso. Enquanto não houver nenhuma mensagem recebida, o envio é recusado com 422 CONVERSATION_NOT_INITIATED; assim que a pessoa mandar a primeira mensagem, o envio passa a ser aceito — e continua aceito, sem contagem regressiva. Não trate esse caso como “janela de 24h”: você recusaria por conta própria um envio que o canal aceita.

O par que decide se DISPATCH_AMBIGUOUS tem volta

Quando o Nuntis não obtém confirmação do canal, a mensagem pode ter saído. Onde echoDeSaida e idDeMensagemReservavel são true, ela reaparece sozinha na conversa e o estado se resolve.

Onde os dois são false, não há reconciliação possível: retryable: false é a palavra final, e reenviar por conta própria pode duplicar a mensagem no aparelho de quem recebe. Ler esses dois campos é o que separa um retry seguro de uma mensagem duplicada — veja Erros.

Canal novo aparece aqui de forma aditiva. Se você lê a declaração em vez de assumir, o seu código já nasce pronto para ele.