Guides

Connecting a Device

Create a WUTS device, pair it by QR code or pair code, check its state, reconnect after a restart, and disconnect.

A device is a WhatsApp session managed by WUTS on behalf of your tenant. Before you can send or receive messages, you create a device record and pair it with a real WhatsApp account on a phone. This guide walks the full lifecycle.

Every request below is authenticated with your tenant token (Authorization: Bearer <token>). See Authentication for how to obtain it.

Device lifecycle at a glance

  1. Create a logical device — POST /devices.
  2. Pair it with WhatsApp — scan a QR (GET /qr), type a pair code (POST /paircode), or approve with a passkey (POST /passkey/response + POST /passkey/confirmation).
  3. Verify the session is live — GET /status.
  4. Reconnect later without re-pairing — POST /connect.
  5. Disconnect when you are done — POST /disconnect.

1. Create a device

POST /devices registers a new device for your tenant. The only field is a human-readable name. Creating a device only registers it — the session is not active until you complete pairing, so a fresh device is returned with connected: false.

curl -X POST "https://api.wuts.com.br/devices" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"WhatsApp Loja 01"}'
{
  "success": true,
  "device": {
    "id": "4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3",
    "name": "WhatsApp Loja 01",
    "connected": false,
    "created_at": "2025-03-02T14:15:17Z"
  }
}

Keep the returned id. The pairing, status, connect, and QR-status endpoints accept it as a device_id so you can target a specific device when your tenant has more than one.

2a. Pair by scanning a QR code

GET /qr generates a QR for the device, starts connecting in the background, and caches the payload for polling. It returns both a raw qr_code string and a base64 PNG image.

curl -X GET "https://api.wuts.com.br/qr" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": {
    "qr_code": "AAEBEiFo...",
    "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
  }
}

Render image and have the user scan it from their phone: WhatsApp → Settings → Linked devices → Link a device.

QR codes expire quickly (around 30 seconds). Render and scan immediately. If GET /qr returns a 504/timeout, simply request a fresh one and retry.

Poll the QR status

While the user scans, poll GET /qr/status to learn whether the cached QR is still valid. Pass the device_id of the device you are pairing.

curl -X GET "https://api.wuts.com.br/qr/status?device_id=4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": {
    "exists": true,
    "generated_at": "2025-03-02T13:55:12Z"
  }
}

When the QR has expired (or was never generated for that device_id), exists is false. After a successful scan, confirm pairing with GET /status. If the device is already paired and connected, GET /qr returns 400 already connecteddisconnect first to generate a new QR.

2b. Pair with a pair code

If scanning is impractical, use POST /paircode. WUTS asks WhatsApp for a code the user types on their phone under Settings → Linked devices → Link a device → Link with phone number instead. Send the WhatsApp number in international format (digits only; +, spaces, and punctuation are stripped automatically).

curl -X POST "https://api.wuts.com.br/paircode" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"phone":"5511999999999","device_id":"4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3"}'
{
  "success": true,
  "data": {
    "code": "ABCD-1234",
    "phone": "5511999999999",
    "expires_in": 300,
    "cached": true,
    "instructions": "Digite este código no WhatsApp: ..."
  }
}

The code is valid for expires_in seconds (5 minutes), and instructions carries a ready-to-show hint. A repeated call returns the same code with cached: true while it is still alive. To force a new code, send the X-WUTS-Refresh-Paircode: true header or add ?refresh=true.

ErrorMeaning
400 phone number is requiredThe phone field was missing.
400 invalid phone number formatPhone must be 10–15 digits, country code included.
400 already connectedDevice is already paired and live — disconnect first.
device already pairedStored credentials already exist; use POST /connect instead.

QR vs pair code

Both methods produce the same paired session — pick whichever fits your UX:

  • QR code — fastest when the user has the screen and phone side by side. Short ~30s window, so render it on demand.
  • Pair code — better for remote setups, phone screenshots, or when the customer cannot scan. Longer 5-minute window and copy-pasteable. Show the QR first, the pair code as fallback.

2c. Pair with a passkey (WebAuthn)

WhatsApp is rolling out passkey-based device linking: instead of scanning a QR, the account owner approves the link with the passkey stored in their password manager. When WhatsApp offers this path during pairing, WUTS relays the whole handshake through webhooks and two endpoints — your app only needs to run the WebAuthn ceremony in the account owner's browser.

