Pular para o conteúdo principal

Phones (canais)

Phones representam cada conexão WhatsApp ou Instagram da sua account. São o pré-requisito para enviar/receber qualquer mensagem.

Base path: /v1/phones

Objeto Phone

{
"id": "5f7b2e1c-3d4a-4e5f-9a8b-1c2d3e4f5a6b",
"accountId": "a1b2c3d4-...",
"type": "BAILEYS",
"label": "Atendimento Vendas",
"phoneNumber": "5511999998888",
"status": "CONNECTED",
"webhookUrl": "https://api.minhaempresa.com/wpp-events",
"webhookEvents": ["message:received", "message:status"],
"createdAt": "2026-05-21T14:00:00.000Z",
"updatedAt": "2026-05-21T14:02:30.000Z"
}

Campos comuns

CampoTipoDescrição
idUUIDIdentificador local (use em todos os endpoints)
typeenumBAILEYS, WABA, INSTAGRAM (imutável)
labelstringNome legível (1-120 chars)
phoneNumberstring | nullE.164 sem +, populado após conexão
statusenumPENDING, PENDING_QR, PENDING_WEBHOOK, CONNECTING, CONNECTED, DISCONNECTED, TOKEN_EXPIRED, FAILED
webhookUrlstring | nullOnde o gateway entrega eventos
webhookEventsstring[]Lista de eventos subscritos (vazio = todos)
disconnectedAtdatetime | nullQuando saiu de CONNECTED

Campos exclusivos WABA

wabaPhoneNumberId, wabaBusinessAccountId, wabaThroughputLevel, wabaQualityRating, wabaWebhookConfigured, wabaCoexistence, wabaContactsSyncAt, wabaHistorySyncAt. Tokens (wabaAccessTokenEnc) são internos — nunca retornados.

Campos exclusivos Instagram

igUserId, igUsername, igAccountType, igTokenExpiresAt, igWebhookConfigured.


Endpoints

POST /v1/phones

Cria um novo phone. Para BAILEYS, inicia geração do QR. Para WABA, o Worker valida as credenciais e hidrata a metadata em seguida, de forma assíncrona: o phone nasce PENDING_WEBHOOK, e token recusado pela Meta o deixa em TOKEN_EXPIRED. Para INSTAGRAM, use o fluxo OAuth dedicado.

Headers

HeaderValorObrigatório
X-API-Keyak_live_...sim
Content-Typeapplication/jsonsim

Body

CampoTipoObrigatórioDescrição
typeenumsimBAILEYS ou WABA (Instagram usa endpoint próprio)
labelstringsim1-120 chars
webhook.urlURLnãoEndpoint HTTPS pra eventos
webhook.secretstringnãoSecret HMAC (≥16 chars; obrigatório se url presente)
webhook.eventsstring[]nãoLista de tipos (vazio = todos)
credentials.phoneNumberIdstringsó WABAPhone Number ID do Meta
credentials.businessAccountIdstringsó WABAWABA ID
credentials.accessTokenstringsó WABASystem User token longo
credentials.verifyTokenstringnãoToken de verificação webhook

Exemplo Baileys:

curl -X POST https://wpp.ogmma.com.br/v1/phones \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "BAILEYS",
"label": "Atendimento Vendas",
"webhook": {
"url": "https://api.acme.com/wpp",
"secret": "supersecret-pelomenos-16chars",
"events": ["message:received", "channel:connected"]
}
}'

Exemplo WABA manual (credenciais via Meta Business Manager):

curl -X POST https://wpp.ogmma.com.br/v1/phones \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"type": "WABA",
"label": "Comercial Oficial",
"credentials": {
"phoneNumberId": "123456789012345",
"businessAccountId": "987654321098765",
"accessToken": "EAAxxx...",
"verifyToken": "meu-verify-token"
},
"webhook": { "url": "https://api.acme.com/wpp", "secret": "supersecret-pelomenos-16chars" }
}'

WABA por credenciais é o caminho legado. Conexão nova deve usar o Embedded Signup (POST /v1/phones/waba/embedded-signup): este endpoint grava as credenciais sem ler a Meta e sem aplicar a política de coexistência — o canal nasce marcado como dedicado. Por isso, quando o type é WABA, a resposta traz Deprecation: true e Link: </v1/phones/waba/embedded-signup>; rel="successor-version". Criar BAILEYS por esta rota é o caminho normal e não leva esses headers.

