> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polymorfa.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect widget

> Create a short-lived, branded WhatsApp connection flow for your customers.

The Polymorfa Connect widget is a hosted pairing flow for QR, pairing-code, and Cloud API onboarding. Your backend creates a widget session; your frontend embeds or redirects to the returned URL. The API never exposes your organization or project credential to the browser.

## Manage saved settings

In the Console, open team or project **Settings → Embedded Signup**. You can
enable the widget, select connection modes and methods, allow exact redirect
URLs and embed origins, and configure its name, colors, shape, theme,
watermark, and history-sync behavior.

Server automation can read the effective settings with `GET /v1/widget` and
save changes with `PUT /v1/widget`. Send `projectId` to manage settings for one
project; omit it to manage the organization defaults. These Platform API
operations accept an organization key with widget permission or a signed-in
Console bearer. They reject project and client tokens.

`PUT /v1/widget` accepts any subset of these fields:

| Field                 | Type         | Allowed values or limits                           |
| --------------------- | ------------ | -------------------------------------------------- |
| `projectId`           | UUID         | Optional project-specific settings target          |
| `enabled`             | boolean      | Enables or disables widget session creation        |
| `modesAllowed`        | array        | `embedded`, `redirect`; 1–50 unique values         |
| `methods`             | array        | `qr`, `pairing`, `cloud-api`; 1–50 unique values   |
| `allowedRedirectUris` | URL array    | Up to 50 exact callback URLs                       |
| `allowedOrigins`      | origin array | Up to 50 origins without paths or credentials      |
| `businessName`        | string       | Up to 120 characters; empty removes the override   |
| `accent`              | string       | Six-digit hex color such as `#6A3DE8`              |
| `theme`               | string       | `light`, `dark`, or `system`                       |
| `hideWatermark`       | boolean      | Shows or hides the Polymorfa footer                |
| `colors`              | object       | Optional `light` and `dark` six-digit hex palettes |
| `shape`               | string       | `square`, `rounded`, `pill`, or `null`             |
| `radiusPx`            | number       | `0`–`999`, or `null`                               |
| `historySync`         | string       | `ask`, `force_on`, or `force_off`                  |

Use HTTPS redirect URLs and origins. Origins must contain only the scheme and
host. Stored settings take precedence over protected per-session values when
you create a widget session.

## Create a widget session

Call `POST /api/widget/sessions` from your backend with an organization key
that has `widget:create`. Widget creation is a human-controlled operation;
project tokens are rejected even when they carry similarly named scopes.

```bash theme={null}
curl -X POST https://api.polymorfa.com/api/widget/sessions \
  -H "Authorization: Bearer $TITAN_ORGANIZATION_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "embedded",
    "methods": ["qr", "pairing"],
    "businessName": "Acme",
    "allowedOrigins": ["https://app.acme.example"],
    "theme": "system"
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "widgetUrl": "https://polymorfa.com/widget/connect?ct=...",
    "redirectUrl": "https://polymorfa.com/widget/connect?ct=...&fullscreen=1",
    "clientToken": "titan_ct_...",
    "session": "widget-5c9f9f171e29"
  }
}
```

The generated client token lasts 15 minutes and is restricted to the new session and the widget actions. Do not log or persist the token or either URL.

### Request fields

| Field             | Type                                   | Notes                                                                            |
| ----------------- | -------------------------------------- | -------------------------------------------------------------------------------- |
| `mode`            | `"embedded" \| "redirect"`             | Defaults to `embedded`. Redirect mode requires `callbackUrl`.                    |
| `projectId`       | `string`                               | Applies project-specific widget settings when using an organization key.         |
| `methods`         | `("qr" \| "pairing" \| "cloud-api")[]` | Connection methods to show.                                                      |
| `businessName`    | `string`                               | Business name displayed in the flow.                                             |
| `historySync`     | `"ask" \| "force_on" \| "force_off"`   | Controls the history-sync choice.                                                |
| `sessionName`     | `string`                               | Optional stable session name; one is generated when omitted.                     |
| `callbackUrl`     | URL                                    | Must exactly match a configured redirect URI.                                    |
| `allowedOrigins`  | URL array                              | Origins allowed to embed the flow; stored Console settings remain authoritative. |
| `theme`           | `"light" \| "dark" \| "system"`        | Appearance override.                                                             |
| `accent`          | hex color                              | Six-digit color such as `#6A3DE8`.                                               |
| `prefillPhone`    | string                                 | 6–15 digits, with an optional leading `+`.                                       |
| `autoSubmitPhone` | boolean                                | Skips phone entry when `prefillPhone` is set.                                    |
| `analytics`       | object                                 | Optional `posthogKey` and `posthogHost`.                                         |
| `metaApp`         | object                                 | Optional Cloud API `appId` and `appSecret`. Keep this call server-side.          |

Project configuration controls whether the widget is enabled, allowed modes, redirect URIs, origins, branding, shape, watermark, connection methods, and history-sync behavior. Protected stored settings take precedence over request values.

## Embed the returned URL

Use the returned `widgetUrl` directly in an iframe, or load the helper served by the API:

```html theme={null}
<div id="polymorfa-connect"></div>
<script
  src="https://api.polymorfa.com/widget/embed.js"
  data-widget-url="WIDGET_URL_FROM_YOUR_BACKEND"
  data-container="#polymorfa-connect"
  data-theme="system"
  data-width="400"
  data-height="540">
</script>
```

The helper emits `titan:widget` `postMessage` events and supports `connected`, `error`, and `cancelled`. The global `Titan.createWidget({ url, container, onConnected, onError, onCancelled })` compatibility API provides programmatic mounting.

<Warning>
  Always fetch `widgetUrl` from your own authenticated backend. Never put an
  organization key in browser code.
</Warning>

## Redirect flow

Set `mode` to `redirect`, supply an allowlisted `callbackUrl`, and send the browser to `redirectUrl`. Redirects are closed by default until exact callback URLs are configured for the project.

## Cancel a pending session

`DELETE /api/widget/sessions/{session}` revokes the widget rules and cancels a pending connection.

## Cross-device handoff

A widget client token for the same session can call `POST /api/widget/sessions/{session}/handoff`. The response contains a `handoffUrl`, an SVG meta-QR, and `expiresAt`. The receiving device opens the URL and polls `GET /widget/handoff/{token}/state` for the current WhatsApp QR and session status.

The handoff token is the bearer credential and expires within 15 minutes. The
handoff becomes unusable after it is consumed or invalidated; a copied token
remains sensitive until it expires. State responses use
`Cache-Control: no-store` so browsers and shared caches do not retain the QR or
session status.
Treat the token like the widget URL and never log it.

## Security model

* The organization key remains on your server; project tokens cannot create
  widget sessions.
* The browser receives a session-bound `titan_ct_` token with widget-only actions.
* Redirect URLs and embed origins are checked against project settings.
* Handoff state uses a short-lived signed token in the URL; no credential exchange occurs on the second device.
* Cancelling a session revokes its client-token rules.

## Where to go next

<Columns cols={2}>
  <Card title="Client tokens" icon="key" href="/console/client-tokens">
    Learn how browser-safe credentials are constrained.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/api/webhooks">
    Observe the resulting session lifecycle.
  </Card>
</Columns>