The flow, end to end:

  1. Start pairing normally (GET /qr or POST /paircode) with your webhook configured.

  2. WhatsApp offers the passkey path → your webhook receives pair.passkey_request:

    {
      "public_key": {
        "challenge": "3q2-7w",
        "timeout": 60000,
        "rpId": "web.whatsapp.com",
        "allowCredentials": [{ "id": "nGViU9nFzQmp7VpAkFyxWQ", "type": "public-key" }],
        "userVerification": "required"
      }
    }
  3. Run the ceremony in the account owner's browser — the payload is already in the shape navigator.credentials.get expects (binary fields are base64url without padding; decode them into ArrayBuffers):

    const credential = await navigator.credentials.get({ publicKey: decodedPublicKey });
  4. Send the credential back with POST /passkey/response:

    curl -X POST "https://api.wuts.com.br/passkey/response" \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{"device_id":"4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3","response":{...credential JSON...}}'
  5. Your webhook receives pair.passkey_confirmation with a short verification code:

    { "code": "123-456", "skip_handoff_ux": false }

    Unless skip_handoff_ux is true, show the code to the user and ask them to confirm it matches the one on their phone.

  6. Finish the handshake with POST /passkey/confirmation (body optional; pass device_id to target a specific device). Pairing completes and pair.success fires as usual.

If anything goes wrong while WhatsApp processes the passkey notifications, the webhook pair.passkey_error delivers { "error": "...", "continuation": true|false }continuation: true means the failure happened after your credential was accepted, at the confirmation stage.

ErrorMeaning
400 response.* is requiredThe WebAuthn credential is incomplete — send the object exactly as returned by navigator.credentials.get.
400 device already pairedStored credentials already exist; use POST /connect instead.
409 no active pairing sessionThe pairing socket is not connected — restart pairing via GET /qr or POST /paircode and wait for pair.passkey_request.

Existing webhook subscriptions are backfilled with the three pair.passkey_* events automatically; new webhooks include them by default.

3. Check the connection state

GET /status reports the live state of the device. Pass device_id to target a specific one.

curl -X GET "https://api.wuts.com.br/status?device_id=4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3" \
  -H "Authorization: Bearer <token>"
{
  "success": true,
  "data": {
    "connected": true,
    "status": "connected",
    "device_id": "4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3",
    "jid": "5511999999999@s.whatsapp.net",
    "phone_number": "5511999999999",
    "is_logged_in": true
  }
}

status is one of: connected (online and logged in), connecting (socket up, login still settling), or disconnected. Treat connected: true as the signal that the device is ready to send and receive messages.

4. Reconnect after a restart

After a gateway or host restart an in-memory session can drop while the stored WhatsApp credentials remain valid. POST /connect reloads those credentials and reconnects without a new QR scan — the recommended self-heal primitive (GET /qr would refuse an already-paired device).

curl -X POST "https://api.wuts.com.br/connect?device_id=4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3" \
  -H "Authorization: Bearer <token>"
ResponseMeaningWhat to do
200 status: connected, needs_qr: falseReconnected (or already live).Nothing — device is ready.
200 status: logged_out, needs_qr: trueCredentials are gone.Re-pair via GET /qr or POST /paircode.
503Transient failure.Retry with backoff.
404Device not found or not owned by this tenant.Verify the device_id.

5. Disconnect

POST /disconnect tears down the live session, removes the in-memory client, and fires the device.disconnected webhook. No body is required.

curl -X POST "https://api.wuts.com.br/disconnect" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json"
{
  "success": true,
  "data": {
    "message": "device disconnected successfully",
    "device_id": "4d0add2c-fdd1-4ee0-9d33-6a8898ce30e3"
  }
}

A 404 device not found or not connected means the session is already down. After disconnecting you can pair again with a fresh GET /qr or POST /paircode, or — if the stored credentials survive — bring it back with POST /connect. The device record itself is not deleted; it stays listed under GET /devices and can be reconnected later.

Multiple devices per tenant

A single tenant can run several devices at once (for example, one WhatsApp number per store). Create each with POST /devices, then pass its device_id to the pairing, status, connect, and disconnect calls to act on exactly the device you mean. GET /devices lists every device your tenant owns with each one's connected flag and whatsapp_jid.

Devices are isolated per tenant. A token can only operate on devices owned by its own tenant; targeting another tenant's device_id is rejected. See Conventions for shared request and response rules.

Next steps

On this page