# Percorso dell'integratore: attivazione e webhook 100 % via API

`GET /v1/onboarding-links/{id}`

Se attivi i tuoi clienti dal tuo sistema, senza entrare nel pannello, questo è il percorso completo, in cinque passi.

**1. Un link per cliente.** `POST /v1/onboarding-links` con il tuo `externalId` (il tuo id per quel cliente) e `webhook: { url }`. Ti restituiamo l'`url` per il tuo cliente e il segreto `whsec_…` del webhook **una sola volta**: salvalo subito, firma tutto ciò che riguarda quel cliente. Un `externalId` già usato da un altro numero del tuo account dà `409` in questo momento, non alla fine dell'attivazione.

**2. Il tuo cliente si collega.** Apre l'`url`, entra con il suo account Meta e sceglie il numero. Alla fine, il numero nasce con il suo webhook (l'URL del link) e lì arriva **`onboarding.completed`**, con `connection.id` (`conn_…`), il tuo `externalId`, `reconnected` e il `webhookId`. Un numero nuovo porta anche `connection.created`, come sempre.

**3. Se non finisce, lo sai lo stesso.** Ogni tentativo interrotto (fallito, annullato o abbandonato) manda **`onboarding.failed`** all'URL del webhook del link, firmato con lo stesso segreto: `reason` (`meta_error`, `incomplete`, `timeout`, `cancelled`, `abandoned`…), `metaErrorCode` e `metaMessage` se Meta li ha dati, e `linkUsable` (se il tuo cliente può riprovare con lo stesso link). Se preferisci chiedere: `GET /v1/onboarding-links?externalId=…` o `GET /v1/onboarding-links/{id}` restituiscono `outcome` (`pending`, `completed` o `failed`, con lo stesso motivo).

**4. Il webhook del numero è tuo.** Lo vedi e lo modifichi con `/v1/webhooks?externalId=…`: URL, eventi, intestazione fissa, ruotare il segreto, provarlo.

**5. La disattivazione.** Quando il numero si scollega (dal pannello, perché l'azienda ci ha tolto l'accesso su Meta o perché il permesso è stato revocato), il suo webhook viene spento (`active: false`, `disabledReason: "connection_disconnected"`) e smette di ricevere. Se si ricollega lo **stesso** numero, si riaccende da solo; quelli che hai messo in pausa tu o che abbiamo spento per errori, no.

## Esempi

### cURL

```bash
# 1) one link per end-customer: your id + the webhook its number will have
curl -X POST https://api.waiaconnect.com/v1/onboarding-links \
  -H "Authorization: Bearer wc_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"externalId": "pharmacy-42", "label": "Pharmacy 42", "mode": "coexistence",
       "webhook": {"url": "https://api.yourcompany.com/hooks/connect"}}'
# → 201 { "publicId": "onb_…", "url": "https://…/c#<token>", "expiresAt": "…",
#         "webhook": { "secret": "whsec_…", "createdOnCompletion": true, … } }   ← store the secret NOW

# 2) send "url" to your customer. Then, on https://api.yourcompany.com/hooks/connect:
#    { "type": "onboarding.completed", "connection": { "id": "conn_…", "externalId": "pharmacy-42", … },
#      "data": { "onboardingLinkId": "onb_…", "reconnected": false, "webhookId": "wh_…", … } }
#    { "type": "onboarding.failed", "connection": { "id": null, … },
#      "data": { "externalId": "pharmacy-42", "reason": "meta_error", "metaErrorCode": 2655093,
#                "metaMessage": "The business is already sharing…", "linkUsable": true, … } }

# 3) or ask (polling) by your id
curl "https://api.waiaconnect.com/v1/onboarding-links?externalId=pharmacy-42" -H "Authorization: Bearer wc_live_YOUR_API_KEY"
# → { "links": [ { "id": "onb_…", "status": "used", "connectionId": "conn_…",
#       "outcome": { "state": "completed", "connectionId": "conn_…", "webhookId": "wh_…", … } } ] }
curl https://api.waiaconnect.com/v1/onboarding-links/onb_… -H "Authorization: Bearer wc_live_YOUR_API_KEY"

# 4) the number's webhook, by your id (PATCH to change url/events, rotate-secret, test)
curl "https://api.waiaconnect.com/v1/webhooks?externalId=pharmacy-42" -H "Authorization: Bearer wc_live_YOUR_API_KEY"

# 5) offboarding: disconnecting the number switches its webhook off
#    (active: false, disabledReason: "connection_disconnected"); reconnecting it switches it back on
```

