Tickets de support, réponses aux notifications, documents envoyés à un bot : les e-mails adressés à une adresse de votre domaine arrivent sur votre point de terminaison HTTPS sous la forme d'une seule requête POST JSON signée, avec le texte, le HTML et toutes les pièces jointes. Aucune boîte aux lettres à interroger, pas d'IMAP.
email:write.$ nameocean email forwards on example.com
Cette opération fait pointer l'enregistrement MX du domaine vers les serveurs de redirection d'e-mails de NameOcean. Si le domaine possède déjà des enregistrements MX, pour des boîtes aux lettres hébergées ailleurs, la commande s'arrête et les affiche : tous les e-mails du domaine seraient alors redirigés ici. N'ajoutez --replace-mx que si c'est bien ce que vous voulez.
L'envoi d'e-mails vers une URL est activé par compte après une brève vérification, afin d'empêcher les relais de spam. Sur la page du domaine, dans Tableau de bord → Redirection d'e-mails, faites-en la demande en indiquant en une ligne l'usage prévu. D'ici là, l'API répond 403 not_approved.
$ nameocean email inbound add example.com support https://app.example.com/hooks/mail
$ curl -X POST https://nameocean.net/api/v1/email/forwarding/example.com/webhooks \ -H "Authorization: Bearer $NAMEOCEAN_API_KEY" -H "Content-Type: application/json" \ -d '{"localpart": "support", "url": "https://app.example.com/hooks/mail"}'
La réponse contient le secret de signature (whsec_…) ; conservez-le dans les paramètres de votre application. Le localpart * envoie toutes les adresses du domaine qui n'ont pas d'alias propre. Un domaine peut avoir jusqu'à trois webhooks de ce type.
Vérifiez la signature sur le corps brut, puis lisez le JSON. Un petit récepteur Flask :
import base64, hashlib, hmac, json, os, time
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["NAMEOCEAN_WEBHOOK_SECRET"] # whsec_...
def verified(header, body, tolerance=300):
fields = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
ts = int(fields.get("t", "0"))
if abs(time.time() - ts) > tolerance:
return False
expected = hmac.new(SECRET.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, fields.get("v1", ""))
@app.post("/hooks/mail")
def mail():
body = request.get_data()
if not verified(request.headers.get("X-NameOcean-Signature", ""), body):
abort(401)
mail = json.loads(body)
print(mail["envelope"]["from"], "→", mail["envelope"]["to"], mail["subject"])
for a in mail["attachments"]:
data = base64.b64decode(a["content_base64"])
print(" attachment", a["filename"], a["content_type"], len(data), "bytes")
return "", 204 # any 2xx: delivered
X-NameOcean-Event: email.received
X-NameOcean-Delivery: 6f1c2a4e-8b3d-4f5a-9c7e-1d2b3a4c5e6f
X-NameOcean-Signature: t=1794300000,v1=5f2b…
{
"id": "6f1c2a4e-8b3d-4f5a-9c7e-1d2b3a4c5e6f",
"event": "email.received",
"received_at": "2026-11-11T09:30:12+00:00",
"envelope": {"from": "alice@example.org", "to": "support@example.com"},
"from": "Alice <alice@example.org>",
"to": "support@example.com",
"cc": null,
"reply_to": null,
"subject": "Order 1042",
"date": "Wed, 11 Nov 2026 09:30:05 +0000",
"message_id": "<…@example.org>",
"in_reply_to": null,
"references": null,
"text": "Hi…",
"html": "<p>Hi…</p>",
"headers": [["From", "Alice <alice@example.org>"], …],
"attachments": [{"filename": "invoice.pdf", "content_type": "application/pdf", "content_id": null,
"size": 48211, "content_base64": "JVBERi0…"}],
"spam_score": 0.4,
"size": 66120
}
L'id reste identique lorsqu'un e-mail est renvoyé : utilisez-le pour ignorer les doublons. in_reply_to et references vous indiquent à quel e-mail envoyé par vous la personne a répondu.
nameocean email forwards log example.com (ou GET /email/forwarding/example.com/messages) et libérez-les : ils sont renvoyés une fois de plus, avec le même id.