@bzapper/client

中文文档
0.16.0发布于 2026/10/21MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2376

SDK official do bZapper para Node/TypeScript——网关德WhatsApp多租户.

中文使用文档
文档目录47 节
  1. 英斯特拉桑
  2. 哈罗世界
  3. 厄立特里亚
  4. 配置
  5. 门生
  6. 文本
  7. 图像
  8. 维多
  9. 文档
  10. 阿乌迪奥(法语:Nota de voz com) ptt (中文(简体) )
  11. 贴纸
  12. 当地
  13. 康塔托( vCard) Name
  14. 组合
  15. 目标
  16. 瓶装
  17. 名单a
  18. Editar, apagar, encaminhar, marcar lido e agendaler 互联网档案馆的…
  19. 因斯通克(números)
  20. API 官方网站(WhatsApp Cloud API)
  21. 游泳池
  22. Perfil do número( 白色标签)
  23. 格鲁波斯语, 普森萨语 e conversas
  24. Presença (funciona em grupos!) (英语)
  25. 对话( 插件)
  26. 咕噜声
  27. 聊天、礼仪、礼仪
  28. 孔塔多斯语Name
  29. Base de contatos(CRM),标签,grupos e supressão
  30. 坎潘哈斯语Name
  31. Projetos, marca, conta e usuários 互联网档案馆的存檔,存档日期2013-12-21
  32. Plano, 添加到e- faturas (英语)
  33. API 密钥( 自服务器)
  34. * 部署 rotateKey (中文(简体) )
  35. 网络呼号
  36. 快递
  37. 无本地主机( T) bzapper listen (中文(简体) )
  38. b Zapper 连接
  39. 后端( Express)
  40. 前线
  41. 激素锥形
  42. Lado do客户端 : apps conectados
  43. Conector de IA (多功能软件公司)
  44. 乌苏
  45. 诺瓦斯山地帐篷
  46. 实例
  47. 语法

@bzapper/ 客户端

SDK的官职 b 泽珀 第11段 节点/节点-网关-WhatsApp 多用户:cenecte números, envie mensages, gerencie instâncias, API keys e companyhe o uso.

  • 无害环境管理+类型
  • 运行时间零度 (苏萨) fetch 节点18+)
  • 埃尔罗斯·皮卡多斯(Erros tipados)BzapperError + 子类 por 状态) com code 报告e requestId
  • 诺瓦斯自制帐篷Idempotency-Key (原始内容存档于2013-10-12) (英语). em toda escrita). – paderão Berni Software
  • Um Método para cada operação pública da API (159), pestado contra a suíte de conferidade (禁止遵守)

英斯特拉桑

npm i @bzapper/client

重新定义 节点18+ (fetch nativo) (英语).

修复一个 versão exata 没有 package.json (单位:千美元)"@bzapper/client": "0.16.0"中标 ^: 卡达释放 (原始内容存档于2013-10-12). declara nas notas se muda a superfície pública ou se é só aditiva.

哈罗世界

import { Bzapper } from "@bzapper/client";

const bz = new Bzapper({ apiKey: "bz_live_..." });

await bz.sendText({ to: "+5511999999999", body: "Olá!" });

欧 baseUrl tem 默认https://api.bzapper.com.br页:1 逻辑 —— 说 通知 def/self-host: new Bzapper({ apiKey: "bz_live_...", baseUrl: "http://localhost:8080" })。 。 。 .

Também há uma 工厂:

import { createClient } from "@bzapper/client";
const bz = createClient({ apiKey: "bz_live_..." });

厄立特里亚

