> For the complete documentation index, see [llms.txt](https://docs.nerovasystems.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nerovasystems.com/ui-components/whatsapp-connect.md).

# WhatsApp connect

The WhatsApp connect component packages the hosted connection flow described in the [host UI integration law](https://docs.nerovasystems.com/documentation/guides/host-ui-integration) as a drop-in piece for the partner page. It exists so partners do not have to hand-roll popup mechanics, blocker handling, and completion detection.

## What it does

The component renders a connect affordance inside the partner page. When the merchant clicks it:

1. The component synchronously opens the Nerova hosted connection page in a popup window, inside the click handler, with no asynchronous work between the user gesture and the open call. This is the popup law: any `await` before the open hands the window to the popup blocker.
2. The hosted page runs the provider authorization. For WhatsApp this is Meta's Embedded Signup, which Meta requires to run from a Meta-allow-listed top-level window. It cannot run inside a partner-origin iframe. Meta's dialog opens as a Meta-branded `facebook.com` popup; that branding is deliberate and not themeable.
3. The merchant's provider credentials are exchanged server-side at Nerova. The partner page and the component never receive them.
4. The component reports the outcome to the partner page. Completion is detected from the popup lifecycle and confirmed against server-side activation state. The activation manifest is the source of truth, and it is correct even if the merchant's browser dies mid-flow.

The partner page never navigates. The merchant stays in the host product for the entire flow, with the connection moment in a window the host page opened and observes.

This is the hosted page the popup opens, as the merchant sees it:

![The hosted connection page in its ready state, showing the Connect WhatsApp with Meta card with a Login with Facebook button](https://2396471767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqI0ECBevw0VmfTzoMA2N%2Fuploads%2FfnrNx6mtdutDOSU7pOkg%2Fhosted-connect-ready.png?alt=media)

When the flow finishes, the hosted page confirms the outcome before the component reports completion to the partner page:

![The hosted connection page in its completed state, showing WhatsApp connected and a Done button](https://2396471767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqI0ECBevw0VmfTzoMA2N%2Fuploads%2FvOO6c5lMB813s502o7Md%2Fhosted-connect-complete.png?alt=media)

## Fallbacks

Popup blockers that eat the window despite the synchronous open are handled by falling back to a top-level navigation to the hosted page, which tells the merchant to return to the host platform when done. Hosts embedding the component inside a sandboxed iframe must grant the sandbox permissions listed in the [embedding security model](https://docs.nerovasystems.com/ui-components/security-model).

## Relationship to the API

The component drives the same connection-session lifecycle documented in [Connect channels](https://docs.nerovasystems.com/guides/zero-to-live/step-8-channels): a session is created through the partner backend, the hosted page consumes its `launchUrl`, and the outcome lands in the activation manifest. Partners who prefer to own the UI entirely can implement the same lifecycle directly under the host UI integration rules; the component and the raw API are two frontends to one flow.

Installation, configuration options, and the event surface are published with the component when it ships.

## Package reference

The component ships as `@nerova/embed`. It has a React peer dependency and nothing else: no Nerova-internal imports, no API client, no styling framework. It never calls a Nerova endpoint itself; the partner backend creates the connection session over the authenticated partner API and hands the component a `launchUrl`.

```tsx
import { WhatsAppConnect } from "@nerova/embed";

<WhatsAppConnect
  session={createSession}
  confirmCompletion={confirmAgainstManifest}
  onCompleted={() => refresh()}
  onFailed={(reason) => report(reason)}
/>;
```

### Props

| Prop                | Type                                                              | Notes                                                                                                                                                                                                                |
| ------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session`           | `{ launchUrl, expiresAt? }` or async factory                      | Pre-created session, or a factory the component calls inside the launch. With a factory the popup opens synchronously on `about:blank` and navigates when the factory resolves.                                      |
| `confirmCompletion` | `(confirmation) => Promise<"connected" \| "pending" \| "failed">` | Required. Client signals (postMessage OR popup-close) only TRIGGER `confirmCompletion`, manifest is truth, the component never self-reports success. The partner backend answers from `GET .../activation/manifest`. |
| `onCompleted`       | `() => void`                                                      | Fired after a confirmed `"connected"` outcome.                                                                                                                                                                       |
| `onFailed`          | `(reason) => void`                                                | `"session-expired"`, `"launch-failed"`, `"confirmation-failed"`, or `"connection-failed"`.                                                                                                                           |
| `onPopupBlocked`    | `() => void`                                                      | Fired before the top-level navigation fallback.                                                                                                                                                                      |
| `onStateChange`     | `(state) => void`                                                 | Observes the phase machine.                                                                                                                                                                                          |
| `render`            | `(state, launch) => ReactNode`                                    | Replaces the default accessible button. `launch` must be invoked synchronously from the user gesture.                                                                                                                |

### Completion message

When the partner backend registers its page origin at session creation (`embedOrigin` on the connection-session request), the hosted page posts an opaque completion message to `window.opener`, targeted at exactly that origin and never `*`:

```json
{ "type": "nerova:whatsapp-connect", "version": 1, "sessionId": "pacs_...", "status": "completed" }
```

The payload carries no tokens, no credentials, and no provider data. The component ignores messages from any other origin, with any other session id, or with any other shape, silently. Without a registered `embedOrigin` no message is posted and the popup-close signal alone triggers confirmation.

Design note: the session-scoped `embedOrigin` (popup and postMessage) and the tenant-level widget embed origins (iframe and `frame-ancestors`) are deliberately distinct primitives in v1. Any future consolidation is a design ruling, not an integration choice.

### Demo mode

Docs and playground pages import the separate `@nerova/embed/demo` entrypoint, which provides a mock session, a scripted popup driver, and a mock confirmation. Production bundles that import only the root entry contain none of the demo code: the separation is at module-graph level, not a runtime flag, so demo behavior cannot be enabled in a partner build.

### Live demo

The connect component below runs in demo mode inside a real iframe, framed by the same CSP `frame-ancestors` mechanics described above. The signup dialog itself still opens as a popup, because Meta requires Embedded Signup to run in a top-level window; the iframe demonstrates the component, not the dialog. It uses the mock session driver and carries no tenant data.

{% embed url="<https://app.nerovasystems.com/embed/signup-demo>" %}
Live WhatsApp connect component in demo mode
{% endembed %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nerovasystems.com/ui-components/whatsapp-connect.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
