# Hosted onboarding (your customer connects their number)

`POST /v1/onboarding-links`

When the number belongs to your customer and you don't want to ask for their Meta credentials, they connect their own WhatsApp on a page with **your brand** — no login, they never see our brand, and you never touch Meta's app. There are **two ways** — pick yours:

**1. Send them a link (no code).** Generate the link from the panel (Numbers → 'Send a link to a customer') and hand it to them yourself (WhatsApp, email, whatever). Great for connecting customers one at a time. Nothing to write.

**2. Integrate it in your system (with code).** Your customer taps 'Connect WhatsApp' inside YOUR product and connects on their own, no intervention from you. The flow: your backend calls `POST /v1/onboarding-links` with `label`, `externalId` (and `returnOrigin` if you'll use a popup) → gets a `url` → you send them there by **redirect** (most robust, recommended on mobile) or **popup** (feels embedded). When done, the result reaches you by `postMessage` AND by the `connection.created` webhook with your `externalId`. The frontend snippet and backend examples are below.

In both cases you can pin the mode (`cloud_api`/`coexistence`, or omit to let the customer choose) and the page language (`lang`: es/it/en/pt, or omit to detect from the browser). The token is **single-use** and expires: the expiry is configurable (`expiresIn` in seconds, 5 min–30 days) — the default is **1 hour** via the API, **7 days** from the panel.

## Examples

### cURL

```bash
curl -X POST https://api.waiaconnect.com/v1/onboarding-links \
  -H "Authorization: Bearer wc_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"López Pharmacy","externalId":"pharmacy-42","lang":"es","returnOrigin":"https://app.yourcompany.com","expiresIn":3600}'
# → { "url": "https://…/c/<token>", "expiresAt": "…", "publicId": "onb_…" }
# Send url to your customer. It is shown ONCE; the token is consumed only when they connect.
```

### Node.js

```javascript
// Call this from YOUR backend when your customer taps "Connect WhatsApp".
const res = await fetch("https://api.waiaconnect.com/v1/onboarding-links", {
  method: "POST",
  headers: { "Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
  "label": "López Pharmacy",
  "externalId": "pharmacy-42",
  "lang": "es",
  "returnOrigin": "https://app.yourcompany.com",
  "expiresIn": 3600
})
});
const { url } = await res.json(); // https://…/c/<token> — hand this to the browser (redirect or popup)
```

### PHP

```php
<?php
// Call this from YOUR backend when your customer taps "Connect WhatsApp".
$ch = curl_init("https://api.waiaconnect.com/v1/onboarding-links");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer wc_live_YOUR_API_KEY", "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode(["label" => "López Pharmacy","externalId" => "pharmacy-42","lang" => "es","returnOrigin" => "https://app.yourcompany.com","expiresIn" => 3600]),
]);
$out = json_decode(curl_exec($ch), true);
echo $out["url"]; // https://…/c/<token> — hand this to the browser (redirect or popup)
```

### Python

```python
import requests
# Call this from YOUR backend when your customer taps "Connect WhatsApp".
res = requests.post("https://api.waiaconnect.com/v1/onboarding-links",
  headers={"Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json"},
  json={"label":"López Pharmacy","externalId":"pharmacy-42","lang":"es","returnOrigin":"https://app.yourcompany.com","expiresIn":3600})
res.raise_for_status()
print(res.json()["url"])  # https://…/c/<token> — hand this to the browser (redirect or popup)
```

You generate a link branded as your customer; they connect their number without entering your panel or seeing our brand. You receive `connection.created` with your `externalId`. The full detail (flow diagram, popup snippet, why never in an iframe) lives inside the panel, with your real data.

## Notes

- The URL (with the token) is returned ONCE, like an API key — send it and store it.
- The token is consumed only when the **connection is created**, not when the page opens. If your customer closes the window or loses signal, they **reopen the SAME link** and continue — until it expires.
- **The webhook is the reliable source.** The `postMessage` is lost if your customer closes the window; the `connection.created` webhook is not. Confirm server-side with that event.
- ⚠ **Never inside an iframe:** the Embedded Signup popup breaks there (popup restrictions + third-party cookie blocking). Use redirect or a top-level popup.
- For the **popup**, `returnOrigin` is required: the exact https origin we reply to (never `*`), validated at link creation.
- Your customer NEVER sees our brand or enters your panel; you NEVER enter their Meta account (PRODUCTO §7.3). Set up your brand in Settings → Brand.
- Link states: `pending` (unused), `used` (connected), `expired`, `revoked` (you cancelled it). Revoke a pending one with `DELETE /v1/onboarding-links/:id`.
- **The panel's Links section** shows the ones you sent (both kinds), which customer, when they expire, and their progress — **not opened** / **opened, not finished** / **completed**. A link with no activity used to be ambiguous: there was no way to tell "they never saw it" from "they stalled in the form", and those call for different actions.
- 🔒 About the open we record **only that it happened and when** (first, last, how many times). **Nothing about the visitor**: no IP, no browser, no referer. `GET /v1/onboarding-links` returns `engagement`, `firstOpenedAt`, `lastOpenedAt` and `openCount`.
- From the panel you can **copy the URL again** for a link that is still alive (owner/admin, audited). A used/expired/revoked one is never revealed: it is good for nothing.
