Skip to main content

Documentation Index

Fetch the complete documentation index at: https://zapo.to/llms.txt

Use this file to discover all available pages before exploring further.

WaClient does not reconnect automatically. This is a deliberate design choice: reconnection policy (backoff, max retries, alerting) belongs to your application. You listen for connection: close and decide what to do.

The connection event

connection is a discriminated union on status:
client.on('connection', (event) => {
  if (event.status === 'open') {
    console.log('connected', { isNewLogin: event.isNewLogin })
    return
  }

  // status === 'close'
  console.log('disconnected', {
    reason: event.reason,
    code: event.code,
    isLogout: event.isLogout
  })
})
On close:
  • isLogout: true — the device was unlinked (server-side logout). Do not reconnect; the credentials are gone and you must re-pair.
  • isLogout: false — a transient drop. Safe to reconnect with the stored credentials.

A reconnection loop with backoff

const MAX_ATTEMPTS = 10

async function connectWithRetry(client: WaClient) {
  let attempt = 0

  client.on('connection', (event) => {
    if (event.status === 'open') {
      attempt = 0 // reset backoff on a healthy connection
      return
    }
    if (event.isLogout) {
      console.error('logged out — re-pairing required')
      return
    }
    void reconnect()
  })

  async function reconnect() {
    if (attempt >= MAX_ATTEMPTS) {
      console.error('giving up after', attempt, 'attempts')
      return
    }
    const delayMs = Math.min(30_000, 1_000 * 2 ** attempt)
    attempt += 1
    console.log(`reconnecting in ${delayMs}ms (attempt ${attempt})`)
    await new Promise((r) => setTimeout(r, delayMs))
    try {
      await client.connect()
    } catch (err) {
      console.error('reconnect failed', err)
      void reconnect()
    }
  }

  await client.connect()
}

After reconnecting

Some state is connection-scoped and must be re-established after a successful reconnect:
  • Presence subscriptions — re-subscribe() to any contacts you were watching (Presence).
  • Newsletter live updates — re-subscribeLiveUpdates() if you rely on them.
Persisted state (credentials, Signal sessions, app-state) is restored from the store automatically — you do not re-pair on a normal reconnect.

Graceful shutdown

Call disconnect() for a clean shutdown that keeps credentials so you can resume later:
process.on('SIGINT', async () => {
  await client.disconnect()
  process.exit(0)
})
This flushes pending write-behind data and closes the socket without unlinking the device.
Last modified on May 27, 2026