Linhas telefônicas no seu app pela API
Ofereça linhas telefônicas — números fixos brasileiros para ativar contas do WhatsApp Business — sem tirar o seu cliente do seu produto. A sua página mostra os números, o botão de compra e o código de ativação; o seu backend fala com/v1/phone-lines usando a sua chave de tenant.
Aqui não há página hospedada nem iframe: tudo passa pelo seu backend, porque o código de ativação é uma credencial e a chave ps_ nunca pode chegar ao browser.
Quer um ponto de partida pronto pra rodar? Baixe o demo (licença MIT) — backend Node + TypeScript (Express) que guarda a chave e frontend React + Vite ligados exatamente a este fluxo: phone-lines-demo.zip. Descompacte, ponha a sua chave
ps_ em PILOT_TENANT_KEY (e, para o webhook, uma URL pública em PUBLIC_BASE_URL), npm run dev (ou docker compose up) — frontend em :5175, backend em :8789.Esta página é o passo a passo: siga os cinco passos na ordem e você tem uma integração funcionando. A referência — todos os campos, estados e códigos de erro — está em API de Linhas Telefônicas. Esta página linka pra lá em vez de repetir, então você sempre lê uma única cópia.
Uma credencial só
Você precisa de exatamente uma coisa: uma chave de APIps_ com escopo de tenant. Pegue em /profile, aba API.
A linha pertence ao workspace, não a um número de WhatsApp, então uma chave com escopo de número recebe 403 NUMBER_SCOPE_NOT_ALLOWED em todo endpoint /v1/phone-lines. Você não precisa de nenhum número de WhatsApp conectado para comprar uma linha.
A chave ps_ fica no seu servidor — o browser nunca chama a API da Pilot Status, ele só chama o seu backend.
Arquitetura
/api/... acima são as do demo — os nomes são seus; o que importa é que cada uma é uma chamada do seu servidor para /v1/phone-lines.
Os cinco passos
1
Confira se o workspace pode comprar
Alugar uma linha exige um titular da conta verificado (CNPJ ou CPF, o documento e a confirmação de contato), feito uma única vez, antes da primeira compra. Antes de mostrar o botão de compra, leia o estado:Decida pelo
cURL
canPurchase: ele é true para APPROVED e IN_REVIEW — em análise manual você já pode comprar. Com false, a compra responde 403 VERIFICATION_REQUIRED (ou VERIFICATION_REJECTED, quando foi recusada), então mostre ao seu cliente o caminho em vez do botão:- No painel — Linhas (
/linhas) → Comprar linhas, que conduz a verificação. É o que o demo faz: ele só lê o estado e aponta para o painel. - Por código — se você quer conduzir a verificação dentro do seu produto, ela também existe na API: envie os dados e o documento, confirme os códigos de contato e leia a decisão em Verificação do titular.
phone_line.verification_updated (passo 5).2
Busque números por DDD
cURL
display ao seu cliente e guarde o number — é exatamente o valor que a compra recebe. price e currency são quanto uma linha custa ao seu workspace por mês — com o seu preço de linha próprio, se houver —, para você mostrar o preço antes da compra. Um ddd ausente ou malformado responde 400 INVALID_AREA_CODE, sem chamar a operadora.- Busque uma vez e reutilize. Toda busca chega à nossa operadora e consome uma cota compartilhada por toda a plataforma; quando ela se esgota, a resposta é 503
SUPPLIER_UNAVAILABLE. Não consulte este endpoint periodicamente. - Buscar não é reservar. Um número listado pode ser comprado por outra pessoa antes da sua compra; ele volta como
NUMBER_UNAVAILABLEno resultado da compra.
3
Compre com Idempotency-Key
POST /v1/phone-lines compra de 1 a 10 números e movimenta dinheiro, por isso exige o header Idempotency-Key. Gere a chave no seu backend, uma por compra, e guarde-a junto do pedido antes de enviar — é ela que você reenvia se a resposta não chegar.cURL
Resposta (200)
200 quando foi processada — um 200 não significa que você comprou alguma coisa: leia results[i].ok de cada número e guarde o lineId dos que deram certo. A tabela completa dos code por número está em Comprar linhas.Pagamento. Cada número custa o preço mensal cheio, descontado primeiro da carteira pré-paga e, no que faltar, do cartão salvo. Não há link de checkout nesta chamada: sem créditos e sem cartão, o item falha com PAYMENT_FAILED e nada é cobrado. Antes de comprar, recarregue a carteira — pelo painel, ou pela API com Pix, página de cartão hospedada ou um formulário de cartão na sua própria página — ou salve um cartão. Veja Carteira e cartão pela API. Preço e ciclo de cobrança: Preço e pagamento.O padrão de retentativa:- Em timeout,
5xxou erro de rede, reenvie o mesmo corpo com a mesma chave. Um número que a primeira tentativa já tratou volta comoREQUEST_ALREADY_PROCESSEDe não é cobrado de novo; os que ela não alcançou são processados agora. - Reconcilie com
GET /v1/phone-lines, comparando pornumber.REQUEST_ALREADY_PROCESSEDnão diz se aquele número foi comprado ou falhou — a lista diz: um número comprado está nela. - Refaça as falhas com uma chave nova. Depois de um
PAYMENT_FAILEDouSUPPLIER_UNAVAILABLE, corrija a causa e envie esses números com uma chave nova — com a mesma, eles voltam comoREQUEST_ALREADY_PROCESSEDdurante uma hora.
4
Obtenha o código de ativação do WhatsApp
O WhatsApp verifica um número fixo ligando para ele e falando um código de 6 dígitos. A ordem importa:A janela é de 5 minutos (
- Abra o pedido primeiro.
POST /v1/phone-lines/{id}/activations(sem corpo), com olineIdda compra. Uma ligação que chegar antes de o pedido existir não é capturada. - Só então peça ao WhatsApp para ligar. No WhatsApp Business, escolha receber o código por ligação (Me ligue; “Call me” no app em inglês). A linha é fixa e não recebe SMS — esperar por um SMS só consome a janela.
cURL
Resposta (201)
pollDeadline). Mostre ao seu cliente um contador e a instrução “toque em Me ligue agora”.Consulte o pedido a cada poucos segundos com GET /v1/phone-lines/activations/{activationId} — o status só muda depois da nossa próxima verificação da linha, feita a cada 15 segundos. Continue enquanto status for WAITING, ou CAPTURED com failureReason: null: CAPTURED é também o estado intermediário enquanto o código é lido, o que pode levar alguns minutos. Pare num estado definitivo e confira o code — TRANSCRIBED não garante um código. Todos os estados: Status.A gravação passa pelo seu backend. Quando hasAudio é true, o seu cliente pode ouvir a ligação — útil quando o código não pôde ser lido. GET /v1/phone-lines/activations/{activationId}/audio exige a sua chave, então o seu backend busca o arquivo e o repassa ao browser (no demo, GET /api/activations/:id/audio). Repasse também o Cache-Control: private, no-store: a gravação fala o código. Veja Baixar a gravação.Cancelar a espera. Só existe um pedido aguardando por linha. Se o seu cliente quiser recomeçar antes de a janela fechar, chame POST /v1/phone-lines/{id}/activations/{activationId}/cancel e abra um novo pedido na hora.Referência completa: Códigos de ativação.
5
Cadastre o webhook das linhas pela API
Para o seu sistema saber na hora que um código chegou, que uma renovação falhou ou que uma linha foi suspensa, cadastre um webhook das linhas. É um cadastro separado dos webhooks de número: o Neste cadastro,
POST /v1/webhooks dos números não recebe eventos phone_line.*. O cadastro das linhas é POST /v1/phone-lines/webhooks:cURL
Resposta (201)
"*" significa todos os eventos de linha. A URL precisa ser https e pública: localhost, um IP privado ou um nome que resolva para um deles é recusado com 400 WEBHOOK_URL_REFUSED — em desenvolvimento, use um túnel com URL pública em PUBLIC_BASE_URL.Verifique a assinatura e deduplique. Toda entrega traz x-pilot-status-signature: o HMAC-SHA256 do corpo bruto, em hexadecimal, com o segredo inteiro (incluindo plwh_) como chave. Calcule sobre os bytes exatamente como chegaram, antes do parse do JSON, e compare em tempo constante:Backend — receptor do webhook (Express)
- Deduplique pelo
id. É o mesmo em todas as retentativas da mesma entrega (e vem também no headerIdempotency-Key). A entrega é pelo menos uma vez, a ordem não é garantida e a assinatura não tem timestamp. - Responda
2xxem até 10 segundos. Qualquer outra coisa conta como falha; são 6 tentativas em cerca de 15 minutos, e um redirecionamento3xxnão é seguido. - Os eventos são notificações, não a fonte da verdade. Um
phone_line.code_receivedevita a consulta do passo 4, mas, se o seu endpoint ficou fora do ar por mais tempo que as retentativas, leia o estado atual comGET /v1/phone-linesouGET /v1/phone-lines/activations/{activationId}.
active: false, excluir e ler o log de entregas também são pela API — PATCH e DELETE /v1/phone-lines/webhooks/{id} e GET …/{id}/deliveries. Todos os eventos, payloads e endpoints: Webhooks das linhas.Backend de exemplo
O passo que mais exige cuidado é a compra: é o único que movimenta dinheiro. O seu backend gera e guarda aIdempotency-Key antes de enviar, repete com a mesma chave quando a resposta não chega, e reconcilia pela lista de linhas.
Backend — Node.js / Express
PAYMENT_FAILED ou SUPPLIER_UNAVAILABLE, o seu cliente corrige a causa e faz uma nova compra — um purchaseId novo, portanto uma chave nova.
Cancelar uma linha
DELETE /v1/phone-lines/{id} responde com a linha como ela fica depois, e o resultado depende do status:
cURL
Mostre ao seu cliente qual dos dois casos aconteceu: pelo
status e pelo cancelAtPeriodEnd da resposta, não pelo 200.
Outros caminhos
A API é o caminho para quem quer as linhas dentro do próprio produto. Conforme o caso, há alternativas:- Pelo painel. Para comprar e ativar poucas linhas sem escrever código — e para verificar o titular —, siga Linhas telefônicas. Ficam só no painel: dar nome a uma linha, desfazer um cancelamento agendado e pagar linhas em atraso na hora.
- Playground. Teste cada endpoint com a sua chave antes de escrever o backend: Playground → Linhas telefônicas. A compra e o cancelamento de lá são reais.
- Construtores de IA. Se você usa Lovable, Replit, Bolt, v0 ou Cursor, há prompts prontos que já geram este mesmo fluxo em Linhas para builders de IA.
- Agentes de IA (MCP). O servidor MCP tem as ferramentas da verificação do titular (
phone_lines_verification_*) e da gravação (phone_lines_activation_audio), com as mesmas regras de credencial com escopo de tenant.
Próximos passos
- Referência completa: API de Linhas Telefônicas — Visão geral
- Verificar o titular por código: Verificação do titular
- Compra e idempotência em detalhe: Comprar linhas
- Estados do pedido de código: Códigos de ativação
- Eventos, payloads e endpoints do webhook: Webhooks das linhas
- Preço e renovação: Preço e cobrança das linhas
- Escopos de chave e cabeçalhos: Autenticação da API
- Prompts para construtores de IA: Linhas para builders de IA