Response 201

{
"id": "5f7b2e1c-...",
"type": "BAILEYS",
"status": "PENDING_QR",
"label": "Atendimento Vendas",
"webhookUrl": "https://api.acme.com/wpp",
"createdAt": "2026-05-21T14:00:00.000Z"
}

Errors

HTTPCodeQuando
400VALIDATION_ERRORCampo inválido
400WABA_CREDENTIALS_REQUIREDtype=WABA sem credentials
401AUTH_MISSINGSem X-API-Key
402ACCOUNT_SUSPENDEDAccount em inadimplência

GET /v1/phones

Lista todos os phones da account, ordenados por createdAt desc.

curl https://wpp.ogmma.com.br/v1/phones \
-H "X-API-Key: ak_live_..."

Response 200

[
{ "id": "...", "type": "BAILEYS", "status": "CONNECTED", ... },
{ "id": "...", "type": "WABA", "status": "CONNECTED", ... }
]

GET /v1/phones/:id

Detalhe de um phone específico.

curl https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-... \
-H "X-API-Key: ak_live_..."

Errors

HTTPCodeQuando
404PHONE_NOT_FOUNDID inexistente ou pertence a outra account

DELETE /v1/phones/:id

Remove um phone. Worker é notificado para encerrar a sessão (Baileys envia logout). Operação destrutiva e irreversível — auth state e mídia local são apagados; mensagens enviadas (messages_sent) ficam para auditoria.

curl -X DELETE https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-... \
-H "X-API-Key: ak_live_..."

Query

ParamTipoDefaultDescrição
deregisterbooleanfalseTira o número da Cloud API na Meta. Irreversível — voltar exige novo /register com PIN. Ignorado em número coexistente (a Meta proíbe)

Response 204 (sem body) — nada ficou pendente do lado da Meta.

Response 200 — sobrou uma ação que só o dono pode fazer:

{
"deleted": true,
"pending": {
"action": "DISCONNECT_IN_WHATSAPP_BUSINESS_APP",
"reason": "Número em coexistência: a Meta não permite desregistrar pela API. ..."
}
}

O phone é apagado nos dois casos. O que muda é o que acontece na Meta:

TipoO que o gateway fazSobra algo?
Baileyslogout da sessãonão
WABA dedicadounsubscribe da WABA (só se era o último número dela) + deregister se ?deregister=truenão, quando deregister=true
WABA coexistenteunsubscribe da WABA (só se era o último número dela)sim — soltar o companion da Cloud API é ação do dono, no celular: Ajustes › Conta › Plataforma Empresarial › Desconectar conta

A Meta proíbe o deregister em número que está ao mesmo tempo na Cloud API e no WhatsApp Business app. Não é limitação do gateway.

unsubscribe é da WABA inteira, não do número (DELETE /{wabaId}/subscribed_apps). Uma WABA hospeda vários números; por isso o gateway só cancela a inscrição quando o phone apagado era o último daquela WABA. Enquanto sobrar outro, a inscrição fica de pé e os demais números continuam recebendo webhook.

deregister é opt-in. Apagar um canal para recriá-lo (trocar de account, refazer webhook, corrigir label) é caso comum, e um deregister silencioso obrigaria a um novo /register com o PIN de 2FA. Passe ?deregister=true quando a intenção for mesmo soltar o número da Cloud API.


PATCH /v1/phones/:id

Atualiza apenas a configuração de webhook do phone. Não permite trocar type ou label.

O bloco webhook é tudo-ou-nada. Campo omitido dentro dele é apagado, não preservado — mandar {webhook:{secret}} zera url e events. E url sem secret é recusado com 400: o dispatcher exige os dois para entregar, então esse estado produzia canal que parece configurado e não entrega nada. Mande sempre o bloco completo.

Body

CampoTipoDescrição
webhook.urlURLOmitido remove o webhook. null é recusado com 400 — para remover, omita
webhook.secretstring≥16 chars
webhook.eventsstring[]Lista de tipos
sharedWebhookIdUUID | nullAponta para um SharedWebhook (futura feature multi-phone). Omitido ou null desvincula
curl -X PATCH https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-... \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "webhook": { "url": "https://api.acme.com/wpp-v2", "secret": "novachavedepelomenos16chars" } }'

