{
  "openapi": "3.0.3",
  "info": {
    "title": "API SMS Mailpro™",
    "version": "2",
    "description": "L'API SMS Mailpro™ vous permet de gérer par programmation des listes de contacts téléphoniques,\ndes numéros de téléphone, et d'envoyer des SMS en masse ou individuels.\n\n## Authentification\n\nChaque endpoint accepte **trois méthodes d'authentification**. Utilisez celle qui convient\nà votre pile technique.\n\n- **OAuth 2.0** — jetons à portée restreinte et révocables pour les applications tierces et\n  les plateformes d'intégration (Zapier, Make, n8n). **Les jetons OAuth sont soumis à un contrôle\n  de portée par endpoint** (voir ci-dessous).\n- **Bearer JWT** (legacy) — un jeton HMAC issu de `POST /token`, sans portée.\n- **id_client + api_key** (legacy) — identifiant client + clé API.\n\n> Les méthodes legacy Bearer JWT et id_client+api_key continuent de fonctionner sans changement et ne\n> sont **pas** soumises au contrôle de portée. Seuls les jetons d'accès OAuth 2.0 sont soumis aux\n> contrôles de portée par endpoint indiqués sur chaque opération ci-dessous.\n\n### Méthode 1 — OAuth 2.0 (recommandée pour les intégrations)\n\nMailpro exploite **un seul** serveur d'autorisation OAuth 2.0 pour l'ensemble de ses APIs, exposé sur\nune surface unique et indépendante de la version : **`https://api.mailpro.com/oauth/`**. Il n'existe\npas d'endpoint OAuth par version — vous obtenez un jeton depuis `/oauth/token` et le **même** jeton\nfonctionne sur cette API SMS, sur le CRM v3 et sur l'Email v2. Les endpoints qu'il peut appeler sont\ndéterminés par les **portées** du jeton, et non par l'URL de base de l'API. Émetteur (claim `iss`) :\n`https://api.mailpro.com/oauth/`.\n\n| Endpoint | URL |\n|----------|-----|\n| Autorisation | `GET https://api.mailpro.com/oauth/authorize` |\n| Jeton / Rafraîchissement | `POST https://api.mailpro.com/oauth/token` |\n| JWKS | `GET https://api.mailpro.com/oauth/.well-known/jwks` |\n| Découverte | `GET https://api.mailpro.com/oauth/.well-known/openid-configuration` |\n\nGrants : `authorization_code` **+ PKCE (S256, obligatoire)**, `client_credentials`,\n`refresh_token` (rotation). Durée de vie du jeton d'accès **20 min** ; jeton de rafraîchissement **30 jours**\n(glissant, à usage unique) ; code d'autorisation **60 s**.\n\n**Démarrage rapide — obtenir un jeton et appeler cette API** (remplacez les valeurs d'exemple ; ne\nversionnez jamais un véritable secret) :\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=sms:send sms:read\"\n```\n\nPuis appelez cette API avec `Authorization: Bearer ACCESS_TOKEN`. Le même jeton fonctionne\nsur les APIs CRM v3 et Email v2. Un jeton dépourvu de la portée requise par un endpoint reçoit\n**403 `insufficient_scope`**. Présentation complète du flux et catalogue des portées : consultez la\ndocumentation de l'API **CRM v3** › Authentification. Portées propres au SMS :\n`sms:send`, `sms:read`, `sms:contacts:read`, `sms:contacts:write`,\n`account:read`.\n\n### Méthode 2 — Bearer JWT (legacy)\n\n```\nAuthorization: Bearer <access_token>\n```\n\nObtenez un jeton via `POST /token` (encodé en formulaire, grant OAuth2 password) :\n\n```\nPOST https://api.mailpro.com/sms/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=password&username=YOUR_USERNAME&password=YOUR_API_KEY\n```\n\nLe même jeton est également valide sur l'API CRM v3 et l'API Email v2 —\nauthentifiez-vous une fois, appelez n'importe quelle API. Les jetons sont valides **30 jours** par défaut ;\nutilisez `grant_type=refresh_token` pour les renouveler sans avoir à se réauthentifier. Sans portée\n— préférez OAuth 2.0 pour les intégrations installables.\n\n### Méthode 3 — id_client + api_key\n\nDeux identifiants :\n\n| Paramètre   | Type    | Description |\n|-------------|---------|-------------|\n| `id_client` | entier  | Votre identifiant client numérique Mailpro, disponible dans **Paramètres → Accès API** |\n| `api_key`   | chaîne  | Votre clé API (format GUID), disponible dans **Paramètres → Accès API** |\n\nEnvoyez-les à l'un des emplacements suivants — choisissez-en un :\n\n1. **En-têtes HTTP (recommandé)** — `id_client: 12345` et `api_key: a1b2c3d4-…`.\n   Les URLs sont journalisées par les serveurs web, les CDN et les proxys, si bien qu'une clé placée\n   dans la chaîne de requête peut fuiter dans les journaux d'accès. Les en-têtes, eux, ne sont pas journalisés.\n2. **Chaîne de requête (legacy, fonctionne toujours)** — `?id_client=12345&api_key=…`.\n   Conservée pour la rétrocompatibilité avec les intégrations existantes. Basculez vers\n   les en-têtes dès que possible.\n3. **Corps JSON / formulaire (POST uniquement)** — mêmes noms de champs à l'intérieur du corps de requête.\n\nSi vous fournissez les identifiants à plusieurs endroits, **l'en-tête l'emporte**.\n\n### Application des portées OAuth par endpoint (SMS)\n\nPour les jetons émis par OAuth, chaque groupe d'endpoints exige une portée (les authentifications\nlegacy en sont exemptes). Chaque opération indique sa portée sous **Autorisation**. Synthèse :\n\n| Endpoints | Portée requise |\n|-----------|----------------|\n| `/send/*` (envoi en masse, unitaire, planifié) | `sms:send` |\n| `/history/list`, `/credit/get` | `sms:read` |\n| `/phone/list`, `/list/list` | `sms:contacts:read` |\n| `/phone/{add,edit,del}`, `/list/{edit,del}`, `/import/upload` | `sms:contacts:write` |\n| `/account/limits` | `account:read` |\n\n> `POST /list/add` (création d'une liste) est actuellement accessible avec tout\n> jeton valide — il n'est pas encore soumis au contrôle de portée. Un rejet de portée sur les\n> endpoints contrôlés renvoie **403** avec\n> `{ \"error\": \"insufficient_scope\", \"required_scope\": \"…\" }`.\n\n## Format de réponse\n\nTous les endpoints prennent en charge `json` (par défaut) et `xml`. Spécifiez le format dans l'URL :\n- `/credit/get.json`\n- `/credit/get.xml`\n\nLes noms de champs dans les réponses sont en **PascalCase** (`TotalCredits`, `FirstName`, …),\ntandis que les paramètres de requête sont en **snake_case** (`id_client`, `address_book_id`, …).\nLes endpoints de liste encapsulent leurs lignes dans un tableau `Items`.\n\n## Pagination\n\n**Même règle sur les trois API Mailpro** (Email v2, CRM v3, SMS) : **5 000 éléments par page,\npar défaut comme au maximum.**\n- `NumberOfRecords` — éléments par page. Absent, `0` ou au-delà de 5 000 = 5 000.\n- `PageNumber` — numéro de page à partir de 1 (par défaut 1).\n- Les deux sont acceptés en paramètre de query **ou** en en-tête HTTP.\n- Une page pleine signifie qu'il peut y en avoir d'autres : demandez la page suivante\n  jusqu'à recevoir une page incomplète.\n\n**Paramètres inconnus** : ils sont ignorés, jamais refusés. Quand une requête porte un\nparamètre que l'API ne reconnaît pas, la réponse contient `X-Ignored-Parameters` (les noms)\net `X-Documentation` (un lien vers cette page), pour qu'une faute de frappe ne passe pas inaperçue.\n\n## Coût en crédits d'un SMS\n\nUn SMS coûte **crédits du pays × nombre de parts**.\n\n**Crédits du pays** — chaque pays de destination a un prix de 1, 2 ou 3 crédits,\nselon le préfixe international du numéro. L'envoi vers un numéro dont le préfixe\nn'est pas tarifé est rejeté avec un `400`.\n\n**Nombre de parts** — un message plus long qu'un SMS est découpé par l'opérateur\net chaque part est facturée. La limite dépend des caractères utilisés :\n\n| Alphabet | Message unique | Par part si découpé |\n|----------|----------------|---------------------|\n| GSM-7 (latin standard) | 160 caractères | 153 caractères |\n| Unicode (accents hors GSM-7, cyrillique, emoji, …) | 70 caractères | 67 caractères |\n\nUn **seul** caractère hors GSM-7 fait passer **tout** le message en Unicode ;\nainsi un emoji peut faire passer un message de 160 caractères de 1 à 3 parts.\nLes caractères `^ { } [ ] ~ | \\` et `€` comptent pour **deux** chacun, bien\nqu'ils soient GSM-7.\n\n*Exemple :* un message de 300 caractères en latin standard = 2 parts ; envoyé\nvers un pays à 2 crédits, il coûte 2 × 2 = **4 crédits** par destinataire.\n\n## Format des numéros de téléphone\n\nLes numéros de téléphone sont normalisés automatiquement. Vous pouvez les envoyer sous de nombreux formats —\nles espaces, tirets, points, barres obliques, parenthèses, traits de soulignement et espaces insécables\nsont supprimés avant validation. Tout autre caractère (lettres, apostrophes,\n`=`, `;`, etc.) rend le numéro invalide.\n\n| Valide                                         | Invalide                                           |\n|------------------------------------------------|----------------------------------------------------|\n| `+41794849980` — international E.164           | `0612345678` — format national (commence par `0`)  |\n| `0041 79 484 99 80` — préfixe 00 + espaces     | `41'794'849'80` — apostrophes (non supprimées)     |\n| `+33 6-12-34-56-78` — + avec tirets            | `tel=0041794849980` — caractères additionnels      |\n| `41794849980` — chiffres bruts                 | `1234567` — trop court (< 8 chiffres)              |\n\n**Règles :**\n- Préfixes acceptés : `+`, `00`, ou aucun préfixe\n- Longueur après normalisation : **8 à 15 chiffres** (maximum E.164)\n- Le premier chiffre après suppression du préfixe doit être **1–9** (pas `0`)\n- Séparateurs supprimés : espace, tabulation, tiret `-`, point `.`, barre oblique `/`, parenthèses `( )`, trait de soulignement `_`, NBSP\n- Tout autre élément (lettres, apostrophes, `=`, `;`, `+` au milieu du numéro, etc.) → rejeté\n- Forme stockée dans notre base de données : chiffres bruts, sans préfixe (ex. `41794849980`)\n\n## Codes d'erreur\n\n| HTTP | Code | Signification |\n|------|------|---------------|\n| 400  | 103  | Entrée malformée — paramètre requis manquant ou invalide |\n| 202  | 202  | Crédits SMS insuffisants |\n| 401  |      | `id_client` / `api_key` invalides |\n| 500  | 500  | Erreur interne |\n\n## Webhooks\n\nMailpro™ peut notifier votre serveur en temps réel lorsque des événements SMS se produisent sur votre\ncompte (un SMS est délivré, un SMS échoue, votre solde de crédits est bas, etc.).\nÀ la manière de Stripe — votre URL reçoit un POST signé en HMAC-SHA256 dès que\nl'événement se déclenche.\n\nChaque événement envoyé à votre webhook compte pour 1 requête dans votre quota de requêtes API.\n\n### Fonctionnement\n\n1. **Abonnez-vous** à un ou plusieurs types d'événements via `POST /webhooks`. La réponse\n   contient un `secret` affiché **une seule fois** (à la manière de Stripe / GitHub) — conservez-le.\n2. **Mailpro envoie un POST** d'une charge utile JSON signée à votre URL dès qu'un événement correspond.\n3. **Vérifiez la signature** côté serveur à l'aide de votre `secret` (extraits de code ci-dessous).\n4. **Répondez par 200–299** sous 10 secondes. Faute de quoi nous réessayons avec un backoff\n   exponentiel (10 s → 1 min → 5 min → 30 min → 2 h → 6 h → 24 h, puis abandon).\n\n> **⏱ Délai d'activation** — après la création, la mise à jour ou la suppression d'un webhook, le\n> changement met jusqu'à **10 minutes** à se propager sur l'ensemble des serveurs de l'API\n> Mailpro. Pendant ce délai, les événements peuvent ne pas correspondre encore à votre nouvel\n> abonnement (ou correspondre encore à un abonnement tout juste supprimé). C'est le temps\n> nécessaire au rafraîchissement du cache d'abonnements en mémoire sur chaque instance d'API.\n\n### Enveloppe de la charge utile\n\nTous les événements partagent une enveloppe commune :\n\n```json\n{\n  \"id\":         \"evt_8f3a9b2c4d1e5f6789abcdef01234567\",\n  \"type\":       \"sms.queued\",\n  \"created\":    1748194800,\n  \"apiVersion\": \"sms\",\n  \"account\":    { \"id\": 12345, \"name\": \"ACME Corp\" },\n  \"data\":       {\n    \"messageId\":   12345,\n    \"to\":          \"41791234567\",\n    \"queuedAt\":    \"2026-05-27T14:32:12.0123456Z\",\n    \"scheduledAt\": \"2026-05-27T14:32:12.0123456Z\",\n    \"parts\":       1\n  }\n}\n```\n\n| Champ | Format | Description |\n|-------|--------|-------------|\n| `id` | `evt_` + 32 hex | Identifiant unique d'événement — à utiliser comme clé d'idempotence de votre côté. |\n| `type` | `<domaine>.<verbe>` | Type d'événement canonique. |\n| `created` | secondes Unix UTC | Inclus dans la signature HMAC. |\n| `apiVersion` | `\"sms\"` pour les événements émis par `POST /sms/send/add_single` ; `\"v3\"` pour les événements émis par `POST /v3/Sms/SingleSMS` ; `\"watcher\"` pour les événements pilotés par DLR émis par le worker. | Quel composant Mailpro a publié l'événement. |\n| `account.id` | int | Votre identifiant client Mailpro (= `id_client`). |\n| `account.name` | string | Le nom de votre compte (pratique pour le débogage côté client). |\n| `data` | object | Charge utile de l'événement — la forme dépend de `type` (voir catalogue ci-dessous). |\n\n### Catalogue des événements\n\nSeuls les événements listés ci-dessous sont effectivement émis. Les formes correspondent au JSON\nlittéral envoyé par la plateforme — faites correspondre ces noms de champs à l'identique lors du parsing.\n\n| Événement | Déclencheur | Forme de `data` |\n|-----------|-------------|-----------------|\n| `ping` | Test via `POST /webhooks/{id}/test` | `{ message }` |\n| `sms.queued` | `POST /sms/send/add_single` ou `POST /v3/Sms/SingleSMS` accepté dans la file d'envoi Mailpro | `{ messageId: int, to: string, queuedAt: ISO-8601, scheduledAt: ISO-8601, parts: int }` |\n| `sms.delivered` | DLR avec code opérateur `004` ou `008` (codes de succès) | `{ messageId: int, phone: string, stateCode: string, dlrDate: datetime }` |\n| `sms.failed` | DLR avec tout autre code opérateur (timeout, non délivré, rejeté, blacklisté, …) | `{ messageId: int, phone: string, stateCode: string, dlrDate: datetime }` |\n| `credits.sms.low` | Le solde de crédits SMS est passé sous votre seuil d'alerte (worker `CreditsLowWatcher`) | `{ remaining: int, threshold: int, alertedAt: ISO-8601 }` |\n\n> **Remarque** — il n'existe pas d'événement `sms.sent`. Le DLR opérateur est le seul retour\n> que la plateforme reçoit entre les états « en file d'attente » et « délivré/échoué » ; ainsi, l'événement\n> suivant `sms.queued` est soit `sms.delivered` (succès), soit `sms.failed` (erreur).\n> La plupart des messages atteignent l'opérateur en quelques secondes, et un DLR en quelques minutes ;\n> les conditions du réseau mobile peuvent occasionnellement étendre ce délai à plusieurs heures.\n\n> **Nommage des champs** — `sms.queued` utilise `to` (le destinataire tel que soumis par\n> l'appelant de l'API, après normalisation). `sms.delivered` / `sms.failed` utilisent\n> `phone` (le destinataire tel que renvoyé par l'opérateur sur le DLR). En pratique, il s'agira des\n> mêmes chiffres, mais les noms diffèrent parce que les événements proviennent de chemins de\n> code distincts.\n\nAbonnez-vous à tous les événements de cette API en passant `[\"*\"]` dans le tableau `events`.\n\n### Signature HMAC\n\nChaque POST embarque un en-tête `X-Mailpro-Signature` :\n\n```\nX-Mailpro-Signature: t=1748194800,v1=7f9ac2b6e4d1c8a3...\nX-Mailpro-Event-Id:  evt_8f3a9b2c4d1e5f6789abcdef01234567\nX-Mailpro-Event-Type: sms.delivered\nX-Mailpro-Attempt:   1\n```\n\nPour la vérifier :\n1. Extrayez `t` et `v1` de l'en-tête.\n2. **Rejetez** si `|now - t| > 300` (fenêtre anti-rejeu de 5 minutes).\n3. Calculez `expected = hex(HMAC_SHA256(secret, t + \".\" + raw_body))`.\n4. **Comparez en temps constant** `v1` avec `expected` (`crypto.timingSafeEqual`,\n   `hash_equals`, `CryptographicOperations.FixedTimeEquals`) — jamais avec `==`.\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### Réessais et désactivation automatique\n\n| Tentative | Délai avant la prochaine tentative |\n|-----------|------------------------------------|\n| 1 → 2 | 10 secondes |\n| 2 → 3 | 1 minute |\n| 3 → 4 | 5 minutes |\n| 4 → 5 | 30 minutes |\n| 5 → 6 | 2 heures |\n| 6 → 7 | 6 heures |\n| 7 | abandonné (livraison marquée `failed`) |\n\nAprès **10 échecs consécutifs** sur le même webhook, celui-ci est **automatiquement\ndésactivé** (`isActive: false`) et vous recevez un e-mail de notification. Réactivez-le\navec `PUT /webhooks/{id}` une fois votre endpoint corrigé.\n\n### Succès = HTTP 2xx\n\nNous considérons votre webhook comme réussi uniquement si vous renvoyez un statut compris dans `200–299`.\nTout 3xx/4xx/5xx ou un timeout (> 10 s) est compté comme un échec et déclenche des réessais.\nSi votre endpoint est définitivement hors service, renvoyez explicitement `410 Gone` — les réessais\nont quand même lieu, mais le motif de l'échec est plus clair dans votre journal de livraison.\n\n### Limites\n\n| Limite | Valeur |\n|--------|--------|\n| Webhooks par compte | 20 |\n| Événements souscrits par webhook | 50 |\n| Taille maximale de la charge utile | 256 Ko |\n| Timeout HTTP par tentative | 10 secondes |\n| Rétention de l'historique de livraison | 30 jours |"
  },
  "servers": [
    {
      "url": "https://api.mailpro.com/sms",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "IdClientHeader": [],
      "ApiKeyHeader": []
    },
    {
      "IdClientQuery": [],
      "ApiKeyQuery": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Jeton Bearer JWT obtenu via `POST /sms/token` (encodé en formulaire, grant\n`password`). Le même jeton est également valable sur l’API CRM v3 et l’API\nEmail v2 — authentifiez-vous une seule fois, appelez n’importe quelle API.\nTransmettez-le ainsi : `Authorization: Bearer <token>`\n"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "Mailpro OAuth 2.0 — la méthode moderne pour permettre aux applications\ntierces et aux plateformes d’intégration (Zapier, Make, n8n) d’agir au nom\nd’un utilisateur Mailpro à l’aide de jetons d’accès à portée limitée et\nrévocables.\n\n- **authorizationCode + PKCE** (S256, obligatoire pour les clients publics) —\n  le flux de consentement « Connect Mailpro ». Émet un jeton d’accès ainsi\n  qu’un refresh token rotatif.\n- **clientCredentials** — machine à machine, sans utilisateur impliqué.\n\nUn unique serveur d’autorisation pour toutes les API, exposé sur la surface\nneutre en versions `https://api.mailpro.com/oauth/` (émetteur / claim `iss`\n`https://api.mailpro.com/oauth/`). Les jetons d’accès sont des JWT RS256\n(à vérifier contre le JWKS situé à `https://api.mailpro.com/oauth/.well-known/jwks`) ;\nleur TTL est de **20 minutes**. Les refresh tokens sont valables **30 jours**\n(glissants) et **à usage unique** (renouvelés à chaque rafraîchissement).\nLes codes d’autorisation vivent **60 secondes**.\n\nLe même jeton d’accès OAuth est accepté par les trois API Mailpro (CRM v3,\nEmail v2, SMS) — authentifiez-vous une seule fois, appelez n’importe quelle\nAPI ; l’accès est régi par les scopes du jeton et non par l’URL de base de\nl’API. L’authentification historique (Bearer JWT / IdClient+ApiKey) continue\nde fonctionner à l’identique et n’est PAS soumise aux scopes.\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": "id_client",
        "description": "Votre identifiant client Mailpro numérique — transport **recommandé**\n(en-tête HTTP). Les URL sont consignées par les serveurs web, Cloudflare et\nles proxys ; les identifiants transmis dans la query string peuvent donc\nfuiter dans les journaux d’accès. Les en-têtes, eux, ne sont pas journalisés.\n"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "api_key",
        "description": "Votre clé d’API (GUID) — transport **recommandé** (en-tête HTTP).\n"
      },
      "IdClientQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "id_client",
        "description": "Votre identifiant client Mailpro numérique — **historique**, conservé pour\nassurer la rétrocompatibilité. Migrez vers l’en-tête `id_client` dès que\npossible.\n"
      },
      "ApiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "api_key",
        "description": "Votre clé d’API (GUID) — **historique**, conservée pour assurer la\nrétrocompatibilité.\n"
      }
    },
    "parameters": {
      "format": {
        "name": "format",
        "in": "path",
        "required": true,
        "description": "Format de la réponse",
        "schema": {
          "type": "string",
          "enum": [
            "json",
            "xml"
          ],
          "default": "json"
        },
        "example": "json"
      },
      "numberOfRecords": {
        "name": "NumberOfRecords",
        "in": "header",
        "required": false,
        "description": "Éléments par page — 5 000 par défaut et au maximum (voir Pagination). En-tête HTTP ou paramètre de query.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 5000,
          "default": 5000
        },
        "example": 100
      },
      "pageNumber": {
        "name": "PageNumber",
        "in": "header",
        "required": false,
        "description": "Numéro de page (base 1) pour les réponses paginées. Transmis en en-tête HTTP.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        },
        "example": 1
      },
      "sortByName": {
        "name": "SortByName",
        "in": "header",
        "required": false,
        "description": "Définir à `true` pour trier les résultats par nom, dans l’ordre alphabétique.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "schemas": {
      "TokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Jeton d’accès JWT — à utiliser comme `Authorization: Bearer <token>` dans les requêtes suivantes."
          },
          "token_type": {
            "type": "string",
            "example": "bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Durée de vie du jeton, en secondes",
            "example": 31535999
          },
          "userName": {
            "type": "string",
            "description": "Identifiant client de l’utilisateur authentifié",
            "example": "12345"
          },
          "refresh_token": {
            "type": "string",
            "description": "Jeton permettant d’obtenir un nouveau jeton d’accès sans se réauthentifier"
          },
          "application": {
            "type": "string",
            "description": "Nom de l’intégration (uniquement pour les grants tiers)",
            "nullable": true
          }
        }
      },
      "PhoneList": {
        "type": "object",
        "properties": {
          "AddressBookId": {
            "type": "integer",
            "description": "Unique identifier of the phone list",
            "example": 42
          },
          "Name": {
            "type": "string",
            "description": "Nom d’affichage de la liste de numéros",
            "example": "Newsletter subscribers"
          },
          "TotalNumberPhones": {
            "type": "integer",
            "description": "Number of phone numbers in this list",
            "example": 1547
          }
        }
      },
      "PhoneNumber": {
        "type": "object",
        "properties": {
          "Id": {
            "type": "integer",
            "description": "Identifiant unique de l’entrée numéro de téléphone",
            "example": 1001
          },
          "Number": {
            "type": "string",
            "description": "Numéro de téléphone au format international nu (forme stockée — voir « Format des numéros de téléphone » dans la description de l’API pour les formats d’entrée acceptés)",
            "example": "41791234567"
          },
          "FirstName": {
            "type": "string",
            "description": "Prénom du contact",
            "example": "Alice"
          },
          "LastName": {
            "type": "string",
            "description": "Nom de famille du contact",
            "example": "Dupont"
          }
        }
      },
      "SingleSend": {
        "type": "object",
        "properties": {
          "Id": {
            "type": "integer",
            "description": "Identifiant unique de l’envoi créé",
            "example": 5001
          },
          "Message": {
            "type": "string",
            "description": "Texte du message SMS",
            "example": "Your verification code is 8472"
          },
          "ScheduledTime": {
            "type": "string",
            "format": "date-time",
            "description": "Date à laquelle le SMS sera (ou a été) envoyé",
            "example": "2026-04-15T09:00:00"
          },
          "To": {
            "type": "string",
            "description": "Numéro de téléphone du destinataire",
            "example": "0041791234567"
          },
          "Charge": {
            "type": "string",
            "description": "Crédits réellement facturés pour cet envoi = **crédits du pays × nombre de parts**.\nEx. un SMS 1 part vers le Panama (+507, 3 crédits/SMS) → `\"3\"` ; un SMS 2 parts → `\"6\"`.\n",
            "example": "3"
          },
          "Parts": {
            "type": "integer",
            "description": "Nombre de segments SMS (parts GSM 03.38) en lesquels le message a été découpé.",
            "example": 1
          }
        }
      },
      "BulkSend": {
        "type": "object",
        "properties": {
          "Id": {
            "type": "integer",
            "description": "Identifiant unique de l’envoi groupé créé",
            "example": 6001
          },
          "Message": {
            "type": "string",
            "description": "Texte du message SMS",
            "example": "Spring sale — 20% off all products this weekend!"
          },
          "ScheduledTime": {
            "type": "string",
            "format": "date-time",
            "description": "Date à laquelle la campagne SMS sera (ou a été) envoyée",
            "example": "2026-04-20T10:00:00"
          },
          "To": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Liste des identifiants des listes ciblées par cet envoi",
            "example": [
              "42"
            ]
          },
          "Charge": {
            "type": "string",
            "description": "Total des crédits facturés pour l'envoi complet = **Σ (crédits du pays × parts)**\nsur tous les numéros de la liste.\n",
            "example": "150"
          },
          "Parts": {
            "type": "integer",
            "description": "Nombre de segments SMS (parts GSM 03.38) par message.",
            "example": 1
          }
        }
      },
      "CreditBalance": {
        "type": "object",
        "properties": {
          "TotalCredits": {
            "type": "number",
            "description": "Solde de crédits SMS restant (crédits du forfait + crédits mensuels).\n\nC'est un nombre de crédits, pas un nombre de messages : un SMS coûte\n`crédits du pays × nombre de parts` — voir « Coût en crédits d'un SMS » dans la\ndescription de l'API.\n",
            "example": 5000
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "Error": {
            "type": "string",
            "description": "Message d’erreur lisible par un humain",
            "example": "Malformed Input"
          },
          "Code": {
            "type": "integer",
            "description": "Code d’erreur propre à Mailpro",
            "example": 103
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "example": 42
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://example.com/hooks/mailpro"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "example": "Slack integration"
          },
          "is_active": {
            "type": "boolean",
            "description": "Lorsque ce champ vaut `false`, aucun événement n’est délivré. Désactivé automatiquement après 10 échecs consécutifs.",
            "example": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-07-01T21:09:52.43"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-07-21T18:56:08.02"
          },
          "last_delivery_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Quand la dernière tentative de livraison a eu lieu",
            "example": "2026-07-21T18:56:07.653"
          },
          "last_delivery_status": {
            "type": "integer",
            "nullable": true,
            "description": "Statut HTTP renvoyé par votre endpoint lors de la dernière livraison",
            "example": 200
          },
          "consecutive_failures": {
            "type": "integer",
            "description": "Remis à 0 au premier succès ; à 10, le webhook est désactivé automatiquement",
            "example": 0
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Types d'événements auxquels vous êtes abonné. Un tableau vide signifie tous les événements de cette API.",
            "example": [
              "sms.delivered",
              "sms.failed"
            ]
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "description": "Enveloppe renvoyée par `GET /webhooks`.",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          },
          "total": {
            "type": "integer",
            "description": "Nombre total de webhooks sur le compte",
            "example": 2
          }
        }
      },
      "WebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Endpoint HTTPS (HTTP refusé en production)",
            "example": "https://example.com/hooks/mailpro"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 50,
            "description": "Liste des types d’évènements auxquels s’abonner (voir le catalogue ci-dessus). Utilisez `[\"*\"]` pour vous abonner à tous les évènements de cette API.",
            "example": [
              "sms.delivered",
              "sms.failed"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "example": "Slack integration"
          }
        }
      },
      "WebhookCreateResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Secret HMAC. **Affiché une seule fois ici, plus jamais ensuite.** Conservez-le côté serveur.",
                "example": "whsec_AbcD1234EfgH5678IjkL9012MnoP3456QrsT"
              }
            }
          }
        ]
      },
      "WebhookUpdate": {
        "type": "object",
        "description": "Mise à jour partielle — tous les champs sont optionnels.",
        "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": "Définir à `true` pour réactiver un webhook désactivé automatiquement, une fois votre endpoint corrigé."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "eventId": {
            "type": "string",
            "example": "evt_8f3a9b2c4d1e5f6789abcdef01234567"
          },
          "eventType": {
            "type": "string",
            "example": "sms.delivered"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "retrying",
              "failed"
            ]
          },
          "statusCode": {
            "type": "integer",
            "nullable": true,
            "example": 200
          },
          "responseTimeMs": {
            "type": "integer",
            "nullable": true,
            "example": 142
          },
          "attemptCount": {
            "type": "integer"
          },
          "lastError": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "nextAttemptAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Authentication",
      "description": "Obtenez un jeton Bearer JWT permettant d’authentifier tous les autres\nappels à l’API. L’endpoint `/token` du service SMS est une redirection 307\nvers l’émetteur canonique `/v3/token` — votre client HTTP suit la\nredirection de manière transparente (la méthode POST et le corps sont\nconservés).\n"
    },
    {
      "name": "Account",
      "description": "Consultez votre solde de crédits SMS, votre quota d’API mensuel et le statut de votre compte."
    },
    {
      "name": "Send SMS",
      "description": "Envoyez des SMS — soit un **SMS transactionnel unique** à un destinataire,\nsoit une **campagne en masse** à une liste de contacts entière.\n"
    },
    {
      "name": "Lists",
      "description": "Gérez les listes de contacts SMS. Une liste de contacts regroupe des numéros de téléphone pour les envois en masse.\nVous pouvez créer jusqu’à 1 000 listes par compte (même limite sur les trois API Mailpro et dans l'app).\n"
    },
    {
      "name": "Phone Numbers",
      "description": "Gérez les numéros de téléphone individuels au sein d’une liste de contacts — ajout, mise à jour et suppression des entrées."
    },
    {
      "name": "Credit",
      "description": "Consultez le solde de crédits SMS restant pour votre compte (distinct du quota de requêtes API mensuelles — voir `/account/limits`)."
    },
    {
      "name": "History",
      "description": "Parcourez le journal complet des campagnes SMS passées."
    },
    {
      "name": "Import",
      "description": "Téléversez un fichier CSV ou XLSX pour importer en masse des numéros de\ntéléphone dans une liste de contacts. Le fichier est mis en file d’attente\npour un traitement asynchrone — suivez son achèvement via l’`ImportJobId`\nretourné ou via le callback optionnel `webhook_url`.\n"
    },
    {
      "name": "Webhooks",
      "description": "Abonnez votre serveur aux notifications d’évènements SMS en temps réel.\nMailpro envoie en POST une charge utile JSON signée par HMAC sur votre URL\ndès que l’évènement souscrit se produit. Consultez la section « Webhooks »\nde l’introduction pour le catalogue complet des évènements, l’enveloppe de\nla charge utile, les extraits de vérification de signature et la politique\nde réessai.\n"
    }
  ],
  "paths": {
    "/token": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "getSmsAccessToken",
        "summary": "Obtenir un jeton d’accès (redirection 307 vers /v3/token)",
        "security": [],
        "description": "Obtient un jeton Bearer JWT à l’aide des identifiants client Mailpro.\nTransmettez l’`access_token` retourné en tant que `Authorization: Bearer <token>`\ndans toutes les requêtes suivantes. Le même jeton est également valable sur\nl’API CRM v3 et l’API Email v2.\n\n> **⚙ Détail d’implémentation** — `POST /sms/token` est une **307 Temporary\n> Redirect** vers `POST /v3/token` (configurée au niveau de la couche de\n> réécriture `web.config` d’IIS). La 307 HTTP préserve à la fois la\n> **méthode** (`POST`) et le **corps de la requête**, si bien que tout\n> client HTTP respectant le standard suit la redirection de manière\n> transparente — vous continuez à poster sur `/sms/token` et récupérez un\n> JWT valide en retour. Il n’existe aucune logique d’émission de jetons\n> dupliquée ; l’API SMS n’émet jamais de jetons par elle-même.\n\n### Types de grants pris en charge\n| Type de grant | Cas d’usage |\n|---------------|-------------|\n| `password` | Intégration standard — nom d’utilisateur + clé d’API |\n| `refresh_token` | Renouveler un jeton d’accès expirant sans se réauthentifier |\n| `client_credentials` | Serveur à serveur avec l’identifiant client seul |\n\n### Durée de vie des jetons\n- Jeton d’accès : **30 jours**\n- Refresh token : **90 jours**\n\nUtilisez `grant_type=refresh_token` pour renouveler le jeton d’accès sans\nvous réauthentifier. Stockez les jetons côté serveur (Vault, variables\nd’environnement) ; ne les intégrez jamais dans du JS navigateur ni dans des\nbinaires d’applications mobiles. Consultez la doc `/token` de l’API CRM v3\npour la note de sécurité complète.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "password",
                      "refresh_token",
                      "client_credentials"
                    ],
                    "example": "password"
                  },
                  "username": {
                    "type": "string",
                    "description": "Votre nom d’utilisateur Mailpro (email). Requis pour le grant `password`.",
                    "example": "user@yourcompany.com"
                  },
                  "password": {
                    "type": "string",
                    "description": "Votre clé d’API Mailpro (au format GUID). Requise pour le grant `password`.",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "refresh_token": {
                    "type": "string",
                    "description": "Refresh token précédent. Requis pour le grant `refresh_token`."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Votre identifiant client Mailpro (entier). Requis pour le grant `client_credentials`.",
                    "example": "12345"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Jeton émis avec succès. Renvoyé par `POST /v3/token` après le suivi\ntransparent de la 307 — votre client HTTP voit une 200 classique avec le\nmême format de corps que celui qu’il aurait reçu directement de l’endpoint\nde jeton v3.\n",
            "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
                }
              }
            }
          },
          "307": {
            "description": "Temporary Redirect vers `POST /v3/token`, émise par la règle de réécriture\nIIS du `web.config`. Les clients HTTP respectant le standard la suivent\nautomatiquement et conservent la méthode POST d’origine ainsi que le corps\nde formulaire. Visible uniquement pour les clients qui désactivent\nexplicitement le suivi des redirections (par exemple `curl --max-redirs 0`).\n",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "example": "https://api.mailpro.com/v3/token"
              }
            }
          },
          "400": {
            "description": "Identifiants invalides ou type de grant non pris en charge",
            "content": {
              "application/json": {
                "examples": {
                  "invalid_grant": {
                    "summary": "Identifiants incorrects",
                    "value": {
                      "error": "invalid_grant",
                      "error_description": "The username or password is incorrect."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/credit/get.{format}": {
      "get": {
        "tags": [
          "Credit"
        ],
        "operationId": "getSmsCredits",
        "security": [
          {
            "OAuth2": [
              "sms:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Récupérer le solde de crédits SMS",
        "description": "Retourne le solde de crédits SMS courant pour votre compte.\nLes crédits SMS sont consommés au moment de l’envoi. Un SMS standard\n(≤ 160 caractères GSM-7) coûte 1 crédit. Les messages plus longs ou les\nmessages avec des caractères spéciaux peuvent en coûter davantage.\n\n> Note : ceci retourne votre **solde de crédits d’envoi SMS**, et non votre\n> quota de requêtes API. Pour le quota d’API, voir `GET /account/limits.{format}`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "responses": {
          "200": {
            "description": "Solde de crédits retourné avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditBalance"
                },
                "example": {
                  "TotalCredits": 5000
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/account/limits.{format}": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getSmsAccountLimits",
        "security": [
          {
            "OAuth2": [
              "account:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Récupérer le quota d’API mensuel",
        "description": "Retourne le quota de requêtes API mensuel du client authentifié.\n\n**Calcul du quota :** la limite est calibrée à partir des crédits\nd’abonnement email (en général `subscription_credits × 5`). Pour un plan à\n10 000 emails/mois, cela donne un budget mensuel de 50 000 requêtes.\nRéinitialisation à la **date anniversaire de l’abonnement** (et non le 1er\ndu mois calendaire).\n\n**Ce qui consomme des crédits :**\n- Chaque requête API authentifiée vers les endpoints `v2`, `v3` ou `sms`.\n- Chaque évènement webhook envoyé à un endpoint abonné.\n\n**Ce qui est GRATUIT (ne consomme pas de crédits) :**\n- Les endpoints `/health`.\n- Les requêtes échouant avec `401`, `403` ou `429`.\n- Les évènements webhook pour les clients sans abonnement correspondant.\n\nLe quota est **partagé entre les 3 API Mailpro** (`v2`, `v3`, `sms`)\n— un client = un budget mensuel pour toute la plateforme. Les envois SMS\nconsomment en plus le solde de crédits SMS dédié\n(`GET /credit/get.{format}`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "responses": {
          "200": {
            "description": "Instantané du quota",
            "content": {
              "application/json": {
                "example": {
                  "monthlyLimit": 50000,
                  "remaining": 32847,
                  "used": 17153,
                  "isFree": false,
                  "remainedSendsForFree": null
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          },
          "404": {
            "description": "Aucun enregistrement de quota trouvé (compte nouvellement créé ; la ligne sera créée lors de la première requête authentifiée)."
          }
        }
      }
    },
    "/history/list.{format}": {
      "get": {
        "tags": [
          "History"
        ],
        "operationId": "listSmsHistory",
        "security": [
          {
            "OAuth2": [
              "sms:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Lister l’historique des envois SMS",
        "description": "Retourne un journal paginé de toutes les campagnes SMS passées envoyées\ndepuis ce compte. Les résultats sont triés par date décroissante (les plus\nrécents en premier). Pagination : `NumberOfRecords` et `PageNumber`\n(en-têtes ou query, 5 000 par page par défaut).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste d’historique retournée avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "Id": {
                            "type": "integer",
                            "description": "Identifiant de l'envoi auquel appartient cette ligne",
                            "example": 583294
                          },
                          "DateSent": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Horodatage de livraison rapporté par l'opérateur",
                            "example": "2026-07-17T18:56:39.813"
                          },
                          "Message": {
                            "type": "string",
                            "example": "Your verification code is 8472"
                          },
                          "Charge": {
                            "type": "number",
                            "description": "Crédits facturés pour cette ligne = **crédits du pays × nombre de parts**\n(ex. 3 pour un SMS 1 part vers le Panama). Auparavant toujours `1`.\n",
                            "example": 3
                          },
                          "Parts": {
                            "type": "integer",
                            "description": "Nombre de segments SMS (parts GSM 03.38) en lesquels le message a été découpé.",
                            "example": 1
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "Items": [
                    {
                      "Id": 583294,
                      "DateSent": "2026-07-17T18:56:39.813",
                      "Message": "Your verification code is 8472",
                      "Charge": 3,
                      "Parts": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/list/list.{format}": {
      "get": {
        "tags": [
          "Lists"
        ],
        "operationId": "listSmsLists",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Lister les listes de contacts téléphoniques",
        "description": "Retourne toutes les listes de contacts SMS du compte.\nUtilisez les valeurs `Id` retournées lors de l’envoi de campagnes SMS en\nmasse ou de l’ajout de numéros de téléphone.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "responses": {
          "200": {
            "description": "Listes de numéros retournées avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PhoneList"
                      }
                    }
                  }
                },
                "example": {
                  "Items": [
                    {
                      "AddressBookId": 42,
                      "Name": "Newsletter subscribers",
                      "TotalNumberPhones": 1547
                    },
                    {
                      "AddressBookId": 43,
                      "Name": "Loyalty program",
                      "TotalNumberPhones": 823
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/list/add.{format}": {
      "post": {
        "tags": [
          "Lists"
        ],
        "operationId": "addSmsList",
        "summary": "Créer une liste de contacts",
        "description": "Crée une nouvelle liste de contacts SMS portant le nom indiqué.\nLa liste démarre vide — ajoutez des numéros de téléphone avec `POST /phone/add`.\nMaximum 1 000 listes par compte (même limite sur les trois API Mailpro et dans l'app). Renvoie HTTP 422 si la limite est atteinte.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "name"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "name": {
                    "type": "string",
                    "description": "Nom d’affichage de la nouvelle liste de contacts",
                    "example": "Spring campaign 2026"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Liste de contacts créée avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneList"
                },
                "example": {
                  "Id": 44,
                  "Name": "Spring campaign 2026",
                  "NumberPhones": 0
                }
              }
            }
          },
          "400": {
            "description": "Champ obligatoire manquant (nom)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Malformed Input",
                  "Code": 103
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/list/edit.{format}": {
      "post": {
        "tags": [
          "Lists"
        ],
        "operationId": "editSmsList",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Renommer une liste de contacts",
        "description": "Met à jour le nom d’affichage d’une liste de contacts SMS existante.\nLes numéros de téléphone de la liste ne sont pas affectés.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "address_book_id",
                  "name"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "address_book_id": {
                    "type": "integer",
                    "description": "ID de la liste de contacts à renommer (issu de `/list/list`)",
                    "example": 42
                  },
                  "name": {
                    "type": "string",
                    "description": "Nouveau nom d’affichage pour la liste de contacts",
                    "example": "Spring campaign 2026 — updated"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Liste de contacts renommée avec succès"
          },
          "400": {
            "description": "Champ obligatoire manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/list/del.{format}": {
      "post": {
        "tags": [
          "Lists"
        ],
        "operationId": "deleteSmsList",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Supprimer une liste de contacts",
        "description": "Supprime définitivement une liste de contacts SMS **ainsi que tous les numéros de téléphone qu’elle contient**.\nCette action est irréversible.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "address_book_id"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "address_book_id": {
                    "type": "integer",
                    "description": "ID de la liste de contacts à supprimer",
                    "example": 42
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Liste de contacts supprimée avec succès"
          },
          "400": {
            "description": "address_book_id manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/phone/list.{format}": {
      "get": {
        "tags": [
          "Phone Numbers"
        ],
        "operationId": "listPhones",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:read"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Lister les numéros de téléphone d’une liste de contacts",
        "description": "Retourne une liste paginée des numéros de téléphone d’une liste de contacts\ndonnée. Utilisez `address_book_id` pour indiquer la liste à récupérer.\nPagination : `NumberOfRecords` et `PageNumber` (5 000 par page par défaut, page 1).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "address_book_id",
            "in": "query",
            "required": true,
            "description": "ID de la liste de contacts dont récupérer les numéros de téléphone",
            "schema": {
              "type": "integer"
            },
            "example": 42
          },
          {
            "$ref": "#/components/parameters/numberOfRecords"
          },
          {
            "$ref": "#/components/parameters/pageNumber"
          },
          {
            "$ref": "#/components/parameters/sortByName"
          }
        ],
        "responses": {
          "200": {
            "description": "Numéros de téléphone retournés avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "Items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PhoneNumber"
                      }
                    }
                  }
                },
                "example": {
                  "Items": [
                    {
                      "Id": 1001,
                      "Number": "41791234567",
                      "FirstName": "Alice",
                      "LastName": "Dupont"
                    },
                    {
                      "Id": 1002,
                      "Number": "33612345678",
                      "FirstName": "Bob",
                      "LastName": "Martin"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/phone/add.{format}": {
      "post": {
        "tags": [
          "Phone Numbers"
        ],
        "operationId": "addPhone",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Ajouter un ou plusieurs numéros de téléphone à une liste",
        "description": "Ajoute un ou plusieurs numéros de téléphone à une liste de contacts.\n\n**Numéro unique :** utilisez le champ `number`.\n\n**Import en masse :** utilisez le champ `phone_list` avec les numéros séparés par des points-virgules :\n`+41791234567;+33612345678;0049301234567`\n\nLes numéros de téléphone acceptent les formats `+`, `00` ou nu — consultez\n**Format des numéros de téléphone** dans la description de l’API pour la\nliste complète des entrées acceptées / rejetées.\nLes numéros en doublon sont ignorés silencieusement, sauf si `force=1` est positionné.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "address_book_id"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "address_book_id": {
                    "type": "integer",
                    "description": "ID de la liste de contacts à laquelle ajouter le ou les numéros",
                    "example": 42
                  },
                  "number": {
                    "type": "string",
                    "description": "Numéro de téléphone unique. Accepte `+41…`, `0041…` ou `41…` avec ou sans\nséparateurs (espaces, tirets, points, barres obliques, parenthèses).\nVoir **Format des numéros de téléphone** dans la description de l’API.\nUtilisez ce champ **OU** `phone_list` — pas les deux.\n",
                    "example": "+41791234567"
                  },
                  "phone_list": {
                    "type": "string",
                    "description": "Liste de numéros de téléphone séparés par des points-virgules pour un import en masse.\nUtilisez ce champ **OU** `number` — pas les deux.\nExemple : `0041791234567;0033612345678`\n",
                    "example": "0041791234567;0033612345678;0049301234567"
                  },
                  "firstname": {
                    "type": "string",
                    "description": "Prénom du contact (optionnel, uniquement pour un numéro unique)",
                    "example": "Alice"
                  },
                  "lastname": {
                    "type": "string",
                    "description": "Nom de famille du contact (optionnel, uniquement pour un numéro unique)",
                    "example": "Dupont"
                  },
                  "force": {
                    "type": "integer",
                    "description": "Définir à `1` pour ajouter un numéro même s’il était précédemment désabonné ou s’il existe déjà.\nValeur par défaut : `0` (les doublons sont ignorés).\n",
                    "enum": [
                      0,
                      1
                    ],
                    "default": 0,
                    "example": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Numéro(s) de téléphone ajouté(s) avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneNumber"
                },
                "example": {
                  "Id": 1003,
                  "Number": "0041791234567",
                  "FirstName": "Alice",
                  "LastName": "Dupont"
                }
              }
            }
          },
          "400": {
            "description": "Champ obligatoire manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/phone/edit.{format}": {
      "post": {
        "tags": [
          "Phone Numbers"
        ],
        "operationId": "editPhone",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Mettre à jour une entrée de numéro de téléphone",
        "description": "Met à jour le numéro de téléphone et/ou les coordonnées d’un contact d’une\nentrée existante. Utilisez `number_id` (issu de `/phone/list`) pour\nidentifier l’entrée à mettre à jour. Seuls les champs fournis dans la\nrequête sont mis à jour.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "number_id"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "number_id": {
                    "type": "integer",
                    "description": "ID de l’entrée numéro de téléphone à mettre à jour (issu de `/phone/list`)",
                    "example": 1001
                  },
                  "number": {
                    "type": "string",
                    "description": "Nouveau numéro de téléphone. Accepte `+41…`, `0041…` ou `41…` avec ou sans séparateurs. Voir **Format des numéros de téléphone** dans la description de l’API.",
                    "example": "+41791234568"
                  },
                  "firstname": {
                    "type": "string",
                    "description": "Nouveau prénom du contact",
                    "example": "Alice"
                  },
                  "lastname": {
                    "type": "string",
                    "description": "Nouveau nom de famille du contact",
                    "example": "Dupont-Martin"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Entrée du numéro de téléphone mise à jour avec succès"
          },
          "400": {
            "description": "number_id manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/phone/del.{format}": {
      "post": {
        "tags": [
          "Phone Numbers"
        ],
        "operationId": "deletePhone",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Retirer un numéro de téléphone d’une liste",
        "description": "Retire définitivement une entrée numéro de téléphone précise de sa liste de contacts.\nUtilisez `number_id` (issu de `/phone/list`) pour identifier l’entrée.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "number_id"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "number_id": {
                    "type": "integer",
                    "description": "ID de l’entrée numéro de téléphone à supprimer (issu de `/phone/list`)",
                    "example": 1001
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Numéro de téléphone retiré avec succès"
          },
          "400": {
            "description": "number_id manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/send/add.{format}": {
      "post": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "sendBulkSms",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Envoyer un SMS en masse à une liste de contacts",
        "description": "Envoie un SMS à **tous les numéros de téléphone** d’une liste de contacts.\nPrend en charge la planification : positionnez `scheduled_time` sur une date\nfuture pour un envoi différé, ou omettez-le pour un envoi immédiat.\n\n**Consommation de crédits :** les crédits sont déduits au moment de l’envoi.\nLes messages longs (>160 caractères GSM-7) sont scindés en plusieurs parties,\nchacune coûtant des crédits supplémentaires.\n\nLa réponse retourne `{ \"Error\": \"Insufficient Credit\", \"Code\": 202 }` si le compte ne dispose pas de crédits suffisants.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "message",
                  "to"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "message": {
                    "type": "string",
                    "description": "Texte du message SMS. Restez sous 160 caractères pour un SMS en une seule partie.\nLes caractères en dehors de l’alphabet GSM-7 (par exemple chinois, arabe, emoji) basculent l’encodage en Unicode, ce qui réduit la limite à 70 caractères par partie.\n",
                    "example": "Flash sale today only — 30% off all orders. Shop now at mailpro.com/shop"
                  },
                  "to": {
                    "type": "integer",
                    "description": "ID de la liste de contacts à laquelle envoyer (issu de `/list/list`)",
                    "example": 42
                  },
                  "scheduled_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Planifie l’envoi pour une date/heure future (ISO 8601).\nOmettre ou laisser vide pour un envoi immédiat.\n",
                    "example": "2026-04-20T10:00:00"
                  },
                  "shorten_urls": {
                    "type": "boolean",
                    "default": false,
                    "description": "Mettre `true` pour remplacer chaque lien du message par un lien court `https://mailp.ro/m/...` : moins de caractères, donc parfois moins de parties de SMS facturées. Un lien qui ne peut pas être raccourci reste tel quel."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envoi SMS en masse créé avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkSend"
                },
                "example": {
                  "Id": 6001,
                  "Message": "Flash sale today only — 30% off all orders. Shop now at mailpro.com/shop",
                  "ScheduledTime": "2026-04-20T10:00:00",
                  "To": [
                    "42"
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Crédits SMS insuffisants",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Insufficient Credit",
                  "Code": 202
                }
              }
            }
          },
          "400": {
            "description": "Champ obligatoire manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Malformed Input",
                  "Code": 103
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/send/add_single.{format}": {
      "get": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "sendSingleSmsGet",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Envoyer un SMS unique (GET)",
        "description": "Envoie un SMS unique à un destinataire via une requête GET.\nUtile pour les intégrations simples lorsque POST n’est pas disponible.\nLes identifiants et le message sont transmis en paramètres de requête.\n\n**Note :** évitez d’utiliser GET pour des messages sensibles — les paramètres de requête apparaissent dans les journaux serveur.\nPréférez `POST /send/add_single`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "description": "Numéro de téléphone du destinataire. Accepte `+41…`, `0041…` ou `41…` avec ou sans séparateurs. Voir **Format des numéros de téléphone** dans la description de l’API.",
            "schema": {
              "type": "string"
            },
            "example": "+41791234567"
          },
          {
            "name": "message",
            "in": "query",
            "required": true,
            "description": "Texte du message SMS (max. 160 caractères pour l’encodage standard)",
            "schema": {
              "type": "string"
            },
            "example": "Your appointment is confirmed for tomorrow at 10:00."
          },
          {
            "name": "scheduled_time",
            "in": "query",
            "description": "Planifie l’envoi (ISO 8601). Omettre pour un envoi immédiat.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "shorten_urls",
            "in": "query",
            "description": "Mettre `true` pour remplacer chaque lien du message par un lien court `https://mailp.ro/m/...` : moins de caractères, donc parfois moins de parties de SMS facturées. Un lien qui ne peut pas être raccourci reste tel quel.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SMS envoyé (ou planifié) avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SingleSend"
                },
                "example": {
                  "Id": 5001,
                  "Message": "Your appointment is confirmed for tomorrow at 10:00.",
                  "ScheduledTime": "2026-04-15T09:00:00",
                  "To": "0041791234567",
                  "Charge": "1"
                }
              }
            }
          },
          "202": {
            "description": "Crédits SMS insuffisants",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "400": {
            "description": "Paramètre obligatoire manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      },
      "post": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "sendSingleSms",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Envoyer un SMS unique",
        "description": "Envoie un SMS unique à un numéro de téléphone destinataire.\nPrend en charge la planification via `scheduled_time`.\n\n**Consommation de crédits :** les crédits sont déduits au moment de l’envoi.\nLe champ `Charge` de la réponse indique combien de crédits ont été consommés.\n\nLa réponse retourne `{ \"Error\": \"Insufficient Credit\", \"Code\": 202 }` si le compte ne dispose pas de crédits suffisants.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "message",
                  "to"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "message": {
                    "type": "string",
                    "description": "Texte du message SMS",
                    "example": "Your verification code is 8472. Valid for 10 minutes."
                  },
                  "to": {
                    "type": "string",
                    "description": "Numéro de téléphone du destinataire. Accepte `+41…`, `0041…` ou `41…` avec ou sans séparateurs. Voir **Format des numéros de téléphone** dans la description de l’API.",
                    "example": "+41791234567"
                  },
                  "scheduled_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Planifie l’envoi pour un horaire futur. Omettre pour un envoi immédiat.",
                    "example": "2026-04-15T14:30:00"
                  },
                  "shorten_urls": {
                    "type": "boolean",
                    "default": false,
                    "description": "Mettre `true` pour remplacer chaque lien du message par un lien court `https://mailp.ro/m/...` : moins de caractères, donc parfois moins de parties de SMS facturées. Un lien qui ne peut pas être raccourci reste tel quel."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SMS envoyé (ou planifié) avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SingleSend"
                },
                "example": {
                  "Id": 5001,
                  "Message": "Your verification code is 8472. Valid for 10 minutes.",
                  "ScheduledTime": "2026-04-15T14:30:00",
                  "To": "0041791234567",
                  "Charge": "1"
                }
              }
            }
          },
          "202": {
            "description": "Crédits SMS insuffisants",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Insufficient Credit",
                  "Code": 202
                }
              }
            }
          },
          "400": {
            "description": "Champ obligatoire manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Malformed Input",
                  "Code": 103
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/send/list.{format}": {
      "get": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "listSmsSends",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Lister les campagnes SMS",
        "description": "Retourne la liste des campagnes SMS (envois en masse) du compte.\nPagination via les en-têtes `NumberOfRecords` et `PageNumber`.\n\nUtilisez `type_to` pour filtrer par type d’envoi :\n- `0` (par défaut) — envois en masse à une liste\n- `1` — envois individuels uniques\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "type_to",
            "in": "query",
            "description": "Filtre sur le type d’envoi :\n- `0` (par défaut) — envois en masse vers une liste de contacts\n- `1` — envois individuels à un seul numéro\n",
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ],
              "default": 0
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des campagnes SMS retournée avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SingleSend"
                  }
                },
                "example": [
                  {
                    "Id": 5001,
                    "Message": "Your verification code is 8472",
                    "ScheduledTime": "2026-04-15T09:00:00",
                    "To": "41791234567"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/send/del.{format}": {
      "post": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "deleteSmsSend",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Annuler ou supprimer un envoi SMS",
        "description": "Annule un envoi SMS **planifié en attente**, ou supprime un envoi **terminé** de l’historique.\nSeuls les envois planifiés à venir peuvent être annulés. Les envois en cours ne peuvent pas être arrêtés.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key",
                  "id_send"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "description": "Votre identifiant client Mailpro",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "description": "Votre clé d’API (GUID)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "id_send": {
                    "type": "integer",
                    "description": "ID de l’envoi SMS à annuler ou supprimer (issu de `/send/list`)",
                    "example": 6001
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envoi SMS annulé ou supprimé avec succès",
            "content": {
              "application/json": {
                "example": {
                  "Result": "OK"
                }
              }
            }
          },
          "400": {
            "description": "id_send manquant",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          },
          "500": {
            "description": "Échec de la suppression (envoi introuvable ou déjà traité)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "Error": "Delete failed",
                  "Code": 500
                }
              }
            }
          }
        }
      }
    },
    "/send/scheduled.{format}": {
      "get": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "listScheduledSms",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Lister mes envois SMS planifiés",
        "description": "Retourne les envois SMS (unitaires ou en campagne) planifiés pour une date\nfuture et qui n’ont pas encore été expédiés. Utile avant d’appeler\n`/cancel` ou `/reschedule`.\n\nLes enregistrements sont triés par `scheduled_time` croissant.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "id_client",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "example": 12345
          },
          {
            "name": "api_key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          {
            "$ref": "#/components/parameters/numberOfRecords"
          },
          {
            "$ref": "#/components/parameters/pageNumber"
          }
        ],
        "responses": {
          "200": {
            "description": "Liste des envois SMS planifiés",
            "content": {
              "application/json": {
                "example": [
                  {
                    "Id": 9001,
                    "Message": "Black Friday: 30% off until midnight.",
                    "ScheduledTime": "2026-11-27T12:00:00",
                    "To": "+33671234567"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/send/{id_send}/cancel.{format}": {
      "post": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "cancelScheduledSms",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Annuler un envoi SMS planifié",
        "description": "Annule un SMS planifié qui n’a pas encore été expédié. Si l’envoi a déjà\nété traité (ou s’il n’existe pas / appartient à un autre client), retourne\n**409 Conflict**.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "id_send",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "example": 9001
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key"
                ],
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "example": "a1b2c3d4-..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envoi annulé",
            "content": {
              "application/json": {
                "example": {
                  "Result": "OK",
                  "Cancelled": 9001
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          },
          "409": {
            "description": "Déjà expédié ou introuvable",
            "content": {
              "application/json": {
                "example": {
                  "error": "conflict",
                  "message": "Send not found, not owned by client, or already dispatched."
                }
              }
            }
          }
        }
      }
    },
    "/send/{id_send}/reschedule.{format}": {
      "patch": {
        "tags": [
          "Send SMS"
        ],
        "operationId": "rescheduleSms",
        "security": [
          {
            "OAuth2": [
              "sms:send"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Replanifier un envoi SMS planifié",
        "description": "Met à jour la date prévue d’un SMS planifié qui n’a pas encore été expédié.\nLa nouvelle date doit être **dans le futur** et **dans un délai de 90 jours**.\n\n**Nom du champ de date :** l’API accepte `scheduled_time` (champ principal),\nainsi que les alias historiques `PlanDate`, `date_planned` et `DatePlanned`.\nFournissez n’importe lequel d’entre eux — le premier non null est utilisé.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          },
          {
            "name": "id_send",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "example": 9001
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "id_client",
                  "api_key"
                ],
                "description": "Fournissez l’un des champs `scheduled_time`, `PlanDate`, `date_planned` ou `DatePlanned`.",
                "properties": {
                  "id_client": {
                    "type": "integer",
                    "example": 12345
                  },
                  "api_key": {
                    "type": "string",
                    "example": "a1b2c3d4-..."
                  },
                  "scheduled_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Nom de champ principal.",
                    "example": "2026-12-31T14:30:00"
                  },
                  "PlanDate": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias historique accepté pour assurer la rétrocompatibilité."
                  },
                  "date_planned": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias historique accepté pour assurer la rétrocompatibilité."
                  },
                  "DatePlanned": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Alias historique accepté pour assurer la rétrocompatibilité."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envoi replanifié",
            "content": {
              "application/json": {
                "example": {
                  "Result": "OK",
                  "Id": 9001,
                  "NewScheduledTime": "2026-12-31T14:30:00"
                }
              }
            }
          },
          "400": {
            "description": "scheduled_time manquant ou invalide"
          },
          "401": {
            "description": "Identifiants invalides"
          },
          "409": {
            "description": "Déjà expédié, introuvable ou nouvelle date hors plage",
            "content": {
              "application/json": {
                "example": {
                  "error": "conflict",
                  "message": "Reschedule failed — send not found, already dispatched, or new date out of range."
                }
              }
            }
          }
        }
      }
    },
    "/import/upload.{format}": {
      "post": {
        "tags": [
          "Import"
        ],
        "operationId": "uploadImportFile",
        "security": [
          {
            "OAuth2": [
              "sms:contacts:write"
            ]
          },
          {
            "bearerAuth": []
          },
          {
            "IdClientHeader": [],
            "ApiKeyHeader": []
          },
          {
            "IdClientQuery": [],
            "ApiKeyQuery": []
          }
        ],
        "summary": "Téléverser un fichier de numéros de téléphone (CSV / XLSX) pour un import en masse",
        "description": "Téléversez un fichier CSV ou XLSX pour importer des numéros de téléphone\ndans une liste de contacts. Le fichier est stocké et mis en file d’attente\npour un traitement asynchrone — l’endpoint retourne immédiatement un\n`ImportJobId`.\n\n**Type de requête :** `multipart/form-data`\n\n**Champs de formulaire obligatoires :**\n- `id_client` (entier) — votre identifiant client Mailpro\n- `api_key` (chaîne) — votre clé d’API\n- `id_list` (entier) — ID de la liste de contacts cible\n- `file` (binaire) — le fichier CSV ou XLSX à téléverser\n\n**Optionnel :**\n- `webhook_url` (chaîne) — URL HTTPS appelée à la fin du traitement.\n  Reçoit une charge utile JSON contenant le statut final (succès / échec)\n  ainsi que le décompte des lignes importées / ignorées / invalides.\n\n**Format du fichier :**\n- Encodage `UTF-8`\n- Première ligne = en-têtes (l’une doit être `Phone` ou `PhoneNumber` — insensible à la casse)\n- Numéros au format international (E.164), par exemple `+41791234567`\n- Taille maximale du fichier : 50 Mo\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "id_list",
                  "file"
                ],
                "properties": {
                  "id_list": {
                    "type": "integer",
                    "format": "int64",
                    "description": "ID de la liste de contacts de destination",
                    "example": 11088
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Fichier CSV ou XLSX (UTF-8, max. 50 Mo)"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL de callback invoquée à la fin du traitement",
                    "example": "https://example.com/hooks/mailpro-import"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fichier accepté et mis en file d’attente",
            "content": {
              "application/json": {
                "example": {
                  "Result": "OK",
                  "ImportJobId": 15,
                  "InternalName": "230575-20260420142616-phones.csv"
                }
              }
            }
          },
          "400": {
            "description": "Aucun fichier téléversé ou id_list manquant"
          },
          "401": {
            "description": "Identifiants invalides"
          },
          "500": {
            "description": "Erreur serveur ou de stockage"
          }
        }
      }
    },
    "/webhooks": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks (distinct de l’hôte principal de l’API)"
        }
      ],
      "get": {
        "summary": "Lister les webhooks",
        "description": "Retourne tous les webhooks configurés sur le compte authentifié.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Tableau de webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                },
                "example": {
                  "items": [
                    {
                      "id": 14,
                      "url": "https://example.com/hooks/mailpro",
                      "description": "Intégration Slack",
                      "is_active": true,
                      "created_at": "2026-07-01T21:09:52.43",
                      "updated_at": "2026-07-21T18:56:08.02",
                      "last_delivery_at": "2026-07-21T18:56:07.653",
                      "last_delivery_status": 200,
                      "consecutive_failures": 0,
                      "events": [
                        "sms.delivered",
                        "sms.failed"
                      ]
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      },
      "post": {
        "summary": "Créer un webhook",
        "description": "Crée un nouvel abonnement webhook. La réponse contient un `secret`\n**affiché une seule fois** — stockez-le immédiatement, côté serveur.\nUtilisez-le pour vérifier la signature HMAC de chaque charge utile entrante.\n\n**Limites** : max. 20 webhooks par compte, max. 50 évènements par webhook.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              },
              "example": {
                "url": "https://example.com/hooks/mailpro",
                "events": [
                  "sms.delivered",
                  "sms.failed"
                ],
                "description": "Intégration Slack"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook créé — le `secret` n’est retourné qu’ici",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreateResponse"
                },
                "example": {
                  "id": 42,
                  "url": "https://example.com/hooks/mailpro",
                  "events": [
                    "sms.delivered",
                    "sms.failed"
                  ],
                  "description": "Intégration 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 invalide, liste d’évènements invalide ou limite dépassée"
          },
          "401": {
            "description": "Identifiants invalides"
          }
        }
      }
    },
    "/webhooks/{id}": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "get": {
        "summary": "Récupérer un webhook",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Le webhook",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      },
      "put": {
        "summary": "Mettre à jour un webhook",
        "description": "Mise à jour partielle — seuls les champs transmis sont modifiés.",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook mis à jour",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Charge utile invalide"
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      },
      "delete": {
        "summary": "Supprimer un webhook",
        "description": "Supprime définitivement le webhook ainsi que l’intégralité de son historique de livraisons.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "204": {
            "description": "Supprimé"
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "post": {
        "summary": "Envoyer un évènement de test",
        "description": "Met en file d’attente un évènement `ping` immédiat (ou tout autre type\nd’évènement via le champ `eventType` du corps) vers votre endpoint. Utile\npour vérifier l’accessibilité et la vérification de signature avant de\ns’appuyer sur de vrais évènements.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "default": "ping"
                  }
                }
              },
              "example": {
                "eventType": "ping"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Évènement mis en file d’attente pour expédition",
            "content": {
              "application/json": {
                "example": {
                  "deliveryId": 1234,
                  "eventId": "evt_8f3a9b2c4d1e5f6789abcdef01234567",
                  "eventType": "ping"
                }
              }
            }
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      }
    },
    "/webhooks/{id}/rotate-secret": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks"
        }
      ],
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "integer",
            "format": "int64"
          }
        }
      ],
      "post": {
        "summary": "Faire pivoter le secret HMAC",
        "description": "Génère un nouveau secret HMAC et invalide immédiatement l’ancien.\nLe nouveau secret est retourné **une seule fois** dans la réponse —\nstockez-le. Toute livraison à partir de ce moment sera signée avec le\nnouveau secret.\n\nUtilisez ceci si vous soupçonnez que votre secret a été compromis.\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Nouveau secret (visible une seule fois)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "secret": "whsec_XyzA9876BcdE5432FghI1098JklM7654NopQ"
                }
              }
            }
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      }
    },
    "/webhooks/{id}/deliveries": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks"
        }
      ],
      "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": "Curseur de pagination (= dernier ID de livraison vu). Retourne les livraisons dont `id < cursor`."
        }
      ],
      "get": {
        "summary": "Lister les tentatives de livraison",
        "description": "Retourne l’historique des tentatives d’expédition pour ce webhook\n(rétention de 30 jours). Utile pour le débogage — quels évènements ont été\nenvoyés, codes de statut reçus, nombre de réessais, messages d’erreur.\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Tableau des tentatives de livraison (les plus récentes en premier)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDelivery"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook introuvable"
          }
        }
      }
    },
    "/webhooks/{id}/deliveries/{deliveryId}/replay": {
      "servers": [
        {
          "url": "https://webhook.mailpro.com/api/sms",
          "description": "Service de gestion des webhooks"
        }
      ],
      "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": "Rejouer une livraison",
        "description": "Met en file d’attente une nouvelle tentative de livraison avec la même\ncharge utile que l’originale. Utile pour redéclencher une livraison en\néchec après avoir corrigé votre endpoint. Une NOUVELLE ligne\n`Webhook_Delivery` est créée (l’originale est préservée à des fins\nd’audit). Le rejeu suit la politique de réessai standard (même planning\nde backoff, même règle de désactivation automatique après 10 échecs).\n",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "202": {
            "description": "Rejeu mis en file d’attente",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "new_delivery_id": {
                      "type": "integer",
                      "format": "int64",
                      "example": 12999
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook ou livraison introuvable"
          }
        }
      }
    }
  }
}