Crie a API 密钥无 pael do bZapper (配置- API 密钥{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}你该死 bz.createKey(...)键bz_live_...低调一个嗯 专业 (números,收件箱e 数据) 伊索拉多斯·普罗耶托(英语:isolados por projeto); para agir em outro projeto da conta, passe projectId (没有标题) X-Project-IdNunca exponha 一把钥匙,不是无名小卒.

配置

new Bzapper({
  apiKey: "bz_live_...",
  locale: "pt-BR",   // opcional → header Accept-Language (mensagens de erro traduzidas)
  timeout: 30_000,   // opcional, ms por tentativa (default 30000)
  maxRetries: 2,     // opcional, novas tentativas além da primeira (default 2; 0 desliga)
  projectId: "…",    // opcional → header X-Project-Id
  baseUrl: "http://localhost:8080", // opcional, só em dev/self-host
});

托达·恩维亚 Authorization: Bearer <apiKey>, (中文(简体) ), Accept: application/json, (中文(简体) ), X-Bzapper-Client: bzapper-node/<versão> (音乐) User-Agent), um (中文(简体) ) X-Request-Id 波尔 沙玛达, Idempotency-Key 纳斯·埃斯克丽塔斯, Content-Type: application/json (夸多哈科波)e Accept-Language编号 :X-Project-Id (原始内容存档于2017-09-27). quando configurados. 托多·梅多·阿西塔(英语: 乌尔蒂莫争论 逻辑 { idempotencyKey?, timeout?, maxRetries?, signal? }。 。 。 .

门生

活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活SendBase: to (obrigatório, E.164 ou JID), (中文(简体) ), instance_id?, (中文(简体) ), pool_id?, (中文(简体) ), quoted_message_id?, (中文(简体) ), quoted_participant? (c) 国家妇女发展委员会, client_reference?, (中文(简体) ), mentions? (JIDs ou Telefones) (美国英语). 托多斯重毁 { message_id, status, client_reference? }。 。 。 .

重试seguro:通过 { idempotencyKey } 科莫 2o artigo de qualquer envio. 中国植物物种信息数据库. 重回原地 重回原地 idempotency_in_progress se a a amda roda; 422 单相机 idempotency_key_reused {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}我...

await bz.sendText({ to: "+5511999999999", body: "Pedido #42 confirmado" }, { idempotencyKey: "pedido-42" });

文本

await bz.sendText({ to: "+5511999999999", body: "Olá!" });

图像

await bz.sendImage({
  to: "+5511999999999",
  media: { url: "https://example.com/foto.jpg", caption: "Veja isto" },
});

维多

await bz.sendVideo({
  to: "+5511999999999",
  media: { url: "https://example.com/video.mp4", caption: "Demo" },
});

文档

await bz.sendDocument({
  to: "+5511999999999",
  media: {
    url: "https://example.com/contrato.pdf",
    filename: "contrato.pdf",
    mimetype: "application/pdf",
  },
});

阿乌迪奥(法语:Nota de voz com) ptt(中文(简体) )

await bz.sendAudio({
  to: "+5511999999999",
  media: { url: "https://example.com/audio.ogg", ptt: true },
});

贴纸

await bz.sendSticker({
  to: "+5511999999999",
  media: { url: "https://example.com/sticker.webp" },
});

弥陀曰: 使用情况 url 尔 base64{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}那又怎样.

当地

await bz.sendLocation({
  to: "+5511999999999",
  latitude: -23.5613,
  longitude: -46.6565,
  name: "Av. Paulista",
  address: "São Paulo, SP",
});

康塔托( vCard) Name

await bz.sendContact({
  to: "+5511999999999",
  contact_name: "Suporte bZapper",
  // telefone DO CONTATO do cartão, não o destino
  contact_phone: "+5511977776666",
});

组合

await bz.sendPoll({
  to: "+5511999999999",
  name: "Qual seu plano?",
  options: ["Free", "Pro", "Enterprise"],
  selectable_count: 1,
});

目标

quoted_message_id {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}请说.

await bz.sendReaction({
  to: "+5511999999999",
  quoted_message_id: "WA_MESSAGE_ID",
  emoji: "👍",
});

瓶装

await bz.sendButtons({
  to: "+5511999999999",
  body: "Confirma o pedido?",
  footer: "bZapper",
  buttons: [
    { id: "yes", title: "Sim" },
    { id: "no", title: "Não" },
  ],
});

名单a

await bz.sendList({
  to: "+5511999999999",
  body: "Escolha uma opção",
  button_text: "Abrir menu",
  footer: "bZapper",
  sections: [
    {
      title: "Planos",
      rows: [
        { id: "pro", title: "Pro", description: "R$ 99/mês" },
        { id: "ent", title: "Enterprise", description: "Sob consulta" },
      ],
    },
  ],
});

卡维特·佩特里奥: 单数 圣孔菲亚韦斯 无"什么"(por em grupos). API( API ) 启动 {\fn华文仿宋\fs16\1chd1d1d1}嗯,嗯.. 文本数字菜单 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}等等 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}等等...

Editar, apagar, encaminhar, marcar lido e agendaler 互联网档案馆的存檔,存档日期2013-12-21

await bz.editMessage(msg.message_id, { text: "Texto corrigido" });
await bz.revokeMessage(msg.message_id, { for_everyone: true });
await bz.forwardMessage({ instance_id: inst.id, to: "+5511988887777", from_chat: jid, wa_message_id: "3EB0…" });
await bz.markRead("3EB0…", { instance_id: inst.id, chat: jid });

// Qualquer envio aceita `scheduled_at` (RFC3339).
const s = await bz.sendText({ to: "+5511999999999", body: "Lembrete", scheduled_at: "2026-10-01T12:00:00Z" });
const { data: pending } = await bz.listScheduled({ limit: 50 });
await bz.cancelScheduled(s.scheduled_id!);

因斯通克(números)

const { data } = await bz.listInstances();

const inst = await bz.createInstance({
  phone: "+5511999999999",
  nickname: "Suporte",
  proxy_url: "http://user:[email protected]:8080", // opcional
});

await bz.getInstance(inst.id);
const up = await bz.getInstanceUptime(inst.id); // up.windows["24h"] / ["7d"] / ["30d"] (null = sem histórico ainda), up.events

// Conexão por QR (default) ou código de pareamento.
const qr = await bz.connectInstance(inst.id, "qr");
console.log(qr.qr_code);

const code = await bz.connectInstance(inst.id, "code");
console.log(code.pair_code);

await bz.disconnectInstance(inst.id);
await bz.logoutInstance(inst.id);          // exige novo QR
await bz.clearInstanceSession(inst.id);    // pareamento travado: apaga a credencial do dispositivo

// Arquivar mantém o histórico; `archived: "1"` lista os arquivados.
await bz.archiveInstance(inst.id);
const archived = await bz.listInstances({ archived: "1" });
await bz.unarchiveInstance(inst.id);