GET /v1/phones/:id/qrcode

Retorna o QR code Baileys atual (cacheado em Redis com TTL curto após o Worker gerar). Faça polling a cada 1-2s; quando o usuário escanear, o status vira CONNECTED e este endpoint passa a retornar 409.

Response 200

{
"qr": "2@LkPq...,QR data raw...",
"dataUrl": "data:image/png;base64,iVBORw0KGgo..."
}

dataUrl é PNG base64 pronto para <img src="...">.

Errors

HTTPCodeQuando
400QR_NOT_APPLICABLEPhone não é BAILEYS
404PHONE_NOT_FOUND
409ALREADY_CONNECTEDPhone já conectou
425QR_NOT_READYQR ainda não emitido pelo Worker — tente em 1-2s
425PAIRING_IN_PROGRESSPhone em CONNECTING: o socket está subindo com credencial — pareamento recém-aceito ou canal pareado reconectando. Continue o polling: depois de um pareamento, vira CONNECTED em segundos; numa reconexão com a rede instável, pode levar minutos. Se passar de ~1 minuto, veja a nota abaixo

O QR rota Baileys naturalmente refresha a cada ~20s. O endpoint devolve o último QR em cache — mas nunca um ref já consumido: assim que o celular aceita o pareamento, o QR é invalidado e o endpoint passa a responder 425 até a sessão abrir.

Isso importa para a sua tela: entre o "aceitei no celular" e o CONNECTED existem alguns segundos. Exibir um QR nessa janela faz o usuário escanear um código morto, e o WhatsApp responde "verifique sua conexão e tente novamente" com o aparelho já vinculado. Trate PAIRING_IN_PROGRESS como "aguarde" — não como "escaneie de novo".

A exceção é quando ele não passa. CONNECTING por mais de um minuto tem duas causas que a API não distingue: credencial obsoleta — o WhatsApp recusa o login e, sem pedido de registro, nenhum QR será emitido; a saída é POST /:id/logout — ou um canal saudável reconectando com a rede instável, em que o Worker recua até 5 minutos entre tentativas. Por isso o logout é decisão do usuário, com confirmação: ofereça-o depois de ~1 minuto, nunca o dispare sozinho. Ele desvincula o aparelho, e num canal saudável obriga a escanear de novo.


POST /v1/phones/:id/logout

Reinicia o pareamento: descarta a credencial do lado do gateway e, para Baileys, abre uma sessão nova em seguida — mesmo id, mesmo webhook. Responde 202 { status: 'logging_out' }; depois é só fazer polling em GET /:id/qrcode até o QR novo sair.

Use quando o canal está preso: credencial obsoleta que não se apagou sozinha. O wipe automático só acontece no 401 (loggedOut); em 403, 411, 440 e no loop de 503 a identidade sobrevive, e com ela presente o Baileys manda nó de LOGIN em vez de registro — nenhum QR é emitido e GET /qrcode responde 425 indefinidamente. Sem esta rota a única saída era DELETE /v1/phones/:id, que troca o id e obriga a recriar o canal.

Não é preciso chamar POST /:id/reconnect depois — o próprio logout reabre. E o reset é exclusivo: um reconnect (ou reconnect automático) que chegue durante ele se junta ao reset em vez de subir a sessão com a credencial velha, e um reconnect agendado antes dele é descartado.

No webhook, o reset aparece como channel:disconnected com reason: "PAIRING_RESET", seguido de channel:qrcode quando a sessão nova emite o QR.

Se a sessão antiga ainda responde, o gateway desvincula o aparelho no WhatsApp. Com a credencial já morta o desvínculo não chega ao WhatsApp, e a entrada antiga pode continuar em Aparelhos conectados no celular até o usuário removê-la.

Para WABA é encerramento de sessão (não há credencial Baileys a descartar), não reabre nada e não toca na Meta.

Errors

HTTPCodeQuando
404PHONE_NOT_FOUND

POST /v1/phones/:id/pairing-code

Alternativa ao QR para Baileys: gera um código de 8 caracteres que o usuário digita no celular em "Aparelhos conectados → Conectar com número".

Body

