Webhook
Registra endpoint che ricevono notifiche firmate degli eventi.
Crea un webhook
Crea un endpoint webhook nel workspace. Emailit invia gli eventi corrispondenti all’URL in batch fino a 100, come array JSON firmato con il secret del webhook. Per il formato delle richieste, vedi Richieste webhook. Richiede una chiave API con il permesso full.
/webhooksCorpo della richiesta
namestringObbligatorioNome del webhook. Deve essere univoco nel workspace; puoi usarlo al posto dell’ID negli altri endpoint dei webhook.
urlstringObbligatorioEndpoint che riceve gli eventi. Sono accettati URL http e https; in produzione usa https.
Emailit risolve il nome host al salvataggio e rifiuta localhost, gli indirizzi IP privati, link-local e gli altri indirizzi riservati. Durante la consegna i reindirizzamenti non vengono seguiti, quindi usa l’URL finale.
all_eventsbooleanInvia tutti i tipi di evento, compresi quelli aggiunti in futuro. Il valore predefinito è false. Quando è true, events viene ignorato.
enabledbooleanIndica se Emailit consegna gli eventi al webhook. Il valore predefinito è true.
eventsstring[]Tipi di evento da inviare, ad esempio ["email.delivered", "email.bounced"]. Vedi Tipi di evento. Il valore predefinito è [], che con all_events: false significa che il webhook non riceve nulla.
I nomi degli eventi non vengono convalidati. Un tipo scritto male viene salvato ma non corrisponde mai a nessun evento.
filterobject | nullFiltro sul payload. Emailit invia solo gli eventi il cui oggetto corrisponde alle regole. Disponibile nei piani Pro, Business e Custom; con Pay as you go un filtro con regole restituisce 403.
filter.matchstringall (predefinito) invia un evento quando corrispondono tutte le regole. any lo invia quando corrisponde almeno una regola.
filter.rulesobject[]Fino a 25 regole.
filter.rules[].fieldstringObbligatorioPercorso con punti all’interno dell’oggetto dell’evento, ad esempio to, status, meta.plan o, per gli eventi di clic e apertura, email.campaign.id. Un prefisso payload. iniziale viene ignorato.
filter.rules[].operatorstringObbligatorioequals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than, is_set, is_not_set, in o not_in. Gli operatori di testo confrontano i valori come stringhe; greater_than e less_than confrontano numeri.
filter.rules[].valueanyValore con cui confrontare. Obbligatorio per tutti gli operatori tranne is_set e is_not_set. Con in e not_in usa un array.
Restituisce
Restituisce 201 Created con l’oggetto webhook, compreso il secret di firma (whsec_ seguito da 64 caratteri esadecimali). Usa il secret per verificare le firme delle richieste. Puoi leggerlo di nuovo con Recupera un webhook e ruotarlo con Ruota il secret di firma.
| Stato | Quando |
|---|---|
400 |
Manca name o url, l’URL non è valido, non può essere risolto o punta a un indirizzo bloccato, oppure il filtro non è valido. |
403 |
Il filtro ha delle regole e il piano non include i filtri dei webhook. Il corpo è {"error": "plan_required", "required_plan": "pro"}. |
409 |
Esiste già un webhook con questo nome. Il corpo include id e name del webhook esistente in existing. |
422 |
Il workspace ha raggiunto il limite di webhook del piano. Il corpo include usage.used e usage.limit. Vedi Limiti e quote. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": null,
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "URL resolves to a private/reserved IP address"
}{
"error": "plan_required",
"required_plan": "pro"
}{
"error": "Webhook with this name already exists",
"existing": {
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events"
}
}{
"error": "Pay as you go includes 3 webhook endpoints.",
"usage": {
"used": 3,
"limit": 3
}
}{
"name": "Enterprise bounces",
"url": "https://api.acme.com/webhooks/emailit",
"events": ["email.bounced", "email.complained"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" },
{ "field": "to", "operator": "not_contains", "value": "@acme.com" }
]
}
}Recupera un webhook
Restituisce un webhook, cercato per ID o per nome. È l’unico endpoint di lettura che restituisce il secret di firma. Richiede una chiave API con il permesso full.
/webhooks/:idParametri di percorso
idstringObbligatorioID del webhook (wh_…) o nome del webhook, codificato per l’URL.
Restituisce
Restituisce 200 OK con l’oggetto webhook, compresi secret e filters_allowed (indica se il piano consente al webhook di usare un filtro sul payload). last_used_at è il momento dell’ultima consegna riuscita, oppure null se non è stato ancora consegnato nulla.
Restituisce 404 con error: "Webhook not found" se nessun webhook corrisponde.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" }
]
},
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "Webhook not found"
}Aggiorna un webhook
Aggiorna un webhook. Invia solo i campi che vuoi modificare; ne serve almeno uno. Il secret di firma non cambia; per ruotarlo usa Ruota il secret di firma. Richiede una chiave API con il permesso full.
/webhooks/:idParametri di percorso
idstringObbligatorioID del webhook (wh_…) o nome del webhook, codificato per l’URL.
Corpo della richiesta
namestringNuovo nome. Deve essere univoco nel workspace.
urlstringNuovo URL dell’endpoint, http o https. Viene convalidato come in creazione.
all_eventsbooleantrue invia tutti i tipi di evento e svuota l’elenco events. Se lo imposti su false, invia anche events, altrimenti il webhook non riceve nulla.
enabledbooleanfalse interrompe le consegne e true le riprende. Gli eventi che si verificano mentre il webhook è disattivato non vengono messi in coda per il webhook e non vengono inviati in seguito.
eventsstring[]Sostituisce l’elenco dei tipi di evento. Viene ignorato finché all_events è true. I nomi non vengono convalidati.
filterobject | nullSostituisce il filtro sul payload, nello stesso formato usato in creazione. Invia null per rimuoverlo. Un filtro con regole richiede un piano Pro, Business o Custom.
Restituisce
Restituisce 200 OK con il webhook aggiornato. Il secret non è incluso; per leggerlo usa Recupera un webhook.
| Stato | Quando |
|---|---|
400 |
Il corpo non contiene nessuno dei campi indicati sopra, oppure l’URL o il filtro non sono validi. |
403 |
Il filtro ha delle regole e il piano non include i filtri dei webhook (plan_required). |
404 |
Nessun webhook corrisponde a id. |
409 |
Un altro webhook usa già il nuovo nome. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": false,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:15:40.000000+00:00"
}{
"error": "No valid fields provided for update. Provide at least one of: name, url, all_events, enabled, events, filter"
}{
"error": "Webhook not found"
}{
"error": "Another webhook with this name already exists"
}Elenca i webhook
Restituisce i webhook del workspace, a partire dal più recente, e quanti ne consente il piano. I secret di firma non sono inclusi nell’elenco. Richiede una chiave API con il permesso full.
/webhooksParametri di query
pageintegerNumero di pagina, a partire da 1. Il valore predefinito è 1.
limitintegerWebhook per pagina, da 1 a 100. Il valore predefinito è 10.
searchstringCorrispondenza sul nome o sull’URL del webhook, senza distinzione tra maiuscole e minuscole.
matchstringall (predefinito) richiede che corrispondano tutti i filtri. or richiede che ne corrisponda almeno uno. Vedi Filtri e ordinamento.
orderstringChiave di ordinamento di questo elenco. Vedi le chiavi di ordinamento qui sotto.
directionstringasc o desc.
Filtri e ordinamento
I filtri degli elenchi sono un unico livello di parametri di query key.condition=value. Vedi Filtri e ordinamento per match, order, direction e l’elenco delle condizioni per tipo.
Chiavi di filtro
| Chiave | Tipo | Condizioni | Note |
|---|---|---|---|
name | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
url | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
enabled | boolean | exact, not_exact | |
created_at | date | exact, before, after, empty, not_empty |
Chiavi di ordinamento
Passa in order una di queste chiavi e in direction il valore asc o desc: name, url, enabled, created_at
Restituisce
Restituisce 200 OK con i webhook in data, next_page_url e previous_page_url (null alle due estremità) e un oggetto usage: used è il numero di webhook nel workspace, limit è il massimo consentito dal piano e filters_allowed indica se il piano include i filtri sul payload.
{
"data": [
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:02:17.845000+00:00"
}
],
"next_page_url": null,
"previous_page_url": null,
"usage": {
"used": 1,
"limit": 10,
"filters_allowed": true
}
}{
"statusCode": 401,
"error": "Unauthorized",
"message": "Invalid API key"
}Elimina un webhook
Elimina definitivamente un webhook e le sue iscrizioni agli eventi. Per sospendere temporaneamente le consegne, aggiorna il webhook con enabled: false. Richiede una chiave API con il permesso full.
/webhooks/:idParametri di percorso
idstringObbligatorioID del webhook (wh_…) o nome del webhook, codificato per l’URL.
Restituisce
Restituisce 200 OK con id e name del webhook eliminato e deleted: true. Restituisce 404 con error: "Webhook not found" se nessun webhook corrisponde.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"deleted": true
}{
"error": "Webhook not found"
}Invia un evento di test
Invia un evento di esempio del tipo che scegli all’URL del webhook e restituisce la risposta del tuo endpoint. Usalo per controllare che l’endpoint sia raggiungibile e verifichi correttamente le firme. Richiede una chiave API con il permesso full.
La richiesta ha lo stesso formato, gli stessi header e la stessa firma di una consegna reale: un array JSON con un evento il cui event_id inizia con evt_test_, firmato con il secret attuale del webhook. Viene inviata anche se il webhook è disattivato o non è iscritto a quel tipo, non viene salvata come richiesta webhook e non viene ritentata. I dati di esempio sono fissi e non si riferiscono a oggetti reali.
Puoi inviare 5 eventi di test al minuto dallo stesso indirizzo IP; oltre questo limite viene restituito 429.
/webhooks/{id}/testParametri di percorso
idstringobbligatoriowh_…) o il nome del webhook.Parametri del corpo
typestringobbligatorio| Risorsa | Tipi di evento |
|---|---|
email.accepted, email.scheduled, email.delivered, email.bounced, email.attempted, email.failed, email.rejected, email.clicked, email.loaded, email.complained, email.received, email.suppressed, email.canceled, email.unsubscribed, email.resubscribed |
|
| Dominio | domain.created, domain.updated, domain.deleted |
| Lista | audience.created, audience.updated, audience.deleted |
| Iscritto | subscriber.created, subscriber.updated, subscriber.deleted |
| Contatto | contact.created, contact.updated, contact.deleted |
| Template | template.created, template.updated, template.deleted |
| Soppressione | suppression.created, suppression.updated, suppression.deleted |
| Verifica email | email_verification.created, email_verification.updated, email_verification_list.created, email_verification_list.updated |
| Campagna | campaign.created, campaign.updated, campaign.deleted, campaign.scheduled, campaign.queued, campaign.sending, campaign.testing, campaign.sent, campaign.canceled, campaign.archived |
Per il significato di ogni evento, vedi Tipi di evento.
Restituisce
okbooleantrue se l’endpoint ha risposto con uno stato 2xx.status_codeinteger0 se Emailit non è riuscito a connettersi, se la richiesta è andata in timeout dopo 30 secondi, se l’endpoint ha restituito un reindirizzamento (i reindirizzamenti non vengono seguiti) o se l’URL punta a un indirizzo bloccato.bodystringtypestringpayloadobject[]Restituisce 400 se type manca o è sconosciuto, 404 se il webhook non esiste e 429 quando superi il limite dei test.
{
"ok": true,
"status_code": 200,
"type": "email.delivered",
"payload": [
{
"event_id": "evt_test_3G0pUekgBm1uIxi9m4C380H70Zq",
"type": "email.delivered",
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
},
"data": {
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
}
}
}
],
"body": "{\"received\":true}"
}{
"error": "Unknown event type"
}{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit exceeded, retry in 1 minute"
}Ruota il secret di firma
Genera un nuovo secret di firma per il webhook e lo restituisce. Richiede una chiave API con il permesso full.
Il vecchio secret smette subito di essere usato: ogni richiesta inviata dopo la rotazione, compresi i nuovi tentativi di eventi precedenti, viene firmata con il nuovo secret. Non c’è un periodo di sovrapposizione, quindi aggiorna il secret nel tuo endpoint subito dopo la rotazione, oppure accetta entrambi i secret per un breve periodo durante il passaggio. Vedi Verifica le firme dei webhook.
/webhooks/{id}/reset-secretParametri di percorso
idstringobbligatoriowh_…) o il nome del webhook.Restituisce
Restituisce l’oggetto webhook con il nuovo secret (whsec_ seguito da 64 caratteri esadecimali). Restituisce 404 se il webhook non esiste.
{
"object": "webhook",
"id": "wh_3NGa6raJ3s1VJ75lO9km6Dqg4cQ",
"name": "Order notifications",
"url": "https://acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced", "email.complained"],
"filter": null,
"last_used_at": "2026-10-01T12:58:40.000000+00:00",
"created_at": "2026-08-14T09:12:03.000000+00:00",
"updated_at": "2026-10-01T13:20:11.000000+00:00",
"secret": "whsec_a0a593d006bdec5aff2f21e88afd543ba239431acbe7d8dc7a9723d76e0a64e2"
}{
"error": "Webhook not found"
}Ritenta le richieste non riuscite
Rimette in coda per la consegna tutte le richieste di questo webhook non riuscite definitivamente negli ultimi 7 giorni. Richiede una chiave API con il permesso full.
Una richiesta non riesce definitivamente dopo l’ultimo nuovo tentativo automatico (11 tentativi nell’arco di diversi giorni; vedi Nuovi tentativi ed errori). Le richieste ritentate ripartono con un calendario completo di nuovi tentativi e vengono consegnate entro pochi secondi. Se almeno una richiesta viene messa in coda e il webhook era disattivato, ad esempio dopo 3 giorni di errori continui, il webhook viene riattivato.
Prima correggi l’endpoint, altrimenti le richieste non riusciranno di nuovo. Per ritentare una sola richiesta, usa Ritenta una richiesta.
/webhooks/{id}/retry-failedParametri di percorso
idstringobbligatoriowh_…) o il nome del webhook.Restituisce
retriedinteger0 se non c’era nulla da ritentare; in quel caso lo stato di attivazione del webhook non cambia.Restituisce 404 se il webhook non esiste.
{
"retried": 37
}{
"error": "Webhook not found"
}Ritenta una richiesta
Rimette in coda per la consegna una richiesta webhook non riuscita definitivamente, con un nuovo calendario dei nuovi tentativi. Se il webhook era disattivato, viene riattivato. Richiede una chiave API con il permesso full.
In questo modo puoi ritentare solo le richieste che hanno esaurito i nuovi tentativi automatici; le richieste ancora in sospeso o in fase di nuovo tentativo restituiscono 400. Trovi gli ID delle richieste (whr_…) nella scheda Requests del webhook in Email APIWebhooks. Per ritentare in una volta sola tutto quello degli ultimi 7 giorni, usa Ritenta le richieste non riuscite.
/webhooks/{id}/requests/{request_id}/retryParametri di percorso
idstringobbligatoriowh_…) o il nome del webhook.request_idstringobbligatoriowhr_…).Restituisce
retriedinteger1.idstring| Stato | Quando |
|---|---|
400 |
La richiesta non è in errore definitivo, oppure non ha un evento da inviare di nuovo. |
404 |
Il webhook non esiste, oppure la richiesta non gli appartiene. |
{
"retried": 1,
"id": "whr_3tWVVMd2eHv3R9B5DWTLtwwU05X"
}{
"error": "Only permanently failed requests can be retried"
}{
"error": "Webhook request not found"
}