// Rede, filtros de entrada e privacidade.
await bz.setInstanceProxy(inst.id, { proxy_url: "http://user:[email protected]:8080" });
await bz.setInboundFilters(inst.id, { ignore_groups: true, ignore_status: true });
await bz.setPrivacy(inst.id, { setting: "last", value: "contacts" });

await bz.deleteInstance(inst.id); // encerra a sessão e remove
await bz.getHealth();             // { status: "ok", version }

API 官方网站(WhatsApp Cloud API)

// Conexão: Embedded Signup (normal) ou credenciais manuais (phone_number_id opcional).
const { enabled, app_id, config_id } = await bz.getOfficialSignupConfig();
const { account, warning } = await bz.completeOfficialSignup({ code, waba_id: "…" });
await bz.connectOfficialAccount({ waba_id: "…", access_token: "…" });
const official = await bz.getOfficialAccount();
const { can_send, live } = await bz.getOfficialStatus(); // conferido ao vivo na Meta

// Vários números na mesma conta comercial.
const { numbers: available } = await bz.listAvailableOfficialNumbers();
const added = await bz.addOfficialNumber({ phone_number_id: available[0].id! });
const { numbers } = await bz.listOfficialNumbers(); // cada um com instance_id e is_default
await bz.setDefaultOfficialNumber(added.id!);
await bz.disconnectOfficialNumber(added.id!); // UM número; o histórico fica

// Perfil comercial (envie o perfil INTEIRO: campo vazio limpa na Meta).
const profile = await bz.getOfficialProfile();
await bz.updateOfficialProfile({ ...profile, about: "Atendimento 24/7" });
await bz.uploadOfficialProfilePhoto(await fileFromPath("./logo.png", "image/png"));
await bz.reregisterOfficialProfile(); // depois que a Meta aprova um nome novo
// Por número: getOfficialNumberProfile / updateOfficialNumberProfile /
// uploadOfficialNumberProfilePhoto / reregisterOfficialNumberProfile (id do número).

// Templates: validados ANTES de ir para a Meta; a decisão chega por webhook.
const { url } = await bz.uploadTemplateMedia(await fileFromPath("./amostra.png", "image/png"));
const { template, advice } = await bz.createTemplate({
  name: "pedido_enviado",
  language: "pt_BR",
  category: "UTILITY",
  header: { type: "image", media_url: url },
  body: { text: "Olá {{1}}, seu pedido {{2}} saiu para entrega.", example: ["Ana", "#123"] },
});
const { templates } = await bz.listTemplates();
await bz.syncTemplates(); // importa os que existem só na Meta
await bz.sendTemplate({ to: "+5511999999999", template: "pedido_enviado", variables: ["Ana", "#123"] });

// Alternativa: com a janela de 24h fechada, o sendText sai como este template
// e a resposta conta o desvio em `delivery` ({ via: "template_fallback", template, note }).
await bz.setFallbackTemplate({ template_id: template!.id });
const sent = await bz.sendText({ to: "+5511999999999", body: "Oi!" });
if (sent.delivery) console.log(sent.delivery.note);

await bz.disconnectOfficialAccount(); // TODOS os números

游泳池

const pool = await bz.createPool({ name: "Vendas", strategy: "health_weighted" });
await bz.addPoolNumber(pool.id, { instance_id: inst.id });
await bz.getPool(pool.id);
const { data: pools } = await bz.listPools();
await bz.sendText({ to: "+5511999999999", body: "Oi!", pool_id: pool.id });

Perfil do número( 白色标签)

await bz.setProfile(inst.id, {
  display_name: "Suporte bZapper",
  status_message: "Atendimento 24/7",
  picture: "<base64>", // opcional
});

status_message 联合会; display_name 电子 picture são experienceais (o servidor pode responseer) (英语: 501) (中文(简体) ).

格鲁波斯语, 普森萨语 e conversas

作为歌剧 vançadas exigem um número específico。 说明o instance_id 发音:na 查询 (grups/conversas)你没有 机构 (presença/chats/contatos)——o SDK já cuida disso pela Assinatura de cada método. 中国植物物种信息数据库.

Presença (funciona em grupos!) (英语)

to 脚踏车,嗯 JID 德克鲁波 ——"数字和"("digitalando")的索引 funciona em grupos.

// Presença num grupo
await bz.presenceChat({
  instance_id: inst.id,
  to: "[email protected]", // JID de grupo
  state: "typing", // typing | recording | paused
});

// Também em 1:1
await bz.presenceChat({ instance_id: inst.id, to: "+5511999999999", state: "recording" });

对话( 插件)

const { data: threads } = await bz.listConversations(inst.id);

const history = await bz.conversationHistory("[email protected]", {
  instance_id: inst.id,
  before: "2026-06-01T00:00:00Z", // opcional, RFC3339
  limit: 100,                     // opcional, ≤ 200
});

await bz.archiveChat("[email protected]", { instance_id: inst.id, on: true });
await bz.pinChat("[email protected]", { instance_id: inst.id, on: true });
await bz.markChat("[email protected]", { instance_id: inst.id, on: true }); // lido

咕噜声

const { data: groups } = await bz.listGroups(inst.id);

