Skip to main content
Baileys communicates everything that happens on your WhatsApp connection through a typed event emitter available as sock.ev. Every event name maps to a specific payload type defined in BaileysEventMap, so your IDE can autocomplete both the event name and the shape of the data you receive.

Listening to events

Use sock.ev.on to subscribe and sock.ev.off to unsubscribe. The type parameter is inferred automatically from the event name.
The BaileysEventEmitter interface is defined as:

The ev.process() pattern

For most applications you should use sock.ev.process instead of individual sock.ev.on calls. The process callback receives a map of all events that fired in a single tick, letting you handle them together and avoid partial state updates.
ev.process batches events fired within the same async tick. This means if a history sync delivers 500 messages and 200 chat updates at once, your handler receives them all together rather than firing 700 separate callbacks.

Event reference

connection.update

Fires whenever the WebSocket state changes. The payload is Partial<ConnectionState>:
Use this event to reconnect after a close, render the QR code, and detect new logins.

creds.update

Fires whenever your authentication credentials change. You must persist these immediately or you will lose your session.

messages.upsert

The primary event for incoming and outgoing messages. The payload shape is:
  • type: 'notify' — messages received while the socket was online (real-time delivery). These should trigger user notifications.
  • type: 'append' — messages loaded from history or backfill. Do not re-notify for these.
Always iterate over messages with a for...of loop. The array may contain more than one message per event, especially during reconnection.

messages.update

Fires when the status of an existing message changes — delivery receipts, read receipts, reactions, or poll vote updates.

messaging-history.set

Fires when a history sync batch arrives from your phone. This is how Baileys delivers past chats, contacts, and messages on first connection (and on demand when you call sock.fetchMessageHistory).
History is delivered in reverse chronological chunks. The isLatest flag on the final chunk tells you the sync is complete. The progress field (0–100) tracks how far along the sync is.

chats.upsert / chats.update / chats.delete

Lifecycle events for chats (conversations).

contacts.upsert / contacts.update

Fires when contacts are created or their metadata (name, profile picture URL) changes.

groups.upsert / groups.update / group-participants.update

Group lifecycle events.

presence.update

Fires when the typing or online status of a contact changes in a chat you have subscribed to with sock.presenceSubscribe(jid).

call

Fires for incoming and outgoing call events. The payload is WACallEvent[].

Event sequence on first connection

Understanding the order events fire helps you sequence your startup logic correctly.
1

connection.update — connecting

The socket begins the WebSocket handshake. connection is 'connecting'.
2

connection.update — QR or open

If credentials are missing, qr is populated for scanning. Once credentials are confirmed, connection becomes 'open'.
3

messaging-history.set (one or more batches)

If syncFullHistory is true, the phone sends past messages and chats in chunks. Each chunk fires this event. Persist to your database here.
4

connection.update — receivedPendingNotifications

After history sync completes, receivedPendingNotifications is set to true. This signals that all offline messages have been delivered and the socket is fully caught up.
5

Real-time events

From this point forward, messages.upsert, chats.update, presence.update, and all other events fire in real time as activity occurs.

Full example with ev.process