Antes de começar
O que você precisa ter em mãos
As duas notificações usam o mesmo endereço. O campo type do corpo decide qual mensagem sai.
- Endereço
POST https://api.biwapp.com/notifications/send— somente HTTPS.- Token da loja
- Um por unidade, começa com
bwp_e tem 52 caracteres. É gerado pela equipe BIWAPP na implantação da loja e entregue a você — ele é exibido uma única vez. - Preço
- R$ 0,06 por mensagem de notificação, pago pela loja. Só conta o que a API do WhatsApp aceita — envios recusados não são cobrados.
- Formato
- JSON em UTF-8, com
Content-Type: application/json. Acentos e cedilha funcionam normalmente. - Pré-requisito
- A notificação precisa estar ligada e com template aprovado na unidade. Isso é configurado pela BIWAPP; se não estiver, a API responde
409dizendo qual das duas coisas falta.
Autenticação
Um cabeçalho, em toda chamada
O token identifica a loja e autoriza o envio. Mande-o no cabeçalho X-Biwapp-Token. Não há login, sessão nem renovação: o mesmo token vale até ser trocado.
Trocar o token no painel BIWAPP invalida o anterior na hora. Por isso ele deve ficar na configuração da loja no seu sistema — nunca fixo no código-fonte, onde uma troca exigiria uma nova versão do PDV.
POST /notifications/send HTTP/1.1
Host: api.biwapp.com
Content-Type: application/json
X-Biwapp-Token: bwp_3f9a1c07b2e44d8a91c6f02e57b83d1a4c9e60f2b7d5a8e1
/notifications/sendtype: "confirmacao"
Confirmação do pedido
Chame logo depois que o pedido for gravado no PDV. Você manda o nome do cliente, o número do pedido e o comprovante em texto — o mesmo que sairia na impressora. A BIWAPP audita esse texto, transforma em PDF e anexa à mensagem. Você não precisa gerar arquivo nem hospedar link.
Corpo da requisição
| Campo | Descrição |
|---|---|
typestringobrigatório |
|
phonestringobrigatório |
Celular do cliente, com ou sem DDI e máscara.
|
parameters.1stringobrigatório |
Nome do cliente, como deve aparecer na mensagem. |
parameters.2stringobrigatório |
Número do pedido. Também dá nome ao anexo: |
parameters.stringobrigatório |
O comprovante em texto puro, com quebras de linha ( Não é variável da mensagem: a BIWAPP retira este campo dos parâmetros antes do envio. Regras do comprovante |
external_refstringopcional |
A sua referência do pedido. Aparece no registro de envios e ajuda o suporte a localizar uma mensagem. |
Resposta de sucesso
| Campo | Descrição |
|---|---|
to | O telefone como foi usado no envio, só dígitos e com DDI. |
provider_id | Identificador da mensagem no provedor do WhatsApp. Guarde-o para rastrear uma entrega com o suporte. |
type · camp | O tipo recebido e o código interno dele (camp7 = confirmação). |
curl -X POST https://api.biwapp.com/notifications/send \
-H "Content-Type: application/json" \
-H "X-Biwapp-Token: bwp_SEU_TOKEN_AQUI" \
--max-time 30 \
-d '{
"type": "confirmacao",
"phone": "(11) 99000-0000",
"parameters": {
"1": "Cliente X",
"2": "12345",
"receipt_text": "Loja X — Pedido 12345\nRua Gaivota, xxx — São Paulo/SP\nContato: (11) 5000-5000\n\n21/09/2026 14:32\nCliente: Cliente X\n\n2x Camisa social .......... R$ 39,80\n1x Calça alfaiataria ...... R$ 32,90\n\nTotal ..................... R$ 72,70\nPrevisão de entrega: 24/09/2026"
},
"external_ref": "PED-12345"
}'
// Node.js 18+ — fetch nativo, sem dependências
const BIWAPP_URL = 'https://api.biwapp.com/notifications/send'
export async function enviarConfirmacao(pedido) {
const resposta = await fetch(BIWAPP_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Biwapp-Token': process.env.BIWAPP_TOKEN
},
body: JSON.stringify({
type: 'confirmacao',
phone: pedido.telefone,
parameters: {
1: pedido.cliente,
2: pedido.numero,
receipt_text: pedido.comprovante // texto puro, com \n
},
external_ref: 'PED-' + pedido.numero
}),
signal: AbortSignal.timeout(30_000)
})
const dados = await resposta.json()
if (!resposta.ok) {
// 4xx: a requisição precisa de ajuste — dados.message explica o quê
throw new Error(`BIWAPP ${resposta.status}: ${dados.message}`)
}
return dados // { message, type, camp, to, provider_id }
}
// uso
const enviado = await enviarConfirmacao({
telefone: '(11) 99000-0000',
cliente: 'Cliente X',
numero: '12345',
comprovante: [
'Loja X — Pedido 12345',
'Contato: (11) 5000-5000',
'',
'2x Camisa social .......... R$ 39,80',
'1x Calça alfaiataria ...... R$ 32,90',
'',
'Total ..................... R$ 72,70',
'Previsão de entrega: 24/09/2026'
].join('\n')
})
console.log('Enviado para', enviado.to, '· id', enviado.provider_id)
# Python 3 — pip install requests
import os
import requests
comprovante = "\n".join([
"Loja X — Pedido 12345",
"Contato: (11) 5000-5000",
"",
"2x Camisa social .......... R$ 39,80",
"1x Calça alfaiataria ...... R$ 32,90",
"",
"Total ..................... R$ 72,70",
"Previsão de entrega: 24/09/2026",
])
resposta = requests.post(
"https://api.biwapp.com/notifications/send",
headers={"X-Biwapp-Token": os.environ["BIWAPP_TOKEN"]},
json={
"type": "confirmacao",
"phone": "(11) 99000-0000",
"parameters": {
"1": "Cliente X",
"2": "12345",
"receipt_text": comprovante,
},
"external_ref": "PED-12345",
},
timeout=30,
)
dados = resposta.json()
if resposta.status_code != 200:
# 4xx: a requisição precisa de ajuste — dados["message"] explica o quê
raise RuntimeError(f"BIWAPP {resposta.status_code}: {dados['message']}")
print("Enviado para", dados["to"], "· id", dados["provider_id"])
// .NET 6+ — System.Net.Http.Json, sem pacotes extras
using System.Net.Http.Json;
using System.Text.Json;
var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Add("X-Biwapp-Token",
Environment.GetEnvironmentVariable("BIWAPP_TOKEN"));
var comprovante = string.Join("\n",
"Loja X — Pedido 12345",
"Contato: (11) 5000-5000",
"",
"2x Camisa social .......... R$ 39,80",
"1x Calça alfaiataria ...... R$ 32,90",
"",
"Total ..................... R$ 72,70",
"Previsão de entrega: 24/09/2026");
var resposta = await http.PostAsJsonAsync(
"https://api.biwapp.com/notifications/send",
new
{
type = "confirmacao",
phone = "(11) 99000-0000",
parameters = new Dictionary<string, string>
{
["1"] = "Cliente X",
["2"] = "12345",
["receipt_text"] = comprovante
},
external_ref = "PED-12345"
});
var dados = await resposta.Content.ReadFromJsonAsync<JsonElement>();
if (!resposta.IsSuccessStatusCode)
throw new InvalidOperationException(
$"BIWAPP {(int)resposta.StatusCode}: {dados.GetProperty("message").GetString()}");
Console.WriteLine($"Enviado para {dados.GetProperty("to").GetString()}");
// Delphi 10.2 Tokyo ou superior — só RTL, sem componentes de terceiros
uses
System.SysUtils, System.Classes, System.JSON,
System.Net.HttpClient, System.Net.URLClient;
function EnviarConfirmacao(const Telefone, Cliente, Pedido,
Comprovante: string): string;
var
Http: THTTPClient;
Corpo, Parametros: TJSONObject;
Envio: TStringStream;
Resposta: IHTTPResponse;
begin
Http := THTTPClient.Create;
Corpo := TJSONObject.Create;
try
Parametros := TJSONObject.Create;
Parametros.AddPair('1', Cliente);
Parametros.AddPair('2', Pedido);
// o cupom como está: quebras de linha #13#10 ou #10
Parametros.AddPair('receipt_text', Comprovante);
Corpo.AddPair('type', 'confirmacao');
Corpo.AddPair('phone', Telefone);
Corpo.AddPair('parameters', Parametros); // Corpo passa a ser o dono
Corpo.AddPair('external_ref', 'PED-' + Pedido);
Http.ConnectionTimeout := 10000;
Http.ResponseTimeout := 30000;
Http.ContentType := 'application/json';
Http.CustomHeaders['X-Biwapp-Token'] := 'bwp_SEU_TOKEN_AQUI';
Envio := TStringStream.Create(Corpo.ToJSON, TEncoding.UTF8);
try
Resposta := Http.Post(
'https://api.biwapp.com/notifications/send', Envio);
Result := Resposta.ContentAsString(TEncoding.UTF8);
if Resposta.StatusCode <> 200 then
raise Exception.CreateFmt('BIWAPP %d: %s',
[Resposta.StatusCode, Result]);
finally
Envio.Free;
end;
finally
Corpo.Free;
Http.Free;
end;
end;
{
"message": "Notificação enviada.",
"type": "confirmacao",
"camp": "camp7",
"to": "5511990000000",
"provider_id": "6700f2c9a8b41e0d93c25f71"
}
Do cupom ao WhatsApp
O que acontece com a requisição acima, do seu lado até a tela do cliente.
parameters.receipt_textOlá, Cliente X! Recebemos o seu pedido 12345 e ele já está em processamento. Avisaremos por aqui assim que estiver pronto.
/notifications/sendtype: "pronto"
Pedido pronto
Chame quando o pedido mudar para pronto para retirada no PDV. É um aviso curto, sem anexo: só o nome do cliente e o número do pedido.
Corpo da requisição
| Campo | Descrição |
|---|---|
typestringobrigatório |
|
phonestringobrigatório |
Celular do cliente, com ou sem DDI e máscara — mesma regra da confirmação. |
parameters.1stringobrigatório |
Nome do cliente. |
parameters.2stringobrigatório |
Número do pedido. |
external_refstringopcional |
A sua referência do pedido, para o registro de envios. |
A resposta tem o mesmo formato da confirmação, com "type": "pronto" e "camp": "camp8".
curl -X POST https://api.biwapp.com/notifications/send \
-H "Content-Type: application/json" \
-H "X-Biwapp-Token: bwp_SEU_TOKEN_AQUI" \
--max-time 30 \
-d '{
"type": "pronto",
"phone": "(11) 99000-0000",
"parameters": {
"1": "Cliente X",
"2": "12345"
},
"external_ref": "PED-12345"
}'
// Node.js 18+ — fetch nativo, sem dependências
const resposta = await fetch('https://api.biwapp.com/notifications/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Biwapp-Token': process.env.BIWAPP_TOKEN
},
body: JSON.stringify({
type: 'pronto',
phone: '(11) 99000-0000',
parameters: { 1: 'Cliente X', 2: '12345' },
external_ref: 'PED-12345'
}),
signal: AbortSignal.timeout(30_000)
})
const dados = await resposta.json()
if (!resposta.ok) throw new Error(`BIWAPP ${resposta.status}: ${dados.message}`)
console.log('Enviado para', dados.to, '· id', dados.provider_id)
# Python 3 — pip install requests
import os
import requests
resposta = requests.post(
"https://api.biwapp.com/notifications/send",
headers={"X-Biwapp-Token": os.environ["BIWAPP_TOKEN"]},
json={
"type": "pronto",
"phone": "(11) 99000-0000",
"parameters": {"1": "Cliente X", "2": "12345"},
"external_ref": "PED-12345",
},
timeout=30,
)
dados = resposta.json()
if resposta.status_code != 200:
raise RuntimeError(f"BIWAPP {resposta.status_code}: {dados['message']}")
print("Enviado para", dados["to"], "· id", dados["provider_id"])
// .NET 6+ — System.Net.Http.Json, sem pacotes extras
using System.Net.Http.Json;
using System.Text.Json;
var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Add("X-Biwapp-Token",
Environment.GetEnvironmentVariable("BIWAPP_TOKEN"));
var resposta = await http.PostAsJsonAsync(
"https://api.biwapp.com/notifications/send",
new
{
type = "pronto",
phone = "(11) 99000-0000",
parameters = new Dictionary<string, string>
{
["1"] = "Cliente X",
["2"] = "12345"
},
external_ref = "PED-12345"
});
var dados = await resposta.Content.ReadFromJsonAsync<JsonElement>();
if (!resposta.IsSuccessStatusCode)
throw new InvalidOperationException(
$"BIWAPP {(int)resposta.StatusCode}: {dados.GetProperty("message").GetString()}");
Console.WriteLine($"Enviado para {dados.GetProperty("to").GetString()}");
// Delphi 10.2 Tokyo ou superior — só RTL, sem componentes de terceiros
uses
System.SysUtils, System.Classes, System.JSON,
System.Net.HttpClient, System.Net.URLClient;
function EnviarPedidoPronto(const Telefone, Cliente, Pedido: string): string;
var
Http: THTTPClient;
Corpo, Parametros: TJSONObject;
Envio: TStringStream;
Resposta: IHTTPResponse;
begin
Http := THTTPClient.Create;
Corpo := TJSONObject.Create;
try
Parametros := TJSONObject.Create;
Parametros.AddPair('1', Cliente);
Parametros.AddPair('2', Pedido);
Corpo.AddPair('type', 'pronto');
Corpo.AddPair('phone', Telefone);
Corpo.AddPair('parameters', Parametros); // Corpo passa a ser o dono
Corpo.AddPair('external_ref', 'PED-' + Pedido);
Http.ConnectionTimeout := 10000;
Http.ResponseTimeout := 30000;
Http.ContentType := 'application/json';
Http.CustomHeaders['X-Biwapp-Token'] := 'bwp_SEU_TOKEN_AQUI';
Envio := TStringStream.Create(Corpo.ToJSON, TEncoding.UTF8);
try
Resposta := Http.Post(
'https://api.biwapp.com/notifications/send', Envio);
Result := Resposta.ContentAsString(TEncoding.UTF8);
if Resposta.StatusCode <> 200 then
raise Exception.CreateFmt('BIWAPP %d: %s',
[Resposta.StatusCode, Result]);
finally
Envio.Free;
end;
finally
Corpo.Free;
Http.Free;
end;
end;
{
"message": "Notificação enviada.",
"type": "pronto",
"camp": "camp8",
"to": "5511990000000",
"provider_id": "6700f3e1c7d52a0b84e19c33"
}
Do caixa ao WhatsApp
O que acontece com a requisição acima, do seu lado até a tela do cliente.
{
"type": "pronto",
"phone": "(11) 99000-0000",
"parameters": {
"1": "Cliente X",
"2": "12345"
}
}
Olá, Cliente X! O seu pedido 12345 está pronto para retirada. Até já!
O comprovante
Mande o cupom como ele é
O receipt_text vira um PDF em fonte monoespaçada, então o alinhamento feito com espaços e pontilhados se mantém exatamente como no cupom impresso. Não precisa reformatar nada.
- 8.000caracteres no máximo. Acima disso a API responde
422. - 85 colunaspor linha no PDF (A4, Courier 10). O cupom térmico de 40 a 48 colunas cabe folgado; linhas mais longas quebram entre palavras.
- 58 linhaspor página. Comprovantes maiores viram várias páginas, numeradas no rodapé.
- \n · \r\nas duas quebras de linha funcionam. TAB vira 4 espaços, porque no PDF ele não avança.
- Latin-1acentos, cedilha, travessão e aspas curvas saem corretos. Emojis e símbolos fora desse conjunto aparecem como
?.
Auditoria de conteúdo
A confirmação sai por um template de utilidade do WhatsApp — a categoria das mensagens que o cliente espera receber por ter feito um pedido. Se o anexo trouxer campanha, a Meta pode reclassificar o template como marketing, e a loja perde o envio transacional. Por isso cada comprovante é lido antes de virar PDF.
O comprovante deve conter só dados do pedido, da loja e de contato. A diferença entre o que passa e o que é barrado raramente é a palavra, e sim o convite:
Passa
- Desconto fidelidade ... -R$ 8,14desconto como linha de valor do pedido
- Pago no cartão VISA ****5678forma de pagamento
- www.lojax.com.br · (11) 5000-5000site e telefone da loja
- Garantia de 30 dias para reclamaçõescondição do serviço contratado
É barrado
- Ganhe 10% de desconto na próxima!oferta para a próxima compra
- Use o cupom VOLTA10cupom
- Siga-nos no Instagram @lojaxpedido de divulgação
- Só hoje: coleta grátisurgência e oferta
Comprovante barrado não é enviado. A resposta é 422, com a lista do que foi encontrado e a linha inteira onde está — para quem opera o PDV achar a origem:
{
"message": "O comprovante tem conteúdo de marketing e não pode ir num template de UTILIDADE. Ele deve conter só dados do pedido, da loja e de contato.\n• \"Aproveite\" (chamada para ação) em: Aproveite: 20% de desconto na próxima lavagem!\n• \"de desconto na próxima\" (oferta para a próxima compra) em: Aproveite: 20% de desconto na próxima lavagem!",
"audit": [
{
"motivo": "chamada para ação",
"termo": "Aproveite",
"linha": "Aproveite: 20% de desconto na próxima lavagem!"
},
{
"motivo": "oferta para a próxima compra",
"termo": "de desconto na próxima",
"linha": "Aproveite: 20% de desconto na próxima lavagem!"
}
]
}
Respostas e erros
Todo erro diz o que corrigir
Toda resposta é JSON e traz o campo message, escrito para ser mostrado ou registrado como está. Os 4xx pedem ajuste na requisição ou na configuração da loja — repetir não resolve. Só os 5xx merecem nova tentativa.
| Status | Quando acontece | O que fazer | Repetir? |
|---|---|---|---|
| 200 | A API do WhatsApp aceitou a mensagem. | Registre o provider_id junto do pedido. | — |
| 401 | Cabeçalho X-Biwapp-Token ausente, ou token que não existe mais. | Confira o cabeçalho. Se o token foi trocado no painel, atualize-o na configuração da loja. | Não |
| 403 | A unidade está inativa na BIWAPP. | Fale com a BIWAPP. | Não |
| 404 | A unidade ainda não tem notificações configuradas. | Fale com a BIWAPP. | Não |
| 409 | Esta notificação está desligada na unidade, ou sem template selecionado. | A message diz qual dos dois. A BIWAPP liga ou configura no painel. | Não |
| 422 | type inválido, phone ausente, comprovante ausente, acima de 8.000 caracteres ou barrado na auditoria. | Corrija conforme a message. Na auditoria, o campo audit aponta a linha. | Não |
| 502 | O PDF não pôde ser publicado, ou a API do WhatsApp recusou o envio. O motivo do provedor vem em error. | Nada foi enviado ao cliente. Tente de novo em alguns segundos. | Sim, com espera |
| 500 | Erro inesperado na BIWAPP. | Repita uma vez. Persistindo, contate o suporte com o external_ref. | Uma vez |
Boas práticas
O que evita dor de cabeça no balcão
Não trave o caixa esperando
A resposta costuma levar poucos segundos, mas depende do WhatsApp. Faça a chamada em segundo plano, com tempo limite de 30 segundos, e deixe o operador seguir com o próximo cliente.
Não repita depois de um tempo esgotado
Cada 200 é uma mensagem entregue ao WhatsApp e não há deduplicação: duas chamadas iguais geram duas mensagens. Se a sua chamada estourou o tempo, o envio pode ter saído. Registre o external_ref e confira antes de reenviar.
200 é aceito, não entregue
O sucesso confirma que o WhatsApp aceitou a mensagem. A entrega ao aparelho acontece em seguida e pode não ocorrer — por exemplo, se o número não tiver WhatsApp. O provider_id permite rastrear cada caso com o suporte.
Chame depois de gravar o pedido
Dispare a confirmação só quando o pedido já estiver salvo no PDV, com o número definitivo. Um pedido cancelado logo em seguida já terá sido confirmado ao cliente.
Um token por loja
Cada unidade tem o seu token, e ele define de qual WhatsApp a mensagem sai. Em rede com várias lojas, guarde o token na configuração de cada uma — nunca um token só para todas.
Telefone de cadastro é suficiente
Não precisa limpar máscara nem adicionar DDI: números brasileiros de 10 ou 11 dígitos recebem o 55 automaticamente. Números de outros países devem vir com o DDI.
Homologação
Antes de liberar nas lojas
Use um celular da sua equipe como cliente de teste. As mensagens de teste são mensagens reais e entram na contagem do mês da unidade.