{
  "openapi": "3.0.3",
  "info": {
    "title": "Mailpro™ CRM API v3",
    "version": "3",
    "description": "L'API CRM Mailpro™ v3 consente di gestire programmaticamente contatti, liste, tag,\ncampi personalizzati, template email e campagne — e di inviare sia email\nmarketing in massa sia email transazionali individuali, nonché SMS.\n\n## Autenticazione\n\nTutti gli endpoint (eccetto `POST /token` e gli endpoint OAuth) accettano **tre\nmetodi di autenticazione**. Utilizzate quello più adatto al vostro stack.\n\n- **OAuth 2.0** (authorization_code + PKCE, oppure client_credentials) — la\n  scelta moderna per applicazioni di terze parti e piattaforme di integrazione\n  (Zapier, Make, n8n) che agiscono per conto di un utente Mailpro. Emette\n  access token revocabili e ristretti a determinati ambiti. **L'unico metodo\n  che gestisce scope granulari e che potete affidare in sicurezza a terze parti.**\n- **Bearer JWT** (legacy) — un token HMAC-HS256 ottenuto tramite `POST /token`.\n  Assomiglia a OAuth ma è legato a un'unica credenziale account e non è\n  associato ad alcun ambito.\n- **IdClient + ApiKey** (legacy) — il vostro client ID e la chiave API negli\n  header HTTP.\n\n> Il Bearer JWT legacy e IdClient+ApiKey **non sono soggetti a controllo degli\n> ambiti** — conservano accesso completo a ogni endpoint che potevano già\n> raggiungere. Soltanto gli access token OAuth 2.0 sono soggetti ai controlli\n> di ambito per endpoint mostrati per ciascuna operazione qui di seguito (un\n> token privo dell'ambito riceve **403 `insufficient_scope`**).\n\n### Metodo 1 — OAuth 2.0 (consigliato per le integrazioni)\n\nMailpro gestisce **un unico** authorization server OAuth 2.0 per tutte le sue\nAPI, esposto su un'unica superficie neutra rispetto alla versione:\n**`https://api.mailpro.com/oauth/`**. Un solo server, un solo token — il\n**medesimo** access token funziona su CRM v3, Email v2 e SMS. Gli endpoint\nche un token può chiamare sono determinati dagli **scope** del token, non\ndall'URL base dell'API chiamata. Non esiste un endpoint OAuth per ciascuna\nversione (nessun `/v3/oauth`, `/v2/oauth` o `/sms/oauth`).\n\n| Endpoint | URL |\n|----------|-----|\n| Authorize | `GET https://api.mailpro.com/oauth/authorize` |\n| Token / Refresh | `POST https://api.mailpro.com/oauth/token` |\n| JWKS (verifica firme) | `GET https://api.mailpro.com/oauth/.well-known/jwks` |\n| Discovery | `GET https://api.mailpro.com/oauth/.well-known/openid-configuration` |\n\nIssuer (la claim `iss` su ogni token): `https://api.mailpro.com/oauth/`.\n\n**Grant supportati**\n\n- `authorization_code` **+ PKCE** (`code_challenge_method=S256` è\n  **obbligatorio**) — il flusso di consenso via browser \"Connect Mailpro\".\n  Restituisce un access token **e** un refresh token rotativo.\n- `client_credentials` — da macchina a macchina (M2M), senza utente. Restituisce solo\n  un access token.\n- `refresh_token` — scambia un refresh token per una nuova coppia\n  access/refresh. **I refresh token ruotano**: ogni utilizzo invalida il\n  precedente.\n\n**Durata dei token**\n\n| Token | TTL |\n|-------|-----|\n| Access token (RS256 JWT) | **20 minuti** |\n| Refresh token (scorrevole, monouso) | **30 giorni** |\n| Authorization code | **60 secondi** |\n\n**Authorization Code + PKCE — passo dopo passo**\n\n1. Generare il `code_verifier` (43–128 caratteri casuali) e\n   `code_challenge = base64url(sha256(code_verifier))`.\n2. Reindirizzate l'utente a:\n   ```\n   GET https://api.mailpro.com/oauth/authorize\n     ?response_type=code\n     &client_id=YOUR_CLIENT_ID\n     &redirect_uri=https://yourapp.example.com/callback\n     &scope=contacts:read contacts:write\n     &state=RANDOM_CSRF_TOKEN\n     &code_challenge=CODE_CHALLENGE\n     &code_challenge_method=S256\n   ```\n3. L'utente approva sulla schermata di consenso di Mailpro; Mailpro effettua\n   il reindirizzamento di ritorno con `?code=…&state=…`. Verificate `state`\n   (protezione CSRF).\n4. Scambiate il codice con i token:\n   ```\n   POST https://api.mailpro.com/oauth/token\n   Content-Type: application/x-www-form-urlencoded\n\n   grant_type=authorization_code&code=AUTH_CODE\n   &redirect_uri=https://yourapp.example.com/callback\n   &client_id=YOUR_CLIENT_ID&code_verifier=CODE_VERIFIER\n   ```\n   ```json\n   { \"access_token\": \"eyJ…\", \"token_type\": \"Bearer\",\n     \"expires_in\": 1200, \"refresh_token\": \"…\" }\n   ```\n5. Chiamate qualsiasi API Mailpro con `Authorization: Bearer ACCESS_TOKEN`.\n6. Alla scadenza, rinnovate con `grant_type=refresh_token` e memorizzate il\n   **nuovo** refresh token (il vecchio è ormai inutilizzabile).\n\n**client_credentials**\n\n```\nPOST https://api.mailpro.com/oauth/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=client_credentials&client_id=YOUR_CLIENT_ID\n&client_secret=YOUR_CLIENT_SECRET&scope=contacts:read email:send\n```\n\nIl medesimo access token OAuth è accettato anche dalle API Email v2\n(`api.mailpro.com/v2`) e SMS (`api.mailpro.com/sms`) — autenticatevi una\nsola volta, chiamate qualsiasi API. Catalogo completo degli scope: vedere\n**Scope OAuth 2.0** qui di seguito.\n\n### Provate OAuth 2.0 in pochi minuti — quickstart\n\nIl modo più rapido per provare OAuth è il grant **client_credentials**\n(server-to-server, senza browser, senza utente). Vi serve una coppia\n`client_id` / `client_secret` (rivolgetevi al vostro referente Mailpro,\noppure createne una in **Impostazioni → Accesso API**). Sostituite i\nsegnaposto qui sotto — **non commitate mai un secret reale**.\n\n**1. Ottenete un access token**\n\n```bash\ncurl -X POST https://api.mailpro.com/oauth/token \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"grant_type=client_credentials\" \\\n  -d \"client_id=YOUR_CLIENT_ID\" \\\n  -d \"client_secret=YOUR_CLIENT_SECRET\" \\\n  -d \"scope=contacts:read\"\n```\n\n```json\n{ \"access_token\": \"eyJhbGciOiJSUzI1Ni…\", \"token_type\": \"Bearer\",\n  \"expires_in\": 1200 }\n```\n\n**2. Chiamate un endpoint di risorsa con il token**\n\n```bash\ncurl https://api.mailpro.com/v3/List \\\n  -H \"Authorization: Bearer eyJhbGciOiJSUzI1Ni…\"\n```\n\nIl **medesimo** token funziona anche su Email v2 (`…/v2/…`) e SMS\n(`…/sms/…`) — purché disponga dello scope richiesto da ciascun endpoint.\nUn token privo dello scope necessario viene rifiutato con\n**403 `insufficient_scope`**.\n\n**\"Connect Mailpro\" interattivo (authorization_code + PKCE)** — quando\nagite per conto di un *utente* (Zapier/Make/n8n, app partner): reindirizzate\nl'utente a `GET https://api.mailpro.com/oauth/authorize` con\n`response_type=code`, `client_id`, `redirect_uri`, `scope`, `state`,\n`code_challenge` e `code_challenge_method=S256`; l'utente fornisce il\nconsenso; scambiate il `code` restituito su\n`POST https://api.mailpro.com/oauth/token` con il `code_verifier`\ncorrispondente. Procedura dettagliata completa qui sopra; scope nella sezione\n**Scope OAuth 2.0** qui di seguito.\n\n> I token sono **JWT RS256** — gli access token vivono **20 minuti**, i\n> refresh token **30 giorni** (scorrevoli, monouso: ogni refresh ruota il\n> token). Verificate le firme rispetto al JWKS disponibile su\n> `https://api.mailpro.com/oauth/.well-known/jwks` e validate lo scope di\n> ogni richiesta confrontandolo con la matrice degli scope qui di seguito.\n\n### Metodo 2 — Bearer JWT (legacy)\n\n```\nAuthorization: Bearer <access_token>\n```\n\nOttenete un token tramite `POST /token` (vedi qui di seguito). Valido per\n**30 giorni** per impostazione predefinita; utilizzate\n`grant_type=refresh_token` per rinnovarlo senza riautenticarvi. Più semplice\nda configurare, ma legato a un'unica credenziale account e **impossibile**\nda delimitare ad uno scope o affidare a terze parti — preferite OAuth 2.0\nper qualsiasi integrazione installabile.\n\n### Metodo 3 — IdClient + ApiKey (più semplice)\n\nInviate il vostro client ID e la chiave API come **header HTTP** (consigliato)\noppure come **parametri di query**:\n\n```\nIdClient: 12345\nApiKey:   a1b2c3d4-e5f6-7890-abcd-ef1234567890\n```\n\nLe stesse credenziali delle API v2 / SMS — copia-incolla da\n**Impostazioni → Accesso API**. Nessuna fase di scambio token. La forma con\nheader è raccomandata perché gli URL vengono registrati nei log di IIS /\nCloudflare / proxy.\n\n### Endpoint token (grant password)\n```\nPOST /token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=password&username=YOUR_USERNAME&password=YOUR_API_KEY\n```\n- `username` — il vostro username Mailpro (email)\n- `password` — la vostra chiave API (GUID), reperibile in\n  **Impostazioni → Accesso API**\n\n### Endpoint token (grant client_credentials)\n```\nPOST /token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=client_credentials&client_id=YOUR_CLIENT_ID\n```\n\n> Il token emesso da `POST /v3/token` è valido anche sull'API Email v2\n> (`api.mailpro.com/v2`) e sull'API SMS (`api.mailpro.com/sms`).\n> Autenticatevi una sola volta, chiamate qualsiasi API.\n\n## Scope OAuth 2.0\n\nQuando la vostra applicazione richiede un access token OAuth dichiara un\ninsieme di **scope** — autorizzazioni che definiscono esattamente cosa il\ntoken può fare. L'utente vede una descrizione in linguaggio naturale di\nciascuno scope richiesto sulla schermata di consenso. Gli scope sono\nseparati da spazi nel parametro `scope`\n(`scope=contacts:read email:send`). Richiedete solo ciò di cui avete bisogno.\n\nGli scope seguono il formato `<resource>:<action>` e sono\n**gerarchicamente indipendenti** — `contacts:write` **non** implica\n`contacts:read`; richiedete entrambi se necessario.\n\n| Scope | Permette di |\n|-------|--------|\n| `contacts:read` | Visualizzare i vostri contatti, liste, tag, segmenti e campi personalizzati. |\n| `contacts:write` | Creare, modificare ed eliminare i vostri contatti, liste, tag e campi personalizzati. |\n| `email:send` | Inviare email transazionali e di campagna per vostro conto. |\n| `email:read` | Visualizzare statistiche di consegna, aperture, click e cronologia bounce. |\n| `email:templates:read` | Visualizzare i vostri template email (oggetto, HTML, corpo plain-text). |\n| `email:templates:write` | Creare, modificare ed eliminare i vostri template email. |\n| `sms:send` | Inviare SMS transazionali e di campagna per vostro conto. |\n| `sms:read` | Visualizzare il saldo crediti SMS e la cronologia messaggi. |\n| `sms:contacts:read` | Visualizzare la vostra rubrica SMS. |\n| `sms:contacts:write` | Creare, modificare ed eliminare voci nella vostra rubrica SMS. |\n| `webhooks:manage` | Sottoscrivere, modificare e rimuovere endpoint webhook per il vostro account. |\n| `account:read` | Visualizzare i crediti, i limiti e lo stato dell'abbonamento del vostro account. |\n\n### Applicazione degli scope per endpoint\n\nI controlli sugli scope sono **attivi** e applicati per ciascun endpoint per\ni token emessi via OAuth (l'autenticazione legacy è esente). Ogni operazione\nin questo riferimento elenca lo scope richiesto sotto **Authorization**.\nSintesi per la superficie CRM v3:\n\n| Endpoint | Scope richiesto |\n|-----------|----------------|\n| `GET /Contact/{id}`, `GET /List`, `GET /List/{id}`, `GET /List/{id}/Contact`, `GET /List/{listId}/Unsubscribers`, `GET /Tag`, `GET /Tag/{id}`, `GET /Tag/{id}/Contact`, `GET /Segment`, verifica appartenenza al segmento | `contacts:read` |\n| `POST /Contact`, `POST /Contact/Multiple`, `PUT`/`DELETE /Contact/{id}`, aggiungere/rimuovere un tag o una lista su un contatto, `POST`/`PUT`/`DELETE /List`, `POST`/`PUT`/`DELETE /Tag` | `contacts:write` |\n\n> Gli endpoint di questa specifica **non** elencati qui sopra (ad esempio\n> `GET /Field`, le campagne, l'invio transazionale, l'invio SMS, i template\n> di messaggio, le informazioni sull'account) sono attualmente raggiungibili\n> con qualunque token valido — non sono ancora soggetti al controllo degli\n> scope sulla superficie CRM v3. Le API Email v2 e SMS limitano inoltre i\n> propri endpoint di invio / template / contatti / account (consultate le\n> rispettive specifiche).\n\nIl rifiuto per scope insufficiente restituisce **403** con\n`{ \"error\": \"insufficient_scope\", \"required_scope\": \"…\" }` (RFC 6750 §3.1).\n\n## Formato dei dati\n\n- Tutti i corpi di richiesta/risposta utilizzano **JSON**\n- I nomi delle proprietà sono in **snake_case**\n- Gli ID dei contatti sono **UUID**\n- Gli ID di lista, tag e campo sono **interi (int64)**\n\n## Paginazione\n\n**Stessa regola sulle tre API Mailpro** (Email v2, CRM v3, SMS): **5.000 elementi per pagina,\nper impostazione predefinita e al massimo.**\n- `numberOfRecords` — elementi per pagina. Assente, `0` o oltre 5.000 = 5.000.\n- `pageNumber` — numero di pagina a partire da 1 (predefinito 1).\n- Entrambi sono accettati come parametro di query **o** come header HTTP.\n- Una pagina piena significa che potrebbero essercene altre: richiedi la pagina successiva finché\n  non ricevi una pagina incompleta.\n- Eccezione: i messaggi e la cronologia con il loro HTML (`includeBodyHTML=true`) restituiscono\n  10 elementi per pagina per impostazione predefinita.\n- Una pagina vuota, o una ricerca senza risultati, restituisce `200` con un elenco vuoto `[]`.\n  `404` significa che la risorsa stessa (elenco, tag, segmento, campagna…) non esiste.\n\n**Parametri sconosciuti**: vengono ignorati, mai rifiutati. Quando una richiesta contiene un parametro che l'API\nnon riconosce, la risposta include `X-Ignored-Parameters` (i nomi) e `X-Documentation` (un link a questa\npagina), così un errore di battitura non passa inosservato.\n\n## Codici di errore di business\n\nLe violazioni delle regole di business restituiscono HTTP **406** con un\ncorpo JSON:\n```json\n{ \"Message\": \"Contact cannot be created. Email cannot be empty.\" }\n```\n\nMotivi 406 più comuni:\n- `Email cannot be empty` — l'email è obbligatoria\n- `Email has an invalid format` — non è un indirizzo email valido\n- `A contact already exist with the same Email` — violazione di unicità email\n- `The email address was unsubscribed permanently` — indirizzo presente\n  nella blacklist\n- `The email address has identified has a hard bounce` — l'indirizzo ha\n  generato un bounce\n- `The email address has identified has a spam trap` — rilevata una spam trap\n- `At least one List is necessary` — i contatti devono appartenere ad almeno\n  una lista\n- `One or more lists have reached the contact limit` — massimo 1M di\n  contatti per lista\n- `Invalid Phone Number` — errore di formato del numero di telefono\n- `Invalid country code for Phone Number` — prefisso paese non supportato\n- `Length exceeded for Phone Number (12)` — numero di telefono troppo lungo\n\n### Codici di stato restituiti\n\n| Codice | Significato |\n|--------|-------------|\n| `400` | Richiesta malformata (JSON non valido, messaggio oltre il limite di parti SMS) |\n| `401` | Credenziali mancanti, non valide o scadute |\n| `402` | Crediti insufficienti per effettuare l'invio |\n| `403` | Consentito dalle tue credenziali, ma rifiutato — account bloccato per l'invio di SMS |\n| `404` | L'oggetto referenziato non esiste (contatto, lista, tag, campo, campagna) |\n| `406` | Violazione di una regola di business del contatto (vedi l'elenco sopra) |\n| `422` | Input rifiutato dalla validazione, o limite dell'account raggiunto |\n\n> **Modifica:** alcuni di questi casi restituivano in precedenza `404`. I crediti\n> insufficienti restituiscono ora **402**, gli errori di validazione e i limiti\n> dell'account restituiscono **422**, e un account bloccato per gli SMS\n> restituisce **403**. `404` è ora usato solo per «questo oggetto non esiste», in\n> modo coerente con l'API SMS. Se la tua integrazione si basa su `404` per questi\n> casi, aggiornala con i codici sopra.\n\n## Formato del numero di telefono\n\nI numeri di telefono vengono normalizzati automaticamente. Potete inviarli\nin molti formati — spazi, trattini, punti, slash, parentesi, underscore e\nspazi non separabili vengono rimossi prima della validazione. Qualsiasi\naltro carattere (lettere, apostrofi, `=`, `;`, ecc.) rende il numero non\nvalido.\n\n| Valido                                          | Non valido                                            |\n|------------------------------------------------|----------------------------------------------------|\n| `+41794849980` — E.164 internazionale           | `0612345678` — formato nazionale (`0` iniziale)       |\n| `0041 79 484 99 80` — prefisso 00 + spazi       | `41'794'849'80` — apostrofi (non rimossi)       |\n| `+33 6-12-34-56-78` — + con trattini            | `tel=0041794849980` — caratteri extra             |\n| `41794849980` — sole cifre                    | `1234567` — troppo corto (< 8 cifre)                 |\n\n**Regole:**\n- Prefissi accettati: `+`, `00`, oppure nessun prefisso\n- Lunghezza dopo la normalizzazione: **da 8 a 15 cifre** (massimo E.164)\n- La prima cifra dopo la rimozione del prefisso deve essere **1–9** (non `0`)\n- Separatori rimossi: spazio, tab, trattino `-`, punto `.`, slash `/`,\n  parentesi `( )`, underscore `_`, NBSP\n- Tutto il resto (lettere, apostrofi, `=`, `;`, `+` all'interno del numero,\n  ecc.) → rifiutato\n- Forma di archiviazione nel nostro database: sole cifre, nessun prefisso\n  (ad esempio `41794849980`)\n\n**Specifiche v3 per Contact:** il campo `phone_number` di un Contact è\nattualmente limitato a **12 cifre** dopo la normalizzazione (contro 15 per\ngli endpoint SMS autonomi). I numeri provenienti da paesi come Germania\n(13), Brasile (13) o Italia per i mobili (12–13) possono essere rifiutati\nsu `/v3/Contact` con `Length exceeded for Phone Number (12)`. Questo\nvincolo legacy potrebbe essere allentato in una futura release.\n\n## Limiti\n\n| Risorsa | Limite |\n|----------|-------|\n| Tag per account | 1.000 |\n| Liste per account | 1.000 |\n| Campi personalizzati per account | 50 |\n| Richieste API mensili per account | 50.000 (5× crediti email del piano) |\n\n## Tracciamento della consegna (email transazionali)\n\nOgni email inviata **singolarmente** (`POST /Send/Mail`, la Email API v2, il relay SMTP o un'automazione) riceve un identificativo. Può essere seguita finché il server del destinatario non la accetta o la rifiuta. Le campagne non sono coperte qui: usate le statistiche.\n\n1. **Inviate**: la risposta di `POST /Send/Mail` contiene `id_single_send`.\n2. **Ricevete il risultato** (consigliato): gli eventi webhook `email.delivered` ed `email.bounced` inviano lo stato finale al vostro server non appena è noto, senza alcuna interrogazione.\n3. **Oppure interrogate**: `GET /Send/{id}/Status` restituisce lo stato attuale (tabella sotto).\n\n| `status` | Significato | Finale? |\n|---|---|---|\n| `queued` | Accettata, in attesa di invio. | no |\n| `scheduled` | Pianificata per più tardi (`date_planned`). | no |\n| `sent` | Consegnata dai nostri server, ancora nessuna risposta dal server del destinatario. | no |\n| `delivered` | Accettata dal server del destinatario (`diag_code` = la sua risposta SMTP). | **sì** |\n| `bounced` | Rifiutata. `bounce_type`: `hard` = indirizzo non valido o rifiuto definitivo; `soft` = rifiuto dopo nuovi tentativi (casella piena, server irraggiungibile…). | **sì** |\n| `not_found` | Identificativo sconosciuto o che non appartiene al vostro account. | **sì** |\n\n**Interrogare senza andare in loop**\n\n- Smettete di interrogare non appena lo stato è **finale**: non cambierà più.\n- Prima interrogazione non prima di **5 minuti** dall'invio: `delivered` compare circa 5 minuti dopo la consegna.\n- Poi **al massimo un'interrogazione ogni 15 minuti**.\n- Se interrogate un invio non finale prima di 15 minuti, ricevete la stessa risposta con l'intestazione `Retry-After` (i secondi da attendere), e la chiamata non conta nella vostra quota.\n- **Al più tardi 48 ore** dopo l'invio, ogni email ha uno stato finale: oltre, non interrogate più.\n- `scheduled`: inutile interrogare prima di `date_planned`.\n- **Ogni interrogazione conta nella vostra quota di richieste API.** Seguendo queste regole, un invio costa al massimo 192 richieste (4 all'ora per 48 ore), e molte meno in pratica, poiché ci si ferma allo stato finale. Lo stesso tracciamento via webhook costa un solo evento.\n\nGli stati sono conservati per **365 giorni**.\n\n## Webhook\n\nMailpro™ può notificare il vostro server in tempo reale quando determinati\neventi si verificano sul vostro account (la creazione di un contatto, la\nconsegna di un'email, il completamento di una campagna, ecc.). Stile Stripe\n— il vostro URL riceve una POST firmata in HMAC-SHA256 non appena l'evento\nsi verifica.\n\nOgni evento inviato al vostro webhook conta come 1 richiesta nella vostra quota di richieste API.\n\n### Come funziona\n\n1. **Sottoscrivete** uno o più tipi di evento tramite `POST /Webhook`. La\n   risposta contiene un `secret` mostrato **una sola volta** (stile Stripe /\n   GitHub) — conservatelo.\n2. **Mailpro invia una POST** con payload JSON firmato al vostro URL non\n   appena un evento corrisponde.\n3. **Verificate la firma** lato server con il vostro `secret` (snippet qui\n   di seguito).\n4. **Rispondete 200–299** entro 10 secondi. In caso contrario riproviamo\n   con backoff esponenziale (10 s → 1 min → 5 min → 30 min → 2 h → 6 h →\n   24 h, dopodiché rinunciamo).\n\n> **⏱ Ritardo di attivazione** — dopo la creazione, l'aggiornamento o\n> l'eliminazione di un webhook, la modifica impiega fino a **10 minuti** per\n> propagarsi su tutti i server API Mailpro. Durante questo intervallo gli\n> eventi potrebbero non corrispondere ancora alla vostra nuova\n> sottoscrizione (oppure potrebbero ancora corrispondere a una appena\n> rimossa). È il tempo necessario per aggiornare la cache delle\n> sottoscrizioni in memoria su ogni istanza API.\n\n### Envelope del payload\n\nTutti gli eventi condividono un envelope comune:\n\n```json\n{\n  \"id\":         \"evt_8f3a9b2c4d1e5f6789abcdef01234567\",\n  \"type\":       \"contact.created\",\n  \"created\":    1748194800,\n  \"apiVersion\": \"v3\",\n  \"account\":    { \"id\": 12345, \"name\": \"ACME Corp\" },\n  \"data\":       { \"id\": \"...\", \"email\": \"alice@example.com\", \"...\": \"...\" }\n}\n```\n\n| Campo | Formato | Descrizione |\n|-------|--------|-------------|\n| `id` | `evt_` + 32 hex | ID univoco dell'evento — da utilizzare come chiave di idempotenza dal vostro lato. |\n| `type` | `<domain>.<verb>` | Tipo di evento canonico (ad esempio `email.delivered`, `contact.created`). |\n| `created` | Secondi Unix UTC | Incluso nella firma HMAC. |\n| `apiVersion` | `\"v3\"` | Quale API Mailpro ha pubblicato l'evento. |\n| `account.id` | int | Il vostro client ID Mailpro. |\n| `account.name` | string | Il nome del vostro account (utile per il debug lato client). |\n| `data` | object | Payload dell'evento — la forma dipende dal `type` (vedi catalogo qui di seguito). |\n\n### Catalogo degli eventi\n\n| Evento | Trigger | Forma di `data` |\n|-------|---------|--------------|\n| `ping` | Test tramite `POST /Webhook/{id}/test` | `{ message }` |\n| `contact.created` | `POST /Contact` | `{ id, email, firstName, lastName, createdAt }` |\n| `contact.updated` | `PUT /Contact/{id}` | `{ id, email, firstName, lastName, updatedAt, changes }` |\n| `contact.deleted` | `DELETE /Contact/{id}` | `{ id, email, deletedAt }` |\n| `list.contact_added` | Contatto aggiunto a una lista | `{ listId, listName, contactId, contactEmail, addedAt }` |\n| `list.contact_removed` | Contatto rimosso da una lista | `{ listId, listName, contactId, contactEmail, removedAt }` |\n| `tag.applied` | Tag applicato a un contatto | `{ tag, contactId, contactEmail, appliedAt }` |\n| `tag.removed` | Tag rimosso | `{ tag, contactId, contactEmail, removedAt }` |\n| `segment.matched` | Un contatto entra in un segmento | `{ segmentId, segmentName, contactId, contactEmail, matchedAt }` |\n| `email.processed` | Invio accettato dalla pipeline | `{ messageId, email, campaignId, processedAt }` |\n| `email.delivered` | Un'email **singola** è stata accettata dal server del destinatario — API v2/v3, SMTP o Automation (**non** le campagne). Emesso dal flusso di consegna PowerMTA. | `{ messageId, email, idSending, sendingType, diagCode, deliveredAt }` |\n| `email.opened` | Tracking pixel attivato | `{ messageId, email, campaignId, openedAt, ip, userAgent }` |\n| `email.clicked` | Click su link tracciato | `{ messageId, email, campaignId, clickedAt, url, ip, userAgent }` |\n| `email.bounced` | Bounce hard o soft | `{ messageId, email, campaignId, bouncedAt, bounceType, reason }` |\n| `email.spam_report` | Reclamo FBL | `{ messageId, email, campaignId, reportedAt }` |\n| `email.unsubscribed` | Click sul link di opt-out | `{ messageId, email, campaignId, unsubscribedAt }` |\n| `email.dropped` | Rifiutata prima dell'invio | `{ messageId, email, campaignId, droppedAt, reason }` |\n| `campaign.started` | La spedizione ha inizio | `{ campaignId, name, totalRecipients, startedAt }` |\n| `campaign.finished` | Tutti i destinatari elaborati | `{ campaignId, name, totalSent, totalFailed, finishedAt }` |\n| `import.started` | Job di import avviato | `{ importId, fileName, totalRows, startedAt }` |\n| `import.progress` | Al 25/50/75% | `{ importId, fileName, processedRows, totalRows, progressPct }` |\n| `import.completed` | Import riuscito | `{ importId, fileName, totalRows, addedCount, updatedCount, errorCount, completedAt }` |\n| `import.failed` | Errore fatale durante l'import | `{ importId, fileName, errorMessage, failedAt }` |\n\nSottoscrivete tutti gli eventi di questa API passando `[\"*\"]` nell'array\n`events`.\n\n### Firma HMAC\n\nOgni POST trasporta un header `X-Mailpro-Signature`:\n\n```\nX-Mailpro-Signature: t=1748194800,v1=7f9ac2b6e4d1c8a3...\nX-Mailpro-Event-Id:  evt_8f3a9b2c4d1e5f6789abcdef01234567\nX-Mailpro-Event-Type: contact.created\nX-Mailpro-Attempt:   1\n```\n\nPer verificarla:\n1. Estraete `t` e `v1` dall'header.\n2. **Rifiutate** se `|now - t| > 300` (finestra di protezione anti-replay di\n   5 minuti).\n3. Calcolate `expected = hex(HMAC_SHA256(secret, t + \".\" + raw_body))`.\n4. **Confrontate a tempo costante** `v1` con `expected`\n   (`crypto.timingSafeEqual`, `hash_equals`,\n   `CryptographicOperations.FixedTimeEquals`) — mai con `==`.\n\n#### Node.js\n\n```js\nconst crypto = require('crypto');\n\nfunction verifyMailproSignature(rawBody, header, secret) {\n  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));\n  if (Math.abs(Date.now() / 1000 - parseInt(parts.t)) > 300) return false;\n  const expected = crypto\n    .createHmac('sha256', secret)\n    .update(parts.t + '.' + rawBody)\n    .digest('hex');\n  return crypto.timingSafeEqual(\n    Buffer.from(parts.v1, 'hex'),\n    Buffer.from(expected, 'hex'));\n}\n```\n\n#### PHP\n\n```php\nfunction verifyMailproSignature(string $rawBody, string $header, string $secret): bool {\n    parse_str(str_replace(',', '&', $header), $p);\n    if (abs(time() - (int)$p['t']) > 300) return false;\n    $expected = hash_hmac('sha256', $p['t'] . '.' . $rawBody, $secret);\n    return hash_equals($expected, $p['v1']);\n}\n```\n\n#### Python\n\n```python\nimport hmac, hashlib, time\n\ndef verify_mailpro_signature(raw_body: bytes, header: str, secret: str) -> bool:\n    parts = dict(p.split('=') for p in header.split(','))\n    t = int(parts['t'])\n    if abs(time.time() - t) > 300:\n        return False\n    expected = hmac.new(\n        secret.encode(),\n        f\"{t}.\".encode() + raw_body,\n        hashlib.sha256\n    ).hexdigest()\n    return hmac.compare_digest(expected, parts['v1'])\n```\n\n#### C#\n\n```csharp\nusing System.Security.Cryptography;\nusing System.Text;\n\nstatic bool VerifyMailproSignature(string rawBody, string header, string secret) {\n    var parts = header.Split(',')\n        .Select(p => p.Split('='))\n        .ToDictionary(a => a[0], a => a[1]);\n    var t = long.Parse(parts[\"t\"]);\n    if (Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false;\n    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));\n    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes($\"{t}.{rawBody}\"));\n    var expected = Convert.ToHexString(hash).ToLowerInvariant();\n    return CryptographicOperations.FixedTimeEquals(\n        Encoding.UTF8.GetBytes(expected),\n        Encoding.UTF8.GetBytes(parts[\"v1\"]));\n}\n```\n\n### Retry e disattivazione automatica\n\n| Tentativo | Attesa prima del successivo |\n|---------|----------------------|\n| 1 → 2 | 10 secondi |\n| 2 → 3 | 1 minuto |\n| 3 → 4 | 5 minuti |\n| 4 → 5 | 30 minuti |\n| 5 → 6 | 2 ore |\n| 6 → 7 | 6 ore |\n| 7 | abbandonato (consegna contrassegnata come `failed`) |\n\nDopo **10 fallimenti consecutivi** sullo stesso webhook, questo viene\n**disabilitato automaticamente** (`isActive: false`) e ricevete un'email di\nnotifica. Riattivatelo con `PUT /Webhook/{id}` una volta corretto il vostro\nendpoint.\n\n### Successo = HTTP 2xx\n\nConsideriamo il vostro webhook andato a buon fine solo se restituite uno\nstato compreso tra `200` e `299`. Qualsiasi 3xx/4xx/5xx o un timeout\n(> 10 s) viene conteggiato come fallimento e attiva i retry. Se il vostro\nendpoint è definitivamente fuori servizio, restituite esplicitamente\n`410 Gone` — i retry avvengono comunque ma la causa del fallimento risulta\npiù chiara nel vostro log di consegna.\n\n### Limiti\n\n| Limite | Valore |\n|-------|-------|\n| Webhook per account | 20 |\n| Eventi sottoscritti per webhook | 50 |\n| Dimensione massima del payload | 256 KB |\n| Timeout HTTP per tentativo | 10 secondi |\n| Conservazione della cronologia di consegna | 30 giorni |"
  },
  "servers": [
    {
      "url": "https://api.mailpro.com/v3",
      "description": "Produzione"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "IdClientHeader": [],
      "ApiKeyHeader": []
    },
    {
      "IdClientQuery": [],
      "ApiKeyQuery": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Token JWT Bearer ottenuto da `POST /token`.\nPassarlo come: `Authorization: Bearer <token>`\n"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "Mailpro OAuth 2.0 — il modo moderno per applicazioni di terze parti e piattaforme\ndi integrazione (Zapier, Make, n8n) di agire per conto di un utente Mailpro con\ntoken di accesso revocabili e con scope.\n\n- **authorizationCode + PKCE** (S256, obbligatorio per i client pubblici) — il\n  flusso di consenso \"Connetti Mailpro\". Emette un token di accesso + un refresh\n  token rotante.\n- **clientCredentials** — da macchina a macchina, senza coinvolgimento dell'utente.\n\nUn unico server di autorizzazione per tutte le API, all'endpoint indipendente dalla versione\n`https://api.mailpro.com/oauth/` (issuer / claim `iss`\n`https://api.mailpro.com/oauth/`). I token di accesso sono JWT RS256\n(verificarli rispetto al JWKS su `https://api.mailpro.com/oauth/.well-known/jwks`);\nTTL **20 minuti**. I refresh token sono validi **30 giorni** (sliding) e\n**a uso singolo** (ruotati ad ogni refresh). I codici di autorizzazione vivono\n**60 secondi**.\n\nLo stesso token di accesso OAuth è accettato da tutte e tre le API Mailpro\n(CRM v3, Email v2, SMS) — autenticati una volta, chiama qualsiasi API; l'accesso\nè regolato dagli scope del token, non dall'URL base dell'API. L'autenticazione\nlegacy (Bearer JWT / IdClient+ApiKey) continua a funzionare invariata e NON\nè soggetta a scope.\n",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://api.mailpro.com/oauth/authorize",
            "tokenUrl": "https://api.mailpro.com/oauth/token",
            "refreshUrl": "https://api.mailpro.com/oauth/token",
            "scopes": {
              "contacts:read": "View your contacts, lists, tags, segments and custom fields.",
              "contacts:write": "Create, edit and delete your contacts, lists, tags and custom fields.",
              "email:send": "Send transactional and campaign emails on your behalf.",
              "email:read": "View delivery stats, opens, clicks and bounce history.",
              "email:templates:read": "View your email templates (subject, HTML, plain-text body).",
              "email:templates:write": "Create, edit and delete your email templates.",
              "sms:send": "Send transactional and campaign SMS messages on your behalf.",
              "sms:read": "View SMS credit balance and message history.",
              "sms:contacts:read": "View your SMS phone book.",
              "sms:contacts:write": "Create, edit and delete entries in your SMS phone book.",
              "webhooks:manage": "Subscribe to, edit and remove webhook endpoints for your account.",
              "account:read": "View your account credits, limits and subscription status."
            }
          },
          "clientCredentials": {
            "tokenUrl": "https://api.mailpro.com/oauth/token",
            "scopes": {
              "contacts:read": "View your contacts, lists, tags, segments and custom fields.",
              "contacts:write": "Create, edit and delete your contacts, lists, tags and custom fields.",
              "email:send": "Send transactional and campaign emails on your behalf.",
              "email:read": "View delivery stats, opens, clicks and bounce history.",
              "email:templates:read": "View your email templates (subject, HTML, plain-text body).",
              "email:templates:write": "Create, edit and delete your email templates.",
              "sms:send": "Send transactional and campaign SMS messages on your behalf.",
              "sms:read": "View SMS credit balance and message history.",
              "sms:contacts:read": "View your SMS phone book.",
              "sms:contacts:write": "Create, edit and delete entries in your SMS phone book.",
              "webhooks:manage": "Subscribe to, edit and remove webhook endpoints for your account.",
              "account:read": "View your account credits, limits and subscription status."
            }
          }
        }
      },
      "IdClientHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "IdClient",
        "description": "Il tuo ID cliente Mailpro numerico — trasporto preferito (header HTTP).\nGli URL sono registrati dai server web, da Cloudflare e dai proxy, quindi le credenziali\nnella query string possono finire nei log di accesso. Gli header non vengono registrati.\n"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "ApiKey",
        "description": "La tua chiave API (GUID) — trasporto preferito (header HTTP)."
      },
      "IdClientQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "IdClient",
        "description": "Il tuo ID cliente Mailpro numerico — legacy, mantenuto per retrocompatibilità.\nPassa all'header `IdClient` quando puoi.\n"
      },
      "ApiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "ApiKey",
        "description": "La tua chiave API (GUID) — legacy, mantenuta per retrocompatibilità."
      }
    },
    "schemas": {
      "TokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Token di accesso JWT — da usare in tutte le richieste successive come `Authorization: Bearer <token>`"
          },
          "token_type": {
            "type": "string",
            "example": "bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Durata del token in secondi",
            "example": 31535999
          },
          "userName": {
            "type": "string",
            "description": "ID cliente dell'utente autenticato",
            "example": "12345"
          },
          "refresh_token": {
            "type": "string",
            "description": "Token per ottenere un nuovo token di accesso senza autenticarsi nuovamente"
          },
          "application": {
            "type": "string",
            "description": "Nome dell'integrazione (solo per concessioni di terze parti)",
            "nullable": true
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Identificatore univoco del contatto (UUID)",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "email": {
            "type": "string",
            "description": "Indirizzo email del contatto",
            "example": "alice@example.com"
          },
          "phone_number": {
            "type": "string",
            "description": "Numero di telefono — accetta `+41…`, `0041…` o `41…` con o senza separatori. Vedi **Formato del numero di telefono** nella descrizione dell'API.",
            "example": "+41791234567"
          },
          "tags": {
            "type": "array",
            "description": "Tag assegnati a questo contatto (inclusi quando `includeTags=true`)",
            "items": {
              "$ref": "#/components/schemas/TagRef"
            }
          },
          "lists": {
            "type": "array",
            "description": "Liste a cui appartiene questo contatto (incluse quando `includeLists=true`)",
            "items": {
              "$ref": "#/components/schemas/ListRef"
            }
          },
          "custom_fields": {
            "type": "array",
            "description": "Valori dei campi personalizzati per questo contatto",
            "items": {
              "$ref": "#/components/schemas/FieldValue"
            }
          }
        }
      },
      "ContactEntry": {
        "type": "object",
        "required": [
          "email",
          "lists"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "Indirizzo email del contatto (obbligatorio, deve essere univoco)",
            "example": "alice@example.com"
          },
          "phone_number": {
            "type": "string",
            "description": "Numero di telefono — accetta `+41…`, `0041…` o `41…` con o senza separatori.\nDeve iniziare con un prefisso internazionale valido. Massimo 12 cifre su Contact (vedi\n**Formato del numero di telefono** nella descrizione dell'API per le regole complete).\n",
            "example": "+41791234567"
          },
          "lists": {
            "type": "array",
            "description": "Liste a cui assegnare questo contatto. **È richiesta almeno una lista.**",
            "items": {
              "$ref": "#/components/schemas/ListRef"
            },
            "example": [
              {
                "id": 101
              }
            ]
          },
          "tags": {
            "type": "array",
            "description": "Tag da assegnare a questo contatto (opzionale)",
            "items": {
              "$ref": "#/components/schemas/TagRef"
            },
            "example": [
              {
                "id": 5
              }
            ]
          },
          "custom_fields": {
            "type": "array",
            "description": "Valori dei campi personalizzati per questo contatto (opzionale)",
            "items": {
              "$ref": "#/components/schemas/FieldValue"
            },
            "example": [
              {
                "id": 1,
                "value": "Alice"
              },
              {
                "id": 2,
                "value": "Dupont"
              }
            ]
          }
        }
      },
      "TagRef": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "ID del tag (da `GET /Tag`)",
            "example": 5
          }
        }
      },
      "ListRef": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "ID della lista (da `GET /List`)",
            "example": 101
          }
        }
      },
      "FieldValue": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "ID del campo personalizzato (da `GET /Field`)",
            "example": 1
          },
          "value": {
            "type": "string",
            "description": "Valore da memorizzare per questo campo. Il tipo deve corrispondere al tipo del campo.",
            "example": "Alice"
          }
        }
      },
      "List": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Identificatore univoco della lista",
            "example": 101
          },
          "name": {
            "type": "string",
            "description": "Nome visualizzato della lista",
            "example": "Newsletter subscribers"
          },
          "total_contacts": {
            "type": "integer",
            "description": "Numero totale di contatti nella lista (incluso quando `includeTotalContacts=true`)",
            "example": 4521
          },
          "date_creation": {
            "type": "string",
            "format": "date-time",
            "example": "2025-01-15T10:30:00"
          }
        }
      },
      "ListEntry": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome visualizzato per la lista",
            "example": "Newsletter subscribers — France"
          }
        }
      },
      "Tag": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Identificatore univoco del tag",
            "example": 5
          },
          "name": {
            "type": "string",
            "description": "Nome visualizzato del tag",
            "example": "VIP customers"
          }
        }
      },
      "TagEntry": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome visualizzato per il tag",
            "example": "VIP customers"
          }
        }
      },
      "Field": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "Identificatore univoco del campo personalizzato",
            "example": 1
          },
          "name": {
            "type": "string",
            "description": "Nome interno del campo",
            "example": "first_name"
          },
          "personalisation_key": {
            "type": "string",
            "description": "Il nome della variabile usato per la personalizzazione nei modelli email (es. `{{first_name}}`)",
            "example": "first_name"
          },
          "field_type": {
            "type": "string",
            "description": "Tipo di dato del campo",
            "enum": [
              "text",
              "number",
              "date"
            ],
            "example": "text"
          }
        }
      },
      "FieldEntry": {
        "type": "object",
        "required": [
          "name",
          "id_field_type",
          "personalize_key"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome visualizzato del campo",
            "example": "First name"
          },
          "id_field_type": {
            "type": "integer",
            "description": "ID del tipo di campo (tipi disponibili tramite `GET /Field/Types`):\n- `1` = testo\n- `2` = numero intero\n- `3` = data e ora\n- `4` = sì / no\n- `12` = numero decimale\n",
            "example": 1
          },
          "personalize_key": {
            "type": "string",
            "description": "Chiave di variabile del modello, per inserire il campo in un messaggio",
            "example": "first_name"
          },
          "sort": {
            "type": "integer",
            "minimum": 0,
            "maximum": 255,
            "description": "Ordine di visualizzazione (da 0 a 255). Obbligatorio per modificare un campo (`PUT /Field/{id}`).",
            "example": 255
          }
        }
      },
      "CampaignEntry": {
        "type": "object",
        "properties": {
          "campaign_title": {
            "type": "string",
            "description": "Nome visualizzato per la campagna",
            "example": "Spring Newsletter April 2026"
          },
          "date_planned": {
            "type": "string",
            "format": "date-time",
            "description": "Orario di invio programmato (ISO 8601). Ometti per inviare immediatamente.",
            "example": "2026-04-20T09:00:00"
          }
        }
      },
      "SendEmailRequest": {
        "type": "object",
        "description": "Richiesta di email transazionale. Fornisci **o** `email` (indirizzo inline) **o**\n`id_contact` (contatto CRM) — non entrambi.\nFornisci **o** `body_html` (HTML inline) **o** `id_message` (modello salvato) — non entrambi.\n",
        "properties": {
          "id_sender_email": {
            "type": "integer",
            "description": "ID dell'indirizzo email del mittente (da `GET /SenderEmail`). Obbligatorio.",
            "example": 201
          },
          "email": {
            "type": "string",
            "description": "Indirizzo email del destinatario (inline). Fornisci questo **OPPURE** `id_contact`.\nSe vengono forniti entrambi, `id_contact` ha la precedenza.\n\n**La parte locale** (prima della `@`) accetta lettere, cifre e `. _ - +` —\nincluso il sub-addressing RFC 5322 (`user+tag@domain`, es. Gmail, Fastmail,\nProton). Gli altri caratteri speciali consentiti dalla RFC 5322\n(es. `! # $ % & ' * / = ? ^ { | } ~`) e le parti locali tra virgolette **non**\nsono accettati.\n",
            "example": "alice@example.com"
          },
          "id_contact": {
            "type": "string",
            "format": "uuid",
            "description": "ID del contatto CRM destinatario. Fornisci questo **O** `email`.\nL'uso di `id_contact` abilita la personalizzazione con i valori dei campi personalizzati del contatto.\n",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "subject": {
            "type": "string",
            "description": "Oggetto dell'email (massimo 255 caratteri). Obbligatorio.",
            "example": "Your order #12345 has been shipped"
          },
          "body_html": {
            "type": "string",
            "description": "Contenuto HTML inline. Fornisci questo **O** `id_message`.\nSe entrambi sono indicati, `id_message` ha la precedenza.\n",
            "example": "<p>Hello {{first_name}}, your order has shipped!</p>"
          },
          "id_message": {
            "type": "integer",
            "description": "ID di un modello di messaggio salvato (da `GET /Message`).\nFornisci questo **O** `body_html`.\n",
            "example": 501
          },
          "reply_to": {
            "type": "string",
            "description": "Indirizzo di risposta (predefinito all'indirizzo del mittente)",
            "example": "support@yourcompany.com"
          },
          "fields": {
            "type": "array",
            "description": "Campi di personalizzazione personalizzati (usati solo con `email` inline, non con `id_contact`)",
            "items": {
              "$ref": "#/components/schemas/FieldValue"
            }
          },
          "date_planned": {
            "type": "string",
            "format": "date-time",
            "description": "Programma l'invio per un orario futuro. Ometti per inviare immediatamente.",
            "example": "2026-04-15T14:00:00"
          },
          "attachments": {
            "type": "array",
            "maxItems": 5,
            "description": "Allegati facoltativi (max **5**). Ogni elemento è un oggetto con un `filename` e un `content` codificato in Base64. Limiti: **10 MB per file** e **15 MB in totale**, misurati dopo la decodifica Base64. Tipi accettati: PDF, Word, Excel, PowerPoint, immagini (JPG, PNG, GIF, WebP), ZIP, CSV, TXT — i file eseguibili e gli script vengono rifiutati. **Fatturazione:** l'invio mantiene il suo credito abituale, più **1 credito ogni 512 KB di allegati oltre i primi 512 KB** (gli allegati ≤ 512 KB non aggiungono nulla).",
            "items": {
              "$ref": "#/components/schemas/EmailAttachment"
            }
          },
          "keep_in_my_files": {
            "type": "boolean",
            "default": false,
            "description": "`false` (predefinito): gli allegati servono solo per questo invio — sono archiviati fuori dal tuo\naccount, non contano nella tua quota di archiviazione e vengono eliminati automaticamente dopo\n35 giorni. `true`: gli allegati vengono anche conservati nei file del tuo account (cartella\n`Secure/Files/Sending/api`) e contano nella tua quota di archiviazione.\n",
            "example": false
          }
        }
      },
      "EmailAttachment": {
        "type": "object",
        "required": [
          "filename",
          "content"
        ],
        "description": "Un singolo allegato di file email con contenuto codificato in Base64.",
        "properties": {
          "filename": {
            "type": "string",
            "description": "Nome file originale mostrato al destinatario, ad es. invoice.pdf.",
            "example": "invoice.pdf"
          },
          "content": {
            "type": "string",
            "format": "byte",
            "description": "Contenuto del file, codificato in Base64.",
            "example": "JVBERi0xLjQKJVBERi0xLjQK..."
          }
        }
      },
      "SendCampaignRequest": {
        "type": "object",
        "required": [
          "id_message",
          "id_sender_email",
          "lists",
          "campaign_title"
        ],
        "properties": {
          "id_message": {
            "type": "integer",
            "description": "ID del modello di messaggio da utilizzare (da `GET /Message`)",
            "example": 501
          },
          "id_sender_email": {
            "type": "integer",
            "description": "ID dell'indirizzo del mittente (da `GET /SenderEmail`)",
            "example": 201
          },
          "id_email_reply_to": {
            "type": "integer",
            "description": "ID dell'indirizzo del mittente di risposta (da `GET /SenderEmail`). Deve essere uno degli indirizzi mittente del tuo account, altrimenti la richiesta viene rifiutata. Predefinito: `id_sender_email`.",
            "example": 201
          },
          "lists": {
            "type": "array",
            "description": "Liste di contatti target (almeno una richiesta)",
            "items": {
              "$ref": "#/components/schemas/ListRef"
            },
            "example": [
              {
                "id": 101
              }
            ]
          },
          "id_segment": {
            "type": "integer",
            "description": "Filtra i destinatari tramite un segmento salvato (opzionale)",
            "example": 7
          },
          "campaign_title": {
            "type": "string",
            "description": "Nome visualizzato per questa campagna (massimo 150 caratteri)",
            "example": "Spring Newsletter April 2026"
          },
          "date_planned": {
            "type": "string",
            "format": "date-time",
            "description": "Orario di invio programmato (ISO 8601). Ometti per inviare immediatamente.",
            "example": "2026-04-20T09:00:00"
          },
          "id_automation": {
            "type": "integer",
            "description": "Collega questo invio a un flusso di lavoro di automazione (opzionale)",
            "example": 3
          }
        }
      },
      "SmsSingleRequest": {
        "type": "object",
        "required": [
          "phone_number",
          "message"
        ],
        "properties": {
          "phone_number": {
            "type": "string",
            "description": "Numero di telefono del destinatario — accetta `+41…`, `0041…` o `41…` con o senza separatori. Vedi **Formato del numero di telefono** nella descrizione dell'API.",
            "example": "+41791234567"
          },
          "message": {
            "type": "string",
            "description": "Testo del messaggio SMS (massimo 160 caratteri per la codifica standard)",
            "example": "Your verification code is 9473. Valid for 10 minutes."
          },
          "date_planned": {
            "type": "string",
            "format": "date-time",
            "description": "Programma l'invio. Ometti per inviare immediatamente.",
            "example": "2026-04-15T14:00:00"
          }
        }
      },
      "SmsCampaignRequest": {
        "type": "object",
        "required": [
          "lists",
          "message"
        ],
        "properties": {
          "lists": {
            "type": "array",
            "description": "Liste di contatti target (utilizza il campo phone_number di ogni contatto)",
            "items": {
              "$ref": "#/components/schemas/ListRef"
            },
            "example": [
              {
                "id": 101
              }
            ]
          },
          "message": {
            "type": "string",
            "description": "Testo del messaggio SMS",
            "example": "Flash sale today — 20% off all products. Shop at mailpro.com"
          },
          "date_planned": {
            "type": "string",
            "format": "date-time",
            "description": "Programma l'invio. Ometti per inviare immediatamente.",
            "example": "2026-04-20T10:00:00"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "Message": {
            "type": "string",
            "description": "Messaggio di errore leggibile dall'utente",
            "example": "Contact cannot be created. Email cannot be empty."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "ID del webhook (Mailpro)",
            "example": 42
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Endpoint HTTPS che riceve gli eventi",
            "example": "https://example.com/hooks/mailpro"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tipi di evento sottoscritti",
            "example": [
              "email.delivered",
              "email.bounced",
              "contact.created"
            ]
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "Descrizione libera (riferimento personale)",
            "example": "Slack integration"
          },
          "isActive": {
            "type": "boolean",
            "description": "Quando è `false`, nessun evento viene consegnato. Disabilitato automaticamente dopo 10 fallimenti consecutivi.",
            "example": true
          },
          "consecutiveFailures": {
            "type": "integer",
            "description": "Numero di tentativi di consegna falliti consecutivi (azzerato in caso di successo)",
            "example": 0
          },
          "autoDisabledAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Impostato quando `isActive` è stato impostato automaticamente su `false`"
          },
          "autoDisabledReason": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Endpoint HTTPS (HTTP rifiutato in produzione)",
            "example": "https://example.com/hooks/mailpro"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "Elenco dei tipi di evento da sottoscrivere (vedi catalogo sopra). Usa `[\"*\"]` per sottoscrivere tutti gli eventi di questa API.",
            "example": [
              "email.delivered",
              "email.bounced",
              "contact.created"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "description": "Descrizione libera per il tuo riferimento",
            "example": "Slack integration"
          }
        }
      },
      "WebhookCreateResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Secret HMAC. **Mostrato qui una sola volta, mai più.** Conservalo lato server — usato per firmare ogni payload inviato al tuo URL.",
                "example": "whsec_AbcD1234EfgH5678IjkL9012MnoP3456QrsT"
              }
            }
          }
        ]
      },
      "WebhookUpdate": {
        "type": "object",
        "description": "Aggiornamento parziale — ogni campo è opzionale.",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 50
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "isActive": {
            "type": "boolean",
            "description": "Imposta `true` per riattivare un webhook disabilitato automaticamente una volta corretto il tuo endpoint."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "description": "ID del tentativo di consegna"
          },
          "eventId": {
            "type": "string",
            "description": "UUID dell'evento (= il campo `id` nel payload ricevuto dal tuo endpoint)",
            "example": "evt_8f3a9b2c4d1e5f6789abcdef01234567"
          },
          "eventType": {
            "type": "string",
            "example": "email.delivered"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "retrying",
              "failed"
            ],
            "description": "Stato attuale di questa consegna"
          },
          "statusCode": {
            "type": "integer",
            "nullable": true,
            "description": "Codice di stato HTTP ricevuto dal tuo endpoint (`null` se non ancora tentato o in caso di timeout)",
            "example": 200
          },
          "responseTimeMs": {
            "type": "integer",
            "nullable": true,
            "example": 142
          },
          "attemptCount": {
            "type": "integer",
            "description": "Numero di tentativi finora (1–7, poi definitivamente fallito)"
          },
          "lastError": {
            "type": "string",
            "nullable": true,
            "description": "Primi 2 KB del corpo della risposta o messaggio di eccezione quando un tentativo fallisce"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Quando avverrà il prossimo nuovo tentativo — `null` se la consegna è riuscita o è stata abbandonata definitivamente."
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Authentication",
      "description": "Ottieni un token JWT Bearer per autenticare tutte le altre chiamate API.\nL'endpoint del token accetta più tipi di concessione: `password`, `refresh_token`,\n`client_credentials` e integrazioni di terze parti (`zapier`, `automateio`, `microsoft-flow`).\n"
    },
    {
      "name": "Account",
      "description": "Recupera il profilo dell'account, il saldo dei crediti e i dettagli dell'abbonamento."
    },
    {
      "name": "Contact",
      "description": "CRUD completo per i contatti CRM. I contatti memorizzano un indirizzo email, un numero di telefono opzionale,\nvalori di campi personalizzati e appartengono a una o più **Liste**. Possono anche essere etichettati con **Tag**.\nGli ID dei contatti sono UUID.\n"
    },
    {
      "name": "List",
      "description": "Gestisci le liste di contatti. Le liste raggruppano i contatti per campagne e segmentazione.\nMassimo 1.000 liste per account (stesso limite sulle tre API Mailpro e nell'app). Massimo 1.000.000 di contatti per lista.\n"
    },
    {
      "name": "Tag",
      "description": "Gestisci i tag per l'etichettatura e la segmentazione dei contatti.\nI tag sono etichette piatte (es. \"VIP\", \"Churned\", \"B2B\").\nMassimo 1.000 tag per account.\n"
    },
    {
      "name": "Field",
      "description": "Gestisci i campi personalizzati dei contatti. I campi ti permettono di memorizzare dati aggiuntivi su ogni contatto\n(es. nome, città, livello di fedeltà). Massimo 50 campi per account.\nTipi di campo: `text`, `number`, `date`.\n"
    },
    {
      "name": "Message",
      "description": "Gestisci i modelli email HTML. I modelli memorizzano l'oggetto, il corpo HTML\ne le impostazioni del link di annullamento dell'iscrizione. Riutilizza i modelli su più campagne.\n"
    },
    {
      "name": "Campaign",
      "description": "Visualizza le statistiche delle campagne e gestisci le campagne programmate.\nLe campagne possono essere modificate o eliminate fino a 5 minuti prima dell'orario di invio programmato.\n"
    },
    {
      "name": "Send",
      "description": "Invia campagne email in blocco (a liste di contatti) e singole email transazionali.\nLe campagne possono essere programmate o inviate immediatamente.\n"
    },
    {
      "name": "Sender Addresses",
      "description": "Gestisci gli indirizzi email mittenti convalidati. Solo gli indirizzi verificati possono essere usati come `From`.\n"
    },
    {
      "name": "History",
      "description": "Sfoglia il registro degli invii email e SMS passati."
    },
    {
      "name": "Segment",
      "description": "Gestisci i segmenti dinamici — regole di filtro salvate che selezionano automaticamente i contatti\nin base ai valori dei campi, al comportamento o all'appartenenza alla lista.\n"
    },
    {
      "name": "SMS",
      "description": "Invia messaggi SMS a singoli contatti o a intere liste di contatti.\nUtilizza il campo `phone_number` di ogni contatto.\n"
    },
    {
      "name": "Folders",
      "description": "Organizza i modelli di messaggio in cartelle."
    },
    {
      "name": "Import",
      "description": "Carica un file CSV o XLSX per importare contatti in una lista in blocco.\nIl file viene messo in coda per l'elaborazione asincrona — traccia il completamento tramite\nl'`import_job_id` restituito o tramite il callback opzionale `webhook_url`.\n"
    },
    {
      "name": "Webhooks",
      "description": "Sottoscrivi il tuo server alle notifiche di eventi in tempo reale. Mailpro effettua un POST\ndi un payload JSON firmato HMAC al tuo URL non appena si verifica l'evento sottoscritto.\nConsulta la sezione \"Webhooks\" nell'introduzione per il catalogo completo degli eventi,\nlo schema del payload, gli snippet di verifica della firma e la politica di retry.\n"
    },
    {
      "name": "Suppression",
      "description": "Bounce e disiscritti dell'account: elencare, aggiungere, rimuovere."
    }
  ],
  "paths": {
    "/token": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "getAccessToken",
        "summary": "Ottieni token di accesso",
        "security": [],
        "description": "Ottiene un token JWT Bearer utilizzando le credenziali client.\nPassa l'`access_token` restituito come `Authorization: Bearer <token>` in tutte le richieste successive.\n\n### Tipi di concessione supportati\n\n| Tipo di concessione | Caso d'uso |\n|------------|----------|\n| `password` | Integrazione standard — username + chiave API |\n| `refresh_token` | Rinnova un token di accesso in scadenza senza autenticarsi di nuovo |\n| `client_credentials` | Server-to-server con solo ID client |\n| `zapier` / `automateio` / `microsoft-flow` | Integrazioni con piattaforme di terze parti |\n\n### Durata del token\n- Token di accesso: **30 giorni**\n- Refresh token: **90 giorni**\n\nUtilizza `grant_type=refresh_token` per ottenere un nuovo token di accesso senza\nautenticarsi di nuovo — il tuo SDK dovrebbe farlo automaticamente quando il\ntoken di accesso si avvicina alla scadenza (strategia tipica: rinnova quando\nrimangono meno di 24 h).\n\n### Conservazione sicura dei token\nAnche con una durata di 30 giorni, chiunque ottenga un `access_token` può\nimpersonare il tuo account fino alla scadenza. Conserva i token in un archivio\nsegreto lato server (Vault, AWS Secrets Manager, Azure Key Vault, variabili\nd'ambiente su un host rinforzato). **Mai** incorporare un token in:\n- JavaScript lato client o qualsiasi bundle del browser\n- App mobili distribuite agli utenti finali (il binario può essere ispezionato)\n- repository Git pubblici o log di build CI\nSe un token viene divulgato, revocalo tramite le impostazioni del tuo account\nMailpro e richiedine uno nuovo.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "description": "Tipo di concessione di autenticazione",
                    "enum": [
                      "password",
                      "refresh_token",
                      "client_credentials",
                      "zapier",
                      "automateio",
                      "microsoft-flow"
                    ],
                    "example": "password"
                  },
                  "username": {
                    "type": "string",
                    "description": "Il tuo username Mailpro (email). Obbligatorio per la concessione `password`.",
                    "example": "user@yourcompany.com"
                  },
                  "password": {
                    "type": "string",
                    "description": "La tua chiave API Mailpro (formato GUID). Obbligatoria per la concessione `password`.",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "refresh_token": {
                    "type": "string",
                    "description": "Refresh token precedente. Obbligatorio per la concessione `refresh_token`."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Il tuo ID client Mailpro (intero). Obbligatorio per la concessione `client_credentials`.",
                    "example": "12345"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token emesso con successo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                },
                "example": {
                  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                  "token_type": "bearer",
                  "expires_in": 31535999,
                  "userName": "12345",
                  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                  "application": null
                }
              }
            }
          },
          "400": {
            "description": "Credenziali non valide o tipo di concessione non supportato",
            "content": {
              "application/json": {
                "examples": {
                  "invalid_grant": {
                    "summary": "Credenziali errate",
                    "value": {
                      "error": "invalid_grant",
                      "error_description": "The username or password is incorrect."
                    }
                  },
                  "no_api_access": {
                    "summary": "Nessun accesso API",
                    "value": {
                      "error": "no_api_access",
                      "error_description": "The account doesn't have API access."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/Account/Me": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getCrmAccountMe",
        "summary": "Ottieni dettagli dell'account",
        "description": "Restituisce i dettagli del profilo e le impostazioni dell'account Mailpro autenticato.",
        "responses": {
          "200": {
            "description": "Dettagli dell'account restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Account/Credits": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getCrmAccountCredits",
        "summary": "Ottieni saldo crediti",
        "description": "Restituisce il saldo dei crediti email e SMS rimanenti per l'account.",
        "responses": {
          "200": {
            "description": "Saldo crediti restituito con successo",
            "content": {
              "application/json": {
                "example": {
                  "email_credits": 48500,
                  "sms_credits": 1200
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Account/SubscriptionEndDate": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getCrmSubscriptionEndDate",
        "summary": "Ottieni data di scadenza dell'abbonamento",
        "description": "Restituisce la data di scadenza dell'attuale abbonamento Mailpro.",
        "responses": {
          "200": {
            "description": "Data di scadenza dell'abbonamento restituita con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Account/Limits": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getCrmAccountLimits",
        "summary": "Ottieni quota API mensile",
        "description": "Restituisce la quota mensile di richieste API per il client autenticato.\n\n**Calcolo della quota:** il limite è calibrato a partire dai crediti\ndell'abbonamento email (tipicamente `subscription_credits × 5`). Per\nun piano da 10.000 email/mese, ciò produce un budget mensile di 50.000\nrichieste. La mappatura esatta è impostata dal job di fatturazione alla\nregistrazione dell'abbonamento e azzerata alla **data di anniversario\ndell'abbonamento** (non il 1° del mese di calendario).\n\n**Cosa consuma crediti:**\n- Ogni richiesta API autenticata agli endpoint `v2`, `v3` o `sms`\n  (ogni chiamata = 1 credito, indipendentemente dal risultato — sia le\n  2xx sia la maggior parte degli errori business 4xx vengono conteggiati).\n- Ogni evento webhook inviato a un endpoint sottoscritto\n  (es. `contact.created` inviato al tuo webhook sottoscritto = 1 credito\n  oltre alla chiamata API che l'ha attivato).\n\n**Cosa è GRATUITO (non consuma crediti):**\n- Endpoint `/health` (monitoraggio).\n- Richieste che falliscono con `401` (fallimento auth), `403` (vietato) o\n  `429` (rate limit superato).\n- Eventi webhook per client senza sottoscrizione corrispondente\n  (cortocircuito tramite cache — nessun impatto sulla quota).\n",
        "security": [
          {
            "BearerAuth": []
          },
          {
            "LegacyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Istantanea della quota",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "monthly_limit": {
                      "type": "integer",
                      "description": "Crediti totali allocati per il mese di fatturazione corrente.",
                      "example": 50000
                    },
                    "remaining": {
                      "type": "integer",
                      "description": "Crediti ancora disponibili prima del prossimo reset.",
                      "example": 32847
                    },
                    "used": {
                      "type": "integer",
                      "description": "Crediti consumati dall'ultimo reset (max(0, monthly_limit - remaining)).",
                      "example": 17153
                    },
                    "is_free": {
                      "type": "boolean",
                      "description": "True per account di livello gratuito.",
                      "example": false
                    },
                    "remained_sends_for_free": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Contatore specifico per il livello gratuito. `null` per gli account a pagamento.",
                      "example": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Nessun record di quota trovato per questo account (account appena creato che non ha mai effettuato una chiamata API; la riga verrà creata alla prima richiesta autenticata)."
          }
        }
      }
    },
    "/Contact": {
      "post": {
        "tags": [
          "Contact"
        ],
        "operationId": "createContact",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Crea un contatto",
        "description": "Crea un nuovo contatto CRM con campi personalizzati opzionali, assegnazioni a liste e tag.\n\n**Obbligatori:**\n- `email` — deve essere univoco nell'account\n- `lists` — è obbligatoria almeno una assegnazione a una lista\n\n**I numeri di telefono** accettano `+41…`, `0041…` o `41…` con o senza separatori\n(massimo 12 cifre su Contact — vedi **Formato del numero di telefono** nella descrizione dell'API).\n\n**I campi personalizzati** sono identificati dal loro `id` (da `GET /Field`).\nI valori vengono validati rispetto al tipo del campo.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactEntry"
              },
              "example": {
                "email": "alice@example.com",
                "phone_number": "+41791234567",
                "lists": [
                  {
                    "id": 101
                  }
                ],
                "tags": [
                  {
                    "id": 5
                  }
                ],
                "custom_fields": [
                  {
                    "id": 1,
                    "value": "Alice"
                  },
                  {
                    "id": 2,
                    "value": "Dupont"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contatto creato con successo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "description": "Formato email non valido"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "406": {
            "description": "Violazione di una regola business (vedi descrizione per l'elenco completo)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "email_exists": {
                    "summary": "Email duplicata",
                    "value": {
                      "Message": "Contact cannot be created. A contact already exist with the same Email"
                    }
                  },
                  "no_list": {
                    "summary": "Nessuna lista fornita",
                    "value": {
                      "Message": "Contact cannot be created. At least one List is necessary"
                    }
                  },
                  "unsubscribed_forever": {
                    "summary": "Disiscritto permanentemente",
                    "value": {
                      "Message": "Contact cannot be created. The email address was unsubscribed permanently"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/Contact/Multiple": {
      "post": {
        "tags": [
          "Contact"
        ],
        "operationId": "createContactsBulk",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Importa contatti (in blocco)",
        "description": "Crea fino a **1.000 contatti** in una singola richiesta per un'importazione in blocco efficiente.\nLa risposta include un elenco dei contatti creati con successo e un elenco di errori\nper i contatti che non è stato possibile creare (es. email duplicata, dati non validi).\n\n**Nota:** le liste e i tag utilizzati nelle importazioni in blocco **non** devono essere collegati ad alcun\nflusso di lavoro di automazione attivo. In tal caso, l'intera importazione viene rifiutata con HTTP 406.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ContactEntry"
                },
                "maxItems": 1000
              },
              "example": [
                {
                  "email": "alice@example.com",
                  "lists": [
                    {
                      "id": 101
                    }
                  ],
                  "custom_fields": [
                    {
                      "id": 1,
                      "value": "Alice"
                    }
                  ]
                },
                {
                  "email": "bob@example.com",
                  "lists": [
                    {
                      "id": 101
                    }
                  ],
                  "custom_fields": [
                    {
                      "id": 1,
                      "value": "Bob"
                    }
                  ]
                }
              ]
            }
          }
        },
        "responses": {
          "200": {
            "description": "Importazione in blocco completata (controlla l'array `errors` per i fallimenti parziali)",
            "content": {
              "application/json": {
                "example": {
                  "Added": [
                    {
                      "Email": "alice@example.com",
                      "Id": "550e8400-e29b-41d4-a716-446655440000"
                    },
                    {
                      "Email": "bob@example.com",
                      "Id": "660e8400-e29b-41d4-a716-446655440001"
                    }
                  ],
                  "Errors": []
                }
              }
            }
          },
          "400": {
            "description": "Input non valido"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "406": {
            "description": "Violazione di una regola business (es. conflitto di automazione, limite di lista raggiunto)"
          }
        }
      }
    },
    "/Contact/{id}": {
      "get": {
        "tags": [
          "Contact"
        ],
        "operationId": "getContact",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Ottieni un contatto",
        "description": "Restituisce i dettagli di un contatto specifico.\nUtilizza `includeTags=true` per restituire anche i tag assegnati al contatto.\nUtilizza `includeLists=true` per restituire anche le liste a cui appartiene il contatto.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto (da `POST /Contact` o `GET /List/{id}/Contact`)",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "includeTags",
            "in": "query",
            "description": "Imposta a `true` per includere le assegnazioni di tag del contatto nella risposta",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "includeLists",
            "in": "query",
            "description": "Imposta a `true` per includere le appartenenze alle liste del contatto nella risposta",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contatto restituito con successo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                },
                "example": {
                  "id": "550e8400-e29b-41d4-a716-446655440000",
                  "email": "alice@example.com",
                  "phone_number": "41791234567",
                  "custom_fields": [
                    {
                      "id": 1,
                      "value": "Alice"
                    },
                    {
                      "id": 2,
                      "value": "Dupont"
                    }
                  ],
                  "tags": [
                    {
                      "id": 5
                    }
                  ],
                  "lists": [
                    {
                      "id": 101
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto non trovato"
          }
        }
      },
      "put": {
        "tags": [
          "Contact"
        ],
        "operationId": "updateContact",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiorna un contatto",
        "description": "Aggiorna l'email, il numero di telefono, i campi personalizzati, le assegnazioni a liste e i tag di un contatto.\nIl corpo della richiesta sostituisce i dati correnti del contatto — includi tutte le liste e i tag\na cui vuoi che il contatto rimanga assegnato.\n\n**Obbligatori nel corpo:** `email` e almeno una voce `lists`.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactEntry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contatto aggiornato con successo",
            "content": {
              "application/json": {
                "example": {
                  "Result": "Successful operation"
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto non trovato"
          },
          "422": {
            "description": "Errore di validazione (formato email, numero di telefono, incompatibilità del tipo di valore del campo)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Contact"
        ],
        "operationId": "deleteContact",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elimina un contatto",
        "description": "Elimina definitivamente un contatto dal CRM.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        ],
        "responses": {
          "200": {
            "description": "Contatto eliminato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto non trovato"
          }
        }
      }
    },
    "/Contact/{id}/Activity": {
      "get": {
        "tags": [
          "Contact"
        ],
        "operationId": "getCrmContactActivity",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Attività di un contatto su 90 giorni",
        "description": "Email ricevute, aperte e cliccate, client di posta preferito e ultime campagne del contatto.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "GUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Attività restituita",
            "content": {
              "application/json": {
                "example": {
                  "period_days": 90,
                  "email_received": 12,
                  "email_opened": 7,
                  "email_clicked": 2,
                  "favorite_email_client": "Gmail",
                  "last_campaigns": [
                    {
                      "id": 5001,
                      "name": "Spring Sale 2026",
                      "date": "2026-10-01T09:00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Il contatto richiesto non esiste"
          }
        }
      }
    },
    "/Contact/{id}/Tag/{tagid}": {
      "put": {
        "tags": [
          "Contact"
        ],
        "operationId": "addTagToContact",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiungi tag al contatto",
        "description": "Assegna un tag a un contatto. Se il tag è già assegnato, l'operazione è inefficace.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "tagid",
            "in": "path",
            "required": true,
            "description": "ID del tag (da `GET /Tag`)",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          }
        ],
        "responses": {
          "200": {
            "description": "Tag assegnato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto o tag non trovato"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact"
        ],
        "operationId": "removeTagFromContact",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Rimuovi tag dal contatto",
        "description": "Rimuove l'assegnazione di un tag da un contatto.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "tagid",
            "in": "path",
            "required": true,
            "description": "ID del tag",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          }
        ],
        "responses": {
          "200": {
            "description": "Tag rimosso con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto o tag non trovato"
          }
        }
      }
    },
    "/Contact/{id}/List/{listid}": {
      "put": {
        "tags": [
          "Contact"
        ],
        "operationId": "addContactToList",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiungi contatto alla lista",
        "description": "Aggiunge un contatto a una lista specifica.\nRestituisce HTTP 404 se il contatto era precedentemente disiscritto da questa lista.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "listid",
            "in": "path",
            "required": true,
            "description": "ID della lista (da `GET /List`)",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "responses": {
          "200": {
            "description": "Contatto aggiunto alla lista con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto, lista non trovata oppure contatto disiscritto da questa lista"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact"
        ],
        "operationId": "removeContactFromList",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Rimuovi contatto dalla lista",
        "description": "Rimuove un contatto da una lista specifica. Il contatto stesso non viene eliminato.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "UUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          {
            "name": "listid",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "responses": {
          "200": {
            "description": "Contatto rimosso dalla lista con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Contatto o lista non trovata"
          }
        }
      }
    },
    "/List": {
      "get": {
        "tags": [
          "List"
        ],
        "operationId": "getLists",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca le liste di contatti",
        "description": "Restituisce tutte le liste di contatti per l'account.\nFiltra per nome o data di creazione. Ordina alfabeticamente con `sortByName=true`.\nIncludi il conteggio totale dei contatti per lista con `includeTotalContacts=true`.\n",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Filtra per nome della lista (corrispondenza parziale)",
            "schema": {
              "type": "string"
            },
            "example": "newsletter"
          },
          {
            "name": "dateCreationFrom",
            "in": "query",
            "description": "Filtra per data di creazione (da, ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateCreationTo",
            "in": "query",
            "description": "Filtra per data di creazione (a, ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "description": "Elementi per pagina — 5.000 predefiniti e al massimo",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "description": "Numero di pagina con base 1",
            "schema": {
              "type": "integer"
            },
            "example": 1
          },
          {
            "name": "sortByName",
            "in": "query",
            "description": "Imposta a `true` per ordinare alfabeticamente per nome",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "includeTotalContacts",
            "in": "query",
            "description": "Imposta a `true` per includere il conteggio `total_contacts` in ogni risultato",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste restituite con successo",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 101,
                    "name": "Newsletter subscribers",
                    "total_contacts": 4521,
                    "date_creation": "2025-01-15T10:30:00"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "tags": [
          "List"
        ],
        "operationId": "createList",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Crea una lista di contatti",
        "description": "Crea una nuova lista di contatti. Massimo 1.000 liste per account.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListEntry"
              },
              "example": {
                "name": "Spring Campaign 2026"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lista creata con successo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                },
                "example": {
                  "id": 102,
                  "name": "Spring Campaign 2026",
                  "date_creation": "2026-04-14T12:00:00"
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "406": {
            "description": "Limite di liste (1.000) raggiunto"
          }
        }
      }
    },
    "/List/{id}": {
      "get": {
        "tags": [
          "List"
        ],
        "operationId": "getList",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Ottieni una lista di contatti",
        "description": "Restituisce i dettagli di una lista di contatti specifica.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "responses": {
          "200": {
            "description": "Lista restituita con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Lista non trovata"
          }
        }
      },
      "put": {
        "tags": [
          "List"
        ],
        "operationId": "updateList",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiorna una lista di contatti",
        "description": "Rinomina una lista di contatti esistente.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListEntry"
              },
              "example": {
                "name": "Newsletter subscribers — Europe"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lista aggiornata con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Lista non trovata"
          }
        }
      },
      "delete": {
        "tags": [
          "List"
        ],
        "operationId": "deleteList",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elimina una lista di contatti",
        "description": "Elimina definitivamente una lista di contatti.\nI contatti stessi non vengono eliminati — viene rimossa solo la loro appartenenza a questa lista.\nLe liste utilizzate da flussi di lavoro di automazione attivi non possono essere eliminate.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "responses": {
          "200": {
            "description": "Lista eliminata con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Lista non trovata"
          },
          "406": {
            "description": "La lista è utilizzata da un flusso di lavoro di automazione attivo"
          }
        }
      }
    },
    "/List/{id}/Contact": {
      "get": {
        "tags": [
          "List"
        ],
        "operationId": "getListContacts",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i contatti in una lista",
        "description": "Restituisce i contatti appartenenti a una lista specifica.\nFiltra per email o data di aggiunta. Includi i tag e le appartenenze alle liste con i flag corrispondenti.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          },
          {
            "name": "email",
            "in": "query",
            "description": "Filtra per email (corrispondenza parziale)",
            "schema": {
              "type": "string"
            },
            "example": "@example.com"
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "description": "Elementi per pagina — 5.000 predefiniti e al massimo",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          },
          {
            "name": "sortByEmail",
            "in": "query",
            "description": "Ordina alfabeticamente per email",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "includeTags",
            "in": "query",
            "description": "Includi le assegnazioni di tag nei risultati",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "includeLists",
            "in": "query",
            "description": "Includi le appartenenze alle liste nei risultati",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contatti restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Lista non trovata o nessun contatto corrisponde ai criteri"
          }
        }
      }
    },
    "/List/{listId}/Unsubscribers": {
      "get": {
        "tags": [
          "List"
        ],
        "operationId": "getListUnsubscribers",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i disiscritti",
        "description": "Restituisce i contatti che si sono disiscritti da una lista specifica.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "ID della lista",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 101
          }
        ],
        "responses": {
          "200": {
            "description": "Disiscritti restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Lista non trovata"
          }
        }
      }
    },
    "/Tag": {
      "get": {
        "tags": [
          "Tag"
        ],
        "operationId": "getTags",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i tag",
        "description": "Restituisce tutti i tag per l'account.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Filtra per nome (corrispondenza parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          },
          {
            "name": "sortByName",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tag restituiti con successo",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 5,
                    "name": "VIP customers"
                  },
                  {
                    "id": 6,
                    "name": "Newsletter openers"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "tags": [
          "Tag"
        ],
        "operationId": "createTag",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Crea un tag",
        "description": "Crea un nuovo tag. Massimo 1.000 tag per account.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagEntry"
              },
              "example": {
                "name": "VIP customers"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag creato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "406": {
            "description": "Limite di tag (1.000) raggiunto"
          }
        }
      }
    },
    "/Tag/{id}": {
      "get": {
        "tags": [
          "Tag"
        ],
        "operationId": "getTag",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Ottieni un tag",
        "description": "Restituisce un tag specifico tramite il suo ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del tag",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          }
        ],
        "responses": {
          "200": {
            "description": "Tag restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Tag non trovato"
          }
        }
      },
      "put": {
        "tags": [
          "Tag"
        ],
        "operationId": "updateTag",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiorna un tag",
        "description": "Rinomina un tag esistente.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagEntry"
              },
              "example": {
                "name": "VIP customers — updated"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tag aggiornato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Tag non trovato"
          }
        }
      },
      "delete": {
        "tags": [
          "Tag"
        ],
        "operationId": "deleteTag",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elimina un tag",
        "description": "Elimina definitivamente un tag. Il tag viene rimosso da tutti i contatti.\nI tag utilizzati da flussi di lavoro di automazione attivi non possono essere eliminati.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          }
        ],
        "responses": {
          "200": {
            "description": "Tag eliminato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Tag non trovato"
          },
          "406": {
            "description": "Il tag è utilizzato da un flusso di lavoro di automazione attivo"
          }
        }
      }
    },
    "/Tag/{id}/Contact": {
      "get": {
        "tags": [
          "Tag"
        ],
        "operationId": "getTagContacts",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i contatti con un tag",
        "description": "Restituisce i contatti a cui è stato assegnato un tag specifico.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del tag",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Contatti restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Tag non trovato"
          }
        }
      }
    },
    "/Field": {
      "get": {
        "tags": [
          "Field"
        ],
        "operationId": "listFields",
        "summary": "Elenca i campi personalizzati",
        "description": "Restituisce tutti i campi personalizzati dei contatti definiti per l'account.\nUtilizza i valori `id` restituiti durante la creazione o l'aggiornamento dei contatti.\n",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Filtra per nome del campo",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "idFieldType",
            "in": "query",
            "description": "Filtra per ID del tipo di campo",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          },
          {
            "name": "sortByName",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campi personalizzati restituiti con successo",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 1,
                    "name": "first_name",
                    "personalisation_key": "first_name",
                    "field_type": "text"
                  },
                  {
                    "id": 2,
                    "name": "last_name",
                    "personalisation_key": "last_name",
                    "field_type": "text"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "tags": [
          "Field"
        ],
        "operationId": "createField",
        "summary": "Crea un campo personalizzato",
        "description": "Crea un nuovo campo personalizzato per i contatti.\nMassimo 50 campi per account.\nUtilizza l'`id` restituito quando assegni valori ai contatti.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FieldEntry"
              },
              "example": {
                "name": "First name",
                "id_field_type": 1,
                "personalize_key": "first_name"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campo creato con successo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Field"
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "406": {
            "description": "Limite di campi (50) raggiunto"
          }
        }
      }
    },
    "/Field/Types": {
      "get": {
        "tags": [
          "Field"
        ],
        "operationId": "listFieldTypes",
        "summary": "Elenca i tipi di campo",
        "description": "Restituisce gli identificatori dei tipi di campo disponibili da utilizzare durante la creazione di un campo personalizzato.\nTipi comuni: `1` = text, `2` = number, `3` = date.\n",
        "responses": {
          "200": {
            "description": "Tipi di campo restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Field/{id}": {
      "get": {
        "tags": [
          "Field"
        ],
        "operationId": "getField",
        "summary": "Ottieni un campo personalizzato",
        "description": "Restituisce la definizione e i metadati di un campo personalizzato specifico.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del campo",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Campo restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campo non trovato"
          }
        }
      },
      "put": {
        "tags": [
          "Field"
        ],
        "operationId": "updateField",
        "summary": "Aggiorna un campo personalizzato",
        "description": "Aggiorna il nome o la chiave di personalizzazione di un campo personalizzato.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 1
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FieldEntry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campo aggiornato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campo non trovato"
          }
        }
      },
      "delete": {
        "tags": [
          "Field"
        ],
        "operationId": "deleteField",
        "summary": "Elimina un campo personalizzato",
        "description": "Elimina definitivamente un campo personalizzato e rimuove il suo valore da tutti i contatti.\nQuesta azione non può essere annullata.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Campo eliminato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campo non trovato"
          }
        }
      }
    },
    "/Campaign": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "listCrmCampaigns",
        "summary": "Elenca le campagne",
        "description": "Restituisce un elenco paginato delle campagne email per l'account.",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 20
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Campagne restituite con successo",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 8001,
                    "title": "Spring Newsletter April 2026",
                    "date_planned": "2026-04-20T09:00:01",
                    "count_open": 1823,
                    "count_sent": 4521,
                    "count_click": 342,
                    "count_badmail": 12,
                    "count_unsubscription": 8,
                    "count_clicking_users": 289,
                    "count_complaints": 1,
                    "spam_trap": 0,
                    "deliverability_score": 98
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaign",
        "summary": "Ottieni una campagna",
        "description": "Restituisce la configurazione e il riepilogo delle statistiche di una campagna specifica.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Campagna restituita con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campagna non trovata"
          }
        }
      },
      "put": {
        "tags": [
          "Campaign"
        ],
        "operationId": "updateCrmCampaign",
        "summary": "Aggiorna una campagna",
        "description": "Aggiorna il titolo o la data di invio di una campagna programmata.\nLe campagne possono essere modificate solo fino a **5 minuti prima** dell'orario di invio programmato.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignEntry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campagna aggiornata con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campagna non trovata"
          }
        }
      },
      "delete": {
        "tags": [
          "Campaign"
        ],
        "operationId": "deleteCrmCampaign",
        "summary": "Elimina una campagna",
        "description": "Annulla ed elimina definitivamente una campagna tramite il suo identificatore.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Campagna eliminata con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Campagna non trovata"
          }
        }
      }
    },
    "/Campaign/Scheduled": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "listScheduledCrmCampaigns",
        "summary": "Elenca le mie campagne programmate",
        "description": "Restituisce le campagne con `date_planned` nel futuro che non sono ancora state\ninviate. Ordinate per `date_planned` crescente.\n",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 5000,
              "maximum": 5000
            }
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Elenco delle campagne programmate",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 5001,
                    "id_client": 12345,
                    "subject": "Spring Sale 2026",
                    "date_planned": "2026-05-01T09:00:00",
                    "title": "Spring Sale",
                    "id_campaign": 7001,
                    "id_list": 101,
                    "id_segment": null,
                    "number_email": 1250
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/Cancel": {
      "post": {
        "tags": [
          "Campaign"
        ],
        "operationId": "cancelScheduledCrmCampaign",
        "summary": "Annulla una campagna programmata",
        "description": "Annulla una campagna programmata nel futuro. Restituisce **409 Conflict** se la\ncampagna è già stata inviata o non esiste.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campagna annullata",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "cancelled": 5001
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Già inviata o non trovata"
          }
        }
      }
    },
    "/Campaign/{id}/Reschedule": {
      "patch": {
        "tags": [
          "Campaign"
        ],
        "operationId": "rescheduleCrmCampaign",
        "summary": "Riprogramma una campagna programmata",
        "description": "Aggiorna la data di invio pianificata di una campagna programmata. La nuova data deve essere\nnel futuro ed entro 90 giorni.\n\n**Nome del campo nel corpo:** l'API accetta `date_planned` (snake_case, primario)\nnonché gli alias legacy `PlanDate` e `scheduled_time`. Fornisci uno qualsiasi\ndi essi — viene utilizzato il primo non-null.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fornisci uno tra `date_planned`, `PlanDate` o `scheduled_time`.",
                "properties": {
                  "date_planned": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Nome del campo primario.",
                    "example": "2026-12-31T14:30:00"
                  },
                  "PlanDate": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias legacy accettato per retrocompatibilità."
                  },
                  "scheduled_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias legacy accettato per retrocompatibilità."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campagna riprogrammata",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "id": 5001,
                  "date_planned": "2026-12-31T14:30:00"
                }
              }
            }
          },
          "400": {
            "description": "Data mancante — fornisci uno tra date_planned / PlanDate / scheduled_time"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Già inviata",
            "not found": null,
            "or out of range": null
          }
        }
      }
    },
    "/Campaign/{id}/ByHour": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignStatsByHour",
        "summary": "Statistiche per ora",
        "description": "Restituisce la suddivisione oraria di aperture e clic per la campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche orarie restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/ByDay": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignStatsByDay",
        "summary": "Statistiche per giorno",
        "description": "Restituisce la suddivisione giornaliera di aperture e clic per la campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche giornaliere restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/Openings": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignOpenings",
        "summary": "Elenca le aperture email",
        "description": "Restituisce un elenco paginato dei contatti che hanno aperto l'email della campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          },
          {
            "name": "email",
            "in": "query",
            "description": "Filtra per email del destinatario",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Elenco aperture restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/Clicks": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignClicks",
        "summary": "Elenca i clic sui link",
        "description": "Restituisce un elenco paginato dei contatti che hanno cliccato un link nella campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Elenco clic restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/HardBounces": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignHardBounces",
        "summary": "Elenca gli hard bounce",
        "description": "Restituisce gli indirizzi email che hanno subito hard bounce durante questa campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Elenco hard bounce restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/Links": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignLinks",
        "summary": "Elenca i link tracciati",
        "description": "Restituisce i dati sulle prestazioni dei clic per ogni link tracciato nella campagna.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          },
          {
            "name": "link",
            "in": "query",
            "description": "Filtra per URL del link (corrispondenza parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche dei link restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/OS": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignStatsByOS",
        "summary": "Statistiche per OS / dispositivo",
        "description": "Restituisce la suddivisione di aperture e clic per sistema operativo e tipo di dispositivo.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche OS restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/EmailClients": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignStatsByEmailClient",
        "summary": "Statistiche per client email",
        "description": "Restituisce la suddivisione delle aperture per client email (Gmail, Outlook, Apple Mail…).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche del client email restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Campaign/{id}/Countries": {
      "get": {
        "tags": [
          "Campaign"
        ],
        "operationId": "getCrmCampaignStatsByCountry",
        "summary": "Statistiche per paese",
        "description": "Restituisce la distribuzione geografica di aperture e clic.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Statistiche per paese restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Send/Mail": {
      "post": {
        "tags": [
          "Send"
        ],
        "operationId": "sendTransactionalEmailCrm",
        "summary": "Invia email transazionale",
        "description": "Invia un'email transazionale una tantum a un singolo destinatario.\n\n**Destinatario:** fornisci `email` (indirizzo inline) **O** `id_contact` (contatto CRM) — non entrambi.\nL'uso di `id_contact` abilita la personalizzazione automatica con i valori dei campi personalizzati del contatto.\n\n**Contenuto:** fornisci `body_html` (HTML inline) **O** `id_message` (modello salvato) — non entrambi.\nSe sono forniti entrambi, `id_message` ha la precedenza.\n\n**Obbligatori:** `id_sender_email`, `subject` e uno tra (`email` o `id_contact`),\ne uno tra (`body_html` o `id_message`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              },
              "example": {
                "id_sender_email": 201,
                "email": "alice@example.com",
                "subject": "Your order #12345 has shipped",
                "body_html": "<p>Hello {{first_name}}, your order is on its way!</p>",
                "reply_to": "support@yourcompany.com",
                "fields": [
                  {
                    "id": 1,
                    "value": "Alice"
                  }
                ],
                "attachments": [
                  {
                    "filename": "invoice.pdf",
                    "content": "JVBERi0xLjQKJVBERi0xLjQK..."
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email inviata o programmata con successo",
            "content": {
              "application/json": {
                "example": {
                  "message": "Successful operation",
                  "id_single_send": 8001
                }
              }
            }
          },
          "400": {
            "description": "Formato email non valido o campo obbligatorio mancante"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Mittente, messaggio o contatto non trovato; oppure destinatario disiscritto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "no_credits": {
                    "summary": "Crediti insufficienti",
                    "value": {
                      "Message": "Not enough credit available"
                    }
                  },
                  "unsubscribed": {
                    "summary": "Destinatario disiscritto",
                    "value": {
                      "Message": "Email address unsubscribed."
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Errore di validazione (oggetto troppo lungo, body e message entrambi vuoti, ecc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/Send/{id}/Status": {
      "get": {
        "tags": [
          "Send"
        ],
        "operationId": "getTransactionalStatusCrm",
        "summary": "Ottieni lo stato di consegna di un'email transazionale",
        "description": "Restituisce lo stato di consegna attuale di un'email transazionale inviata con `POST /Send/Mail`. Passa l'`id_single_send` restituito da quella chiamata.\n\nValori possibili: `delivered` (accettata dal server del destinatario, vedi `diag_code`), `bounced` (rifiutata — `bounce_type: hard` indirizzo non valido o rifiuto definitivo, `soft` rifiuto dopo nuovi tentativi), `sent` (consegnata al server di posta, ancora nessuna risposta), `scheduled` (non ancora inviata, `date_planned` nel futuro) e `queued` (in attesa di invio). `delivered` e `bounced` sono finali: smettete di interrogare. Ogni email ha uno stato finale al più tardi 48 ore dopo l'invio; interrogate al massimo ogni 15 minuti (ogni chiamata conta nella vostra quota API) oppure usate i webhook `email.delivered` / `email.bounced`. Solo il cliente che ha inviato l'email può leggerne lo stato — qualsiasi altro id restituisce 404.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "L'`id_single_send` restituito da `POST /Send/Mail`.",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 8001
          }
        ],
        "responses": {
          "200": {
            "description": "Stato di consegna attuale",
            "content": {
              "application/json": {
                "example": {
                  "id_single_send": 8001,
                  "email": "alice@example.com",
                  "status": "delivered",
                  "date_planned": "2026-04-15T14:30:00",
                  "status_date": "2026-04-15T14:30:07",
                  "diag_code": "2.0.0 (success)",
                  "bounce_type": null
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Nessun invio transazionale trovato con questo id per questo cliente"
          }
        }
      }
    },
    "/Send/Campaign": {
      "post": {
        "tags": [
          "Send"
        ],
        "operationId": "sendCampaignCrm",
        "summary": "Invia campagna email",
        "description": "Invia una campagna email in blocco a una o più liste di contatti.\n\n**Obbligatori:**\n- `id_message` — il modello da inviare (da `GET /Message`)\n- `id_sender_email` — l'indirizzo del mittente (da `GET /SenderEmail`)\n- `lists` — almeno una lista di contatti target\n- `campaign_title` — etichetta per il tracciamento delle statistiche\n\n**Segmento:** opzionalmente filtra i destinatari tramite un segmento salvato (`id_segment`).\nIl segmento filtra i contatti all'interno delle liste specificate.\n\n**Programmazione:** imposta `date_planned` a una data/ora ISO 8601 futura.\nLe campagne possono essere modificate o annullate fino a 5 minuti prima dell'orario programmato.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendCampaignRequest"
              },
              "example": {
                "id_message": 501,
                "id_sender_email": 201,
                "lists": [
                  {
                    "id": 101
                  }
                ],
                "campaign_title": "Spring Newsletter April 2026",
                "date_planned": "2026-04-20T09:00:00"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campagna programmata o inviata con successo",
            "content": {
              "application/json": {
                "example": {
                  "Message": "Successful operation. Campaigns can be edited or deleted until 5 minutes prior to send.",
                  "IdCampaign": 8001
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "402": {
            "description": "Crediti insufficienti",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Messaggio, mittente, lista o segmento non trovato; oppure crediti insufficienti",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Titolo della campagna vuoto o troppo lungo (massimo 150 caratteri)"
          }
        }
      }
    },
    "/Send": {
      "get": {
        "tags": [
          "Send"
        ],
        "operationId": "getSendWaiting",
        "summary": "Elenca gli invii programmati",
        "description": "Restituisce l'elenco delle campagne email in attesa di essere inviate (programmate, non ancora elaborate).",
        "parameters": [
          {
            "name": "subjectMessage",
            "in": "query",
            "description": "Filtra per oggetto (corrispondenza parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Invii in attesa restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Send/{id}": {
      "delete": {
        "tags": [
          "Send"
        ],
        "operationId": "deleteSendWaiting",
        "summary": "Annulla un invio programmato",
        "description": "Annulla ed elimina un invio email programmato che non è ancora stato elaborato.\nNon è possibile annullare gli invii attualmente in fase di elaborazione.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID dell'invio (da `GET /Send`)",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 3001
          }
        ],
        "responses": {
          "200": {
            "description": "Invio programmato annullato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Invio programmato non trovato"
          }
        }
      }
    },
    "/Send/Mail/Scheduled": {
      "get": {
        "tags": [
          "Send"
        ],
        "operationId": "listScheduledCrmMails",
        "security": [
          {
            "OAuth2": [
              "email:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca le email singole pianificate",
        "description": "Restituisce le email singole inviate con `POST /Send/Mail` e una data futura, non ancora partite. Le campagne sono elencate da `GET /Campaign/Scheduled`.",
        "responses": {
          "200": {
            "description": "Email pianificate restituite",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id_single_send": 880123,
                    "email": "jane@example.com",
                    "subject": "Your invoice",
                    "date_planned": "2026-12-01T09:00:00"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Send/Mail/{id}/Cancel": {
      "post": {
        "tags": [
          "Send"
        ],
        "operationId": "cancelCrmMail",
        "security": [
          {
            "OAuth2": [
              "email:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Annulla un'email singola pianificata",
        "description": "Annulla l'email finché non è partita.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id_single_send` restituito da `POST /Send/Mail`",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Email annullata",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "cancelled": 880123
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Invio non trovato o già partito"
          }
        }
      }
    },
    "/Send/Mail/{id}/Reschedule": {
      "patch": {
        "tags": [
          "Send"
        ],
        "operationId": "rescheduleCrmMail",
        "security": [
          {
            "OAuth2": [
              "email:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Ripianifica un'email singola pianificata",
        "description": "Cambia la data di invio. La nuova data deve essere futura ed entro 90 giorni. È accettato anche `POST`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id_single_send` restituito da `POST /Send/Mail`",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "date_planned"
                ],
                "properties": {
                  "date_planned": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Nuova data di invio (ISO 8601)",
                    "example": "2026-12-15T09:00:00"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email ripianificata",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "id": 880123,
                  "date_planned": "2026-12-15T09:00:00"
                }
              }
            }
          },
          "400": {
            "description": "Data mancante"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Invio non trovato, già partito o data fuori dai limiti"
          }
        }
      }
    },
    "/SenderEmail": {
      "get": {
        "tags": [
          "Sender Addresses"
        ],
        "operationId": "listCrmSenderEmails",
        "summary": "Elenca gli indirizzi mittente",
        "description": "Restituisce gli indirizzi email mittenti validati configurati per l'account.",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Indirizzi mittente restituiti con successo",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 201,
                    "email": "newsletter@yourcompany.com",
                    "sender_name": "Your Company Newsletter",
                    "is_validated": true
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "tags": [
          "Sender Addresses"
        ],
        "operationId": "addCrmSenderEmail",
        "security": [
          {
            "OAuth2": [
              "email:templates:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiungi un indirizzo mittente",
        "description": "Aggiunge l'indirizzo con gli stessi controlli dell'applicazione e gli invia un'email di convalida. È utilizzabile dopo aver cliccato il link in quell'email. Un account può avere fino a 25 mittenti, convalidati o in attesa.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "sender_name"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "Indirizzo del mittente",
                    "example": "news@example.com"
                  },
                  "sender_name": {
                    "type": "string",
                    "description": "Nome mostrato ai destinatari",
                    "example": "Example News"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Indirizzo aggiunto. `domain_has_mx` è false quando il dominio non ha un record MX.",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "confirmation_sent": true,
                  "domain_has_mx": true
                }
              }
            }
          },
          "400": {
            "description": "`email` o `sender_name` mancante"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "422": {
            "description": "Indirizzo rifiutato: non valido, già tra i mittenti dell'account, dominio di posta pubblico, nome vietato o limite di 25 raggiunto. Il messaggio indica il motivo."
          },
          "503": {
            "description": "Temporaneamente non disponibile, riprova"
          }
        }
      }
    },
    "/SenderEmail/{id}/ResendValidation": {
      "post": {
        "tags": [
          "Sender Addresses"
        ],
        "operationId": "resendCrmSenderValidation",
        "security": [
          {
            "OAuth2": [
              "email:templates:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Invia di nuovo l'email di convalida",
        "description": "Per un indirizzo ancora in attesa di convalida.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id_sender_email` da `GET /SenderEmail`",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Email inviata di nuovo",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "confirmation_sent": true
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Mittente non trovato"
          },
          "409": {
            "description": "Questo mittente è già convalidato"
          },
          "503": {
            "description": "Temporaneamente non disponibile, riprova"
          }
        }
      }
    },
    "/SenderEmail/{id}/Authentication": {
      "get": {
        "tags": [
          "Sender Addresses"
        ],
        "operationId": "getCrmSenderAuthentication",
        "security": [
          {
            "OAuth2": [
              "email:templates:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Controlla il dominio di un mittente (SPF, DKIM, DMARC, MX)",
        "description": "Controlla il dominio in tempo reale nel DNS e restituisce i record da pubblicare. Sola lettura. Il DKIM si attiva nell'applicazione: `dkim_settings_url` apre la schermata giusta.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id_sender_email` da `GET /SenderEmail`",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stato del dominio. `spf.recommended_record` è costruito a partire dal tuo record attuale: pubblicalo solo se `spf.change_needed` è true.",
            "content": {
              "application/json": {
                "example": {
                  "domain": "example.com",
                  "spf": {
                    "valid": true,
                    "record": "v=spf1 include:spf.mailpro.com -all",
                    "recommended_record": "v=spf1 include:spf.mailpro.com -all",
                    "change_needed": false
                  },
                  "dkim": {
                    "valid": true,
                    "records": [
                      {
                        "host": "key._domainkey.example.com",
                        "value": "v=DKIM1;k=rsa;p=MIIBIjANBgkq...",
                        "validated": true
                      }
                    ]
                  },
                  "dmarc": {
                    "valid": true,
                    "record": "v=DMARC1; p=quarantine"
                  },
                  "mx": {
                    "valid": true,
                    "record": "mail.example.com"
                  },
                  "dkim_settings_url": "https://app.mailpro.com/dkim/list?d=example.com"
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Mittente non trovato"
          },
          "503": {
            "description": "Temporaneamente non disponibile, riprova"
          }
        }
      }
    },
    "/History/Email": {
      "get": {
        "tags": [
          "History"
        ],
        "operationId": "getCrmHistoryEmail",
        "summary": "Elenca lo storico degli invii email",
        "description": "Restituisce il registro degli invii email passati per l'account.",
        "parameters": [
          {
            "name": "subjectMessage",
            "in": "query",
            "description": "Filtra per oggetto (corrispondenza parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "idCampaign",
            "in": "query",
            "description": "Filtra per ID campagna",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "includeBodyHTML",
            "in": "query",
            "description": "Includi il corpo HTML completo nella risposta (più lento)",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Storico email restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/History/SMS": {
      "get": {
        "tags": [
          "History"
        ],
        "operationId": "getCrmHistorySms",
        "summary": "Elenca lo storico degli invii SMS",
        "description": "Restituisce il registro degli invii SMS passati per l'account.",
        "parameters": [
          {
            "name": "dateSend",
            "in": "query",
            "description": "Filtra da questa data (ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateSendEnd",
            "in": "query",
            "description": "Filtra fino a questa data (ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "phoneNumber",
            "in": "query",
            "description": "Filtra per telefono del destinatario (parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Filtro per tipo di SMS",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "statusCode",
            "in": "query",
            "description": "Filtro per codice di stato DLR",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "sortByStatus",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Storico SMS restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Suppression/Bounces": {
      "get": {
        "tags": [
          "Suppression"
        ],
        "operationId": "listCrmBounces",
        "security": [
          {
            "OAuth2": [
              "email:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i bounce dell'account",
        "description": "Prima i più recenti. Il totale è nell'intestazione `X-Total-Count`.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "`hard` o `soft`",
            "schema": {
              "type": "string",
              "enum": [
                "hard",
                "soft"
              ]
            }
          },
          {
            "name": "source",
            "in": "query",
            "description": "Origine del bounce: `list`, `contact`, `api` o `smtp`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "Indirizzo esatto",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "description": "A partire da questa data",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "description": "Fino a questa data",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounce restituiti",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 5521,
                    "source": "contact",
                    "type": "hard",
                    "email": "old@example.com",
                    "date": "2026-10-12T08:30:00",
                    "diag_code": "550 5.1.1 User unknown",
                    "id_list": 101
                  }
                ]
              }
            }
          },
          "400": {
            "description": "`type` deve essere `hard` o `soft`"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Suppression/Bounces/{id}": {
      "delete": {
        "tags": [
          "Suppression"
        ],
        "operationId": "deleteCrmBounce",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Rimuovi un bounce",
        "description": "`source` e `type` sono obbligatori, come restituiti da `GET /Suppression/Bounces`: un `id` da solo non è univoco.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id` del bounce",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": true,
            "description": "`list`, `contact`, `api` o `smtp`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "description": "`hard` o `soft`",
            "schema": {
              "type": "string",
              "enum": [
                "hard",
                "soft"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounce rimosso",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "id": 5521,
                  "restored": false
                }
              }
            }
          },
          "400": {
            "description": "`source` o `type` mancante"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Bounce non trovato"
          }
        }
      }
    },
    "/Suppression/Bounces/{id}/Restore": {
      "post": {
        "tags": [
          "Suppression"
        ],
        "operationId": "restoreCrmBounce",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Rimetti nelle sue liste un contatto in hard bounce",
        "description": "Solo per un hard bounce con origine `contact`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id` del bounce",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contatto rimesso in lista",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "id": 5521,
                  "restored": true
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Hard bounce di un contatto non trovato o indirizzo non più attivo"
          }
        }
      }
    },
    "/Suppression/Unsubscribers": {
      "get": {
        "tags": [
          "Suppression"
        ],
        "operationId": "listCrmUnsubscribers",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i disiscritti dell'account",
        "description": "Prima i più recenti. Il totale è nell'intestazione `X-Total-Count`.",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "description": "Origine della disiscrizione: `contact`, `list`, `smtp`, `api` o `api3`",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "description": "Indirizzo esatto",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "description": "A partire da questa data",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "description": "Fino a questa data",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disiscritti restituiti",
            "content": {
              "application/json": {
                "example": [
                  {
                    "id": 9012,
                    "source": "api3",
                    "email": "stop@example.com",
                    "date": "2026-10-20T16:05:00",
                    "list_name": null,
                    "comment": "Asked by phone"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "tags": [
          "Suppression"
        ],
        "operationId": "addCrmUnsubscriber",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Aggiungi un disiscritto",
        "description": "Senza `id_list`, l'indirizzo viene disiscritto per tutto l'account. Con `id_list`, solo da quella lista di disiscrizione.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "Indirizzo da disiscrivere",
                    "example": "stop@example.com"
                  },
                  "comment": {
                    "type": "string",
                    "description": "Commento libero",
                    "example": "Asked by phone"
                  },
                  "id_list": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Lista di disiscrizione dell'account (facoltativo)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fatto. `already_unsubscribed` è true quando l'indirizzo era già disiscritto.",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "already_unsubscribed": false
                }
              }
            }
          },
          "400": {
            "description": "È richiesto un indirizzo valido"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "`id_list` non è una lista di disiscrizione di questo account"
          }
        }
      }
    },
    "/Suppression/Unsubscribers/{id}": {
      "delete": {
        "tags": [
          "Suppression"
        ],
        "operationId": "deleteCrmUnsubscriber",
        "security": [
          {
            "OAuth2": [
              "contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Rimuovi un disiscritto",
        "description": "`source` è obbligatorio, come restituito da `GET /Suppression/Unsubscribers`: un `id` da solo non è univoco.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "`id` del disiscritto",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": true,
            "description": "`contact`, `list`, `smtp`, `api` o `api3`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disiscritto rimosso",
            "content": {
              "application/json": {
                "example": {
                  "result": "ok",
                  "removed": 9012
                }
              }
            }
          },
          "400": {
            "description": "`source` è obbligatorio"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Disiscritto non trovato"
          }
        }
      }
    },
    "/Segment": {
      "get": {
        "tags": [
          "Segment"
        ],
        "operationId": "getSegments",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i segmenti",
        "description": "Restituisce tutti i segmenti dinamici salvati per l'account.",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Segmenti restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Segment/{id}/Contact": {
      "get": {
        "tags": [
          "Segment"
        ],
        "operationId": "getCrmSegmentContacts",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Elenca i contatti di un segmento",
        "description": "Con `id_list`, restituisce solo i contatti di quella lista presenti nel segmento: quelli che una campagna lista + segmento raggiungerebbe. Il totale è nell'intestazione `X-Total-Count`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del segmento",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "id_list",
            "in": "query",
            "description": "Limita a questa lista (facoltativo)",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Contatti restituiti"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Segmento o lista non trovati"
          }
        }
      }
    },
    "/Segment/{id}/Count": {
      "get": {
        "tags": [
          "Segment"
        ],
        "operationId": "countCrmSegmentContacts",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Conta i contatti di un segmento",
        "description": "Solo il numero, per conoscere in anticipo la dimensione di un invio. Con `id_list`, conta solo i contatti di quella lista presenti nel segmento.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del segmento",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "id_list",
            "in": "query",
            "description": "Limita a questa lista (facoltativo)",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Numero di contatti",
            "content": {
              "application/json": {
                "example": {
                  "total": 1250
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Segmento o lista non trovati"
          }
        }
      }
    },
    "/Segment/{id}/contact/{contactId}/check": {
      "get": {
        "tags": [
          "Segment"
        ],
        "operationId": "checkContactInSegment",
        "security": [
          {
            "OAuth2": [
              "contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Verifica se un contatto corrisponde a un segmento",
        "description": "Verifica se un contatto specifico (tramite GUID) corrisponde attualmente alle regole\ndi un determinato segmento. Utile per attivare la logica di automazione su richiesta.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID del segmento",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 4001
          },
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "GUID del contatto",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "11111111-2222-3333-4444-555555555555"
          }
        ],
        "responses": {
          "200": {
            "description": "Restituisce il risultato della corrispondenza"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Segmento o contatto non trovato"
          }
        }
      }
    },
    "/FolderList": {
      "get": {
        "tags": [
          "Folders"
        ],
        "operationId": "getFolderLists",
        "summary": "Elenca le cartelle delle liste",
        "description": "Restituisce le cartelle utilizzate per organizzare le liste di contatti.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Filtra per nome della cartella (parziale)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Cartelle delle liste restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/FolderList/{id}": {
      "get": {
        "tags": [
          "Folders"
        ],
        "operationId": "getFolderListById",
        "summary": "Ottieni una singola cartella di liste",
        "description": "Restituisce i dettagli di una cartella di liste tramite il suo ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID della cartella",
            "schema": {
              "type": "integer"
            },
            "example": 12
          }
        ],
        "responses": {
          "200": {
            "description": "Cartella restituita con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Cartella non trovata"
          }
        }
      }
    },
    "/Sms/SingleSMS": {
      "post": {
        "tags": [
          "SMS"
        ],
        "operationId": "sendSingleSmsCrm",
        "summary": "Invia singolo SMS",
        "description": "Invia un singolo SMS direttamente a un numero di telefono.\nUtilizzalo per messaggi transazionali (OTP, conferme d'ordine, avvisi).\nIl numero di telefono accetta `+41…`, `0041…` o `41…` con o senza separatori —\nvedi **Formato del numero di telefono** nella descrizione dell'API per gli input accettati / rifiutati.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSingleRequest"
              },
              "example": {
                "phone_number": "+41791234567",
                "message": "Your verification code is 9473. Valid for 10 minutes."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SMS inviato o programmato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "422": {
            "description": "Formato del numero di telefono non valido"
          }
        }
      }
    },
    "/Sms/Campaign": {
      "post": {
        "tags": [
          "SMS"
        ],
        "operationId": "sendSmsCampaignCrm",
        "summary": "Invia campagna SMS",
        "description": "Invia un messaggio SMS al campo `phone_number` di tutti i contatti in una o più liste.\nI contatti senza un numero di telefono vengono saltati.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsCampaignRequest"
              },
              "example": {
                "lists": [
                  {
                    "id": 101
                  }
                ],
                "message": "Flash sale today — 20% off. Shop at mailpro.com",
                "date_planned": "2026-04-20T10:00:00"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campagna SMS inviata o programmata con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Sms": {
      "get": {
        "tags": [
          "SMS"
        ],
        "operationId": "getSmsSends",
        "summary": "Elenca lo storico SMS",
        "description": "Restituisce il registro degli invii SMS (sia singoli che di campagna) per l'account.",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Storico SMS restituito con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Sms/{id}": {
      "delete": {
        "tags": [
          "SMS"
        ],
        "operationId": "deleteSmsSendCrm",
        "summary": "Annulla un SMS programmato",
        "description": "Annulla un invio SMS programmato in attesa.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID dell'invio SMS",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "example": 5001
          }
        ],
        "responses": {
          "200": {
            "description": "SMS programmato annullato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "404": {
            "description": "Invio SMS non trovato"
          }
        }
      }
    },
    "/Sms/Scheduled": {
      "get": {
        "tags": [
          "SMS"
        ],
        "operationId": "listScheduledSmsCrm",
        "summary": "Elenca gli invii SMS programmati",
        "description": "Restituisce gli invii SMS con una `date_planned` futura (non ancora inviati), ordinati per data crescente.",
        "parameters": [
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 100
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Invii SMS programmati restituiti con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Sms/{id}/Cancel": {
      "post": {
        "tags": [
          "SMS"
        ],
        "operationId": "cancelScheduledSmsCrm",
        "summary": "Annulla un SMS programmato",
        "description": "Annulla un invio SMS programmato solo se non è ancora stato inviato.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID dell'invio SMS",
            "schema": {
              "type": "integer"
            },
            "example": 5001
          }
        ],
        "responses": {
          "200": {
            "description": "SMS programmato annullato con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Invio SMS non trovato, non di proprietà del client o già inviato"
          }
        }
      }
    },
    "/Sms/{id}/Reschedule": {
      "patch": {
        "tags": [
          "SMS"
        ],
        "operationId": "rescheduleScheduledSmsCrm",
        "summary": "Riprogramma un SMS programmato",
        "description": "Aggiorna la data di invio pianificata di un SMS programmato. La nuova data deve essere\nnel futuro ed entro 90 giorni. Fallisce se l'SMS è già stato inviato.\n\n**Nome del campo nel corpo:** l'API accetta `date_planned` (snake_case, primario)\nnonché gli alias legacy `PlanDate` e `scheduled_time`. Fornisci uno qualsiasi\ndi essi.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "ID dell'invio SMS",
            "schema": {
              "type": "integer"
            },
            "example": 5001
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fornisci uno tra `date_planned`, `PlanDate` o `scheduled_time`.",
                "properties": {
                  "date_planned": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Nome del campo primario.",
                    "example": "2026-05-10T14:00:00Z"
                  },
                  "PlanDate": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias legacy accettato per retrocompatibilità."
                  },
                  "scheduled_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias legacy accettato per retrocompatibilità."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SMS programmato riprogrammato con successo"
          },
          "400": {
            "description": "Data mancante — fornisci uno tra date_planned / PlanDate / scheduled_time"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "409": {
            "description": "Riprogrammazione fallita (non trovato, già inviato o data fuori intervallo)"
          }
        }
      }
    },
    "/FolderMessage": {
      "get": {
        "tags": [
          "Folders"
        ],
        "operationId": "getFolderMessages",
        "summary": "Elenca le cartelle dei messaggi",
        "description": "Restituisce le cartelle utilizzate per organizzare i modelli di messaggi email.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "description": "Filtra per nome della cartella",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "numberOfRecords",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 50
          },
          {
            "name": "pageNumber",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "example": 1
          }
        ],
        "responses": {
          "200": {
            "description": "Cartelle dei messaggi restituite con successo"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Import/Upload": {
      "post": {
        "tags": [
          "Import"
        ],
        "operationId": "uploadImportFile",
        "summary": "Carica un file di contatti (CSV / XLSX) per l'importazione in blocco",
        "description": "Carica un file CSV o XLSX per importare contatti in una lista.\nIl file viene memorizzato e messo in coda per l'elaborazione asincrona — l'endpoint\nrestituisce immediatamente un `import_job_id`.\n\n**Tipo di richiesta:** `multipart/form-data`\n\n**Campi del modulo obbligatori:**\n- `id_list` (intero) — ID della lista di destinazione\n- `file` (binario) — il file CSV o XLSX da caricare\n- `json_match_fields` (stringa, JSON) — quale colonna del file va in\n  quale campo del contatto. `email_position` = colonna dell'e-mail,\n  numerata da 0 (obbligatorio), `phone_position` = colonna del numero di\n  cellulare o `-1`, `fields` = ID degli altri campi (vedi `GET /Field`),\n  `fields_position` = colonna di ciascuno di essi, `update_fields` =\n  aggiornare i contatti esistenti. Corrispondenza assente o non valida:\n  risposta `400`, nessuna importazione viene creata.\n\n**Opzionali:**\n- `webhook_url` (stringa) — URL HTTPS chiamato al termine dell'elaborazione.\n  Riceve un payload JSON con lo stato finale (success / failed) e\n  i conteggi delle righe importate / saltate / non valide.\n\n**Formato del file:**\n- Codifica `UTF-8`\n- La prima riga può contenere le intestazioni (rilevamento automatico: una\n  prima riga senza alcun indirizzo e-mail viene ignorata)\n- Le colonne sono associate ai campi tramite `json_match_fields`\n- Dimensione massima del file: 50 MB\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "id_list",
                  "file",
                  "json_match_fields"
                ],
                "properties": {
                  "id_list": {
                    "type": "integer",
                    "format": "int64",
                    "description": "ID della lista di destinazione",
                    "example": 11088
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "File CSV o XLSX (`UTF-8`, max 50 MB)"
                  },
                  "json_match_fields": {
                    "type": "string",
                    "description": "Corrispondenza JSON tra le colonne del file e i campi del contatto",
                    "example": "{\"email_position\":0,\"phone_position\":-1,\"fields\":[1315597,1315598],\"fields_position\":[1,2],\"update_fields\":true}"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL di callback invocato al termine dell'elaborazione",
                    "example": "https://example.com/hooks/mailpro-import"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File accettato e messo in coda",
            "content": {
              "application/json": {
                "example": {
                  "result": "OK",
                  "import_job_id": 14,
                  "internal_name": "230575-20260420142615-contacts.csv"
                }
              }
            }
          },
          "400": {
            "description": "Nessun file caricato, id_list mancante, oppure json_match_fields assente / non valido"
          },
          "401": {
            "description": "Token non valido o scaduto"
          },
          "500": {
            "description": "Errore del server o dell'archiviazione"
          }
        }
      }
    },
    "/Webhook": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook (separato dall'host API principale)"
        }
      ],
      "get": {
        "summary": "Elenca i webhook",
        "description": "Restituisce ogni webhook configurato sull'account autenticato.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Array di webhook",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Webhook"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      },
      "post": {
        "summary": "Crea un webhook",
        "description": "Crea una nuova sottoscrizione webhook. La risposta contiene un `secret` che\nviene **mostrato solo una volta** — memorizzalo immediatamente, lato server. Utilizzalo\nper verificare la firma HMAC di ogni payload in arrivo.\n\n**Limiti**: massimo 20 webhook per account, massimo 50 eventi per webhook.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "url": "https://example.com/hooks/mailpro",
                "events": [
                  "email.delivered",
                  "email.bounced",
                  "contact.created"
                ],
                "description": "Integrazione Slack"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook creato — il `secret` viene restituito solo qui",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreateResponse"
                },
                "example": {
                  "id": 42,
                  "url": "https://example.com/hooks/mailpro",
                  "events": [
                    "email.delivered",
                    "email.bounced",
                    "contact.created"
                  ],
                  "description": "Integrazione Slack",
                  "isActive": true,
                  "consecutiveFailures": 0,
                  "autoDisabledAt": null,
                  "autoDisabledReason": null,
                  "createdAt": "2026-05-25T10:00:00Z",
                  "updatedAt": "2026-05-25T10:00:00Z",
                  "secret": "whsec_AbcD1234EfgH5678IjkL9012MnoP3456QrsT"
                }
              }
            }
          },
          "400": {
            "description": "URL non valido, elenco eventi o limite superato"
          },
          "401": {
            "description": "Token non valido o scaduto"
          }
        }
      }
    },
    "/Webhook/{id}": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "get": {
        "summary": "Ottieni un webhook",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Il webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      },
      "put": {
        "summary": "Aggiorna un webhook",
        "description": "Aggiornamento parziale — vengono modificati solo i campi inviati.",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook aggiornato",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Payload non valido"
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      },
      "delete": {
        "summary": "Elimina un webhook",
        "description": "Elimina definitivamente il webhook e l'intero storico delle consegne.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "204": {
            "description": "Eliminato"
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      }
    },
    "/Webhook/{id}/test": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "post": {
        "summary": "Invia un evento di test",
        "description": "Mette in coda un evento `ping` immediato (o qualsiasi altro tipo di evento tramite il\ncampo `eventType` nel corpo) verso il tuo endpoint. Utile per verificare la raggiungibilità\ne la verifica della firma prima di affidarsi agli eventi reali.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "default": "ping",
                    "description": "Sovrascrivi il tipo di evento (deve essere sottoscritto da questo webhook). `ping` funziona sempre."
                  }
                }
              },
              "example": {
                "eventType": "ping"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Evento messo in coda per la spedizione",
            "content": {
              "application/json": {
                "example": {
                  "deliveryId": 1234,
                  "eventId": "evt_8f3a9b2c4d1e5f6789abcdef01234567",
                  "eventType": "ping"
                }
              }
            }
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      }
    },
    "/Webhook/{id}/rotate-secret": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "post": {
        "summary": "Ruota il secret HMAC",
        "description": "Genera un nuovo secret HMAC e invalida immediatamente quello vecchio.\nIl nuovo secret viene restituito **una sola volta** nella risposta — memorizzalo. Ogni\nconsegna da questo momento in poi sarà firmata con il nuovo secret.\n\nUtilizzalo se sospetti che il tuo secret sia stato compromesso.\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Nuovo secret (visibile una sola volta)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "secret": "whsec_XyzA9876BcdE5432FghI1098JklM7654NopQ"
                }
              }
            }
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      }
    },
    "/Webhook/{id}/deliveries": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        },
        {
          "in": "query",
          "name": "limit",
          "schema": {
            "type": "integer",
            "default": 50,
            "maximum": 200
          }
        },
        {
          "in": "query",
          "name": "cursor",
          "schema": {
            "type": "integer",
            "format": "int64"
          },
          "description": "Cursor di paginazione (= ultimo ID di consegna visto). Restituisce le consegne con `id < cursor`."
        }
      ],
      "get": {
        "summary": "Elenca i tentativi di consegna",
        "description": "Restituisce lo storico dei tentativi di spedizione per questo webhook (retention di 30 giorni).\nUtile per il debug — quali eventi sono stati inviati, codici di stato ricevuti,\nnumero di tentativi, messaggi di errore.\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Array dei tentativi di consegna (dal più recente)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDelivery"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook non trovato"
          }
        }
      }
    },
    "/Webhook/{id}/deliveries/{deliveryId}/replay": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/v3",
          "description": "Servizio di gestione webhook"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        },
        {
          "in": "path",
          "name": "deliveryId",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "post": {
        "summary": "Rinvia una consegna",
        "description": "Mette in coda un nuovo tentativo di consegna con lo stesso payload dell'originale.\nUtile per riattivare una consegna fallita dopo aver corretto il tuo endpoint.\nViene creata una NUOVA riga `Webhook_Delivery` (l'originale viene preservata\nper audit). Il replay segue la politica di retry standard\n(10s → 1min → 5min → 30min → 2h → 6h → 24h, poi abbandonato).\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "202": {
            "description": "Replay messo in coda",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "new_delivery_id": {
                      "type": "integer",
                      "format": "int64",
                      "example": 12999
                    },
                    "queued_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook o consegna non trovato"
          }
        }
      }
    }
  }
}