CampoTipoObrigatórioDescrição
phoneNumberstringsimE.164 (com ou sem +), 8-20 dígitos
curl -X POST https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../pairing-code \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "phoneNumber": "+5511988887777" }'

Response 202

{ "status": "pending", "phoneNumber": "5511988887777" }

A criação do código é assíncrona (Worker recebe o comando, cria a sessão Baileys com requestPairingCode, salva o resultado em Redis). Faça polling em GET /pairing-code.

Errors

HTTPCodeQuando
400PAIRING_NOT_APPLICABLEPhone não é BAILEYS
409ALREADY_CONNECTEDPhone já conectou

GET /v1/phones/:id/pairing-code

Busca o pairing code gerado no comando anterior. Faça polling até 200.

Response 200

{ "code": "ABCD1234", "formatted": "ABCD-1234" }

Errors

HTTPCodeQuando
425PAIRING_NOT_READYCódigo ainda sendo gerado pelo Worker — tente em 1-2s

O pairing code é único e descartável, e fica em cache por 2 minutos — depois disso este endpoint volta a responder 425. Quando o usuário digita o código no celular e a sessão abre, o Worker emite channel:connected.


POST /v1/phones/:id/reconnect

Força o Worker a reabrir a sessão (útil quando o phone entrou em DISCONNECTED por instabilidade temporária). Para Baileys, tenta recuperar via auth state persistido. Para WABA, recarrega credenciais.

curl -X POST https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../reconnect \
-H "X-API-Key: ak_live_..."

Response 202

{ "status": "reconnecting" }

GET /v1/phones/:id/identities/resolve

Resolve uma identidade do contato. Aceita qualquer formato: canonicalKey, phone (5511999...), BSUID (BR.1234...), username (@maria), ou JID Baileys (5511999@s.whatsapp.net, 123@lid).

Query

ParamTipoObrigatório
keystringsim
curl 'https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../identities/resolve?key=5511988887777' \
-H "X-API-Key: ak_live_..."

Response 200

{
"canonicalKey": "+5511988887777",
"pn": "5511988887777@s.whatsapp.net",
"lid": null,
"bsuid": null,
"username": null,
"parentBsuid": null,
"type": "pn"
}

Errors

HTTPCodeQuando
404IDENTITY_NOT_FOUNDChave não encontrada nesse phone

WABA Embedded Signup

Fluxo recomendado para WABA quando o cliente não tem credenciais Meta ainda. Usa o SDK JS da Meta no browser.

GET /v1/phones/waba/embedded-signup/config

Retorna config para o SDK Meta inicializar.

curl https://wpp.ogmma.com.br/v1/phones/waba/embedded-signup/config \
-H "X-API-Key: ak_live_..."

Response 200

{
"appId": "1234567890",
"configId": "9876543210",
"graphVersion": "v25.0",
"extras": {
"featureType": "whatsapp_business_app_onboarding",
"sessionInfoVersion": "3"
}
}

extras vai direto no FB.login({ config_id, response_type: 'code', override_default_response_type: true, extras: { setup: {}, ...extras } }).

featureType: "whatsapp_business_app_onboarding" é o que troca a tela de seleção de WABA pela de conectar o WhatsApp Business existente (coexistência). Sem ele, o fluxo é dedicado e o número sai do celular do dono. Não hardcode esses valores no seu app: são contrato da Meta e mudam por decisão dela.

Errors

HTTPCodeQuando
503META_APP_ID_MISSINGGateway sem META_APP_ID configurado

POST /v1/phones/waba/embedded-signup

Finaliza o signup após o usuário completar o fluxo no SDK Meta.

O callback devolve code + waba_id. Em coexistência não vem phone_number_id — veja o aviso abaixo.

Body

