Guide destiné aux développeurs d'applications tierces qui envoient des emails via l'API INOMAIL.
https://messagerie.inovertix.com/api/v1
Authorization: Bearer inv_…
{ "statusCode", "name", "message" }
Obtenir une clé API — dashboard INOMAIL → Clés API → Nouvelle clé. Copiez la clé inv_… (affichée une seule fois). Permission full (lecture + envoi) ou sending_only (envoi uniquement).
inv_…
full
sending_only
Vérifier un domaine d'envoi — dashboard INOMAIL → Domaines : publiez les enregistrements SPF/DKIM/DMARC fournis, puis Vérifier. Le from devra utiliser ce domaine.
from
Envoyer :
curl -X POST https://messagerie.inovertix.com/api/v1/emails \ -H "Authorization: Bearer inv_VOTRE_CLE" \ -H "Content-Type: application/json" \ -d 39;{ "from": "Mon App <no-reply@inovertix.com>", "to": ["client@exemple.com"], "subject": "Bienvenue", "html": "<h1>Bonjour 👋</h1>" }39;
Réponse : { "id": "01k…" }.
{ "id": "01k…" }
GET /emails/{id}
email.sent
email.bounced
Toutes les requêtes portent Authorization: Bearer inv_….
restricted_api_key
Débit : 120 requêtes/minute par clé (429 rate_limit_exceeded au-delà ; en-tête Retry-After).
rate_limit_exceeded
Retry-After
POST /emails
{ "id" }
POST /emails/batch
{ "data": [ { "id" } ] }
GET /emails
{ data, links, meta }
{ "data": { … , events: [] } }
POST /emails/{id}/resend
{ "data" }
GET /suppressions
POST /suppressions
DELETE /suppressions/{id}
{ "message" }
{ "from": "Nom <email@domaine>", // requis, domaine vérifié "to": ["a@x.com"], // requis (string ou tableau) "cc": [], "bcc": [], "reply_to": [], // optionnels (string ou tableau) "subject": "…", // requis "html": "<p>…</p>", // html OU text requis "text": "…", "headers": { "X-Custom": "v" }, // optionnel "tags": [ { "name": "campagne", "value": "onboarding" } ], "attachments": [ { "filename": "f.pdf", "content": "<base64>", "mime": "application/pdf" } ], "scheduled_at": "2026-08-10T09:00:00Z" // envoi différé (ISO 8601) }
Pièces jointes : 10 Mo maximum au total.
En-tête Idempotency-Key: <votre-clé-unique> sur POST /emails et POST /emails/batch : rejouer la même requête renvoie la même réponse (mémorisée 24 h) sans renvoyer l'email. Deux requêtes identiques simultanées : la 2ᵉ reçoit 409 concurrent_request.
Idempotency-Key: <votre-clé-unique>
409 concurrent_request
queued → sent | failed | bounced | suppressed. Événements journalisés (dans data.events) : queued, sent, failed, bounced, opened, clicked, suppressed, unsubscribe.
queued
sent
failed
bounced
suppressed
data.events
queued, sent, failed, bounced, opened, clicked, suppressed, unsubscribe
Toutes au format { "statusCode", "name", "message" }.
name
missing_api_key
invalid_api_key
validation_error
message
from_not_allowed
no_recipients
not_found
concurrent_request
Configurez un endpoint dans INOMAIL → Webhooks et abonnez-le à des événements (email.sent, email.failed, email.bounced, email.opened, email.clicked, ou *). opened/clicked ne sont émis qu'à la première occurrence.
email.failed
email.opened
email.clicked
*
opened
clicked
Chaque livraison est un POST JSON { type, created_at, data } avec l'en-tête :
{ type, created_at, data }
X-Inomail-Signature: t=<timestamp>,v1=<hmac>
où hmac = HMAC-SHA256("<timestamp>.<corps_brut>", secret_du_webhook).
hmac = HMAC-SHA256("<timestamp>.<corps_brut>", secret_du_webhook)
[$t, $v1] = sscanf($_SERVER['HTTP_X_INOMAIL_SIGNATURE'], 't=%d,v1=%s'); $body = file_get_contents(39;php://input'); $expected = hash_hmac('sha256', "{$t}.{$body}", $secret); if (! hash_equals($expected, $v1) || abs(time() - $t) > 300) { http_response_code(403); exit; // signature invalide ou trop ancienne (anti-rejeu) } $event = json_decode($body, true);
import crypto from "node:crypto"; function verify(rawBody, header, secret) { const [, t, v1] = header.match(/^t=(\d+),v1=([a-f0-9]+)$/) || []; if (!t) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); return ok && Math.abs(Date.now() / 1000 - Number(t)) < 300; } // ⚠️ Utilisez le corps BRUT (raw), pas l'objet JSON re-sérialisé.
Livraisons : 5 tentatives, backoff 10s / 60s / 300s / 900s. Répondez 2xx rapidement (traitement asynchrone conseillé).
10s / 60s / 300s / 900s
2xx
suppressed n'est pas une erreur : si tous les destinataires sont sur la liste de suppression, POST /emails renvoie quand même 200 { "id" } avec le statut suppressed — l'email n'est pas envoyé. Vérifiez le statut, pas seulement le 200.
200 { "id" }
Lecture réservée aux clés full : une clé sending_only ne peut pas appeler GET /emails* (403).
GET /emails*
Domaine du from : il doit appartenir à un compte SMTP autorisé, sinon 422 from_not_allowed. Utilisez un domaine vérifié pour la délivrabilité.
422 from_not_allowed
Corps brut pour les webhooks : signez/vérifiez le corps tel que reçu, pas une re-sérialisation JSON.
Désinscription : les emails incluent un en-tête List-Unsubscribe one-click ; les adresses désinscrites passent en suppression (reason=unsubscribe) et sont filtrées.
List-Unsubscribe
reason=unsubscribe
Deux clients de référence sont disponibles sur demande auprès d'INOVERTIX — un pour PHP (InomailClient), un pour JavaScript (inomail.js). Ils n'ajoutent rien à ce que fait déjà un POST : si vous préférez appeler l'API directement, les exemples des sections précédentes suffisent. Pensez simplement à définir la base URL sur https://messagerie.inovertix.com/api/v1.
InomailClient
inomail.js
POST