const group = await bz.createGroup(inst.id, {
  name: "Equipe bZapper",
  participants: ["+5511988887777", "+5511977776666"],
});

await bz.getGroup(group.jid, inst.id);

await bz.updateGroupParticipants(group.jid, inst.id, {
  action: "promote", // add | remove | promote | demote
  participants: ["+5511988887777"],
});

const invite = await bz.groupInvite(group.jid, inst.id);
console.log(invite.invite_link);

await bz.previewGroupInvite(inst.id, { code: "AbCdEf123456" }); // nome/tamanho SEM entrar
await bz.joinGroup(inst.id, { code: "AbCdEf123456" });
await bz.leaveGroup(group.jid, inst.id);

await bz.updateGroup(group.jid, inst.id, { name: "Equipe", announce: true });
const fresh = await bz.groupInvite(group.jid, inst.id, { reset: true }); // revoga o link antigo
await bz.listJoinRequests(group.jid, inst.id);
await bz.updateJoinRequests(group.jid, inst.id, { participants: ["+5511988887777"], approve: true });

聊天、礼仪、礼仪

await bz.muteChat(jid, { instance_id: inst.id, on: true });
const label = await bz.createLabel({ instance_id: inst.id, name: "VIP" });
await bz.applyChatLabel(jid, { instance_id: inst.id, label_id: label.id!, apply: true });
await bz.listLabels(inst.id);
await bz.deleteLabel(label.id!, inst.id);

await bz.blockContact("[email protected]", { instance_id: inst.id });
await bz.getBlocklist(inst.id);
await bz.unblockContact("[email protected]", { instance_id: inst.id });

await bz.rejectCall({ instance_id: inst.id, call_from: "[email protected]", call_id: "…" });

孔塔多斯语Name

const { data } = await bz.contactsCheck({
  instance_id: inst.id,
  phones: ["+5511988887777", "+5511977776666"],
});
for (const c of data) console.log(c.query, c.in_whatsapp, c.jid);

Base de contatos(CRM),标签,grupos e supressão

O vínculo contato Projeto/número é mantido) 互联网档案馆的存檔,存档日期2013-12-02 自动降温 pela API; os filtros só leem. 中国植物物种信息数据库.

const page = await bz.listContacts({ tags: ["vip"], status: "active", has_email: true, limit: 50 });
const contact = await bz.createContact({ phone: "+5511988887777", name: "Ana", email: "[email protected]" });
await bz.updateContact(contact.id!, { address: { city: "São Paulo", state: "SP" } });
await bz.getContactHistory(contact.id!, { limit: 20 });
await bz.addContactNote(contact.id!, { body: "Pediu retorno à tarde" });

await bz.createTag({ key: "vip", name: "VIP" });
await bz.mutateContactTags(contact.id!, { add: ["vip"] });
await bz.createContactGroup({ key: "clientes", name: "Clientes" });
await bz.mutateContactGroups(contact.id!, { add: ["clientes"] });

await bz.optOutContact(contact.id!);   // LGPD
await bz.optInContact(contact.id!);
await bz.createSuppression({ phone: "+5511977776666", reason: "pediu" });
await bz.deleteSuppression("+5511977776666");
await bz.deleteContact(contact.id!);

进口地块( E)importContacts(中文(简体) )

腾讯风,até 1000林纳 鸡尾酒 林哈·鲁伊姆·瓦伊(Linha ruim vai para) errors 电子 无 drruba o reso do lote; supresso/ opt-out/ 口述 平分 em skipped_rows (原始内容存档于2013-10-12). não resuscita. (原始内容存档于2018-09-29). Tags e grupos são criados sob requesta. 新的香肠 pending_validation (中文(简体) ) (原始内容存档于2013-10-12). otto- in antes de campanha.

const rows = [
  { phone: "+5511988887777", name: "Ana", email: "[email protected]", tags: ["vip"], groups: ["clientes"] },
  { phone: "+5511977776666", name: "Bruno", address: { city: "São Paulo", state: "SP" } },
];

const dry = await bz.importContacts({ contacts: rows, dry_run: true }); // valida, não escreve
if (dry.failed === 0) {
  const res = await bz.importContacts({ contacts: rows });
  console.log(res.created, res.updated, res.skipped, res.failed);
  for (const row of res.skipped_rows ?? []) console.warn(row.index, row.phone, row.reason);
}

出口 em CSV (exportContacts(中文(简体) )

阿西塔奥斯 膜膜 页:1 listContacts (声) offset· ; limit o teto de linhas) e e e a 维基语录链接:名人名言 - 文学作品 - 谚语 (原始内容存档于2018-03-21) (英语). unica Rota da SDK que jSON系统页:1 文本 CSV crus (单位:千美元)string(原始内容存档于2018-09-21) (中文(简体) ) (b) 国家、领土、领土、领土和领土.

import { writeFile } from "node:fs/promises";

const csv = await bz.exportContacts({ tags: ["vip"], status: "active", limit: 50_000 });
await writeFile("contatos.csv", csv, "utf8");
// phone,name,email,status,source,tags,groups,created_at,last_activity_at
// +5511988887777,"Silva, Ana",[email protected],active,import,vip;novo,clientes,2026-09-21T12:00:00Z,…

