> ## Documentation Index
> Fetch the complete documentation index at: https://zapo.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrando do Baileys

> Vindo do Baileys? Veja como o ciclo de conexão, layout de store, API de mensagens e nomes de eventos mapeiam para o client com coordinators do zapo.

O `zapo` é uma implementação **independente** do protocolo do WhatsApp Web — não é um fork do Baileys. Os conceitos se sobrepõem (pareamento companion, sessões Signal, um stream de eventos), mas a API é diferente e **não** é um substituto drop-in. Esta página mapeia os padrões que você já conhece.

<Note>
  O estado de auth, os formatos de mensagem e os nomes de método diferem. Planeje reescrever a configuração do socket e os handlers — mas o modelo mental (parear → escutar → enviar) se transfere diretamente.
</Note>

<h2 id="move-existing-sessions">
  Migre sessões existentes (sem reparear)
</h2>

Trocar de biblioteca não precisa significar reparear cada sessão. O [`wa-store-migrate`](https://www.npmjs.com/package/wa-store-migrate) converte o auth state do Baileys direto para o layout de store do zapo — mesma identidade de device, mesmas sessões Signal com cada peer, sem ler QR.

```bash theme={null}
npm i wa-store-migrate
```

A lib é **conversão pura**: não lê arquivos, não abre sockets, não fala com nenhum banco. Você entrega um `BaileysAuthSnapshot` (o mesmo formato `{ creds, keys }` que o `useMultiFileAuthState` mantém em memória), ela devolve um `ZapoStoreSnapshot`, e você escreve isso numa store nova do zapo.

```ts theme={null}
import { migrate, bufferJsonReviver, type BaileysAuthSnapshot } from 'wa-store-migrate'
import { createStore } from 'zapo-js'
import { createSqliteStore } from '@zapo-js/store-sqlite'
import { readFileSync, readdirSync } from 'node:fs'
import { join } from 'node:path'

// 1. Lê a pasta de auth do Baileys para um snapshot
function readBaileysMultiFile(dir: string): BaileysAuthSnapshot {
  const creds = JSON.parse(readFileSync(join(dir, 'creds.json'), 'utf-8'), bufferJsonReviver)
  const keys: Record<string, Record<string, unknown>> = {}
  for (const f of readdirSync(dir)) {
    if (f === 'creds.json') continue
    const m = /^([a-z-]+)-(.+)\.json$/i.exec(f)
    if (!m) continue
    const id = m[2]!.replace(/__/g, '/').replace(/-/g, ':')
    ;(keys[m[1]!] ??= {})[id] = JSON.parse(
      readFileSync(join(dir, f), 'utf-8'),
      bufferJsonReviver
    )
  }
  return { creds, keys: keys as never }
}

// 2. Converte para o snapshot do zapo (pura, sem I/O)
const baileys = readBaileysMultiFile('.auth/baileys')
const { data, losses } = migrate({ from: 'baileys', to: 'zapo', data: baileys })
for (const l of losses) {
  console.warn(`[${l.severity}] ${l.domain} x${l.count}: ${l.reason}`)
}

// 3. Escreve em uma store nova do zapo
const store = createStore({
  backends: { sqlite: createSqliteStore({ path: '.auth/zapo.sqlite', driver: 'auto' }) },
  providers: {
    auth: 'sqlite', signal: 'sqlite', preKey: 'sqlite', session: 'sqlite',
    identity: 'sqlite', senderKey: 'sqlite', appState: 'sqlite',
    privacyToken: 'sqlite',
    messages: 'none', threads: 'none', contacts: 'none'
  }
})
const s = store.session('default')

await s.auth.save(data.credentials)
for (const k of data.preKeys ?? []) await s.preKey.putPreKey(k)
if (data.identities?.length) {
  await s.identity.setRemoteIdentities(
    data.identities.map((i) => ({ address: i.address, identityKey: i.identityKey }))
  )
}
if (data.sessions?.length) {
  await s.session.setSessionsBatch(
    data.sessions.map((x) => ({ address: x.address, session: x.record as never }))
  )
}
for (const sk of data.senderKeys ?? []) await s.senderKey.upsertSenderKey(sk.record as never)
if (data.appState?.keys?.length) await s.appState.upsertSyncKeys(data.appState.keys)
if (data.privacyTokens?.length) await s.privacyToken.upsertBatch(data.privacyTokens)
```

Aponte um novo `WaClient({ store, sessionId: 'default' })` para a store resultante e chame `connect()` — ele sobe como o device existente, com toda sessão Signal com cada peer intacta.

<Note>
  **Auth state em banco, não em arquivos?** A forma `BaileysAuthSnapshot` é só `{ creds, keys: { 'pre-key': {...}, 'session': {...}, 'sender-key': {...}, ... } }`. Se sua storage do Baileys é custom (uma tabela MySQL por sessão, linhas no Postgres, hashes no Redis, …), aponte seu reader para essa fonte e produza o mesmo objeto — a chamada `migrate()` e o lado de escrita do zapo continuam idênticos. Para várias sessões, rode o pipeline uma vez por `sessionId`.
</Note>

<Note>
  **Expectativas de perda.** Migrar para o zapo **não tem drops** — todo domínio Signal é transferido. Espere algumas entradas com severidade `warn` em `losses`: chaves de mensagem HKDF puladas são descartadas (as sessões se autocorrigem na próxima mensagem), apenas o último estado de sender-key é mantido (o libsignal guarda até 5), e timestamps de privacy-token perdem precisão sub-segundo. Tabela completa em [`wa-store-migrate`](https://www.npmjs.com/package/wa-store-migrate#loss-matrix).
</Note>

<h2 id="creating-the-socket">
  Criando o socket
</h2>

O Baileys te dá um socket a partir de uma factory; o zapo te dá um `WaClient` mais uma [store](/pt-br/concepts/stores) explícita.

```ts theme={null}
// Baileys (típico)
const { state, saveCreds } = await useMultiFileAuthState('auth')
const sock = makeWASocket({ auth: state })
sock.ev.on('creds.update', saveCreds)

// zapo
import { createStore, WaClient } from 'zapo-js'
import { createSqliteStore } from '@zapo-js/store-sqlite'

const store = createStore({
  backends: { sqlite: createSqliteStore({ path: '.auth/state.sqlite', driver: 'auto' }) },
  providers: {
    auth: 'sqlite',
    signal: 'sqlite',
    preKey: 'sqlite',
    session: 'sqlite',
    identity: 'sqlite',
    senderKey: 'sqlite',
    appState: 'sqlite',
    privacyToken: 'sqlite',
    messages: 'none',
    threads: 'none',
    contacts: 'none'
  }
})
const client = new WaClient({ store, sessionId: 'default' }, logger)
await client.connect()
```

**Não há `creds.update` para salvar na mão** — a store persiste as credenciais automaticamente. Escolha qualquer backend (SQLite, Postgres, MySQL, Redis, Mongo) na [Instalação](/pt-br/installation#add-a-storage-backend).

<h2 id="events">
  Eventos
</h2>

O Baileys multiplexa tudo por `sock.ev`; o zapo expõe um evento tipado por área em `client`.

| Baileys                                                                      | zapo                                                                                     |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `sock.ev.on('connection.update', ({ connection, qr, lastDisconnect }) => …)` | `client.on('connection', …)` + `client.on('auth_qr', …)` + `client.on('auth_paired', …)` |
| `sock.ev.on('creds.update', saveCreds)`                                      | *(automático — a store persiste as creds)*                                               |
| `sock.ev.on('messages.upsert', ({ messages }) => …)`                         | `client.on('message', (event) => …)`                                                     |
| `sock.ev.on('messages.update', …)` (edições/reações/enquetes)                | `client.on('message_addon', …)` / `client.on('message_protocol', …)`                     |
| `sock.ev.on('message-receipt.update', …)`                                    | `client.on('receipt', …)`                                                                |
| `sock.ev.on('groups.update' / 'group-participants.update', …)`               | `client.on('group', …)`                                                                  |
| `sock.ev.on('presence.update', …)`                                           | `client.on('presence', …)` / `client.on('chatstate', …)`                                 |

O mapa completo está em [Eventos](/pt-br/concepts/events). Cada evento é fortemente tipado via `WaClientEventMap`.

<h2 id="sending-messages">
  Enviando mensagens
</h2>

```ts theme={null}
// Baileys
await sock.sendMessage(jid, { text: 'hello' })
await sock.sendMessage(jid, { image: { url: './pic.jpg' }, caption: 'hi' })

// zapo
await client.message.send(jid, 'hello') // atalho de string para texto
await client.message.send(jid, { type: 'image', media: './pic.jpg', mimetype: 'image/jpeg', caption: 'hi' })
```

O zapo usa uma [union de conteúdo](/pt-br/guides/sending-messages#the-content-union) discriminada (`{ type: 'image' | 'video' | 'audio' | 'document' | 'poll' | 'reaction' | … }`) em vez do objeto por-chave do Baileys. Citação/menções saem do objeto de conteúdo e vão para o argumento `options` (`{ quote, mentions }`).

<h2 id="api-mapping">
  Mapeamento de API
</h2>

| Baileys                                                       | zapo                                                                                                      |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `makeWASocket(...)`                                           | `new WaClient(options, logger)`                                                                           |
| `useMultiFileAuthState(...)`                                  | `createStore({ backends, providers })`                                                                    |
| `sock.sendMessage(jid, content)`                              | `client.message.send(jid, content, options?)`                                                             |
| `downloadMediaMessage(...)`                                   | `client.message.download(event)` / `downloadToFile(event, path)`                                          |
| `sock.groupMetadata(jid)`                                     | `client.group.queryGroupMetadata(jid)`                                                                    |
| `sock.groupCreate(...)` / `groupParticipantsUpdate(...)`      | `client.group.createGroup(...)` / `addParticipants` / `removeParticipants` / `promoteParticipants` / …    |
| `sock.updateProfilePicture(...)` / `updateProfileStatus(...)` | `client.profile.setProfilePicture(...)` / `setStatus(...)`                                                |
| `sock.updateBlockStatus(...)`                                 | `client.privacy.blockUser(jid)` / `unblockUser(jid)`                                                      |
| `sock.sendPresenceUpdate(...)`                                | `client.presence.send(...)` / `sendChatstate(...)`                                                        |
| `sock.logout()`                                               | `client.logout()`                                                                                         |
| `jidNormalizedUser(...)` / `jidDecode(...)`                   | `toUserJid(...)` / `splitJid(...)` / `parseJidFull(...)` ([helpers de JID](/pt-br/reference/jid-helpers)) |
| `proto.Message`                                               | `proto` (exportado da raiz do pacote)                                                                     |

<h2 id="key-differences">
  Diferenças-chave a ter em mente
</h2>

* **LID-first.** O zapo prefere a identidade [LID](/pt-br/concepts/identities), que preserva privacidade, ao JID de número de telefone. Responda para `event.key.remoteJid` e prefira LIDs quando os tiver.
* **API de coordinators.** As funcionalidades são agrupadas em getters (`client.message`, `client.group`, `client.privacy`, …) em vez de métodos planos no socket — veja [Arquitetura](/pt-br/concepts/architecture).
* **Stores plugáveis e tipadas.** Persistência é uma camada de primeira classe com backends oficiais, não uma pasta de JSON. Veja [Stores](/pt-br/concepts/stores).
* **Sem reconexão automática.** Como no Baileys, você conduz a reconexão — mas leia [Erros & desconexões](/pt-br/guides/errors) para os códigos de razão.
* **Sem registro de número.** O zapo conecta com credenciais já pareadas/registradas; ele não registra números novos.