CampoTipoObrigatórioDescrição
codestringsimAuthorization code retornado pelo SDK Meta
phoneNumberIdstringnãoID do número. Em coexistência a Meta não devolve esse id ao popup — omita e o gateway descobre pela WABA
wabaIdstringsimID da WABA
labelstringnãoDefault: "WhatsApp Business"
pinstringnãoPIN 6 dígitos para register. Só usado no fluxo dedicado — número de coexistência já chega registrado pela Meta e não tem PIN. Omitido, o gateway tenta "000000" (default pensado para desenvolvimento)
permitirDedicadobooleannãoDefault false. Aceita conectar número que a Meta afirma não estar em coexistência — para reconectar um número que já era dedicado. Veja o aviso abaixo
syncobjectnão{ "contacts": bool, "history": bool } — importa os dados do celular. Só coexistência. Default: ambos false
webhookobjectnãoConfig webhook (mesma do POST /phones)
curl -X POST https://wpp.ogmma.com.br/v1/phones/waba/embedded-signup \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{
"code": "AQDxYzAbCd...",
"wabaId": "987654321098765",
"label": "WhatsApp Acme",
"sync": { "contacts": true, "history": true },
"webhook": { "url": "https://api.acme.com/wpp", "secret": "supersecret-pelomenos-16chars" }
}'

Sem phoneNumberId (coexistência) e sem pin (só o fluxo dedicado usa). O webhook vai junto porque o sync só é pedido à Meta se houver para onde entregar os dados.

Response 201 — phone criado, com um bloco registration aditivo:

{
"id": "5f7b2e1c-...",
"status": "CONNECTED",
"registration": { "ok": true, "coexistence": true, "reconnected": false, "platformType": "CLOUD_API", "reason": null },
"sync": {
"contacts": { "ok": true, "flagPersisted": true, "requestId": "..." },
"history": { "ok": true, "flagPersisted": true, "requestId": "..." }
}
}

O gateway executa:

  1. Trocar code por system user token (server-side, com META_APP_SECRET).
  2. Sem phoneNumberId no corpo, descobrir o número pela WABA.
  3. Ler o estado do número (platform_type + is_on_biz_app) — é essa leitura que separa coexistência de dedicado. Ela vem antes de qualquer escrita na Meta: pedido recusado não deixa o app inscrito na WABA nem o número registrado.
  4. subscribeAppToWaba para receber webhooks.
  5. Registrar o número só se ele ainda não fala pela Cloud API — e só em canal novo. Número de coexistência a Meta já registrou sozinha quando o dono confirmou no celular; chamar /register nele é errado.
  6. Persistir phone (com a metadata da leitura mais recente do número) + disparar channel:connect no Worker.
  7. Se sync foi pedido e o número é coexistente, disparar a SMB App Data API.

registration.coexistence: false num número que você esperava coexistente significa que o WhatsApp Business do celular não continua ativo — e que não haverá eco nem histórico. reason traz o texto pronto pra UI.

Coexistência devolve só o waba_id. Com sessionInfoVersion: 3, o evento de sucesso do popup é FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING e o data carrega apenas waba_id — não há phone_number_id. Seu app precisa escutar esse nome de evento (não só FINISH) e pode chamar este endpoint sem phoneNumberId.

Errors

HTTPCodeQuando
503META_APP_NOT_CONFIGUREDSem META_APP_ID/META_APP_SECRET no servidor
409WABA_SEM_NUMEROA WABA não tem número — o Embedded Signup não foi concluído
409PHONE_NUMBER_ID_INDISPONIVELA Meta não devolveu o id do único número da WABA. Tente de novo
409PHONE_NUMBER_ID_AMBIGUOMais de um número da WABA está em coexistência. Informe phoneNumberId — os candidatos vêm em details.candidatos
409COEXISTENCIA_INDETERMINADAA Meta ainda não confirmou o número (propagação): ou não informou quais números da WABA estão em coexistência (details.candidatos traz os ids — dá para informar phoneNumberId), ou ainda não reporta o número como ativo na Cloud API. Espere um minuto e repita. permitirDedicado não contorna este erro
409NENHUM_NUMERO_COEXISTENTENenhum número da WABA está em coexistência. Refaça o Embedded Signup escolhendo o número do celular — ou, se o número já era dedicado, informe phoneNumberId (os ids vêm em details.candidatos) com permitirDedicado: true
409PHONE_JA_CONECTADOO número já está conectado em outra account
409CONEXAO_NOVA_EXIGE_COEXISTENCIAConexão nova cujo número a Meta informou não estar em coexistência. Refaça escolhendo o número do WhatsApp Business do celular — ou, se o número já era dedicado, repita com permitirDedicado: true

