- API access: who may call GOWA. This is HTTP Basic Auth.
- WhatsApp pairing: linking a WhatsApp account to a device slot, as you would link WhatsApp Web.
API access: Basic Auth
Basic Auth is the only API authentication method. There are no API keys or JWTs for REST calls. Set one or moreuser:password pairs, comma-separated:
:) stops the server at startup. When APP_BASIC_AUTH is empty, the API is unauthenticated; only do this on a trusted network.
Send credentials on every request:
401 Unauthorized.
What is not behind Basic Auth
Everything else, including the dashboard at
/, /mcp and /ws, requires Basic Auth when it is configured.
WebSocket
Browsers cannot set headers on WebSocket connections, so/ws takes the credential in the query string:
authorization is base64(user:password). Use TLS (wss://), since the credential is visible in the URL and in proxy logs.
CORS
CORS allows any origin by default. Restrict it for browser clients withAPP_CORS_ALLOWED_ORIGINS=https://ui.example.com (comma-separated). Allowed request headers are Origin, Content-Type, Accept, Authorization and X-Device-Id.
WhatsApp pairing
Every WhatsApp account lives in a device slot. Create the slot first, then pair it. Routes under/devices/{device_id} take the device from the path. The equivalent /app/* routes use the X-Device-Id header.
QR code
qr_link and scan it from the phone: Settings → Linked devices → Link a device. Request a new code after qr_duration seconds. The dashboard at / shows the QR directly and updates live over the WebSocket.
Pairing code
results.pair_code, an 8-character code. On the phone, choose Link with phone number instead and type it in. phone must be the number of the account being linked, with country code.
Passkey
If the phone asks for passkey verification during QR login, GOWA stores a WebAuthn challenge and broadcastsPASSKEY_REQUEST on the WebSocket. The dashboard handles this flow. To drive it yourself:
GET /app/passkeyreturns the pending challenge.POST /app/passkey/responsesubmits the WebAuthn assertion (PublicKeyCredential.toJSON()format).- If the assertion arrives after the 5-minute handoff window, GOWA emits
PASSKEY_CONFIRMATIONwith a code. Confirm it matches the phone, then callPOST /app/passkey/confirm.
X-Device-Id on all three calls.
Check the result
results.is_connected and results.is_logged_in are both true once the account is linked. Sessions persist in storages/, so restarts do not require pairing again.
Logging out and removing
An account unlinked from the phone (Linked devices → Log out) is logged out the same way: the slot remains and needs pairing again.
Security checklist
- Always set
APP_BASIC_AUTHwith strong passwords on anything reachable from a network. - Terminate TLS at a reverse proxy; Basic Auth credentials are only base64-encoded.
- Restrict
APP_CORS_ALLOWED_ORIGINSwhen a browser UI calls the API. - Change
WHATSAPP_WEBHOOK_SECRETfrom its defaultsecret. - Treat
storages/as sensitive: it contains the WhatsApp session keys of every linked account.
