{"_id":"@binary-black-holes/whatsapp-api","name":"@binary-black-holes/whatsapp-api","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@binary-black-holes/whatsapp-api","version":"0.1.0","description":"Type-safe, class-oriented WhatsApp personal-account integration module (QR / pairing-code login) built on the WhatsApp Web protocol — no WhatsApp Business API required.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"sideEffects":false,"publishConfig":{"access":"public"},"engines":{"node":">=18"},"scripts":{"build":"vite build","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"keywords":["whatsapp","whatsapp-api","whatsapp-web","baileys","qr-code","pairing-code","personal-account","messaging","typescript","multi-device"],"author":{"name":"hrustalq"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/Binary-Black-Holes/whatsapp-api.git"},"homepage":"https://github.com/Binary-Black-Holes/whatsapp-api#readme","bugs":{"url":"https://github.com/Binary-Black-Holes/whatsapp-api/issues"},"dependencies":{"baileys":"^6.7.23"},"devDependencies":{"@types/node":"^22.15.29","typescript":"^5.8.3","vite":"^6.3.5","vite-plugin-dts":"^4.5.4","vitest":"^3.2.1"},"gitHead":"6f5035a1b1baf546d31a065bbde55d6cacf3538f","_id":"@binary-black-holes/whatsapp-api@0.1.0","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-WEDvOGuSy7u7rhvYxm6tlyg+7a9mTg9IeNeT4UHx62/C/tm4eTyw6saGLqOfvDSinfArmowdYLhjd/kDNc4h5A==","shasum":"f3c353f2faa8e24d8e36a366ca7f0de9a0f9103f","tarball":"https://registry.npmjs.org/@binary-black-holes/whatsapp-api/-/whatsapp-api-0.1.0.tgz","fileCount":10,"unpackedSize":347584,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFURgUsO51hmSLX8V9JWFJSut/u1B+xIyXXXG9x5yEzpAiBcAApIb8sYPY+i/aE8K0YvmyzjXvXNb8CN9PM23UF7Fw=="}]},"_npmUser":{"name":"hrustalq","email":"n1k3f1t@gmail.com"},"directories":{},"maintainers":[{"name":"hrustalq","email":"n1k3f1t@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/whatsapp-api_0.1.0_1780587524449_0.4413327535279974"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-04T15:38:44.285Z","0.1.0":"2026-06-04T15:38:44.677Z","modified":"2026-06-04T15:38:44.873Z"},"maintainers":[{"name":"hrustalq","email":"n1k3f1t@gmail.com"}],"description":"Type-safe, class-oriented WhatsApp personal-account integration module (QR / pairing-code login) built on the WhatsApp Web protocol — no WhatsApp Business API required.","homepage":"https://github.com/Binary-Black-Holes/whatsapp-api#readme","keywords":["whatsapp","whatsapp-api","whatsapp-web","baileys","qr-code","pairing-code","personal-account","messaging","typescript","multi-device"],"repository":{"type":"git","url":"git+https://github.com/Binary-Black-Holes/whatsapp-api.git"},"author":{"name":"hrustalq"},"bugs":{"url":"https://github.com/Binary-Black-Holes/whatsapp-api/issues"},"license":"MIT","readme":"# @binary-black-holes/whatsapp-api\n\nType-safe, class-oriented WhatsApp integration module for **personal accounts**, linked over the WhatsApp Web protocol via **QR code** or **pairing code** — no WhatsApp Business API (WABA) and no Meta developer app required.\n\nBuilt with Vite library mode, rich TypeScript interfaces, declarative JSDoc, and semantic versioning. Powered by [Baileys](https://github.com/WhiskeySockets/Baileys), a pure-WebSocket WhatsApp Web client (no browser/Puppeteer).\n\n## Features\n\n- **Personal account, no WABA** — link a normal WhatsApp account by scanning a QR code or entering a pairing code, exactly like WhatsApp Web / Linked Devices.\n- **Class-oriented architecture** — `WhatsAppClient` exposes focused resource modules (`messages`, `chats`, `contacts`, `groups`, `presence`, `profile`).\n- **Rich TypeScript interfaces** — strongly typed configuration, events, message payloads, and JID utilities.\n- **Strongly-typed event bus** — `on`/`once`/`off`/`waitFor` with a compile-time-checked event map.\n- **Resilient connection** — automatic reconnection with exponential backoff and clear lifecycle events.\n- **Pluggable sessions** — filesystem and in-memory auth-state providers, or supply your own (database, Redis, …).\n- **Dual package exports** — ESM (`import`) and CommonJS (`require`) with bundled declaration files.\n- **Normalized messages** — an ergonomic `IncomingMessage` view, with the raw protocol message always available.\n\n## Requirements\n\n- Node.js 18 or newer\n- A personal WhatsApp account on a phone (to link the device)\n\n> [!IMPORTANT]\n> This SDK automates a **personal** WhatsApp account through the unofficial WhatsApp Web protocol. It is **not** the WhatsApp Business Platform / Cloud API. Automating personal accounts can violate WhatsApp's Terms of Service and may lead to your number being banned. Use responsibly, at your own risk, and never for spam.\n\n## Installation\n\n```bash\nnpm install @binary-black-holes/whatsapp-api\n```\n\n## Quick Start: QR code login\n\n```ts\nimport {\n  WhatsAppClient,\n  createMultiFileAuthState,\n} from \"@binary-black-holes/whatsapp-api\";\n\n// Persist the session so you only scan once.\nconst auth = await createMultiFileAuthState(\"./auth\");\n\nconst client = new WhatsAppClient({ auth });\n\nclient.on(\"qr\", (qr) => {\n  // `qr` is the raw QR string. Render it however you like — see \"Rendering the QR code\" below.\n  console.log(\"Scan this QR code with WhatsApp → Linked Devices:\", qr);\n});\n\nclient.on(\"ready\", (me) => console.log(\"Connected as\", me.id));\n\nclient.on(\"message\", async (msg) => {\n  if (msg.fromMe) return;\n  console.log(`${msg.author}: ${msg.text}`);\n  if (msg.text?.toLowerCase() === \"ping\") {\n    await client.messages.reply(msg, \"pong 🏓\");\n  }\n});\n\nawait client.connect();\nawait client.messages.sendText(\"15551234567\", \"Hello from the SDK!\");\n```\n\n## Quick Start: Pairing-code login\n\nPrefer entering an 8-character code on your phone instead of scanning a QR? Use `loginMethod: \"pairing-code\"` and supply your account's phone number (digits only, international format).\n\n```ts\nimport {\n  WhatsAppClient,\n  createMultiFileAuthState,\n} from \"@binary-black-holes/whatsapp-api\";\n\nconst client = new WhatsAppClient({\n  auth: await createMultiFileAuthState(\"./auth\"),\n  loginMethod: \"pairing-code\",\n  phoneNumber: \"15551234567\",\n});\n\nclient.on(\"pairing-code\", (code) => {\n  // On your phone: WhatsApp → Linked Devices → Link a device → Link with phone number\n  console.log(\"Enter this pairing code on your phone:\", code);\n});\n\nclient.on(\"ready\", () => console.log(\"Linked!\"));\n\nawait client.connect();\n```\n\n## Rendering the QR code\n\nThe SDK emits the raw QR string and stays dependency-free about how you display it. A common choice for terminals:\n\n```bash\nnpm install qrcode-terminal\n```\n\n```ts\nimport qrcode from \"qrcode-terminal\";\n\nclient.on(\"qr\", (qr) => qrcode.generate(qr, { small: true }));\n```\n\nFor web apps, pass the string to any QR component (e.g. `qrcode.toDataURL(qr)`).\n\n## Architecture\n\n```text\nWhatsAppClient\n├── messages   → send/reply/react/edit/delete/forward, read receipts, media download\n├── chats      → read state, mute, archive, pin, delete, disappearing messages\n├── contacts   → registration check, profile pictures, status, block/unblock\n├── groups     → create, membership, admin roles, metadata, invite links\n├── presence   → online/offline, typing, recording, presence subscription\n└── profile    → own display name, status, and profile picture\n\nConnection      → owns the Baileys socket, login, reconnection, event bridging\nEventBus        → strongly-typed on/once/off/waitFor over WhatsAppEventMap\nAuthStateProvider → pluggable session storage (multi-file, in-memory, custom)\n```\n\nEach resource extends `BaseResource` and borrows the **single** shared connection on demand, so a transparent reconnect re-points every resource at the new socket automatically.\n\n## Events\n\nSubscribe with the strongly-typed `on` (it returns an unsubscribe function):\n\n```ts\nconst off = client.on(\"message\", (msg) => console.log(msg.text));\noff(); // stop listening\n```\n\n| Event                       | Payload                    | Description                                         |\n| --------------------------- | -------------------------- | --------------------------------------------------- |\n| `qr`                        | `string`                   | A QR string is ready to be rendered/scanned.        |\n| `pairing-code`              | `string`                   | A pairing code was issued for phone-number linking. |\n| `connecting`                | —                          | The transport began connecting.                     |\n| `ready`                     | `ConnectedAccount`         | Connection open and authenticated.                  |\n| `disconnected`              | `DisconnectedEvent`        | Connection closed (inspect `reconnecting`).         |\n| `logged-out`                | —                          | Session invalidated; re-authentication required.    |\n| `connection.update`         | `ConnectionStatus`         | High-level status changed.                          |\n| `message`                   | `IncomingMessage`          | New inbound (or self-echo) message.                 |\n| `message.update`            | `WAMessageUpdate[]`        | Delivery/read/edit/revoke updates.                  |\n| `message.reaction`          | `ReactionEvent`            | A reaction was added or removed.                    |\n| `contacts.update`           | `Partial<Contact>[]`       | Contacts added/changed.                             |\n| `chats.update`              | `Partial<Chat>[]`          | Chats added/changed.                                |\n| `groups.update`             | `Partial<GroupMetadata>[]` | Group metadata changed.                             |\n| `group.participants.update` | `GroupParticipantsEvent`   | Members joined/left/role-changed.                   |\n| `presence.update`           | `PresenceEvent`            | A contact's presence changed.                       |\n| `error`                     | `Error`                    | An SDK or listener error occurred.                  |\n\n## API overview\n\n### Messages\n\n```ts\nawait client.messages.sendText(\"15551234567\", \"Hello\", {\n  mentions: [\"15559998888\"],\n});\nawait client.messages.sendImage(\n  \"15551234567\",\n  { url: \"./photo.jpg\" },\n  {\n    caption: \"Sunset 🌅\",\n  },\n);\nawait client.messages.sendVideo(\"15551234567\", videoBuffer, {\n  caption: \"Clip\",\n});\nawait client.messages.sendAudio(\"15551234567\", voiceBuffer, {\n  voiceNote: true,\n});\nawait client.messages.sendDocument(\"15551234567\", pdfBuffer, {\n  fileName: \"invoice.pdf\",\n  mimetype: \"application/pdf\",\n});\nawait client.messages.sendLocation(\"15551234567\", {\n  latitude: 37.422,\n  longitude: -122.084,\n  name: \"Googleplex\",\n});\n\n// React, reply, edit, delete, forward\nawait client.messages.react(message, \"🔥\");\nawait client.messages.reply(message, \"Got it!\");\nconst sent = await client.messages.sendText(\"15551234567\", \"tpyo\");\nawait client.messages.edit(sent!, \"typo, fixed\");\nawait client.messages.delete(message, /* forEveryone */ true);\nawait client.messages.forward(\"15553334444\", message);\n\n// Read receipts and media\nawait client.messages.markAsRead(message);\nconst bytes = await client.messages.downloadMedia(message); // Buffer\n```\n\n### Contacts\n\n```ts\nconst [result] = await client.contacts.isRegistered(\"15551234567\");\nif (result.exists) {\n  await client.messages.sendText(result.jid!, \"You're on WhatsApp!\");\n}\n\nawait client.contacts.getProfilePictureUrl(\"15551234567\");\nawait client.contacts.getStatus(\"15551234567\");\nawait client.contacts.block(\"15551234567\");\nawait client.contacts.unblock(\"15551234567\");\nawait client.contacts.listBlocked();\n```\n\n### Groups\n\n```ts\nconst group = await client.groups.create(\"Project X\", [\n  \"15551112222\",\n  \"15553334444\",\n]);\n\nawait client.groups.setSubject(group.id, \"Project X — Q3\");\nawait client.groups.setDescription(group.id, \"Where the magic happens\");\nawait client.groups.addParticipants(group.id, [\"15555556666\"]);\nawait client.groups.promote(group.id, [\"15551112222\"]);\nawait client.groups.setMessagesAdminsOnly(group.id, true);\n\nconst link = await client.groups.getInviteLink(group.id);\nawait client.groups.join(\"https://chat.whatsapp.com/XXXXXXXXXXXX\");\nawait client.groups.leave(group.id);\n\nconst all = await client.groups.listJoined();\n```\n\n### Presence\n\n```ts\nawait client.presence.subscribe(\"15551234567\");\nawait client.presence.startTyping(\"15551234567@s.whatsapp.net\");\nawait client.presence.stopTyping(\"15551234567@s.whatsapp.net\");\nawait client.presence.setOnline();\n```\n\n### Chats\n\n```ts\nawait client.chats.markRead(\"15551234567\");\nawait client.chats.archive(\"15551234567\", true);\nawait client.chats.pin(\"15551234567\", true);\nawait client.chats.mute(\"15551234567\", Date.now() + 8 * 60 * 60 * 1000);\nawait client.chats.setDisappearing(\"15551234567\", 7 * 24 * 60 * 60);\n```\n\n### Profile\n\n```ts\nawait client.profile.setName(\"Ada Lovelace\");\nawait client.profile.setStatus(\"Computing ✨\");\nawait client.profile.setPicture({ url: \"./avatar.png\" });\n```\n\n## Sessions & authentication state\n\nA session is the cryptographic material that keeps your device linked. Persist it so users only authenticate once.\n\n```ts\nimport {\n  createMultiFileAuthState, // filesystem (recommended)\n  createInMemoryAuthState, // ephemeral (tests/scripts)\n} from \"@binary-black-holes/whatsapp-api\";\n\nconst auth = await createMultiFileAuthState(\"./auth\");\n```\n\n> [!WARNING]\n> Session files are equivalent to a logged-in device. **Never** commit them or expose them publicly. The package's `.gitignore` already excludes `auth_info/`-style folders — keep yours ignored too.\n\n### Custom session storage\n\nImplement `AuthStateProvider` to store sessions anywhere (Postgres, Redis, S3, …):\n\n```ts\nimport type { AuthStateProvider } from \"@binary-black-holes/whatsapp-api\";\n\nconst provider: AuthStateProvider = {\n  state, // an AuthenticationState you load/build\n  saveCreds: async () => {\n    /* persist state.creds */\n  },\n  close: async () => {\n    /* optional teardown */\n  },\n};\n```\n\n## JIDs (addresses)\n\nWhatsApp addresses chats by **JID** (e.g. `15551234567@s.whatsapp.net`, `120363…@g.us`). The send helpers accept either a JID or a bare phone number — numbers are normalized automatically.\n\n```ts\nimport {\n  normalizeToJid,\n  classifyJid,\n  isGroupJid,\n  phoneNumberFromJid,\n} from \"@binary-black-holes/whatsapp-api\";\n\nnormalizeToJid(\"+1 (555) 123-4567\"); // \"15551234567@s.whatsapp.net\"\nclassifyJid(\"120363@g.us\"); // \"group\"\nphoneNumberFromJid(\"15551234567@s.whatsapp.net\"); // \"15551234567\"\n```\n\n## Error handling\n\nAll failures extend `WhatsAppError`:\n\n| Class                 | Typical cause                                                     |\n| --------------------- | ----------------------------------------------------------------- |\n| `NotConnectedError`   | An operation was attempted before `connect()` / after disconnect. |\n| `AuthenticationError` | The session was logged out or revoked.                            |\n| `ValidationError`     | Invalid SDK input before a request.                               |\n| `NotFoundError`       | A target JID is not a registered WhatsApp user.                   |\n| `TimeoutError`        | An operation exceeded its timeout.                                |\n| `TransportError`      | An error surfaced by the WhatsApp Web transport.                  |\n\n```ts\nimport {\n  NotConnectedError,\n  AuthenticationError,\n} from \"@binary-black-holes/whatsapp-api\";\n\ntry {\n  await client.messages.sendText(\"15551234567\", \"hi\");\n} catch (error) {\n  if (error instanceof NotConnectedError) {\n    await client.connect();\n  } else if (error instanceof AuthenticationError) {\n    // re-scan QR / re-pair\n  }\n}\n```\n\n## Configuration\n\n```ts\nconst client = new WhatsAppClient({\n  auth, // AuthStateProvider (defaults to in-memory)\n  loginMethod: \"qr\", // or \"pairing-code\"\n  phoneNumber: \"15551234567\", // required for \"pairing-code\"\n  browser: [\"My App\", \"Chrome\", \"1.0.0\"], // shown under Linked Devices\n  markOnlineOnConnect: true,\n  syncFullHistory: false,\n  defaultQueryTimeoutMs: 60_000,\n  reconnect: {\n    enabled: true,\n    maxRetries: 10,\n    baseDelayMs: 2_000,\n    maxDelayMs: 30_000,\n  },\n  logger: console, // any { debug, info, warn, error }\n});\n```\n\n## Lifecycle\n\n```ts\nawait client.connect(); // open + authenticate\nclient.status; // \"idle\" | \"connecting\" | \"connected\" | \"reconnecting\" | \"logged-out\" | \"closed\"\nclient.me; // ConnectedAccount | undefined\n\nawait client.destroy(); // close without invalidating the session (reusable later)\nawait client.logout(); // invalidate the session (re-auth required next time)\n```\n\n## Development\n\n```bash\nnpm install\nnpm run typecheck\nnpm test\nnpm run build\n```\n\nOutputs:\n\n- `dist/index.js` — ESM entry\n- `dist/index.cjs` — CommonJS entry\n- `dist/index.d.ts` — bundled type declarations\n\n## Semantic versioning\n\nThis package follows [Semantic Versioning](https://semver.org/):\n\n- **MAJOR** — incompatible public API changes\n- **MINOR** — backward-compatible functionality\n- **PATCH** — backward-compatible bug fixes\n\nSee [CHANGELOG.md](./CHANGELOG.md) for release history.\n\n## Notes & limitations\n\n- Built on the unofficial WhatsApp Web protocol via Baileys; WhatsApp may change it at any time.\n- Chat-state operations (mute/archive/pin) depend on history sync, which streams in shortly after `ready`.\n- Media helpers may require optional native peers (e.g. `sharp`, `jimp`, `link-preview-js`) for certain transforms; install them only if you hit a related runtime hint.\n- This is **not** affiliated with or endorsed by WhatsApp or Meta.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-bc620099bc1a0ec5a2987d67decdf263"}