Neste deploy, toda conexão nova nasce em coexistência (ADR 0003). A regra vale enquanto META_ES_FEATURE_TYPE servir coexistência — em deploy configurado para o fluxo dedicado, ela não se aplica. Um número novo que a Meta informe não estar em coexistência é recusado com CONEXAO_NOVA_EXIGE_COEXISTENCIA. Só o false explícito recusa: campo ausente é propagação do Graph, não recusa, e passa.

Número que já era dedicado: mande permitirDedicado: true. É o caso de um canal WABA dedicado apagado sem ?deregister=true — o número segue registrado na Cloud API, a Meta responde is_on_biz_app: false e, sem o opt-in, ele seria recusado para sempre. O opt-in vale só para essa recusa: um número que ainda não opera na Cloud API continua recebendo COEXISTENCIA_INDETERMINADA, porque seguir registraria um dedicado novo. O número conectado assim não volta ao WhatsApp do celular.

Reconectar canal que já existe continua funcionando, inclusive dedicado antigo — a recusa acima vale só para canal novo. Nesse caso a resposta é 200 com registration.reconnected: true, e o canal é reavaliado: o gateway relê a Meta, atualiza o modo de conexão (só promove a coexistência, nunca rebaixa) e aplica o bloco webhook do corpo (inteiro, nunca campo a campo). O sync só roda se o número for coexistente — em canal dedicado ele é ignorado em silêncio, então confira registration.coexistence antes de esperar dados. É o caminho normal depois de um offboard.

POST /v1/phones/:id/waba/smb-sync

Importa contatos e/ou histórico do WhatsApp Business do celular. Só para número coexistente.

Body

CampoTipoDefaultDescrição
contactsbooleanfalseImporta a agenda do celular
historybooleanfalseImporta até 180 dias de conversa
forcebooleanfalseIgnora a marca local de "já sincronizado" e deixa a Meta decidir

Pelo menos um entre contacts e history precisa ser true.

curl -X POST https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../waba/smb-sync \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "contacts": true, "history": true }'

Response 202

{
"contacts": { "ok": true, "flagPersisted": true, "requestId": "..." },
"history": { "ok": false, "error": "ALREADY_SYNCED" }
}
CampoDescrição
okA Meta aceitou o pedido
requestIdGuarde — é o que o suporte da Meta pede
flagPersistedfalse = a Meta aceitou mas o gateway não conseguiu gravar a marca. A cota foi gasta; uma nova tentativa será recusada pela Meta
errorALREADY_SYNCED (marca local — use force para insistir), SEM_DESTINO_DE_WEBHOOK (o phone não tem webhook que entregue — nada foi pedido à Meta), WEBHOOK_EVENTS_MISSING (o tipo não está assinado; missingEvents diz o que falta) ou a mensagem da Meta

Saída da porta de mão única: se a Meta aceitou mas o webhook nunca chegou (o dono demorou, o processo reiniciou), force: true refaz o pedido. Aí quem recusa é a Meta, com o erro real dela, em vez da nossa marca local — que de outro modo só sairia com UPDATE no banco.

O 202 diz que a Meta aceitou o pedido — não que o dono autorizou compartilhar. Os dados chegam depois, nos webhooks contacts:sync e history:messages; a recusa chega como history:failed.

Duas restrições da Meta, as duas irreversíveis:

  • uma vez por número e por tipo (repetido vira ALREADY_SYNCED);
  • dentro de 24h do onboarding.

Passado o prazo ou gasta a chance, a única saída é o dono desconectar no celular e refazer o Embedded Signup inteiro.

Errors

HTTPCodeQuando
400SMB_SYNC_EMPTYNem contacts nem history pedidos
404PHONE_NOT_FOUNDPhone não é WABA ou está sem credenciais
409NOT_COEXISTENTNúmero dedicado — não há dados de app de celular
409WEBHOOK_EVENTS_MISSINGNenhum dos tipos pedidos está assinado: o webhook do phone tem lista explícita de events sem os de importação. details.missingEvents diz quais faltam

Assine os eventos ANTES de pedir o sync. Se o phone tem webhook.events preenchido, ele precisa incluir history:messages, history:failed e contacts:sync — lista vazia recebe tudo. O gateway recusa o pedido em vez de queimar a chance única da Meta em dados que não teriam para onde ir. Ajuste com PATCH /v1/phones/:id.


Instagram OAuth

