Documentazione API
API REST e webhook di Coonvert · versione v1
Cosa si può fare
Con l'API colleghi Coonvert al tuo CRM. Puoi:
- leggere i lead, anche solo quelli cambiati dall'ultima volta;
- creare lead raccolti altrove (campagne, telefono, altri siti);
- aggiornare stato, valore e campi personalizzati di un lead quando il commerciale lo lavora;
- leggere lo storico di un lead, registrare un esito di contatto e fissare il prossimo richiamo;
- leggere gli ordini pagati (con i pagamenti nel piano) ed eliminare un lead;
- ricevere i nuovi lead e le modifiche in tempo reale con i webhook;
- iscrivere e togliere i webhook da un altro programma (Zapier, Make);
- assegnare i gruppi ai lead e farli entrare nelle automazioni o uscirne.
API e webhook sono inclusi nel piano Agency. Ogni token vale per un solo account: se gestisci più clienti, crea un token dentro ogni cliente.
Primo passo: il token
- Entra nel pannello e apri l'account (o il cliente) da collegare.
- Vai su Integrazioni → API e crea un token. Copialo subito: non sarà più mostrato.
- Prova il token:
curl https://www.coonvert.com/api/v1/user \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json"Risposta
{
"id": 57,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"role": "owner"
}Il token agisce con i permessi del suo utente. Per leggere e modificare i lead l'utente deve poter vedere i dati dei lead nell'account del token.
Autenticazione e indirizzo
Tutte le chiamate partono da questo indirizzo:
https://www.coonvert.com/api/v1Manda il token in ogni richiesta, con queste intestazioni:
Authorization: Bearer <token>
Accept: application/json
Content-Type: application/jsonCorpi e risposte sono in JSON (UTF-8). Le date sono in formato ISO 8601 e in UTC, ad esempio 2026-10-27T16:40:00+00:00. Un token mancante o revocato risponde 401.
Il lead
L'API restituisce il lead sempre in questa forma, uguale a quella dei webhook.
{
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"utm_source": "facebook",
"utm_campaign": "autunno-2026"
}| Campo | Tipo | Significato |
|---|---|---|
id | numero | Identificativo del lead. Usalo come chiave nel CRM. |
name, email, phone | testo o null | Dati del contatto. C'è sempre almeno l'email o il telefono. |
page | testo o null | Titolo della pagina da cui è arrivato. |
page_id | numero o null | Id della pagina (vedi GET /pages). |
form_id | numero o null | Id del form esterno da cui è arrivato. |
status | testo | Stato di lavorazione, vedi sotto. |
valore | numero o null | Valore in euro della vendita (es. 1250.5). Esiste solo per i lead convertiti. |
convertito_at | data o null | Quando è diventato convertito l'ultima volta. |
source | testo | Provenienza: form, meta, api, pagamento, manuale, form_esterno, email, webhook, importazione. I lead creati con l'API hanno sempre api. |
source_detail | testo o null | Provenienza leggibile, es. «Google Ads – campagna primavera». |
data | oggetto o null | Gli altri campi inviati col form (o custom nel POST). |
utm | oggetto | source, medium, campaign, term, content (ognuno testo o null). |
gruppi | lista di testi | Nomi dei gruppi del lead, in ordine alfabetico. Vuota se non ne ha. |
campi | oggetto | Campi personalizzati, per chiave. Solo i campi che esistono oggi nell'account. |
created_at, updated_at | data | Creazione e ultima modifica. updated_at cambia anche quando cambiano gruppi o richiamo. |
utm_source, utm_campaign | testo o null | Deprecati: restano per compatibilità, usa utm. Non ci sono nei webhook. |
Stati possibili
| Valore | Nel pannello |
|---|---|
nuovo | Nuovo |
contattato | Contattato |
qualificato | Qualificato |
convertito | Convertito (chiude il lead) |
perso | Perso (chiude il lead) |
falso_duplicato | Falso/Duplicato (chiude il lead) |
Uno stato che chiude il lead toglie il richiamo in programma. Diventando convertito si aggiorna convertito_at.
Campi personalizzati e gruppi
I campi personalizzati si creano nel pannello in Lead & Analisi → Campi personalizzati. Ogni campo ha una chiave (es. budget) e un tipo. Nei corpi JSON il valore segue il tipo:
| Tipo | Valore |
|---|---|
| Testo | testo, massimo 255 caratteri |
| Testo lungo | testo, massimo 5000 caratteri |
| Numero | numero |
| Data | testo AAAA-MM-GG, es. 2026-11-15 |
| Scelta | una delle opzioni del campo, scritta uguale |
| Sì/No | true o false |
Un valore vuoto (null o "") toglie il campo dal lead. Una chiave che non esiste risponde 422 con l'elenco delle chiavi valide. I gruppi si assegnano per nome, nel PATCH e nel POST (vedi PATCH /leads/{id}); l'elenco di quelli esistenti e dei campi è in Gruppi e campi.
Endpoint
I percorsi qui sotto si aggiungono all'indirizzo https://www.coonvert.com/api/v1.
GET/leads
Elenco dei lead, a pagine. Senza filtri parte dal più recente.
| Parametro | Significato |
|---|---|
updated_since | Solo i lead con updated_at uguale o successivo a questa data (ISO 8601). Ordina dal meno recente: è il filtro per la sincronizzazione. |
created_since | Solo i lead creati da questa data in poi (ISO 8601). |
status | Uno degli stati. |
page_id, form_id | Solo i lead di quella pagina o di quel form. |
per_page | Lead per pagina, da 1 a 100. Predefinito 50. |
page | Numero della pagina, da 1. |
Ordine: con updated_since per updated_at crescente, poi id crescente; senza, per data di creazione decrescente, poi id decrescente. Un parametro non valido risponde 422.
curl -G https://www.coonvert.com/api/v1/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "status=convertito" \
--data-urlencode "per_page=100"Risposta
{
"data": [
{
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"utm_source": "facebook",
"utm_campaign": "autunno-2026"
}
],
"links": {
"first": "https://www.coonvert.com/api/v1/leads?per_page=100&status=convertito&page=1",
"last": "https://www.coonvert.com/api/v1/leads?per_page=100&status=convertito&page=3",
"prev": null,
"next": "https://www.coonvert.com/api/v1/leads?per_page=100&status=convertito&page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://www.coonvert.com/api/v1/leads?per_page=100&status=convertito&page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "https://www.coonvert.com/api/v1/leads?per_page=100&status=convertito&page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "https://www.coonvert.com/api/v1/leads",
"per_page": 100,
"to": 1,
"total": 201
}
}links.next è null sull'ultima pagina. meta.total è il numero totale dei lead che corrispondono ai filtri.
Errori possibili: 401, 403, 422, 429.
Sincronizzazione incrementale
Per tenere il CRM allineato senza rileggere tutto:
- Salva l'
updated_atdell'ultimo lead che hai letto (la prima volta usa una data lontana, es.2020-01-01T00:00:00Z). - Chiedi
GET /leads?updated_since=<quel valore>&per_page=100. - Per ogni lead: se nel CRM c'è già lo stesso
idaggiornalo, altrimenti crealo. Deduplica sempre perid: il filtro include il valore indicato, quindi l'ultimo lead della volta prima torna anche questa volta. - Salva l'
updated_atdell'ultimo lead ricevuto e richiedi di nuovo la prima pagina da lì. Ripeti finché ricevi meno di 100 lead. - Solo se tutti i 100 lead hanno lo stesso
updated_at(es. dopo una modifica di massa) passa apage=2,page=3… con lo stesso valore, finché l'updated_atcambia.
Ripartire ogni volta dalla prima pagina evita di saltare lead che cambiano mentre stai leggendo. Nella query string il + del fuso va codificato (%2B): con cURL usa --data-urlencode.
curl -G https://www.coonvert.com/api/v1/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "updated_since=2026-10-27T16:40:00+00:00" \
--data-urlencode "per_page=100"Esempio in JavaScript (Node 18+)
async function sincronizza(cursore, salvaLead, salvaCursore) {
let pagina = 1;
for (;;) {
const url = new URL('https://www.coonvert.com/api/v1/leads');
url.search = new URLSearchParams({ updated_since: cursore, per_page: '100', page: String(pagina) });
const risposta = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.COONVERT_TOKEN}`, Accept: 'application/json' },
});
if (!risposta.ok) throw new Error(`Coonvert ha risposto ${risposta.status}`);
const { data } = await risposta.json();
for (const lead of data) await salvaLead(lead); // crea o aggiorna per lead.id
if (data.length === 0) return cursore;
const ultimo = data[data.length - 1].updated_at;
if (data.length < 100) { await salvaCursore(ultimo); return ultimo; } // finito
if (ultimo === cursore) { pagina++; continue; } // tutta la pagina con lo stesso istante
cursore = ultimo;
pagina = 1;
await salvaCursore(cursore);
}
}L'API non segnala i lead eliminati. Se ti servono gli aggiornamenti in tempo reale usa i webhook e tieni la sincronizzazione come controllo periodico.
GET/leads/{id}
Un lead solo.
curl https://www.coonvert.com/api/v1/leads/4821 \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json"Risposta
{
"data": {
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"utm_source": "facebook",
"utm_campaign": "autunno-2026"
}
}Errori possibili: 401, 403, 404 (inesistente o di un altro account), 429.
POST/leads
Crea un lead. Serve almeno email o phone. La provenienza (source) è sempre api.
| Campo | Significato |
|---|---|
name | Nome, massimo 255 caratteri. |
email | Email valida. Obbligatoria se manca il telefono. |
phone | Telefono, massimo 50 caratteri. Obbligatorio se manca l'email. |
page_id | Facoltativo. Una pagina pubblicata dell'account. |
form_id | Facoltativo. Un form esterno dell'account. Non insieme a page_id. |
source_detail | Facoltativo. Provenienza leggibile, massimo 255 caratteri. |
campi | Facoltativo. Campi personalizzati, come nel PATCH. |
gruppi | Facoltativo. Elenco di nomi di gruppo già esistenti, es. ["Caldi"]. |
custom | Facoltativo. Oggetto libero, finisce in data. |
utm_source, utm_medium, utm_campaign, utm_term, utm_content | Facoltativi, massimo 255 caratteri. |
Ci sono tre modi:
- Senza pagina né form: un lead dell'account. Lo ricevono i destinatari generali dei nuovi lead; nessuna risposta automatica.
- Con
page_id: come se arrivasse dal form della pagina, con la sua risposta automatica. - Con
form_id: come se arrivasse da quel form esterno: destinatari, risposta automatica e automazioni del form. L'id del form si legge nell'indirizzo della sua scheda in Lead & Analisi → Form.
In tutti e tre i casi il lead entra nelle automazioni con sorgente «API» e manda il webhook lead.created.
Senza pagina né form
curl -X POST https://www.coonvert.com/api/v1/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-contatto-98765" \
-d '{
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"source_detail": "Google Ads – campagna primavera",
"utm_source": "google",
"utm_campaign": "primavera",
"campi": { "budget": 5000 }
}'Con una pagina
curl -X POST https://www.coonvert.com/api/v1/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "page_id": 312, "name": "Mario Rossi", "email": "mario.rossi@example.com" }'Con un form esterno
curl -X POST https://www.coonvert.com/api/v1/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "form_id": 18, "name": "Mario Rossi", "phone": "+393331234567" }'Risposta (201)
{
"data": {
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": null,
"page_id": null,
"form_id": null,
"status": "nuovo",
"valore": null,
"convertito_at": null,
"source": "api",
"source_detail": "Google Ads – campagna primavera",
"data": null,
"utm": {
"source": "google",
"medium": null,
"campaign": "primavera",
"term": null,
"content": null
},
"gruppi": [],
"campi": {
"budget": 5000
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-28T09:30:00+00:00",
"updated_at": "2026-10-28T09:30:00+00:00",
"utm_source": "google",
"utm_campaign": "primavera"
}
}Errori possibili: 401, 402 (limite dei lead del piano raggiunto), 403, 404 (pagina non pubblicata, form inesistente o di un altro account, oppure pagine dell'account sospese), 409, 422, 429. Per non creare doppioni nei tentativi vedi l'idempotenza.
PATCH/leads/{id}
Aggiorna un lead dal CRM. Tutti i campi sono facoltativi, ma ne serve almeno uno:
status: uno degli stati;valore: euro come numero JSON (es.1250.50, massimo999999999.99), oppurenullper toglierlo. Solo su un lead che è giàconvertitoo lo diventa nella stessa richiesta;campi: oggetto{chiave: valore}. Si uniscono a quelli che il lead ha già; un valore vuoto toglie il campo;gruppi: elenco di nomi di gruppo. Sostituisce i gruppi del lead ([]li toglie tutti);gruppi_aggiungiegruppi_togli: elenchi di nomi per aggiungere o togliere qualche gruppo senza toccare gli altri. Si possono mandare insieme; se lo stesso nome è in entrambi, vince la rimozione;richiamo: oggetto{ "quando": "2026-11-02T09:00:00+01:00", "nota": "…" }(notafacoltativa, massimo 255 caratteri) per fissare o spostare il richiamo del lead, oppurenullper toglierlo. Un lead ha un solo richiamo;notes: le note interne del lead (massimo 5000 caratteri), oppurenullper svuotarle.
Il richiamo risponde 422 con «Indica quando richiamare.» se manca quando, «Il richiamo deve essere nel futuro.» se la data è passata e «Un lead chiuso non può avere un richiamo.» se il lead è (o diventa nella stessa richiesta) convertito, perso o falso/duplicato. Il lead nelle risposte ha il campo richiamo (quando e nota, oppure null).
Un PATCH che cambia solo il richiamo non manda lead.updated (ma aggiorna updated_at, quindi si vede nella sincronizzazione incrementale). In un PATCH che cambia anche altro, il lead.updated mostra ancora il richiamo di prima: quello nuovo si applica subito dopo il salvataggio.
gruppi non si manda insieme a gruppi_aggiungi o gruppi_togli (422: «Usa gruppi oppure gruppi_aggiungi/gruppi_togli, non entrambi.»). I nomi si confrontano senza badare a maiuscole e spazi ai lati; un nome che non esiste risponde 422 e non crea niente: i gruppi nuovi si creano dal pannello. Anche i gruppi si cambiano solo con il permesso di modifica dei lead.
Nome, email e telefono non si modificano via API. L'effetto è lo stesso di una modifica dal pannello: la cronologia del lead registra il cambio di stato «via API», uno stato chiuso toglie il richiamo, le automazioni che si fermano su quello stato fanno uscire il lead e parte il webhook lead.updated. Se non cambia niente non succede niente: puoi ripetere la stessa richiesta senza effetti doppi.
curl -X PATCH https://www.coonvert.com/api/v1/leads/4821 \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "status": "convertito", "valore": 1250.50, "campi": { "settore": "Ristorazione" } }'Risposta
{
"data": {
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"utm_source": "facebook",
"utm_campaign": "autunno-2026"
}
}Valore su un lead non convertito (422)
{
"message": "Il valore si imposta solo su un lead convertito.",
"errors": {
"valore": [
"Il valore si imposta solo su un lead convertito."
]
}
}Errori possibili: 401, 403 (ruolo senza permesso di modifica, o account in sola lettura), 404, 422, 429.
GET/pages
Le pagine dell'account, dalla più recente, a pagine come /leads (per_page da 1 a 100, predefinito 50; page). Serve a trovare il page_id.
curl -G https://www.coonvert.com/api/v1/pages \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "per_page=100"Risposta
{
"data": [
{
"id": 312,
"title": "Consulenza gratuita",
"slug": "consulenza-gratuita",
"status": "pubblicata",
"published_at": "2026-10-01T08:00:00+00:00",
"created_at": "2026-09-28T14:05:10+00:00"
}
],
"links": {
"first": "https://www.coonvert.com/api/v1/pages?per_page=100&page=1",
"last": "https://www.coonvert.com/api/v1/pages?per_page=100&page=3",
"prev": null,
"next": "https://www.coonvert.com/api/v1/pages?per_page=100&page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://www.coonvert.com/api/v1/pages?per_page=100&page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "https://www.coonvert.com/api/v1/pages?per_page=100&page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "https://www.coonvert.com/api/v1/pages",
"per_page": 100,
"to": 1,
"total": 201
}
}status è bozza o pubblicata. Errori possibili: 401, 403, 422, 429.
GET/pages/{id}
curl https://www.coonvert.com/api/v1/pages/312 \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json"Risposta
{
"data": {
"id": 312,
"title": "Consulenza gratuita",
"slug": "consulenza-gratuita",
"status": "pubblicata",
"published_at": "2026-10-01T08:00:00+00:00",
"created_at": "2026-09-28T14:05:10+00:00"
}
}Errori possibili: 401, 403, 404, 429.
GET/user
L'utente del token: utile per controllare che il token funzioni (vedi Primo passo). Risponde id, name, email, role, senza data attorno.
DELETE/leads/{id}
Elimina il lead per sempre, con il suo storico, le note e i percorsi nelle automazioni. Risponde 204 senza corpo. Serve il permesso di eliminare i lead, che hanno il proprietario e gli editor dell'account; con un altro ruolo la risposta è 403 («Il tuo ruolo non permette di eliminare i lead.»). Un lead già eliminato risponde 404.
curl -X DELETE https://www.coonvert.com/api/v1/leads/4821 \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json"Parte l'evento lead.deleted (vedi Webhook), anche quando il lead si elimina dal pannello. Con la cancellazione dell'intero account non parte nessun evento. Le chiavi di idempotenza del lead spariscono con lui: dopo una cancellazione, un POST /leads con la stessa Idempotency-Key crea un lead nuovo. Errori possibili: 401, 403, 404, 429.
Storico ed esiti
Lo storico è la cronologia del lead che vedi nel pannello. Come per gli altri endpoint dei lead, serve un ruolo che vede i dati dei lead (403 altrimenti).
GET/leads/{id}/activities
Le voci dello storico, dalla più recente, a pagine come /leads (per_page da 1 a 100, predefinito 50; fuori da questo intervallo 422). Ogni voce ha type (il tipo di evento, ad esempio esito), descrizione, nota (o null), utente (il nome di chi l'ha registrata, null se è stato il sistema) e created_at in UTC.
curl https://www.coonvert.com/api/v1/leads/4821/activities \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json"Risposta
{
"data": [
{
"id": 9150,
"type": "esito",
"descrizione": "Telefonata: ha risposto",
"nota": "Interessato, vuole il preventivo",
"utente": "Giulia Bianchi",
"created_at": "2026-10-27T16:30:00Z"
}
],
"links": {
"first": "https://www.coonvert.com/api/v1/leads/4821/activities&page=1",
"last": "https://www.coonvert.com/api/v1/leads/4821/activities&page=3",
"prev": null,
"next": "https://www.coonvert.com/api/v1/leads/4821/activities&page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://www.coonvert.com/api/v1/leads/4821/activities&page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "https://www.coonvert.com/api/v1/leads/4821/activities&page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "https://www.coonvert.com/api/v1/leads/4821/activities",
"per_page": 100,
"to": 1,
"total": 201
}
}Errori possibili: 401, 403, 404, 422, 429.
POST/leads/{id}/outcomes
Registra l'esito di un contatto, come «Registra esito» nel pannello. Campi:
come(obbligatorio):telefonata,whatsapp,email,smsopersona;risultato:risposto,non_rispostoooccupato. Conta solo per latelefonata; per gli altri canali viene ignorato;nota: massimo 2000 caratteri;status: il nuovo stato del lead;valore: euro, solo se il lead è (o diventa)convertito(422altrimenti). Su un lead già convertito, senzastatus, il valore viene salvato;richiamo:{ "quando", "nota" }come nelPATCH, nel futuro. Senzarichiamoun lead ancora aperto perde il richiamo che aveva, perché l'esito lo chiude; se il nuovo stato è chiuso il richiamo non si imposta; su un lead già chiuso (senzastatus) il richiamo è ignorato e la risposta resta201.
curl -X POST https://www.coonvert.com/api/v1/leads/4821/outcomes \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "come": "telefonata", "risultato": "risposto", "nota": "Interessato, vuole il preventivo", "richiamo": { "quando": "2026-11-02T09:00:00+01:00", "nota": "Mandare il preventivo" } }'Risposta (201)
{
"data": {
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"utm_source": "facebook",
"utm_campaign": "autunno-2026"
}
}È il lead aggiornato. Errori possibili: 401, 403 (ruolo senza permesso di modifica, o account in sola lettura), 404, 422, 429.
Ordini
Gli ordini delle pagine di vendita, in tutti gli stati (in attesa, pagato, fallito, rimborsato, annullato). Servono i pagamenti nel piano e un ruolo che vede i dati dei lead; senza i pagamenti la risposta è 403 («Il tuo piano non include i pagamenti.»), senza i dati dei lead 403 («Il tuo ruolo non permette di vedere i dati dei lead.»).
GET/orders
Gli ordini dell'account, dal più recente, a pagine come /leads. Filtri facoltativi:
status:in_attesa,pagato,fallito,rimborsatooannullato;lead_id: solo gli ordini di quel lead;updated_since: data ISO 8601; restituisce gli ordini cambiati da quel momento (incluso), dal meno al più recente, come nella sincronizzazione incrementale;per_page: da 1 a 100, predefinito 50.
curl -G https://www.coonvert.com/api/v1/orders \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "status=pagato"Risposta
{
"data": [
{
"order": {
"id": 88,
"status": "pagato",
"amount": 29000,
"currency": "EUR",
"option_name": "Consulenza completa",
"provider": "stripe",
"mode": "live",
"provider_order_id": "cs_live_a1B2c3D4",
"customer_email": "mario.rossi@example.com",
"paid_at": "2026-10-27T16:38:00+00:00",
"created_at": "2026-10-27T16:35:00+00:00",
"updated_at": "2026-10-27T16:38:00+00:00"
},
"lead": {
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00"
},
"page": {
"id": 312,
"title": "Consulenza gratuita",
"slug": "consulenza-gratuita"
},
"funnel": {
"id": 21,
"name": "Funnel consulenza",
"slug": "funnel-consulenza"
}
}
],
"links": {
"first": "https://www.coonvert.com/api/v1/orders?status=pagato&page=1",
"last": "https://www.coonvert.com/api/v1/orders?status=pagato&page=3",
"prev": null,
"next": "https://www.coonvert.com/api/v1/orders?status=pagato&page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://www.coonvert.com/api/v1/orders?status=pagato&page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "https://www.coonvert.com/api/v1/orders?status=pagato&page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "https://www.coonvert.com/api/v1/orders",
"per_page": 100,
"to": 1,
"total": 201
}
}L'importo amount è in centesimi della currency. La forma è quella del corpo dell'evento order.paid, con in più order.updated_at; lead, page e funnel sono null quando l'ordine non li ha. Errori possibili: 401, 403, 422, 429.
GET/orders/{id}
Un ordine, nella stessa forma, dentro data. Errori possibili: 401, 403, 404, 429.
Webhook
Con i webhook Coonvert chiama il tuo indirizzo quando succede qualcosa. Si configurano in Integrazioni → Webhook: indirizzo pubblico (https consigliato), eventi da ricevere e un secret per la firma.
| Evento | Quando | Corpo |
|---|---|---|
lead.created | Arriva un lead nuovo, da qualunque fonte (anche dall'API). | Il lead. |
lead.updated | Cambiano stato, nome, email, telefono, valore o campi personalizzati; cambiano anche le note, che l'evento segnala ma non fanno parte del lead nel corpo. Cambiano anche i gruppi. I richiami no. | Il lead più origine. |
lead.deleted | Un lead viene eliminato: dall'API o dal pannello, uno alla volta o in blocco. Non parte quando si elimina l'intero account. | id, email, deleted_at, origine. |
order.paid | Un ordine risulta pagato (con i pagamenti nel piano). | order, lead, page, funnel, come in GET /orders. |
order.refunded | Un ordine pagato passa a rimborsato; parte una volta sola. | Lo stesso corpo di order.paid. L'endpoint di notifica di uno step di pagamento riceve anche order.refunded: distingui i due con l'intestazione X-Coonvert-Event o con order.status. |
page.published | Una pagina viene pubblicata. | id, title, slug, status, published_at. |
La richiesta è un POST con il JSON nel corpo, senza involucro: il corpo è direttamente il lead (o la pagina). Il lead ha la stessa forma dell'API, senza i campi deprecati utm_source e utm_campaign. Intestazioni:
Content-Type: application/json
X-Coonvert-Event: lead.updated
X-Coonvert-Signature: sha256=<HMAC-SHA256 del corpo con il secret, in esadecimale>Corpo di lead.updated
{
"id": 4821,
"name": "Mario Rossi",
"email": "mario.rossi@example.com",
"phone": "+393331234567",
"page": "Consulenza gratuita",
"page_id": 312,
"form_id": null,
"status": "convertito",
"valore": 1250.5,
"convertito_at": "2026-10-27T16:40:00+00:00",
"source": "form",
"source_detail": null,
"data": {
"messaggio": "Vorrei un preventivo per il mio ristorante"
},
"utm": {
"source": "facebook",
"medium": "cpc",
"campaign": "autunno-2026",
"term": null,
"content": null
},
"gruppi": [
"Caldi"
],
"campi": {
"budget": 5000,
"settore": "Ristorazione"
},
"richiamo": {
"quando": "2026-10-29T09:00:00+00:00",
"nota": "Mandare il preventivo"
},
"created_at": "2026-10-25T09:12:30+00:00",
"updated_at": "2026-10-27T16:40:00+00:00",
"origine": "coonvert"
}Corpo di lead.deleted
{
"id": 4821,
"email": "mario.rossi@example.com",
"deleted_at": "2026-10-30T11:05:00+00:00",
"origine": "api"
}Il pulsante «Test» del pannello manda invece {"test": true, "message": "Evento di test da Coonvert"}.
Verificare la firma
Calcola l'HMAC-SHA256 del corpo così come è arrivato (prima di decodificare il JSON) e confrontalo con l'intestazione in tempo costante.
PHP
<?php
$secret = getenv('COONVERT_WEBHOOK_SECRET');
$corpo = file_get_contents('php://input');
$firma = $_SERVER['HTTP_X_COONVERT_SIGNATURE'] ?? '';
$attesa = 'sha256=' . hash_hmac('sha256', $corpo, $secret);
if (! hash_equals($attesa, $firma)) {
http_response_code(401);
exit;
}
$evento = $_SERVER['HTTP_X_COONVERT_EVENT'] ?? '';
$lead = json_decode($corpo, true);
// ... salva il lead, poi rispondi 2xx
http_response_code(200);Node (Express)
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/coonvert', express.raw({ type: 'application/json' }), (req, res) => {
const attesa = Buffer.from(
'sha256=' + crypto.createHmac('sha256', process.env.COONVERT_WEBHOOK_SECRET).update(req.body).digest('hex')
);
const firma = Buffer.from(req.get('X-Coonvert-Signature') || '');
if (firma.length !== attesa.length || !crypto.timingSafeEqual(firma, attesa)) {
return res.sendStatus(401);
}
const evento = req.get('X-Coonvert-Event');
const lead = JSON.parse(req.body.toString('utf8'));
// ... salva il lead
res.sendStatus(200);
});Tentativi
Una consegna riesce se il tuo indirizzo risponde con un codice 2xx entro 10 secondi. Altrimenti Coonvert riprova: al massimo 3 tentativi in tutto, il secondo dopo circa 10 secondi e il terzo dopo circa 1 minuto. Le consegne fallite restano in Integrazioni → Webhook con il motivo e si possono rispedire. Rispondi subito e fai il lavoro lungo dopo; lo stesso evento può arrivare più di una volta, quindi riconoscilo per id e updated_at.
Il campo origine: niente eco
Se il tuo CRM aggiorna un lead con il PATCH, Coonvert manda anche a te il lead.updated di quella modifica. Per non riapplicarla (e rimandarla, in un giro senza fine) guarda origine:
"api": la modifica è arrivata dall'API, quindi probabilmente da te. Puoi ignorarla."coonvert": la modifica è nata in Coonvert (pannello, automazioni). Applicala al CRM.
origine c'è solo in lead.updated. Se più sistemi usano l'API sullo stesso account, confronta anche i valori con quelli che hai già prima di scartare l'evento.
Webhook via API
Gli stessi webhook del pannello si gestiscono anche via API: serve a Zapier, Make e simili, che si iscrivono da soli. Le iscrizioni fatte via API compaiono in Integrazioni → Webhook con origine "api"; quelle fatte dal pannello hanno "pannello" e si leggono nello stesso elenco. Il tetto è di 25 iscrizioni per account: contano tutte, quelle create via API e quelle create dal pannello, e oltre il tetto non se ne aggiungono altre né da una parte né dall'altra.
Questi tre percorsi li usa solo il titolare (owner) dell'account, con un piano che include i webhook.
GET/webhooks
Le iscrizioni dell'account, dalla più vecchia. Il secret non c'è: si vede solo alla creazione.
{
"data": [
{
"id": 17,
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
"events": [
"lead.created",
"lead.updated"
],
"is_active": true,
"origine": "api",
"created_at": "2026-10-30T10:15:00+00:00"
}
]
}POST/webhooks
Si iscrive un indirizzo. Campi (tutti obbligatori):
url: indirizzo pubblico (https consigliato), massimo 255 caratteri. Indirizzi interni o privati sono rifiutati;events: elenco con almeno un evento tralead.created,lead.updated,lead.deleted,page.published,order.paid,order.refunded.
curl -X POST https://www.coonvert.com/api/v1/webhooks \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/", "events": ["lead.created", "lead.updated"] }'Risposta (201)
{
"data": {
"id": 17,
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
"events": [
"lead.created",
"lead.updated"
],
"is_active": true,
"origine": "api",
"created_at": "2026-10-30T10:15:00+00:00",
"secret": "e3b0c44298fc1c149afbf4c8996fb924"
}
}Il secret arriva solo in questa risposta: conservalo, serve a verificare la firma delle consegne. Quando le iscrizioni (anche quelle del pannello) sono già 25 la risposta è 422 («Hai raggiunto il numero massimo di webhook (25).»).
DELETE/webhooks/{id}
Toglie l'iscrizione. Risposta 204, senza corpo; 404 se non esiste o è di un altro account.
Da Zapier o Make
Il modo di lavorare è sempre lo stesso: quando lo Zap (o lo scenario) si accende, chiama POST /webhooks con l'indirizzo che gli dà la piattaforma e salva l'id e il secret della risposta. Quando si spegne, chiama DELETE /webhooks/{id}. Per ogni consegna, controlla la firma con il secret ricevuto alla creazione, come in «Verificare la firma».
Errori possibili: 401, 403 («Il tuo piano non include i webhook.», «Solo il titolare dell'account può gestire i webhook.», «Questo account è in sola lettura.» per POST e DELETE), 404, 422, 429.
Gruppi e campi
Due elenchi in sola lettura, utili per riempire i menu a tendina nei programmi collegati. Gruppi e campi si creano dal pannello. Il webhook lead.updated arriva anche quando cambiano i gruppi (dal pannello o via API), una sola volta per modifica, anche se cambiano insieme stato e gruppi.
GET/groups
I gruppi dell'account, in ordine alfabetico.
{
"data": [
{
"id": 9,
"nome": "Caldi",
"colore": "#ef4444"
}
]
}GET/lead-fields
I campi personalizzati. chiave è quella da usare in campi; tipo è uno tra testo, testo_lungo, numero, data, scelta, si_no. opzioni è l'elenco dei valori ammessi per i campi di tipo scelta e null per tutti gli altri.
{
"data": [
{
"chiave": "settore",
"etichetta": "Settore",
"tipo": "scelta",
"opzioni": [
"Ristorazione",
"Hotel",
"Negozi"
]
}
]
}Errori possibili: 401, 403, 429.
Automazioni
Per far entrare un lead in un'automazione (o toglierlo) da un altro programma. Servono un piano che include le automazioni e il permesso di modificare i lead. Le automazioni si costruiscono dal pannello.
GET/automations
Le automazioni dell'account, in ordine alfabetico. Il filtro facoltativo ?stato= accetta attiva, bozza o in_pausa; un altro valore viene ignorato e l'elenco è completo.
{
"data": [
{
"id": 5,
"nome": "Prova di 15 giorni",
"stato": "attiva"
}
]
}POST/automations/{id}/leads
Fa entrare un lead in un'automazione attiva. Campi: lead_id (obbligatorio) e rientra (facoltativo, true per far rientrare un lead che ci è già passato).
curl -X POST https://www.coonvert.com/api/v1/automations/5/leads \
-H "Authorization: Bearer $COONVERT_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "lead_id": 4821 }'Risposta (201)
{
"data": {
"id": 733,
"automazione_id": 5,
"lead_id": 4821,
"ingresso": 1,
"stato": "in_corso",
"motivo": null,
"entrato_at": "2026-10-30T10:20:00+00:00",
"uscito_at": null
}
}stato è in_corso, finito oppure uscito; motivo dice perché è finito o uscito. Se il lead non entra la risposta è 422 con un motivo leggibile dal programma:
{
"message": "Il lead non è entrato: non ha un'email.",
"motivo": "senza_email"
}motivo | Significato |
|---|---|
senza_email | L'automazione parte solo da lead con email e questo non ce l'ha. |
gia_dentro | Il lead è già dentro l'automazione. |
gia_passato | Ci è già passato e l'automazione non ammette un secondo giro: con "rientra": true entra comunque. |
altro_tenant, senza_inizio | Non può entrare (automazione senza inizio valido). |
Con un'automazione non attiva la risposta è 422 («L'automazione non è attiva.», senza motivo).
DELETE/automations/{id}/leads/{lead_id}
Toglie il lead dall'automazione: il percorso in corso diventa uscito con motivo a_mano. Risponde 200 con il percorso; 404 («Il lead non è dentro questa automazione.») se non c'è un percorso in corso.
Errori possibili per i due percorsi: 401, 403 («Il tuo piano non include le automazioni.», «Il tuo ruolo non permette di modificare i lead.», «Questo account è in sola lettura.», «Le pagine di questo account sono sospese.», «Questo account è archiviato.»), 404 (automazione o lead inesistenti), 422, 429.
Limiti, errori, idempotenza
Limite di richieste
Ogni token può fare 120 richieste al minuto. Ogni risposta indica il limite e quante richieste restano:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119Oltre il limite la risposta è 429 con l'intestazione Retry-After (secondi da aspettare). Aspetta quel tempo e riprova.
Errori
Gli errori sono sempre JSON con un message in italiano. Gli errori di validazione (422) hanno anche errors, con i messaggi per campo:
{
"message": "Indica una pagina oppure un form, non entrambi.",
"errors": {
"page_id": [
"Indica una pagina oppure un form, non entrambi."
]
}
}| Codice | Quando | Messaggio |
|---|---|---|
401 | Token mancante, sbagliato o revocato. | Token mancante o non valido. |
402 | POST /leads oltre il limite dei lead del piano. | Limite lead del piano raggiunto. |
403 | Il piano non include l'API. | Questa funzione non è inclusa nel tuo piano. |
| L'utente del token non ha più accesso all'account. | Il token non ha più accesso a questo account. | |
| Il ruolo dell'utente non mostra i dati dei lead. | Il tuo ruolo non permette di vedere i dati dei lead. | |
PATCH con un ruolo che non modifica i lead. | Il tuo ruolo non permette di modificare i lead. | |
PATCH, POST …/outcomes o DELETE su un cliente in attesa di decisione. | Questo account è in sola lettura. | |
DELETE /leads/{id} con un ruolo che non elimina i lead. | Il tuo ruolo non permette di eliminare i lead. | |
/orders senza i pagamenti nel piano. | Il tuo piano non include i pagamenti. | |
404 | Risorsa inesistente o di un altro account; nel POST anche pagina non pubblicata o pagine dell'account sospese. | Risorsa non trovata. |
405 | Metodo non previsto per quell'indirizzo (ad esempio PUT su un lead). L'intestazione Allow elenca i metodi ammessi. | Metodo non supportato. |
409 | Due POST insieme con la stessa Idempotency-Key. | Richiesta concorrente in corso con la stessa Idempotency-Key, riprova. |
422 | Dati non validi (il messaggio dipende dal campo). | es. Lo stato indicato non esiste. |
valore su un lead non convertito. | Il valore si imposta solo su un lead convertito. | |
richiamo senza data, nel passato o su un lead chiuso (PATCH). | Indica quando richiamare. · Il richiamo deve essere nel futuro. · Un lead chiuso non può avere un richiamo. | |
page_id e form_id insieme. | Indica una pagina oppure un form, non entrambi. | |
429 | Troppe richieste. | Troppe richieste: riprova tra N secondi. |
Idempotenza
Un POST /leads ripetuto (timeout, rete che cade) può creare due lead uguali. Per evitarlo manda l'intestazione Idempotency-Key con un valore unico per ogni lead, ad esempio l'id del contatto nel tuo CRM (massimo 255 caratteri).
Per 24 ore la stessa chiave restituisce il lead già creato invece di crearne un altro. Se la prima richiesta è fallita (es. 402) non resta nulla: puoi riprovare con la stessa chiave. Il PATCH non ne ha bisogno: ripetuto, non cambia niente.
Novità e compatibilità
Da ottobre 2026:
GET /pagesè a pagine eGET /leadsrisponde condata,linksemeta: il totale è inmeta.totale non più in cima alla risposta. Chi legge solodatadella prima pagina non deve cambiare niente.- Il lead ha la forma completa dei webhook.
utm_sourceeutm_campaignrestano ma sono deprecati: usautm. - Nuovi (ottobre 2026, seconda parte):
/webhooks,/groups,/lead-fields,/automations, i gruppi inPOSTePATCH /leadselead.updatedquando cambiano i gruppi. - Nuovi (30 ottobre 2026):
GET /orderseGET /orders/{id},GET /leads/{id}/activities,POST /leads/{id}/outcomes,DELETE /leads/{id},richiamoenotesnelPATCH(erichiamonel lead), gli eventilead.deleted,order.paideorder.refunded. - Nuovi: i filtri di
GET /leads,PATCH /leads/{id},POST /leadssenza pagina o con un form, il limite di 120 richieste al minuto eorigineinlead.updated.