# WAHA 2026.7.2 NOWEB Engine Integration: Webhook, QR‑Code Onboarding, and Session Persistence in DeskcommCRM

> Explore WAHA 2026.7.2 NOWEB engine integration with DeskcommCRM. Discover webhook, QR-code onboarding, and session persistence for seamless connectivity.

- Repository: [Rafael Melgaço/DeskcommCRM](https://github.com/melgarafael/DeskcommCRM)
- Tags: how-to-guide
- Published: 2026-09-13

---

**The WAHA 2026.7.2 NOWEB engine integrates with DeskcommCRM through a public webhook endpoint that receives QR‑code scan events, maps them to persisted database sessions with status tracking, and enables real‑time UI polling to maintain connections across browser reloads.**

DeskcommCRM implements a complete lifecycle management system for the WAHA 2026.7.2 NOWEB engine that couples Docker‑hosted WAHA containers with a Next.js backend and Postgres persistence. The integration relies on capability detection, Zod‑validated webhook payloads, and a state machine that transitions sessions from `STARTING` through `SCAN_QR_CODE` to `WORKING`.

## Engine Discovery and Capability Detection

Before establishing any connection, the platform queries the WAHA server to determine supported features. The `describeWahaServer` function in **[`lib/channels/waha-server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/waha-server.ts)** accepts version, engine, and tier parameters to return capability flags.

For version **2026.7.2** with engine **NOWEB**, this function returns `multipleSessions: "supported"` at line 26. This flag is critical because it instructs the orchestration layer that the engine can manage multiple concurrent QR‑code sessions simultaneously, enabling multi‑tenant deployments where different organizations maintain separate WhatsApp connections.

```typescript
// lib/channels/waha-server.ts
import { describeWahaServer } from "@/lib/channels/waha-server";

const caps = describeWahaServer({ 
  version: "2026.7.2", 
  engine: "NOWEB", 
  tier: "standard" 
});
// caps.multipleSessions === "supported"

```

## Webhook Configuration and Endpoint Setup

WAHA communicates with DeskcommCRM via HTTP POST requests to a dedicated webhook endpoint. According to the runbook in **[`docs/runbooks/waha-hostgator.md`](https://github.com/melgarafael/DeskcommCRM/blob/main/docs/runbooks/waha-hostgator.md)**, the Docker container sets the environment variable `WHATSAPP_HOOK_URL` to `https://<your-domain>/api/webhooks/waha` (lines 41‑43).

The WAHA engine emits JSON events whenever the session state changes—particularly when generating a new QR code for authentication. Nginx reverse‑proxies these requests to the Next.js application, where the route handler in **[`app/api/v1/webhooks/waha/route.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/api/v1/webhooks/waha/route.ts)** processes the payload using generic `ok()` and `fail()` wrappers from **[`lib/api/wrappers.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/api/wrappers.ts)**.

## Validating and Mapping Webhook Payloads

Incoming webhook data undergoes strict validation before database insertion. The specification in **[`docs/specs/03-spec-whatsapp-waha.md`](https://github.com/melgarafael/DeskcommCRM/blob/main/docs/specs/03-spec-whatsapp-waha.md)** defines a Zod schema that expects fields including `name`, `status`, and `engine`.

When WAHA sends a `SCAN_QR_CODE` event, the `connectWahaChannel` function in **[`lib/channels/connect-waha.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/connect-waha.ts)** triggers. This function creates a new row in the `channel_sessions` table with `status: "SCAN_QR_CODE"`, deriving the `session_name` from the phone number contained in the QR payload. The implementation handles the state transition from `STARTING` to `SCAN_QR_CODE` as defined in the Zod schema at line 10.

```typescript
// Simplified webhook handler
import { z } from "zod";
import { ok, fail } from "@/lib/api/wrappers";
import { createWahaSession } from "@/lib/supabase/server";

const webhookSchema = z.object({
  name: z.string(),
  status: z.enum(["STARTING", "SCAN_QR_CODE", "WORKING"]),
  engine: z.string(),
});

export async function POST(req: Request) {
  const data = await req.json();
  const parsed = webhookSchema.safeParse(data);
  if (!parsed.success) return fail(400, "invalid_payload");

  const { name, status } = parsed.data;
  await createWahaSession({ waha_session_name: name, status });
  return ok({ received: true });
}

```

## Database Persistence and Multi‑Tenancy

Session state survives application restarts through Postgres persistence. The **[`lib/supabase/server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/supabase/server.ts)** service‑role client exposes an RPC named `fn_create_waha_session` that writes session records linking each WAHA session to a specific tenant via `organization_id`.

The database stores status values as a Postgres enum with states: `STARTING`, `SCAN_QR_CODE`, `WORKING`, `STOPPED`, and `FAILED`. Because the NOWEB engine is stateless on the client side, storing the `session_id` in the `channel_sessions` row allows the platform to resume operations after browser refreshes or server redeployments.

## Frontend Integration and Real‑Time Polling

The React frontend monitors database changes through the `useChannelSessions` hook defined in **[`hooks/channels/useChannelSessions.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/hooks/channels/useChannelSessions.ts)**. This hook watches for rows where `status` is `STARTING` or `SCAN_QR_CODE`.

When the hook detects `SCAN_QR_CODE`, it renders a QR code UI component and initiates a 3‑second polling interval to the WAHA `/api/sessions/:name` endpoint. This polling continues until the status advances to `WORKING`, at which point the session is considered authenticated and ready for message transmission.

```typescript
// hooks/channels/useChannelSessions.ts
import { useChannelSessions } from "@/hooks/channels/useChannelSessions";

export function QRCodeOnboarding({ orgId }: { orgId: string }) {
  const { sessions, state } = useChannelSessions(orgId);
  if (state === "connecting") return <Spinner />;
  if (sessions.some(s => s.status === "SCAN_QR_CODE")) {
    return <QRCodeDisplay sessionName={sessions[0].waha_session_name!} />;
  }
  return null;
}

```

## Health Monitoring and Automated Recovery

Prolonged `SCAN_QR_CODE` states indicate potential connection failures. The health monitoring system in **[`lib/channels/health.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/health.ts)** treats this status as a critical alert (`STATUS_QUE_AVISAM`).

The watchdog worker in **[`workers/agent-watchdog.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/workers/agent-watchdog.ts)** continuously scans for sessions stuck in the QR code phase beyond expected timeouts. When detected, the worker can trigger a Docker container restart for the specific WAHA instance, forcing a fresh session generation without manual intervention.

## Summary

- **Capability Detection**: The `describeWahaServer` function in [`lib/channels/waha-server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/waha-server.ts) reports `multipleSessions: "supported"` for WAHA 2026.7.2 NOWEB, enabling concurrent multi‑tenant sessions.
- **Webhook Integration**: The platform exposes `/api/webhooks/waha` to receive state change events, configured via the `WHATSAPP_HOOK_URL` environment variable in the Docker deployment.
- **State Persistence**: Postgres stores session states as enums (`STARTING`, `SCAN_QR_CODE`, `WORKING`), with `fn_create_waha_session` linking each session to an `organization_id` for tenant isolation.
- **UI Resilience**: The `useChannelSessions` hook polls the database every 3 seconds, rendering QR codes during the `SCAN_QR_CODE` phase and maintaining connection state across page reloads.
- **Failure Recovery**: The [`agent-watchdog.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/agent-watchdog.ts) worker monitors for stuck sessions and automates WAHA container restarts when QR code scanning fails to complete.

## Frequently Asked Questions

### How does DeskcommCRM handle multiple concurrent WAHA sessions?

The platform leverages the `multipleSessions: "supported"` flag returned by `describeWahaServer` in [`lib/channels/waha-server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/waha-server.ts). Because WAHA 2026.7.2 NOWEB explicitly advertises this capability, the orchestration layer can spawn separate Docker containers and database rows for each tenant’s `organization_id`, isolating WhatsApp connections while sharing the same webhook endpoint.

### What happens to active sessions when the browser refreshes?

Sessions persist because the NOWEB engine stores all state in the Postgres database rather than browser memory. The `channel_sessions` table retains the `session_id` and `status` fields, and the `useChannelSessions` hook re‑hydrates the UI state on mount by querying the latest database row. This design ensures that a QR‑code scan in progress survives page reloads without requiring re‑authentication.

### Which database fields track the QR‑code onboarding progress?

The `channel_sessions` table uses a Postgres enum for the `status` column with values including `STARTING`, `SCAN_QR_CODE`, and `WORKING`. When WAHA generates a new QR code via webhook, the `connectWahaChannel` function in [`lib/channels/connect-waha.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/channels/connect-waha.ts) updates this column to `SCAN_QR_CODE`. The `session_name` field stores the phone number identifier, while foreign keys link the record to the tenant’s `organization_id`.

### How is the webhook payload validated for security?

All incoming requests to `/api/webhooks/waha` pass through Zod schema validation defined in [`docs/specs/03-spec-whatsapp-waha.md`](https://github.com/melgarafael/DeskcommCRM/blob/main/docs/specs/03-spec-whatsapp-waha.md). The schema requires fields such as `name`, `status`, and `engine`, rejecting malformed payloads with a 400 response via the `fail()` wrapper in [`lib/api/wrappers.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/api/wrappers.ts). This prevents database pollution from unauthorized or corrupted webhook calls.