标记 e grupos vêm unidos por ; e 作为数据em RFC 3339(UTC). (原始内容存档于2018-09-21) (英语). Campos com vírgula ou Aspas vêm escapados pelo paperão CSV——使用um parser de verdade se来再进口.

坎潘哈斯语Name

const camp = await bz.createCampaign({
  name: "Black Friday",
  pacing_profile: "conservative",
  variations: [{ body: "Oi {nome}! {Oferta|Promoção} só hoje." }],
});
await bz.addCampaignRecipients(camp.id, { contact_filter: { tags: ["vip"] } });
await bz.getCampaignEligibility();          // números aptos (conexão e tempo de conexão)
const dry = await bz.dryRunCampaign(camp.id);
console.log(dry.estimated_human, dry.warnings);
await bz.startCampaign(camp.id);
await bz.pauseCampaign(camp.id);
await bz.resumeCampaign(camp.id);

// Imagem de cabeçalho (multipart).
import { fileFromPath } from "@bzapper/client";
const { url } = await bz.uploadCampaignMedia(await fileFromPath("./banner.png", "image/png"));

Projetos, marca, conta e usuários 互联网档案馆的存檔,存档日期2013-12-21

const proj = await bz.createProject({ name: "Loja 2" });
await bz.updateProject(proj.id, { name: "Loja SP", color: "#0a7" });
await bz.getProjectsHealth();                       // semáforo de números por projeto
await bz.setProjectBrand(proj.id, { about: "Atendimento Loja SP" });
await bz.uploadProjectLogo(proj.id, { content: bytes, filename: "logo.png", contentType: "image/png" });
await bz.uploadBrandLogo(await fileFromPath("./logo.png", "image/png"));

const me = await bz.getMe();
await bz.updateProfile({ name: "Ana", job_title: "Suporte" });
await bz.updateAccount({ name: "Minha Empresa" });
await bz.inviteUser({ email: "[email protected]", role: "agent" });

Plano, 添加到e- faturas (英语)

const ent = await bz.getMyEntitlements();
await bz.upgradePlan();                               // Pro no carrinho
await bz.changeAddon({ kind: "number", delta: 2 });   // +2 números
const cart = await bz.getAddonCart();
const { client_secret } = await bz.checkoutAddonCart({ save_card: true });
const { data: invoices } = await bz.listMyInvoices();
await bz.getPricing();

API 密钥( 自服务器)

const { data } = await bz.listKeys();

const created = await bz.createKey({ name: "CI", role: "agent" });
console.log(created.api_key); // guarde — mostrada só uma vez!

await bz.revokeKey(created.key.id);

* 部署rotateKey(中文(简体) )

rotateKey 克里亚·马沙夫 诺瓦 (赫尔达·帕佩尔、埃斯科波斯、诺梅) 反藻类 (原始内容存档于2018-03-25) (英语). funcionando por um período de carência - padrão 24 h, máximo 30 dias, 0 (原始内容存档于2013-10-12). Revoga na hora. 二级a rova, roque o segredo onde a integração roda, e a antga morre sozinha (depois do prazo ela) 互联网档案馆的存檔,存档日期2013-12-02 响应 401 key_expired) (中文(简体) ). Só admin; a chave crua vem uma única vez. 中国植物物种信息数据库.

const rot = await bz.rotateKey(keyId, { revoke_in_seconds: 3600 }); // 1h de carência
console.log(rot.api_key);              // guarde — mostrada só uma vez!
console.log(rot.old_key_expires_at);   // quando a antiga para (null = revogada na hora)
console.log(rot.previous_key?.rotated_to === rot.key.id); // true

(bZapper Connection) 转接站 partner.rotateConnectionKey。 。 。 .

网络呼号

格伦西 网路上的相片:

const hook = await bz.createWebhook({
  url: "https://seuapp.com/webhooks/bzapper",
  event_types: ["message.received", "instance.banned"], // omita = todos os eventos
});
console.log(hook.secret); // segredo de assinatura — devolvido UMA vez, guarde agora

await bz.listWebhooks();
await bz.updateWebhook(hook.id, { active: false });        // pausar
await bz.updateWebhook(hook.id, { secret: "regenerate" }); // rotacionar segredo
await bz.deleteWebhook(hook.id);

Receba e 处理e 作为谷类 Webhooks assinatura HMAC, 维基百科中的相关条目: 活塞活塞, e. even to tipado e roteia para seus 处理器(零) 依赖; usa o crypto nativo do 节点 :

import { Webhooks } from "@bzapper/client";

const hooks = new Webhooks(process.env.BZAPPER_WEBHOOK_SECRET!); // o secret do createWebhook

hooks.on("message.received", (event) => {
  console.log(event.sender?.name, event.payload.body);
});

hooks.on("instance.banned", (event) => {
  alert(event.instanceId);
});

// No seu endpoint HTTP. Passe o corpo CRU e o header X-Bzapper-Signature.
// Lança WebhookSignatureError se a assinatura for inválida — não processe nesse caso.
await hooks.handle(rawBody, signature);

