WhatIsUp.dev
Get started

Instances

An instance is one logical WhatsApp connection β€” one phone number, one paired session, one set of webhooks. Every message you send and every message you receive belongs to exactly one instance.

Lifecycle

Instances move through a finite state machine. The status field on the API mirrors the FSM 1:1:

            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   create β†’ β”‚     pending      β”‚ β€” instance row exists, no socket yet
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚  start session
                     β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚   qr_required    β”‚ β€” Baileys produced a QR, waiting for scan
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              scan   β”‚  timeout / cancel
                     β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚    connected     β”‚ β€” session live; can send + receive
            β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       network  β”‚     β”‚  user logout / sessionInvalidated
       blip     β”‚     β–Ό
            β”Œβ”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚   reconnecting   β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ recovery fails
                     β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚   disconnected   β”‚ β€” needs human to re-pair
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Notable transitions:

  • pending β†’ qr_required happens lazily on the first GET /v1/instances/:id/qr call. We don't burn a Baileys socket until you ask.
  • connected β†’ reconnecting is automatic on transient socket errors. The manager backs off with jitter and tries to resume the session from the persisted auth-state.
  • reconnecting β†’ disconnected happens when WhatsApp explicitly invalidates the session (sessionInvalidated). This means the user logged out from another device, banned the number, or hit a Baileys protocol mismatch. The auth-state is wiped; you'll need a fresh QR scan to reconnect.

The event stream and the instance.connected / instance.disconnected webhooks emit on every transition.

Listing and creation

You can have multiple instances per customer β€” one per number you want to use. There's no hard cap in the schema, but each connected instance holds a WebSocket and ~20 MB of memory in the Node process, so a single backend can comfortably hold ~50 active sessions.

Create an instance

curl -sX POST "$WHATISUP_API/v1/instances" \
  -H "Authorization: Bearer $WHATISUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"support-line"}'

List your instances

curl -s "$WHATISUP_API/v1/instances" \
  -H "Authorization: Bearer $WHATISUP_API_KEY"

Names are free-form; they're for humans, not routing.

Soft-delete

DELETE /v1/instances/:id doesn't drop the row β€” it stops the Baileys socket, wipes the on-disk auth-state, and sets deleted_at. Webhook deliveries and audit events that reference this instance stay queryable. If you re-create with the same name, you get a brand-new id.

Deleting an instance does not unlink the device on the WhatsApp side. The user remains paired until either the gateway emits a logout (which Baileys does automatically on auth-state wipe) or the user manually unlinks from the phone's Linked devices screen.

Heartbeat and reconciliation

The session manager runs a heartbeat tick (default 30s, configurable via HEARTBEAT_INTERVAL_MS) that:

  1. Bumps last_seen_at for every connected session β€” observability for soft-stuck sockets.
  2. Reconciles each in-memory session against the DB row. If the row is gone (hard delete or out-of-band SQL), the stale socket is stopped.

This is invisible to your application; you'll just notice that orphan sessions don't pile up.