Pareamento de números extras pela API
O pareamento vincula o WhatsApp do número ao Turbo Notify como dispositivo conectado. Depois de cadastrar o número extra, quem tem o celular dele em mãos conclui o pareamento no WhatsApp, com um código de pareamento ou lendo um QR code. Pela API, você escolhe o método em cada pedido. O dashboard oferece os mesmos dois métodos, veja Pareamento pelo dashboard. O número principal é pareado pelo dashboard.
Se você oferece números de WhatsApp aos seus próprios clientes, quem usa o código ou lê o QR code é o seu cliente, na sua tela. Esta página mostra como montar essa tela para que ele consiga concluir sozinho, inclusive quando algo dá errado.
Como funciona
- Você pede o pareamento com
POST /v1/numbers/{ref}/pair, com o métodopairing_code(o padrão) ouqr. O número passa parastatus: "pairing". - Em poucos segundos o código, ou a imagem do QR code, fica disponível no
bloco
pairingdeGET /v1/numbers/{ref}. - No celular do número, a pessoa abre o WhatsApp. Para o código, vai em Dispositivos conectados > Conectar dispositivo > Conectar com número de telefone e digita o código. Para o QR code, vai em Dispositivos conectados > Conectar dispositivo e aponta a câmera para a imagem.
- O número passa para
status: "connected"(ou"failed", com o motivo emreason).
Depois de pareado, o número aparece em Dispositivos conectados no celular com o nome da sua organização, para que a pessoa reconheça a conexão e não a remova por engano. No WhatsApp Business para Android, o aplicativo pede que a pessoa digite um nome para o dispositivo e mostra esse nome no lugar.
Pedir o código ou o QR code
O corpo da requisição é opcional: {"method": "pairing_code"} ou {"method": "qr"}. Sem corpo, o método é pairing_code. Qualquer outro valor responde 422 validation_error.
curl -X POST \ https://api.turbonotify.com/v1/numbers/vendas_sp/pair \ -H "Authorization: Bearer SUA_CHAVE"const response = await fetch('https://api.turbonotify.com/v1/numbers/vendas_sp/pair', { method: 'POST', headers: { 'Authorization': 'Bearer SUA_CHAVE' }});
const result = await response.json();console.log(result);import requests
url = "https://api.turbonotify.com/v1/numbers/vendas_sp/pair"headers = {"Authorization": "Bearer SUA_CHAVE"}
response = requests.post(url, headers=headers)print(response.json())package main
import ( "fmt" "io" "net/http")
func main() { req, _ := http.NewRequest("POST", "https://api.turbonotify.com/v1/numbers/vendas_sp/pair", nil) req.Header.Set("Authorization", "Bearer SUA_CHAVE") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))}<?php$url = 'https://api.turbonotify.com/v1/numbers/vendas_sp/pair';
$options = [ 'http' => [ 'header' => ['Authorization: Bearer SUA_CHAVE'], 'method' => 'POST' ]];
$context = stream_context_create($options);$result = file_get_contents($url, false, $context);var_dump(json_decode($result, true));?>var client = new HttpClient();client.DefaultRequestHeaders.Add("Authorization", "Bearer SUA_CHAVE");
var response = await client.PostAsync( "https://api.turbonotify.com/v1/numbers/vendas_sp/pair", null);Console.WriteLine(await response.Content.ReadAsStringAsync());import java.net.http.*;import java.net.URI;
public class TurboNotify { public static void main(String[] args) throws Exception { var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder() .uri(URI.create("https://api.turbonotify.com/v1/numbers/vendas_sp/pair")) .header("Authorization", "Bearer SUA_CHAVE") .POST(HttpRequest.BodyPublishers.noBody()) .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); }}O exemplo acima pede o código (o padrão). Para pedir o QR code, envie method: "qr":
curl -X POST \ https://api.turbonotify.com/v1/numbers/vendas_sp/pair \ -H "Authorization: Bearer SUA_CHAVE" \ -H "Content-Type: application/json" \ -d '{"method": "qr"}'const response = await fetch('https://api.turbonotify.com/v1/numbers/vendas_sp/pair', { method: 'POST', headers: { 'Authorization': 'Bearer SUA_CHAVE', 'Content-Type': 'application/json' }, body: JSON.stringify({ method: 'qr' })});
const result = await response.json();console.log(result);import requests
url = "https://api.turbonotify.com/v1/numbers/vendas_sp/pair"headers = {"Authorization": "Bearer SUA_CHAVE"}
response = requests.post(url, headers=headers, json={"method": "qr"})print(response.json())Escopo obrigatório: numbers:write
{
"alias": "vendas_sp",
"status": "pairing"
} A resposta confirma o pedido. O código ou o QR code é gerado pelo WhatsApp e chega de forma assíncrona.
Mostrar o código
Consulte GET /v1/numbers/{ref} enquanto status for "pairing". Quando
o código estiver pronto, o bloco pairing vem preenchido. Leia sempre
pairing.method antes de mostrar pairing.data:
{
"id": "num_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"contact_id": null,
"name": null,
"type": "extra",
"alias": "vendas_sp",
"number": "5511988887777",
"status": "pairing",
"at": "2026-09-28T14:00:00Z",
"description": null,
"scheduled_deletion_at": null,
"reason": null,
"pairing": {
"method": "pairing_code",
"data": "Z5X7-LTYL",
"expires_at": "2026-09-28T14:02:40Z"
}
} pairing.methodé"pairing_code".pairing.dataé o código, com 8 caracteres, exibido com hífen. Mostre como texto.pairing.expires_até o fim da tentativa inteira: use para a contagem regressiva.- Enquanto o código ainda está sendo gerado,
pairingvemnull.
Quando a contagem chegar a zero sem uso, ofereça um botão para pedir um novo
código ou QR code. Se ninguém concluir, o número passa a failed com
reason: "pairing_timeout" logo em seguida. A consulta exige o escopo
numbers:read.
Se pairing.method for "qr", veja Mostrar o QR code.
Mostrar o QR code
Com method: "qr", o bloco pairing traz a imagem do QR code:
{
"id": "num_0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"contact_id": null,
"name": null,
"type": "extra",
"alias": "vendas_sp",
"number": "5511988887777",
"status": "pairing",
"at": "2026-09-28T14:00:00Z",
"description": null,
"scheduled_deletion_at": null,
"reason": null,
"pairing": {
"method": "qr",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"expires_at": "2026-09-28T14:02:40Z"
}
} pairing.methodé"qr".pairing.dataé uma imagem PNG em formato data URI (data:image/png;base64,...). Use comosrcde um<img>, nunca como texto.pairing.expires_até o fim da tentativa inteira, como no código: use para a contagem regressiva.- O WhatsApp renova o QR code sozinho enquanto a tentativa dura, e o Turbo
Notify mantém
pairing.dataatualizado. ConsulteGET /v1/numbers/{ref}de novo e troque a imagem a cada consulta. Não guarde a primeira. - Enquanto o QR code ainda está sendo gerado,
pairingvemnull.
<img id="qr" alt="QR code para vincular o WhatsApp" /><script> // resultado: o retorno de consultar(alias), a cada 2 segundos if (resultado.tela === 'qr') document.getElementById('qr').src = resultado.imagem;</script>Código digitado errado
Se o código for digitado errado no celular, o WhatsApp invalida esse código na hora: nem o código correto é aceito depois disso. O WhatsApp não avisa esse erro a ninguém, então a sua tela precisa oferecer um novo código a qualquer momento.
Para gerar um novo código, chame POST /v1/numbers/{ref}/pair de novo
enquanto o número estiver com status: "pairing". O código anterior é
descartado, o bloco pairing volta a vir null e, em seguida, traz o novo
código. Mostre sempre o código mais recente. O mesmo vale quando o código
expira.
Substituir a tentativa em andamento
Chamar POST /v1/numbers/{ref}/pair enquanto o número está com
status: "pairing" substitui a tentativa em andamento: sai um código ou um
QR code novo, e você pode trocar o método (de código para QR code, ou o
contrário). Vale também para uma tentativa iniciada no dashboard: o painel do
número passa a acompanhar a tentativa nova. O código ou o QR code anterior
deixa de funcionar.
O WhatsApp do número cadastrado
O pareamento precisa ser concluído no WhatsApp do número cadastrado. Se
outro WhatsApp usar o código ou ler o QR code, o pareamento é recusado, o
dispositivo é desconectado desse outro WhatsApp e o número fica com
status: "failed" e reason: "paired_phone_mismatch". Para tentar de novo,
peça um novo código ou QR code e conclua o pareamento no celular do número
cadastrado.
Resultado do pareamento
| Situação | Pelo status | Pelo webhook |
|---|---|---|
| Deu certo | status: "connected" | number.connected |
| Falhou | status: "failed" e o motivo em reason | number.connect.failed, com o mesmo reason |
O código e o QR code só são entregues pela consulta de status: por serem uma credencial de curta duração, não são enviados em webhooks. Mostre-os apenas à pessoa que está pareando o número.
Motivos de falha
reason | O que dizer ao seu cliente |
|---|---|
pairing_timeout | O código ou o QR code foi gerado, mas não foi usado a tempo. Peça um novo código ou QR code. |
paired_phone_mismatch | O código ou o QR code foi usado em outro WhatsApp. Use o celular do número cadastrado. |
phone_already_bound | Este número já está em uso na conta. |
main_number_not_connected | O número principal da conta precisa estar conectado antes. Reconecte o número principal e tente de novo. |
code_generation_failed | Não foi possível obter o código ou o QR code do WhatsApp agora. Tente de novo em instantes. |
connection_lost | O número perdeu a conexão com o WhatsApp. Pareie de novo. |
pairing_failed | Não foi possível concluir o pareamento. Tente de novo. |
Novos motivos podem ser adicionados. Trate um valor que você não conhece como
pairing_failed.
Experiência de pareamento recomendada
Uma tela de pareamento que resolve sozinha os casos comuns:
- Deixe a pessoa escolher o método e peça o pareamento com
POST /v1/numbers/{ref}/pair: QR code quando o cliente vê a tela em um computador e aponta o celular para ela, código quando ele abre a tela no próprio celular e digita o código no WhatsApp. - Consulte o status a cada 2 segundos enquanto
statusfor"pairing". Compairingpreenchido, confirapairing.method. Se forpairing_code, mostrepairing.datacomo texto. Se forqr, mostrepairing.datacomo imagem (<img src>) e troque a imagem a cada consulta, porque o QR code se renova. Nos dois casos, mostre a contagem regressiva atépairing.expires_at. Compairingnull, mostre “gerando”. - Deixe “Gerar novo código” ou “Gerar novo QR code” sempre visível, não
só quando a tentativa expira: é a saída para o código digitado errado. O
botão chama
POST /v1/numbers/{ref}/pairde novo, e pode trocar o método. - Encerre pelo resultado:
inactivemostra “finalizando” por alguns instantes;connectedmostra sucesso;failedmostra a mensagem doreasone oferece tentar de novo. - Respeite o limite de tentativas: em um
429, mostre quanto falta a partir do cabeçalhoRetry-Afterem vez de repetir a chamada.
const API = 'https://api.turbonotify.com/v1/numbers';const headers = { 'Authorization': 'Bearer SUA_CHAVE', 'Content-Type': 'application/json',};
// Chamado ao abrir a tela e pelo botão "Gerar novo código" ou "Gerar novo QR code".// metodo: 'pairing_code' ou 'qr'.async function pedirPareamento(alias, metodo) { const res = await fetch(`${API}/${alias}/pair`, { method: 'POST', headers, body: JSON.stringify({ method: metodo }), }); if (res.status === 429) { return { aguardarSegundos: Number(res.headers.get('Retry-After')) }; } return { aguardarSegundos: 0 };}
// Chamado a cada 2 segundos enquanto a tela estiver aberta.async function consultar(alias) { const res = await fetch(`${API}/${alias}`, { headers }); const numero = await res.json(); if (numero.status === 'connected') return { tela: 'sucesso' }; if (numero.status === 'failed') return { tela: 'falha', motivo: numero.reason }; if (numero.status === 'inactive') return { tela: 'finalizando' }; if (numero.pairing) { const { method, data, expires_at } = numero.pairing; // Na tela 'qr', troque o src do <img> a cada consulta: o QR code se renova. if (method === 'qr') return { tela: 'qr', imagem: data, expiraEm: expires_at }; return { tela: 'codigo', codigo: data, expiraEm: expires_at }; } return { tela: 'gerando' };}import requests
API = "https://api.turbonotify.com/v1/numbers"HEADERS = {"Authorization": "Bearer SUA_CHAVE"}
def pedir_pareamento(alias: str, metodo: str) -> int: """Chamado ao abrir a tela e pelo botão "Gerar novo código" ou "Gerar novo QR code".
metodo: "pairing_code" ou "qr". Retorna quantos segundos esperar antes de permitir outro pedido. """ res = requests.post(f"{API}/{alias}/pair", headers=HEADERS, json={"method": metodo}) if res.status_code == 429: return int(res.headers["Retry-After"]) return 0
def consultar(alias: str) -> dict: """Chamado a cada 2 segundos enquanto a tela estiver aberta.""" numero = requests.get(f"{API}/{alias}", headers=HEADERS).json() if numero["status"] == "connected": return {"tela": "sucesso"} if numero["status"] == "failed": return {"tela": "falha", "motivo": numero["reason"]} if numero["status"] == "inactive": return {"tela": "finalizando"} if numero["pairing"]: pairing = numero["pairing"] # Na tela "qr", troque o src do <img> a cada consulta: o QR code se renova. if pairing["method"] == "qr": return {"tela": "qr", "imagem": pairing["data"], "expira_em": pairing["expires_at"]} return {"tela": "codigo", "codigo": pairing["data"], "expira_em": pairing["expires_at"]} return {"tela": "gerando"}Limite de tentativas
Cada POST /v1/numbers/{ref}/pair pede ao WhatsApp um código novo, que gera uma
notificação no celular do número, ou um QR code novo. Pedidos seguidos demais fazem o próprio
WhatsApp bloquear novos códigos para aquele número por um tempo. Para
proteger o número, o pareamento aceita até 10 pedidos por minuto por
número e 30 por minuto na conta. Acima disso a resposta é
429 pairing_rate_limited, com o cabeçalho Retry-After em segundos:
- Retry-After: 42
{
"error": "pairing_rate_limited"
} Cancelar o pareamento
Enquanto o número estiver com status: "pairing", você pode cancelar a
solicitação em andamento e voltar o número para "pending":
curl -X POST \ https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing \ -H "Authorization: Bearer SUA_CHAVE"const response = await fetch('https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing', { method: 'POST', headers: { 'Authorization': 'Bearer SUA_CHAVE' }});
const result = await response.json();console.log(result);import requests
url = "https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing"headers = {"Authorization": "Bearer SUA_CHAVE"}
response = requests.post(url, headers=headers)result = response.json()print(result)package main
import ( "fmt" "io" "net/http")
func main() { req, _ := http.NewRequest("POST", "https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing", nil) req.Header.Set("Authorization", "Bearer SUA_CHAVE") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))}<?php$url = 'https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing';
$options = [ 'http' => [ 'header' => ['Authorization: Bearer SUA_CHAVE'], 'method' => 'POST' ]];
$context = stream_context_create($options);$result = file_get_contents($url, false, $context);$response = json_decode($result, true);var_dump($response);?>var client = new HttpClient();client.DefaultRequestHeaders.Add("Authorization", "Bearer SUA_CHAVE");
var response = await client.PostAsync( "https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing", null);var result = await response.Content.ReadAsStringAsync();Console.WriteLine(result);import java.net.http.*;import java.net.URI;
public class TurboNotify { public static void main(String[] args) throws Exception { var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder() .uri(URI.create("https://api.turbonotify.com/v1/numbers/vendas_sp/cancel-pairing")) .header("Authorization", "Bearer SUA_CHAVE") .POST(HttpRequest.BodyPublishers.noBody()) .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); }}{
"alias": "vendas_sp",
"status": "pending"
} O cancelamento vale só enquanto o número está com status: "pairing". Para
parear depois, peça um novo código ou QR code.
Erros possíveis
Pedir o código ou o QR code (POST /v1/numbers/{ref}/pair)
| Status | Código de erro | Descrição |
|---|---|---|
402 | subscription_inactive | A assinatura da organização não está ativa (resposta inclui details.status). |
402 | feature_not_available | O plano atual não inclui números extras. |
403 | main_number_pair_not_allowed | O número principal é pareado pelo dashboard. |
404 | number_not_found | O alias ou id informado não existe na conta. |
409 | already_paired_or_connected | O número já está pareado ou conectado. |
422 | validation_error | method não é pairing_code nem qr (ou o corpo traz outro campo). |
429 | pairing_rate_limited | Limite de tentativas atingido: aguarde Retry-After segundos. |
Cancelar (POST /v1/numbers/{ref}/cancel-pairing)
| Status | Código de erro | Descrição |
|---|---|---|
402 | feature_not_available | O plano atual não inclui números extras. |
404 | number_not_found | O alias ou id informado não existe na conta. |
409 | not_pairing | O número não está com status: "pairing". |
As duas rotas também retornam 401 not_authenticated (chave inválida ou
ausente) e 403 insufficient_scope (falta o escopo numbers:write).