### Node.js

```javascript
const H = { "Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json" };

// 1) one link per end-customer: your id + the webhook its number will have
const link = await (await fetch("https://api.waiaconnect.com/v1/onboarding-links", {
  method: "POST", headers: H,
  body: JSON.stringify({ externalId: "pharmacy-42", label: "Pharmacy 42", mode: "coexistence",
    webhook: { url: "https://api.yourcompany.com/hooks/connect" } })
})).json();
await saveSecret("pharmacy-42", link.webhook.secret); // shown ONCE — it signs this customer's events
await sendToCustomer(link.url);

// 2) your webhook handler (verify the signature first — see «Verify the signature»)
app.post("/hooks/connect", express.raw({ type: "application/json" }), async (req, res) => {
  const evt = JSON.parse(req.body);
  if (evt.type === "onboarding.completed") await markConnected(evt.data.externalId, evt.connection.id);
  if (evt.type === "onboarding.failed") await tellCustomer(evt.data.externalId, evt.data.reason, evt.data.metaErrorCode, evt.data.linkUsable);
  res.sendStatus(200);
});

// 3) or poll by your id
const { links } = await (await fetch("https://api.waiaconnect.com/v1/onboarding-links?externalId=pharmacy-42", { headers: H })).json();
console.log(links[0]?.outcome); // { state: "pending" | "completed" | "failed", reason, metaErrorCode, … }
```

### PHP

```php
<?php
function connect($method, $url, $body = null) {
  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer wc_live_YOUR_API_KEY", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
  ]);
  return json_decode(curl_exec($ch), true);
}
// 1) one link per end-customer: your id + the webhook its number will have
$link = connect("POST", "https://api.waiaconnect.com/v1/onboarding-links", [
  "externalId" => "pharmacy-42", "label" => "Pharmacy 42", "mode" => "coexistence",
  "webhook" => ["url" => "https://api.yourcompany.com/hooks/connect"],
]);
save_secret("pharmacy-42", $link["webhook"]["secret"]); // shown ONCE

// 2) in your webhook handler: onboarding.completed / onboarding.failed (data.reason, data.metaErrorCode)
// 3) or poll by your id
$links = connect("GET", "https://api.waiaconnect.com/v1/onboarding-links?externalId=pharmacy-42")["links"];
$outcome = $links[0]["outcome"] ?? null; // state: pending | completed | failed
```

### Python

```python
import requests
H = {"Authorization": "Bearer wc_live_YOUR_API_KEY"}

# 1) one link per end-customer: your id + the webhook its number will have
link = requests.post("https://api.waiaconnect.com/v1/onboarding-links", headers=H, json={
    "externalId": "pharmacy-42", "label": "Pharmacy 42", "mode": "coexistence",
    "webhook": {"url": "https://api.yourcompany.com/hooks/connect"},
}).json()
save_secret("pharmacy-42", link["webhook"]["secret"])  # shown ONCE

# 2) in your webhook handler: onboarding.completed / onboarding.failed (data.reason, data.metaErrorCode)
# 3) or poll by your id
links = requests.get("https://api.waiaconnect.com/v1/onboarding-links", headers=H, params={"externalId": "pharmacy-42"}).json()["links"]
outcome = links[0]["outcome"] if links else None  # state: pending | completed | failed
```

## Note

- Senza `webhook` nel link, `onboarding.completed` e `onboarding.failed` vanno ai webhook dell'account (in `onboarding.failed`, `connection.id` è `null`: il tuo `externalId` è in `data`).
- Un webhook con `events` esplicito che non include questi tipi non li riceve; uno con tutti gli eventi inizia a riceverli.
- Nei link di attivazione, `connectionId` ora esce come `conn_…`; l'id interno resta in `legacyConnectionId`, che verrà ritirato.
- `outcome.state = failed` anche quando il link è scaduto (`link_expired`) o l'hai annullato (`link_revoked`) prima che il cliente finisse.
