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

# API de baixo nível

> O escape hatch bruto — envie nodes de protocolo, faça IQs ao WhatsApp e registre handlers e filtros customizados quando a API de alto nível não basta.

`client.lowlevel` (`WaLowLevelCoordinator`) é o escape hatch bruto por baixo dos coordinators tipados. Use-o para enviar stanzas de protocolo que a API de alto nível não cobre, fazer IQs customizados ou interceptar stanzas recebidas.

<Warning>
  Isto é inseguro por design — você está construindo nodes de protocolo manualmente. Prefira os coordinators tipados quando existir um; recorra ao `lowlevel` apenas para superfícies de protocolo que o zapo ainda não encapsula.
</Warning>

<h2 id="binary-nodes">
  Binary nodes
</h2>

Tudo aqui fala `BinaryNode` — a representação do zapo para uma stanza de protocolo do WhatsApp:

```ts theme={null}
interface BinaryNode {
  tag: string
  attrs: Record<string, string>
  content?: Uint8Array | string | readonly BinaryNode[]
}
```

<h2 id="sending-a-node">
  Enviando um node
</h2>

`sendNode` escreve uma stanza bruta. Falhas que parecem um problema transitório de envio de recibo são bufferizadas na fila de recibos e logadas em vez de lançadas.

```ts theme={null}
await client.lowlevel.sendNode({
  tag: 'presence',
  attrs: { type: 'available' }
})
```

<h2 id="issuing-an-iq">
  Fazendo um IQ
</h2>

`query` envia uma stanza IQ e aguarda a resposta correspondente (dentro de `timeoutMs`). Lança um erro se o client não estiver conectado.

```ts theme={null}
const result = await client.lowlevel.query(
  {
    tag: 'iq',
    attrs: { to: '@s.whatsapp.net', type: 'get', xmlns: 'w:profile:picture' },
    content: [{ tag: 'picture', attrs: { type: 'image' } }]
  },
  30_000 // timeout opcional (ms); padrão WA_DEFAULTS.IQ_TIMEOUT_MS
)
// result é o BinaryNode de resposta
```

| Param                 | Tipo         | Notas                                               |
| --------------------- | ------------ | --------------------------------------------------- |
| `node`                | `BinaryNode` | O IQ a enviar.                                      |
| `timeoutMs`           | `number`     | Timeout da resposta. Padrão é o padrão do IQ (60s). |
| `options.useSystemId` | `boolean`    | Usa um id de stanza gerado pelo sistema.            |

<h2 id="intercepting-incoming-nodes">
  Interceptando nodes recebidos
</h2>

Registre um handler para nodes recebidos que correspondam a um `tag` (e um `subtype` opcional). O handler retorna um `Promise<boolean>` — retorne `true` quando você tiver tratado o node. `registerIncomingHandler` retorna uma função de `unregister`.

```ts theme={null}
const unregister = client.lowlevel.registerIncomingHandler({
  tag: 'notification',
  subtype: 'server_sync', // opcional
  prepend: false,          // roda antes dos handlers padrão quando true
  handler: async (node) => {
    console.log('got notification', node.attrs)
    return false // false = deixa outros handlers também processá-lo
  }
})

// depois
unregister()
```

`WaIncomingNodeHandlerRegistration`:

```ts theme={null}
interface WaIncomingNodeHandlerRegistration {
  tag: string
  subtype?: string
  handler: (node: BinaryNode) => Promise<boolean>
  prepend?: boolean
}
```

Você também pode remover um registro explicitamente:

```ts theme={null}
client.lowlevel.unregisterIncomingHandler(registration) // retorna boolean
```

<h2 id="filtering-inbound-stanzas">
  Filtrando stanzas recebidas
</h2>

Um **filtro de stanza** roda antes dos handlers tipados. Retorne `true` para **descartar** uma stanza por completo. O zapo ainda envia o ack apropriado para `message`/`receipt`/`notification`, então o servidor para de reentregá-la.

```ts theme={null}
const unregister = client.lowlevel.registerIncomingStanzaFilter((node) => {
  // Descarta tudo de um JID barulhento
  return node.attrs.from === 'spam@s.whatsapp.net'
})
```

<Note>
  Stanzas críticas de auth (`success` e `failure`) **ignoram os filtros** — você não consegue descartá-las, então o fluxo de conexão e pareamento sempre permanece intacto.
</Note>

| Método                         | Assinatura                                            |
| ------------------------------ | ----------------------------------------------------- |
| `sendNode`                     | `(node: BinaryNode) => Promise<void>`                 |
| `query`                        | `(node, timeoutMs?, options?) => Promise<BinaryNode>` |
| `registerIncomingHandler`      | `(registration) => () => void`                        |
| `unregisterIncomingHandler`    | `(registration) => boolean`                           |
| `registerIncomingStanzaFilter` | `(filter) => () => void`                              |

<Tip>
  Os nodes recebidos também são observáveis em modo somente leitura via os [eventos](/pt-br/concepts/events#debug-events) `debug_transport_node_in` / `debug_transport_node_out` — útil para descobrir formatos de stanza antes de escrever um handler.
</Tip>