欧 event tipado tem 电话 id, (中文(简体) ), type, (中文(简体) ), timestamp, (中文(简体) ), instanceId, (中文(简体) ), clientReference, (中文(简体) ), group, (中文(简体) ), sender, (中文(简体) ), mentions, (中文(简体) ), payload e 个 raw 原文 (JSON做信封,EM蛇 case). 使用 event.id a API pode) 互联网档案馆的存檔,存档日期2013-12-02 (原始内容存档于2018-10-21). Reentregar. {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我们的新浪微博 verifyWebhook(secret, rawBody, signature) 电子 constructWebhookEvent(secret, rawBody, signature)。 。 。 .

快递

Webhooks#middleware() 调出 um 中间软件 Estilo Express。 伊莱·普里西萨 铜屑,该寺 express.raw() 旋转正弦 req.body {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}嗯 谢佳 嗯 Buffer (法语:não um objeto ja parseado):

import express from "express";
import { Webhooks } from "@bzapper/client";

const app = express();
const hooks = new Webhooks(process.env.BZAPPER_WEBHOOK_SECRET!);

hooks.on("message.received", (e) => console.log(e.payload.body));

app.post(
  "/webhooks/bzapper",
  express.raw({ type: "application/json" }), // entrega req.body como Buffer cru
  hooks.middleware(),                        // verifica, dispara e responde 200/400
);

app.listen(3000);

无本地主机( T)bzapper listen(中文(简体) )

哦,帕科特特特车,嗯,执行。 URL pública Nenhuma, ele abre o 溪流 事件 e 本地服务中心时,屁股 igualzinho à produção(无埃斯皮里多) stripe listen:

npx @bzapper/client listen --forward-to http://localhost:3000/webhooks/bzapper
bZapper v0.16.0 — relay de webhooks para o localhost
  ouvindo    https://api.bzapper.com.br/webhooks/listen
  reenviando http://localhost:3000/webhooks/bzapper
  secret     whsec_Hs3…

✓ conectado ao stream de eventos. Ctrl+C para sair.

14:02:11  message.received           evt_01HZX…  →  200 12ms
14:02:19  message.sent               evt_01HZY…  →  500 8ms
页:1
-f, (中文(简体) ), --forward-to <url>URL 本地 que recebe os POSTs (obrigatória, salvo com) (英语) --print-only(中文(简体) )
--api-key <key>键 bz_live_…页:1 $BZAPPER_API_KEY
--base-url <url>基础 da API; 父版 $BZAPPER_BASE_URL {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}你为我辩护
--project <id>塞西昂-塞西昂-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁-塞西翁
--events <a,b,c>só estes tipos (前: message.received,message.sent(中文(简体) )
--secret <whsec_…>segredo de assinatura; padrão: um novo, 有印象的动画
--print-only不,不,不,不,不,不,不
--help, (中文(简体) ), --version

网络地图: o stream esppelha (英语: 待办事宜 连做 猪笼草(projeto, exista ou nuão uma assinatura)——由牛笼草(justamente o modo de senvolver antes)组成 de ter URL pública. 互联网档案馆的存檔,存档日期2014-12-02. 密钥无信头 Authorization尼尼卡纳URL.

卡达 POST 列瓦 Os mesmos headers da produção 互联网档案馆的存檔,存档日期2013-10-02 —— 说 X-Bzapper-Signature: sha256=<hmac do corpo cru>, (中文(简体) ), X-Bzapper-Event-Id, (中文(简体) ), X-Bzapper-Event-Type 电子 Content-Type: application/json ——,腾讯 Webhooks 中继代码 有效参数 a bZapper :

const hooks = new Webhooks(process.env.BZAPPER_WEBHOOK_SECRET!); // o secret que a CLI imprimiu

⚠️ 哦,秘密的,诺索. 萨姆 --secret一个CLI Gera um na hora e o (原始内容存档于2012-10-12) (英语). primate: use-o no sue app durante o teste. Assinatura Prova que o POST veio (英语) 脱去CLI, não da b Zapper—— la vale exatamente o que o secret vale. 中国植物物种信息数据库. 额 秘密 o do webhook cadastrado (英语:createWebhook) (中文(简体) ).

背对一个部分 retry 对 (一) 服务员; Ctrl+C {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}哦! {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}哦! Credencial 回避 (原始内容存档于2018-03-21). encerra na hora, com código de saida diferente de 0.

b Zapper 连接

段落 软件(原始内容存档于2018-03-26) (英语). o seu produto deixa os SEUS clients assinarem o bZapper Pro e conectarem o WhatsApp sem sair dele, e recebe uma API 键 (单位:千美元)bz_live_...自动服务员 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}哦 客户 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}哦 bZapper serrata; a conexão só funciona enquanto o Pro dele estivers pago. 中国植物物种信息数据库.

O通量:

  1. 苏 后端 (cria uma sessão com o partners (cria uma sessão com com o partners secret)) 秘密合作(cria uma sessão com com o partners 秘密合作) 秘密合作(cria uma sessão com com o 伙伴合作(cbz_partner_...) (中文(简体) ).
  2. 苏 前面 abre 组件 com session_token。 。 。 .
  3. Ao concluir (Pro pago + número conectado), 构件 emite um 等 code (10分钟).
  4. Seu 后端为 troca o code pela API 密钥用于客户端 e guarda 密钥 .
  5. Dali em diante , 请使用 Bzapper 普通的相机 essa 密钥.

O 搭档秘密 e só de 后端 ——"无所事事"("dunica o mande par o navegador").

后端( Express)

import express from "express";
import {
  Bzapper,
  BzapperError,
  BzapperPartner,
  Webhooks,
  isConnectEvent,
  type PartnerWebhookEvent,
} from "@bzapper/client";

const app = express();
const partner = new BzapperPartner({ partnerSecret: process.env.BZAPPER_PARTNER_SECRET! });

// 1) Cria a sessão do componente para o cliente logado no SEU produto.
app.post("/bzapper/session", express.json(), async (req, res) => {
  const user = req.user; // seu usuário autenticado
  const session = await partner.createConnectSession({
    external_id: user.id, // o id do cliente no SEU sistema (mesmo id = mesma conexão)
    customer: {
      name: user.name,
      email: user.email,
      phone: user.phone,     // opcional, E.164 — pré-preenche o número
      company: user.company, // opcional — vira o nome da conta/projeto
      country: "BR",         // opcional — define a moeda
    },
    locale: "pt-BR",
  });
  res.json({ session: session.session_token });
});

// 2) Troca o code emitido pelo componente pela API key do cliente.
app.post("/bzapper/exchange", express.json(), async (req, res) => {
  const connection = await partner.exchangeCode(req.body.code);
  // Guarde agora — a key não é mostrada de novo (use rotateConnectionKey se perder).
  await db.saveBzapperKey(connection.external_id, connection.id, connection.api_key);
  res.json({ status: connection.status });
});

// 3) Webhook do parceiro: mesma assinatura HMAC (X-Bzapper-Signature) dos webhooks
//    normais, com o segredo do webhook do parceiro. Toda entrega traz `connection`.
const hooks = new Webhooks(process.env.BZAPPER_PARTNER_WEBHOOK_SECRET!);

hooks.onAny(async (event) => {
  if (!isConnectEvent(event)) return; // mensagens/status dos números também chegam aqui
  const { connection } = event as PartnerWebhookEvent;
  switch (event.type) {
    case "connect.completed":
    case "connect.resumed":
      await db.setBzapperStatus(connection.externalId, "active");
      break;
    case "connect.suspended": // Pro do cliente sem pagamento — volta sozinho ao pagar
      await db.setBzapperStatus(connection.externalId, "suspended");
      break;
    case "connect.revoked": // encerrada — a key não vale mais
      await db.deleteBzapperKey(connection.externalId);
      break;
  }
});

app.post(
  "/webhooks/bzapper-partner",
  express.raw({ type: "application/json" }), // corpo CRU, obrigatório para a assinatura
  hooks.middleware(),
);

// 4) Usando a key do cliente: trate a suspensão (402) e a revogação (401).
app.post("/avisar", express.json(), async (req, res) => {
  const bz = new Bzapper({ apiKey: await db.getBzapperKey(req.user.id) });
  try {
    await bz.sendText({ to: req.body.to, body: req.body.text });
    res.sendStatus(202);
  } catch (err) {
    if (err instanceof BzapperError && err.code === "connect_suspended") {
      // 402: o Pro do cliente está sem pagamento. Peça para ele regularizar.
      return res.status(402).json({ error: "Regularize sua assinatura do bZapper." });
    }
    if (err instanceof BzapperError && err.code === "connect_revoked") {
      // 401: a conexão acabou. Ofereça conectar de novo (nova sessão).
      return res.status(409).json({ error: "Conecte o WhatsApp novamente." });
    }
    throw err;
  }
});

app.listen(3000);

前线

<script src="https://widget.bzapper.com.br/v1/connect.js"></script>
<script>
  async function conectarWhatsApp() {
    const { session } = await fetch("/bzapper/session", { method: "POST" }).then((r) => r.json());

    BzapperConnect.open({
      session,
      onComplete: ({ code }) =>
        fetch("/bzapper/exchange", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ code }),
        }),
    });
  }
