waiaconnect

Documentation / Complete

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.

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.
// 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
// 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)
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.

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.