# email.campaigns.create

Créer un brouillon de campagne et son premier message, sans envoyer immédiatement.

POST `/api/mcp/v1/email.campaigns.create` · contrat 2.0.0 · OAuth `mcp:write` · **aucune offre requise**.

Outil MCP : [`prescriptio_campagne`](/docs/api/prescriptio_campagne.md) avec action=creer ; même handler métier.

## Entrée

```json
{
  "additionalProperties": false,
  "properties": {
    "corps": {
      "description": "creer/variante : le message. Memes champs. Pour un lien mesure, ecrire [lien1], [lien2] et les declarer par action liens.",
      "type": "string"
    },
    "expediteur": {
      "description": "creer/modifier : adresse d'expedition. Doit appartenir a un domaine VERIFIE pour cet espace (action expediteur les liste) ou au domaine de repli de la plateforme. Laisser vide prend l'adresse par defaut de l'espace. Un domaine seulement declare est refuse : sans SPF/DKIM le message part en indesirable.",
      "type": "string"
    },
    "expediteur_nom": {
      "description": "creer/modifier : le nom affiche par la messagerie du destinataire.",
      "type": "string"
    },
    "nom": {
      "description": "creer/modifier : nom de la campagne (120 caracteres au plus).",
      "type": "string"
    },
    "objet": {
      "description": "creer/variante : objet du message. Champs : [prenom] [nom] [entreprise] [ville] [fonction] (l'accent est optionnel : [prenom] et [prénom] valent pareil, [societe] vaut [entreprise]).",
      "type": "string"
    },
    "repondre_a": {
      "description": "creer/modifier : la boite ou arrivent les reponses (celle de l'utilisateur). Obligatoire avant de planifier.",
      "type": "string"
    }
  },
  "required": [
    "nom",
    "objet",
    "corps"
  ],
  "type": "object"
}
```

## Exemple synthétique

```json
{
  "corps": "Bonjour [prenom], …",
  "nom": "Relance isolation",
  "objet": "Votre projet à [ville]"
}
```

## Sortie

```json
{
  "properties": {
    "action": {
      "enum": [
        "campagne_creee"
      ],
      "type": "string"
    },
    "fields": {
      "properties": {
        "campagne_id": {
          "type": "string"
        }
      },
      "required": [
        "campagne_id"
      ],
      "type": "object"
    },
    "note": {
      "type": "string"
    },
    "ok": {
      "const": true
    },
    "url": {
      "type": "string"
    }
  },
  "required": [
    "ok",
    "action",
    "fields"
  ],
  "type": "object"
}
```

## Erreurs

Le connecteur MCP renvoie une erreur JSON-RPC pour ce qui empêche l'appel, et une erreur *métier* (`isError`) lisible par le modèle pour ce que l'appel n'a pas pu faire.

| Code JSON-RPC | Quand | Que faire |
|---|---|---|
| `-32601` | Outil inconnu | Appeler `tools/list` ; ne pas deviner un nom |
| `-32602` | Argument absent ou hors schéma | Corriger l'argument nommé dans le message |
| `-32003` | Portée OAuth absente de l'autorisation | Reconnecter en acceptant `mcp:read` ou `mcp:write` |
| `-32004` | Quota dépassé | Attendre le délai indiqué ; ne jamais boucler |
| `-32603` | Dépendance indisponible | Réessayer plus tard ; ne pas rejouer une écriture sans vérifier |

Sur les routes REST `/api/mcp/v1/*`, les mêmes situations sont des codes HTTP : `400 invalid_arguments` / `invalid_cursor`, `401 unauthenticated`, `403 insufficient_scope` / `access_denied` / `user_required` / `organization_required` / `role_denied`, `404 not_found`, `409 conflict`, `429 quota_exceeded` (avec `Retry-After` quand il est connu), `503 unavailable`, `504 timeout`. Le corps porte `error.code`, `error.message`, `error.retry_after_seconds` et `api_contract_version`.

⚠ Un message d'erreur n'est jamais une donnée : ne pas le recopier comme un fait, et ne pas inventer un identifiant ou une URL qu'il ne contient pas.

## Quotas

Un appel compte pour **une unité** dans les deux compteurs d'appels, partagés entre le connecteur MCP et les routes REST :

| Compteur | Compte gratuit | Abonné | Accès interne |
|---|---|---|---|
| Appels / 24 h glissantes | 100 | 5000 | illimité |
| Appels / 60 s (anti-rafale) | 5 | 30 | illimité |
| Dossiers distincts / jour civil (Paris) | 5 | 500 | illimité |

Cet outil ne consomme aucune unité de dossier : chercher est gratuit, c'est la lecture d'un dossier (`prescriptio_dce`, `prescriptio_mairies` et leurs téléchargements) qui est décomptée.

**Aucun outil n'est réservé à une offre** (décision du 2026-09-17) : un compte gratuit voit et appelle le catalogue entier, ce sont ces plafonds qui bornent son usage.

[Authentification, quotas, pagination et erreurs](/docs/reference/concepts.md). Les propriétés additionnelles des résultats historiques restent autorisées ; ce schéma n'invente pas les champs manquants.