</script>

激素锥形

const me = await partner.me(); // de quem é o partner secret

const { data } = await partner.listConnections({ status: "suspended" }); // ou { external_id }
const conn = await partner.getConnection(data[0].id); // status, conta, números

const rotated = await partner.rotateConnectionKey(conn.id); // nova key; a anterior para de valer
console.log(rotated.api_key);

await partner.revokeConnection(conn.id); // revoga a key (NÃO cancela o plano do cliente)

埃斯塔多斯·达孔克桑(Estados da Conexão)status: pending_account, (中文(简体) ), pending_payment, (中文(简体) ), pending_number, (中文(简体) ), active, (中文(简体) ), suspended 电子 revoked. eventos entlegues ao webhook (英语) 做小说: connect.completed, (中文(简体) ), connect.suspended, (中文(简体) ), connect.resumed 电子 connect.revoked (名单 CONNECT_EVENT_TYPES)),"活"(além dos eventos oldais),"活"(活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活"活 (原始内容存档于2018-09-30). dos números das conexõs antives. 欧 event.connection 调试 id, (中文(简体) ), externalId, (中文(简体) ), accountId, (中文(简体) ), projectId 电子 status。 。 。 .

Lado do客户端 : apps conectados

API key da própria conta, o cliente vê e desconecta os parceiros: 互联网档案馆的存檔,存档日期2011-12-04

const { data: apps } = await bz.listConnectedApps(); // com partner_name / partner_logo_url
await bz.revokeConnectedApp(apps[0].id);             // admin; a key do parceiro para na hora

Conector de IA (多功能软件公司)

Apps de IA (前: Claude) que uma pessoa autorizou a operator a conta pelo conector MCP: 水晶相机: 水晶相机:

const { data: conns } = await bz.listAIConnections(); // client_name, user_email, user_role, scopes, last_used_at
await bz.revokeAIConnection(conns[0].id);             // os tokens dele param na próxima chamada

乌苏

const usage = await bz.getUsage({
  from: "2026-06-01T00:00:00Z",
  to: "2026-06-30T23:59:59Z",
});
console.log(usage.total, usage.delivery_rate);

诺瓦斯山地帐篷

Em qualquer resposta não-2xx o SDK lança (英语) BzapperError (ou uma sub classe.) (中文(简体) ). 拉米菲克铸币局 皮罗 code (stável),尼尼卡·皮洛(英语: message (文本为traduzido).

状态类
400,422 (中文(简体) )ValidationError铜矿/铜矿
401 (中文(简体) )AuthenticationError密钥 inválida/ revogada(重排)connect_revoked(中文(简体) )
403 (韩语)PermissionDeniedErrorrequiredScope diz o escopo que faltou (法语)
404 (韩语)NotFoundError
409个ConflictError实例: not_connected, (中文(简体) ), idempotency_in_progress
429个RateLimitErrorretryAfter 自动交换
5 (简体中文)ServerError
sem reposta (中文(简体) )NetworkErrorstatus = 0, (中文(简体) ), code = "NETWORK_ERROR"
(前402页)BzapperError实例: connect_suspended

托达斯牧民 BzapperError缩写: code, (中文(简体) ), message, (中文(简体) ), status (= (单位:千美元) statusCode, (中文(简体) ), locale, (中文(简体) ), requestId (通知ao suporte),(请回答), retryAfter, (中文(简体) ), requiredScope 电子 body (公司解码器). 2xxx que não é JSON vira (英语) code = "INVALID_RESPONSE". . . . . , "." 尔 ".." 总统府 TypeError (原始内容存档于2018-03-26). antes de qualquer requisição.

import { Bzapper, BzapperError, RateLimitError } from "@bzapper/client";

try {
  await bz.sendText({ to: "+5511999999999", body: "Olá!" });
} catch (err) {
  if (err instanceof RateLimitError) {
    console.error(`aguarde ${err.retryAfter}s`);
  } else if (err instanceof BzapperError) {
    console.error(err.code, err.status, err.requestId); // ex.: "not_connected" 409 "a1b2…"
    // Key de bZapper Connect: "connect_suspended" (402) e "connect_revoked" (401).
  } else {
    throw err;
  }
}

诺瓦斯帐篷: (原始内容存档于2012-10-12) (英语). SDK tenta de novo sozinho em wrongo de rede/time out, 429, (中文(简体) ), 502, (中文(简体) ), 503 电子 504 (nada mais——嗯) 500 尔 4xx (原始内容存档于2018-09-21). volta na hora, até maxRetries 韦兹(padrão 2),埃斯佩兰多o Retry-After (计到60分) min(8, 0,5 × 2^n) s + 25% de jitter.

同上: (POST/PUT/PATCH/DELETE) (波兰语) Idempotency-Key 盖拉多波尔 沙玛达e 重复 鼻涕虫 X-Request-Id)——一个API递出一个mesma 重新定义歌剧Idempotent-Replayed: true) (中文(简体) ). 龙卷风再现 接通地铁站:

await bz.sendText({ to: "+5511999999999", body: "Pedido #42 confirmado" }, { idempotencyKey: "pedido-42" });
await bz.createContact({ phone: "+5511999999999" }, { idempotencyKey: "crm-import-42" });

实例

韦雅 examples/quickstart.ts。 。 。 .

语法

麻省理工学院伯尼软件

版本与发布记录

@bzapper/client 共发布 27 个版本,首次发布于 2026/06/26;过去 12 个月发布了 27 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.16.0latest2026/10/2118 天前
0.15.22026/10/1920 天前
0.15.12026/10/1821 天前
0.15.02026/10/1821 天前
0.14.12026/10/1821 天前
0.14.02026/10/1821 天前

关于 @bzapper/client 的常见问题

@bzapper/client 是什么?

SDK official do bZapper para Node/TypeScript——网关德WhatsApp多租户.

如何安装 @bzapper/client?

在项目目录运行 npm install @bzapper/client,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bzapper,可以用 npx bzapper 直接运行。

@bzapper/client 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bzapper/client 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bzapper/client 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bzapper/client 可以商用吗?

@bzapper/client 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bzapper/client 的下载量有多少?

根据 npm 官方下载统计,@bzapper/client 在 2026/10/09 – 2026/11/07 这 30 天内被下载 2,376 次,日均约 79 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bzapper/client 还在维护吗?

最新版本 0.16.0 发布于 2026/10/21(18 天前);至今共发布 27 个版本,其中过去 12 个月发布了 27 个;首次发布于 2026/06/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。