GET /v1/phones/instagram/oauth/config

Retorna config para iniciar o fluxo OAuth Instagram.

Response 200

{
"appId": "...",
"redirectUri": "https://app.acme.com/oauth/instagram/callback",
"scope": "instagram_business_basic,instagram_business_manage_messages",
"authorizationUrl": "https://www.instagram.com/oauth/authorize?client_id=...&redirect_uri=..."
}

POST /v1/phones/instagram

Finaliza o OAuth com o code recebido no redirect.

Body

CampoTipoObrigatório
codestringsim
labelstringnão
webhookobjectnão
curl -X POST https://wpp.ogmma.com.br/v1/phones/instagram \
-H "X-API-Key: ak_live_..." \
-H "Content-Type: application/json" \
-d '{ "code": "AQDxYz...", "label": "Instagram Acme" }'

Response 201 — phone com type: INSTAGRAM, status: CONNECTED, token long-lived (60d) armazenado criptografado.

Errors

HTTPCodeQuando
503IG_NOT_CONFIGUREDServidor sem credenciais Instagram

WABA Business Profile

GET /v1/phones/:id/business-profile

Retorna o perfil público do número WABA (about, websites, vertical, etc) direto do Meta.

curl https://wpp.ogmma.com.br/v1/phones/5f7b2e1c-.../business-profile \
-H "X-API-Key: ak_live_..."

PATCH /v1/phones/:id/business-profile

Atualiza o perfil.

Body (todos opcionais)

CampoTipoLimite
aboutstring139 chars
addressstring256 chars
descriptionstring512 chars
emailemail
websitesURL[]até 2
verticalenumAUTO, BEAUTY, RETAIL, RESTAURANT, etc.

Response 204


WABA Phone Number lifecycle

Endpoints administrativos do número (alternativa ao Embedded Signup quando o cliente quer controle granular).

POST /v1/phones/:id/request-code

Solicita código de verificação SMS ou voz.

Body

CampoTipoDefault
methodSMS | VOICESMS
localestringpt_BR

Response 204

POST /v1/phones/:id/verify-code

Confirma o código recebido por SMS/voz.

Body: { "code": "123456" }Response 204

POST /v1/phones/:id/register

Registra o número na WABA (passo final). PIN obrigatório.

Body: { "pin": "123456" }Response 204

POST /v1/phones/:id/deregister

Desregistra o número (libera para outra WABA).

Response 204

POST /v1/phones/:id/request-name-change

Solicita mudança de display name (precisa aprovação Meta).

Body: { "name": "Acme Atendimento" } (3-60 chars) → Response 200 { "id": "..." } — o id do pedido na Meta.

POST /v1/phones/:id/two-step-pin

Define o PIN de 6 dígitos (2-step verification).

Body: { "pin": "123456" }Response 204

POST /v1/phones/:id/rotate-token

Substitui o access token armazenado (sem precisar re-onboardar). Útil quando o System User Token é rotacionado no Business Manager.

Body: { "accessToken": "EAAxxx..." }Response 204

Validação: o gateway chama getPhoneNumberInfo com o novo token antes de salvar. Token recusado pela Meta responde 400 INVALID_TOKEN, e o antigo continua valendo.


WABA Block/Unblock

GET /v1/phones/:id/blocked

Lista usuários bloqueados pelo número (anti-spam).

Response 200

{ "users": [{ "input": "5511988887777", "wa_id": "5511988887777", ... }] }

POST /v1/phones/:id/block

Body: { "users": ["5511988887777", ...] } (1-100 itens) → Response 204

POST /v1/phones/:id/unblock

Mesmo formato. Response 204


WhatsApp Business Account (WABA-level)

Endpoints para inspeção da WABA inteira (não do número individual).

GET /v1/phones/:id/waba

Retorna info da WABA.

GET /v1/phones/:id/waba/phones

Lista todos os números registrados naquela WABA.

GET /v1/phones/:id/waba/subscribed-apps

Lista apps Meta com webhook subscrito.

POST /v1/phones/:id/waba/subscribed-apps

Re-subscreve o app gateway aos webhooks.

Body (opcional)

CampoTipo
overrideCallbackUriURL
verifyTokenstring

Response 204

DELETE /v1/phones/:id/waba/subscribed-apps

Remove subscription. Response 204