Chapitre 14 Clients, groupware & CLI Admin : #pApiAuth

Référence complète des champs API & CLI : les 12 types d'objets, les endpoints REST & POST /api/v1/send

Cette référence documente chaque propriété JSON, type de données, valeur d'énumération, valeur par défaut et rôle RBAC minimal des 12 objets de gestion (/api/admin/cli/objects/{type} & nextxch-cli), ainsi que la remise d'e-mails transactionnels (POST /api/v1/send). Utilisez le champ de filtre instantané au-dessus du tableau pour rechercher une propriété comme dkimSigningMode, tlsMode, allowedCidrs ou warmingEnabled.

Conseil d'exploitation & note d'architecture

Introspection en direct dans votre terminal ou via HTTP : chaque nextxch expose ce schéma complet en JSON lisible par machine sous GET /api/admin/cli/schema (avec l'en-tête SHA-256 X-Nextxch-Schema-Version). Dans votre terminal, exécutez à tout moment nextxch-cli describe <Type> (par ex. nextxch-cli describe Domain ou nextxch-cli describe SmtpQueue).

Envoi transactionnel

TransactionalSendRequest (POST /api/v1/send)

Clé : Idempotency-Key RBAC : User+
13 propriétés

Point de terminaison HTTP JSON à haut débit pour la remise d'e-mails depuis applications web, ERP, CRM et codes 2FA, avec priorisation par catégorie, préchauffage d'IP, pré-vérification de suppression et idempotence sur 24 h.

POST /api/v1/send Met en file d'attente et signe un e-mail transactionnel (UTF-8 Multipart/Alternative + pièces jointes)
POST /api/v1/unsubscribe/{token} Point de terminaison public de désabonnement en un clic RFC 8058 (jeton signé DKIM)
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ from string FACULTATIF
Par défaut : Key senderAddress
Adresse de l'expéditeur. Prend automatiquement par défaut la senderAddress liée à la clé pour les clés Role=User.
├─ to string[] OBLIGATOIRE
Tableau des adresses e-mail des destinataires principaux (au moins 1 obligatoire).
├─ cc string[] FACULTATIF
Par défaut : []
Destinataires en copie facultatifs.
├─ bcc string[] FACULTATIF
Par défaut : []
Destinataires en copie cachée facultatifs.
├─ replyTo string FACULTATIF
Adresse Reply-To facultative.
├─ subject string OBLIGATOIRE
Objet de l'e-mail (UTF-8).
├─ textBody string FACULTATIF
Corps en texte brut (text/plain). Au moins l'un de textBody ou htmlBody est requis.
├─ htmlBody string FACULTATIF
Corps HTML (text/html). À fournir en plus de textBody pour une délivrabilité de 10/10.
├─ category string FACULTATIF
Par défaut : transactional
Sélectionne la priorité de file d'attente, le contournement de la montée en charge IP et le compartiment de quota du fournisseur destinataire.
Enum :"critical""transactional""notification""marketing"
├─ overflowMode string FACULTATIF
Par défaut : DeferQueue
Comportement lorsque le quota horaire du fournisseur de destination est atteint (étalement en douceur dans la file d'attente ou HTTP 429 + Retry-After).
Enum :"DeferQueue""Reject429"
├─ idempotencyKey string FACULTATIF
Par défaut : Header Idempotency-Key
Clé de déduplication déterministe (par ex. numéro de facture) qui empêche les envois en double pendant 24 heures.
├─ headers Record<string, string> FACULTATIF
Par défaut : {}
Dictionnaire d'en-têtes X personnalisés RFC 5322.
├─ attachments SendAttachmentDto[] FACULTATIF
Par défaut : []
Tableau de { fileName: string, contentType: string, base64Content: string }.
{
  "from": "Abrechnung <rechnung@firma.de>",
  "to": ["einkauf@kunde.de"],
  "cc": ["buchhaltung@firma.de"],
  "subject": "Ihre elektronische Rechnung RE-2026-90412",
  "category": "transactional",
  "overflowMode": "DeferQueue",
  "textBody": "Guten Tag,\nanbei erhalten Sie Ihre Rechnung RE-2026-90412.",
  "htmlBody": "<p>Guten Tag,</p><p>anbei erhalten Sie Ihre Rechnung <strong>RE-2026-90412</strong>.</p>",
  "headers": {
    "X-ERP-Document-Id": "RE-2026-90412"
  },
  "attachments": [
    {
      "fileName": "RE-2026-90412.pdf",
      "contentType": "application/pdf",
      "base64Content": "JVBERi0xLjcKJcfs..."
    }
  ]
}
# 1) Kleinstprivilegierten Sende-Key (Role=User) für rechnung@firma.de erstellen:
nextxch-cli create ApiKey \
  --set label="ERP Rechnungsversand" \
  --set role=User \
  --set senderAddress=rechnung@firma.de \
  --set defaultCategory=transactional \
  --set allowedCidrs=10.20.0.0/16

# 2) Risikofreier Trockentest gegen die integrierte Sandbox-Sink-Domain:
# ok@sink.nextxch.local | bounce@sink.nextxch.local | defer@sink.nextxch.local
curl -fsS -X POST "https://mail.firma.de/api/v1/send" \
  -H "Authorization: Bearer smk_01968f2a_..." \
  -H "Idempotency-Key: inv-2026-90412" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "rechnung@firma.de",
    "to": ["einkauf@kunde.de"],
    "subject": "Ihre Rechnung RE-2026-90412",
    "category": "transactional",
    "textBody": "Guten Tag, anbei Ihre Rechnung RE-2026-90412.",
    "htmlBody": "<p>Guten Tag, anbei Ihre Rechnung <b>RE-2026-90412</b>.</p>"
  }'
using System.Net.Http.Headers;
using System.Net.Http.Json;

using var http = new HttpClient { BaseAddress = new Uri("https://mail.firma.de") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "smk_01968f2a_...");
http.DefaultRequestHeaders.Add("Idempotency-Key", "inv-2026-90412");

var response = await http.PostAsJsonAsync("/api/v1/send", new
{
    from = "rechnung@firma.de",
    to = new[] { "einkauf@kunde.de" },
    subject = "Ihre Rechnung RE-2026-90412",
    category = "transactional",
    textBody = "Guten Tag, anbei Ihre Rechnung RE-2026-90412.",
    htmlBody = "<p>Guten Tag, anbei Ihre Rechnung <strong>RE-2026-90412</strong>.</p>"
});
response.EnsureSuccessStatusCode();
Objet de collection (CRUD)

IntegrationKey (API v1)

Clé : id RBAC : TenantAdmin+
9 propriétés

Clé d'intégration avec scopes pour /api/v1. Une clé agit pour un utilisateur ou, en tant qu'intégration de locataire, pour l'utilisateur indiqué dans l'en-tête X-Nextxch-Act-As. Sans le scope, l'API répond 403 ; un utilisateur d'un autre locataire donne 404. Client credentials OAuth 2.0 en option (jeton valable 1 heure).

POST /api/admin/integrations/keys Créer une clé (admin, affichée une seule fois)
PUT /api/admin/integrations/keys/{id} Modifier les portées
DELETE /api/admin/integrations/keys/{id} Révoquer une clé (et ses jetons OAuth)
POST /api/v1/oauth/token OAuth 2.0 client_credentials : ID de clé + clé pour obtenir un jeton d'accès
GET /api/v1/me Clé, scopes et utilisateur agissant
GET /api/v1/openapi.json Document OpenAPI 3.1 de l'installation
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ label string OBLIGATOIRE
Libellé, par ex. CRM ou ERP.
├─ mode string OBLIGATOIRE
Par défaut : user
user : agit toujours pour userEmail. tenant : agit pour X-Nextxch-Act-As (uniquement les utilisateurs de son propre locataire).
Enum :"user""tenant"
├─ userEmail string FACULTATIF
Utilisateur pour mode=user.
├─ scopes string[] OBLIGATOIRE
Autorisations ; .write inclut .read. crypto.read et crypto.manage (S/MIME, PGP) sont réservés.
Enum :"calendar.read""calendar.write""rooms.read""booking.read""booking.write""contacts.read""contacts.write""directory.read""sharing.manage""webhooks.manage""mail.send"
├─ domainId guid FACULTATIF
Intégration du locataire limitée aux utilisateurs de ce domaine.
├─ allowedCidrs string[] FACULTATIF
Par défaut : []
Réseaux IP autorisés.
├─ expiresInDays integer FACULTATIF
Expiration en jours (vide : illimitée).
├─ senderAddress string FACULTATIF
Expéditeur pour mail.send (POST /api/v1/send).
├─ apiKey string LECTURE SEULE
smk_<id>_<secret>, uniquement dans la réponse de création ; seul le hachage est stocké.
{
  "label": "CRM",
  "mode": "tenant",
  "scopes": ["calendar.write", "rooms.read", "contacts.write", "webhooks.manage"],
  "allowedCidrs": ["203.0.113.0/24"],
  "expiresInDays": 365
}
# Integrations-Schlüssel: Admin > API & Integrationen > Neuer Schlüssel
# Danach z. B. ein Access-Token per OAuth 2.0 Client Credentials holen:
curl -fsS -X POST "https://mail.firma.de/api/v1/oauth/token" \
  -d grant_type=client_credentials \
  -d client_id=0196c3a2-5f1e-7c11-9a20-1c2d3e4f5a6b \
  -d client_secret=smk_0196c3a25f1e7c119a201c2d3e4f5a6b_... \
  -d scope="calendar.read rooms.read"
curl -fsS "https://mail.firma.de/api/v1/me" \
  -H "Authorization: Bearer smk_0196c3a2..." \
  -H "X-Nextxch-Act-As: anna@firma.de"
using var http = new HttpClient { BaseAddress = new Uri("https://mail.firma.de") };
http.DefaultRequestHeaders.Authorization = new("Bearer", "smk_0196c3a2...");
http.DefaultRequestHeaders.Add("X-Nextxch-Act-As", "anna@firma.de");
var me = await http.GetFromJsonAsync<JsonElement>("/api/v1/me");
Objet de collection (CRUD)

CalendarEvent (API v1)

Clé : hrefName RBAC : User+
12 propriétés

Événements avec séries et exceptions, participants, salles, invitations, réponses, disponibilité, calendriers partagés et globaux et liens d'invitation. Scopes calendar.read et calendar.write. Heures en UTC (ISO-8601), fuseaux horaires sous forme de noms IANA.

GET /api/v1/calendars Calendriers personnels, partagés, globaux et de ressources
GET /api/v1/calendar/events?from&to&calendarId&limit&cursor Occurrences dans une fenêtre (séries développées)
POST /api/v1/calendar/events Créer un événement (inviter des personnes et des salles)
GET /api/v1/calendar/events/{hrefName} Événement avec série et exceptions
PUT /api/v1/calendar/events/{hrefName} Modifier ; scope=occurrence|following pour des parties d'une série
DELETE /api/v1/calendar/events/{hrefName} Supprimer, avec annulations facultatives (notify=true)
POST /api/v1/calendar/events/{hrefName}/respond Répondre à une invitation
POST /api/v1/calendar/events/{hrefName}/invite-link Lien d'invitation signé
GET /api/v1/calendar/events/{hrefName}/ics iCalendar brut
POST /api/v1/calendar/freebusy Disponibilité des personnes et des salles
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ title string OBLIGATOIRE
Titre.
├─ startUtc datetime OBLIGATOIRE
Début en UTC.
├─ endUtc datetime OBLIGATOIRE
Fin en UTC.
├─ timeZone string FACULTATIF
Par défaut : Europe/Berlin
Fuseau horaire IANA de l'événement (série, affichage).
├─ allDay boolean FACULTATIF
Par défaut : false
Toute la journée.
├─ attendees Attendee[] FACULTATIF
Par défaut : []
{ email, name, role } ; les salles et ressources acceptent ou refusent selon leurs règles.
├─ sendInvitations boolean FACULTATIF
Par défaut : false
Envoyer des invitations iMIP.
├─ recurrence Récurrence FACULTATIF
{ freq, interval, byDay, byMonthDay, count, until }.
├─ showAs string FACULTATIF
Par défaut : busy
Afficher comme.
Enum :"free""tentative""busy""oof""workingElsewhere"
├─ isPrivate boolean FACULTATIF
Par défaut : false
Privé (détails pour le propriétaire uniquement).
├─ calendarId string FACULTATIF
Calendrier cible ownerId:slug avec droits d'écriture.
├─ teamsMeeting boolean FACULTATIF
Créer une réunion Microsoft Teams.
{
  "title": "Projektstart",
  "startUtc": "2026-12-03T09:00:00Z",
  "endUtc": "2026-12-03T10:00:00Z",
  "timeZone": "Europe/Berlin",
  "attendees": [
    { "email": "bernd@firma.de" },
    { "email": "raum-1@firma.de" }
  ],
  "sendInvitations": true
}
# Benötigte Scopes: calendar.read (lesen), calendar.write (schreiben)
# Einen Raum buchen: Raumadresse als Teilnehmer eintragen
curl -fsS -X POST "https://mail.firma.de/api/v1/calendar/events" \
  -H "Authorization: Bearer smk_0196c3a2..." \
  -H "X-Nextxch-Act-As: anna@firma.de" \
  -H "Content-Type: application/json" \
  -d '{"title":"Projektstart","startUtc":"2026-12-03T09:00:00Z","endUtc":"2026-12-03T10:00:00Z","timeZone":"Europe/Berlin","attendees":[{"email":"raum-1@firma.de"}]}'
var res = await http.PostAsJsonAsync("/api/v1/calendar/events", new
{
    title = "Projektstart",
    startUtc = DateTimeOffset.Parse("2026-12-03T09:00:00Z"),
    endUtc = DateTimeOffset.Parse("2026-12-03T10:00:00Z"),
    timeZone = "Europe/Berlin",
    attendees = new[] { new { email = "raum-1@firma.de" } }
});
string href = (await res.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("data").GetProperty("hrefName").GetString()!;
Objet de collection (CRUD)

Rooms & Booking (API v1)

Clé : id RBAC : User+
6 propriétés

Salles et ressources avec disponibilité (rooms.read) et pages de réservation à la Calendly (booking.read, booking.write) : page de réservation, types d'événements, créneaux libres, lecture et annulation des réservations. Une salle est réservée via un événement dont elle est participante.

GET /api/v1/rooms?startUtc&endUtc&category Salles et ressources, avec leur disponibilité sur la période
GET /api/v1/rooms/{email}/availability Créneaux occupés d'une salle
GET /api/v1/booking/profile Page de réservation avec horaires et types d'événements
GET /api/v1/booking/event-types Types d'événements
POST /api/v1/booking/event-types Créer un type d'événement
GET /api/v1/booking/event-types/{id}/slots?from&to Heures de début libres (42 jours max.)
GET /api/v1/booking/bookings?status&from&to Réservations
POST /api/v1/booking/bookings/{id}/cancel Annuler une réservation (l'invité est informé)
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ slots[].startUtc datetime LECTURE SEULE
Début de la plage libre en UTC.
├─ slots[].endUtc datetime LECTURE SEULE
Fin (début plus durée).
├─ slots[].staff object[] LECTURE SEULE
Personnel disponible pour les types d'événements d'équipe.
├─ status string LECTURE SEULE
Statut de la réservation.
Enum :"confirmed""cancelled"
├─ guest object LECTURE SEULE
{ name, email, phone, timeZone }.
├─ free boolean LECTURE SEULE
Salle libre dans le créneau.
{
  "ok": true,
  "data": {
    "eventTypeId": "beratung-3f2a",
    "durationMinutes": 30,
    "timeZone": "Europe/Berlin",
    "slots": [
      { "startUtc": "2026-12-04T08:00:00Z", "endUtc": "2026-12-04T08:30:00Z", "staff": [] }
    ]
  }
}
# Benötigte Scopes: rooms.read, booking.read, booking.write
curl -fsS "https://mail.firma.de/api/v1/rooms?startUtc=2026-12-03T09:00:00Z&endUtc=2026-12-03T10:00:00Z" \
  -H "Authorization: Bearer smk_0196c3a2..."
var slots = await http.GetFromJsonAsync<JsonElement>("/api/v1/booking/event-types/beratung/slots");
foreach (var s in slots.GetProperty("data").GetProperty("slots").EnumerateArray())
    Console.WriteLine(s.GetProperty("startUtc").GetString());
Objet de collection (CRUD)

Contact, Directory & Sharing (API v1)

Clé : hrefName RBAC : User+
9 propriétés

Contacts personnels avec tous les champs et photo (contacts.read, contacts.write, If-Match pour la protection contre les conflits), liste d'adresses globale (directory.read) ainsi que partages et délégation (sharing.manage, délégation = autorisation sur la boîte aux lettres avec sendAs ou sendOnBehalf).

GET /api/v1/contacts?q Rechercher des contacts
POST /api/v1/contacts Créer un contact
PUT /api/v1/contacts/{hrefName} Modifier un contact (les champs null restent inchangés)
DELETE /api/v1/contacts/{hrefName} Supprimer un contact
GET /api/v1/directory?q&type Liste d'adresses globale
GET /api/v1/sharing/grants Autorisations propres
PUT /api/v1/sharing/grants Définir ou supprimer une autorisation
GET /api/v1/sharing/shared-with-me Partagés avec moi
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ givenName string FACULTATIF
Prénom.
├─ familyName string FACULTATIF
Nom de famille.
├─ organization string FACULTATIF
Entreprise.
├─ emails TypedValue[] FACULTATIF
Par défaut : []
{ type: work|home|other, value }.
├─ phones TypedValue[] FACULTATIF
Par défaut : []
{ type: cell|work|home|fax, value }.
├─ addresses Address[] FACULTATIF
Par défaut : []
{ type, street, city, region, postalCode, country }.
├─ birthday string FACULTATIF
yyyy-MM-dd ou --MM-dd.
├─ photo string ÉCRITURE SEULE (SECRET)
data:image/jpeg;base64,... (max. 512 Ko), une valeur vide supprime l'image.
├─ permissions string[] FACULTATIF
Droits d'un partage (PUT /sharing/grants).
Enum :"freeBusy""read""write""delete""sendOnBehalf""sendAs""manage"
{
  "givenName": "Clara",
  "familyName": "Kunde",
  "organization": "Kunde AG",
  "emails": [{ "type": "work", "value": "clara@kunde.example" }],
  "phones": [{ "type": "cell", "value": "+49 170 1234567" }]
}
# Benötigte Scopes: contacts.read, contacts.write, directory.read, sharing.manage
curl -fsS -X POST "https://mail.firma.de/api/v1/contacts" \
  -H "Authorization: Bearer smk_0196c3a2..." \
  -H "Content-Type: application/json" \
  -d '{"givenName":"Clara","familyName":"Kunde","emails":[{"type":"work","value":"clara@kunde.example"}]}'
await http.PutAsJsonAsync("/api/v1/sharing/grants", new
{
    kind = "calendar", granteeType = "user", grantee = "bernd@firma.de", permissions = new[] { "read" }
});
Objet de collection (CRUD)

Webhook (API v1)

Clé : id RBAC : User+
4 propriétés

Événements sous forme de POST JSON signé : calendar.event.created, calendar.event.updated, calendar.event.deleted, calendar.invitation.replied, booking.created, booking.rescheduled, booking.cancelled. Signature X-Nextxch-Signature: v1=hex(HMAC-SHA256(secret, X-Nextxch-Timestamp + "." + body)). Nouvelles tentatives après 30 s, 2 min, 10 min, 1 h et 6 h.

GET /api/v1/webhooks Webhooks du locataire
POST /api/v1/webhooks Enregistrer un webhook (secret uniquement dans la réponse)
DELETE /api/v1/webhooks/{id} Supprimer un webhook
GET /api/v1/webhooks/{id}/deliveries Remises récentes
POST /api/v1/webhooks/{id}/test Envoyer un événement de test
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ url string OBLIGATOIRE
Cible HTTPS (les adresses internes sont bloquées).
├─ events string[] OBLIGATOIRE
Événements ; la clé nécessite les portées .read correspondantes.
Enum :"calendar.event.created""calendar.event.updated""calendar.event.deleted""calendar.invitation.replied""booking.created""booking.rescheduled""booking.cancelled""*"
├─ secret string LECTURE SEULE
whsec_..., uniquement à la création ; stocké chiffré.
├─ id (Ereignis) string LECTURE SEULE
evt_... stable par modification, pour la déduplication.
{
  "id": "evt_5c1f0e9a2b7d4c3e8f601a2b",
  "type": "calendar.invitation.replied",
  "createdAtUtc": "2026-12-01T10:15:02Z",
  "tenantId": "0196c3a2-0000-7000-8000-000000000001",
  "data": {
    "calendarId": "0196c3a25f1e7c11:default",
    "hrefName": "0196c3a2@nextxch.ics",
    "title": "Projektstart",
    "startUtc": "2026-12-03T09:00:00Z",
    "attendee": { "email": "bernd@firma.de", "partStat": "ACCEPTED" }
  }
}
# Signatur prüfen (Bash):
# expected="v1=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)"
curl -fsS -X POST "https://mail.firma.de/api/v1/webhooks" \
  -H "Authorization: Bearer smk_0196c3a2..." \
  -H "Content-Type: application/json" \
  -d '{"url":"https://crm.firma.de/hooks/nextxch","events":["calendar.event.created","booking.created"]}'
// ASP.NET Core receiver
app.MapPost("/hooks/nextxch", async (HttpRequest req) =>
{
    using var ms = new MemoryStream();
    await req.Body.CopyToAsync(ms);
    string ts = req.Headers["X-Nextxch-Timestamp"]!;
    byte[] mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), [.. Encoding.ASCII.GetBytes(ts + "."), .. ms.ToArray()]);
    return req.Headers["X-Nextxch-Signature"] == "v1=" + Convert.ToHexStringLower(mac) ? Results.Ok() : Results.Unauthorized();
});
Objet de collection (CRUD)

Tenant

Clé : slug RBAC : ServerAdmin+
11 propriétés

Limite organisationnelle multi-locataire isolant les domaines, les principaux, les quotas de stockage (Gio) et la conservation de conformité GoBD WORM par défaut.

GET /api/admin/cli/objects/Tenant Liste tous les locataires
GET /api/admin/cli/objects/Tenant/{slugOrId} Récupère un locataire par slug ou UUID
POST /api/admin/cli/objects/Tenant Crée un nouveau locataire
PATCH /api/admin/cli/objects/Tenant/{slugOrId} Met à jour les quotas du locataire sur place
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ id guid LECTURE SEULE
Par défaut : UUIDv7
Identifiant interne immuable UUIDv7.
├─ slug string PRIMARY KEY
Clé de locataire unique compatible URL (par ex. "default", "acme").
├─ name string OBLIGATOIRE
Nom d'affichage lisible de l'organisation.
├─ isActive boolean FACULTATIF
Par défaut : true
Activer ou suspendre le locataire.
├─ maxDomains integer FACULTATIF
Par défaut : 100
Nombre maximal de domaines autorisés dans ce locataire.
├─ maxPrincipals integer FACULTATIF
Par défaut : 10000
Nombre maximal de comptes et de listes de distribution.
├─ quotaGb integer FACULTATIF
Par défaut : 100
Plafond de stockage du locataire en Gio.
├─ outboundDailyLimit integer FACULTATIF
Par défaut : 25000
Nombre maximal d'e-mails sortants par jour pour l'ensemble du locataire.
├─ defaultLockMode string FACULTATIF
Par défaut : Normal
Mode de verrouillage GoBD WORM par défaut pour les domaines nouvellement créés.
Enum :"Normal""Governance""Compliance""LegalHold"
├─ defaultRetentionDays integer FACULTATIF
Par défaut : 0
Durée de conservation obligatoire par défaut en jours (3650 = 10 ans GoBD).
├─ createdAtUtc datetime LECTURE SEULE
Horodatage de création en UTC.
{
  "slug": "acme",
  "name": "ACME Logistik GmbH",
  "isActive": true,
  "maxDomains": 50,
  "maxPrincipals": 2500,
  "quotaGb": 250,
  "outboundDailyLimit": 25000,
  "defaultLockMode": "Compliance",
  "defaultRetentionDays": 3650
}
# Mandant anlegen:
nextxch-cli create Tenant \
  --set slug=acme \
  --set name="ACME Logistik GmbH" \
  --set quotaGb=250 \
  --set defaultLockMode=Compliance \
  --set defaultRetentionDays=3650

# Mandant abfragen & Kontingent erhöhen:
nextxch-cli get Tenant acme
nextxch-cli update Tenant acme --set quotaGb=500
curl -fsS -X POST "https://mail.firma.de/api/admin/cli/objects/Tenant" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"slug":"acme","name":"ACME Logistik GmbH","quotaGb":250,"defaultLockMode":"Compliance","defaultRetentionDays":3650}'
var payload = new
{
    slug = "acme",
    name = "ACME Logistik GmbH",
    quotaGb = 250,
    defaultLockMode = "Compliance",
    defaultRetentionDays = 3650
};
using var res = await http.PostAsJsonAsync("/api/admin/cli/objects/Tenant", payload);
Objet de collection (CRUD)

Domain

Clé : name RBAC : TenantAdmin+
19 propriétés

Domaine de messagerie hébergé avec génération parallèle automatique des clés Dual-DKIM (RSA-2048 + Ed25519), politique de conservation GoBD WORM, plus-addressing et limites de débit.

GET /api/admin/cli/objects/Domain Liste tous les domaines
GET /api/admin/cli/objects/Domain/{name} Récupère la configuration du domaine & les sélecteurs DKIM
POST /api/admin/cli/objects/Domain Crée le domaine & génère les clés DKIM RSA-2048 + Ed25519
PATCH /api/admin/cli/objects/Domain/{name} Met à jour le mode DKIM, les quotas ou la conservation en 0 ms
GET /api/admin/cli/dns/{name}?live=true Vérifie les enregistrements DNS en direct ou exporte une zone BIND RFC 1035
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ id guid LECTURE SEULE
Par défaut : UUIDv7
UUIDv7 interne du domaine.
├─ name string PRIMARY KEY
Nom de domaine DNS (par ex. "muster-logistik.de").
├─ tenant string FACULTATIF
Par défaut : default
Slug ou GUID du locataire propriétaire.
├─ isActive boolean FACULTATIF
Par défaut : true
Activer ou suspendre le domaine.
├─ postmasterAddress string FACULTATIF
Par défaut : postmaster@<name>
Adresse de contact postmaster selon RFC 5321.
├─ dkimSigningMode string FACULTATIF
Par défaut : Dual
Mode de signature DKIM actif (Dual signe avec RSA-2048 et Ed25519).
Enum :"Rsa2048""Ed25519""Dual"
├─ dkimRsaSelector string FACULTATIF
Par défaut : s2026
Sélecteur DNS RSA-2048.
├─ dkimRsaDnsName string LECTURE SEULE
Par défaut : s2026._domainkey.<name>
FQDN _domainkey RSA-2048 calculé.
├─ dkimEd25519Selector string FACULTATIF
Par défaut : ed2026
Sélecteur DNS Ed25519.
├─ dkimEd25519DnsName string LECTURE SEULE
Par défaut : ed2026._domainkey.<name>
FQDN _domainkey Ed25519 calculé.
├─ retentionLockMode string FACULTATIF
Par défaut : Normal
Mode de verrouillage de l'archive GoBD WORM pour ce domaine.
Enum :"Normal""Governance""Compliance""LegalHold"
├─ retentionDays integer FACULTATIF
Par défaut : 0
Durée de conservation obligatoire en jours (3650 = 10 ans GoBD).
├─ quotaGb integer FACULTATIF
Par défaut : 50
Plafond de stockage du domaine en Gio.
├─ maxMessageSizeMb integer FACULTATIF
Par défaut : 50
Taille maximale des messages en MiB.
├─ maxAccounts integer FACULTATIF
Par défaut : 1000
Nombre maximal de boîtes aux lettres dans ce domaine.
├─ outboundDailyLimit integer FACULTATIF
Par défaut : 5000
Limite quotidienne d'envoi sortant pour ce domaine.
├─ outboundHourlyLimit integer FACULTATIF
Par défaut : 1000
Limite horaire d'envoi sortant pour ce domaine.
├─ usePlusAddressing boolean FACULTATIF
Par défaut : true
Active le plus-addressing RFC 5233 (user+tag@domain).
├─ createdAtUtc datetime LECTURE SEULE
Horodatage de création en UTC.
{
  "name": "muster-logistik.de",
  "tenant": "default",
  "isActive": true,
  "postmasterAddress": "postmaster@muster-logistik.de",
  "dkimSigningMode": "Dual",
  "dkimRsaSelector": "s2026",
  "dkimEd25519Selector": "ed2026",
  "retentionLockMode": "Compliance",
  "retentionDays": 3650,
  "quotaGb": 100,
  "maxMessageSizeMb": 50,
  "maxAccounts": 500,
  "outboundDailyLimit": 10000,
  "outboundHourlyLimit": 2000,
  "usePlusAddressing": true
}
# Domain mit Dual-DKIM und 10 Jahren GoBD-Compliance anlegen:
nextxch-cli create Domain \
  --set name=muster-logistik.de \
  --set dkimSigningMode=Dual \
  --set retentionLockMode=Compliance \
  --set retentionDays=3650

# Fertige RFC-1035 BIND-Zonendatei exportieren & DNS live prüfen:
nextxch-cli dns muster-logistik.de --bind > muster-logistik.de.zone
nextxch-cli dns muster-logistik.de --live
curl -fsS -X PATCH "https://mail.firma.de/api/admin/cli/objects/Domain/muster-logistik.de" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"dkimSigningMode":"Dual","retentionLockMode":"Compliance","retentionDays":3650}'
using var res = await http.PatchAsJsonAsync(
    "/api/admin/cli/objects/Domain/muster-logistik.de",
    new { dkimSigningMode = "Dual", quotaGb = 200 });
res.EnsureSuccessStatusCode();
Objet de collection (CRUD)

Account (Account/User & Account/List)

Clé : email RBAC : TenantAdmin+
18 propriétés

Représente une boîte aux lettres utilisateur (@type="User", avec IMAP/JMAP/CalDAV/CardDAV/ActiveSync) ou une liste de distribution (@type="List" avec members[]).

GET /api/admin/cli/objects/Account?filter=role=TenantAdmin Liste/filtre les comptes et listes de distribution
POST /api/admin/cli/objects/Account/User Crée une nouvelle boîte aux lettres utilisateur avec les dossiers RFC 6154
POST /api/admin/cli/objects/Account/List Crée une nouvelle liste de distribution
PATCH /api/admin/cli/objects/Account/{email} Met à jour le quota, le transfert, l'absence ou le mot de passe
DELETE /api/admin/cli/objects/Account/{email} Supprime un compte de manière réversible (soft delete)
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ id guid LECTURE SEULE
Par défaut : UUIDv7
UUIDv7 interne du principal.
├─ @type string FACULTATIF
Par défaut : User
Variante de compte : boîte aux lettres utilisateur (User) ou groupe de distribution (List).
Enum :"User""List"
├─ email string PRIMARY KEY
Adresse e-mail principale de la boîte aux lettres ou de la liste.
├─ displayName string FACULTATIF
Par défaut : Lokalteil
Nom d'affichage visible dans le Webmail, la GAL et les en-têtes From.
├─ password string ÉCRITURE SEULE (SECRET)
Mot de passe en clair lors de la création/mise à jour (immédiatement haché avec Argon2id).
├─ role string FACULTATIF
Par défaut : User
Rôle RBAC d'administration.
Enum :"User""ComplianceAuditor""TenantAdmin""ServerAdmin"
├─ isActive boolean FACULTATIF
Par défaut : true
Activer ou suspendre le compte.
├─ quotaMb integer FACULTATIF
Par défaut : 5120
Quota de stockage de la boîte aux lettres en MiB (5120 = 5 GiB).
├─ usedBytes long LECTURE SEULE
Par défaut : 0
Espace de stockage actuellement utilisé par la boîte aux lettres, en octets.
├─ outboundDailyLimit integer FACULTATIF
Par défaut : 500
Limite quotidienne de messages sortants pour ce compte.
├─ forwardAddress string FACULTATIF
E-mail de destination de transfert facultatif, compatible SRS.
├─ forwardKeepOriginal boolean FACULTATIF
Par défaut : true
Conserver une copie locale dans la Boîte de réception lorsque le transfert est actif.
├─ vacationEnabled boolean FACULTATIF
Par défaut : false
Activer le répondeur d'absence automatique.
├─ vacationSubject string FACULTATIF
Objet de la réponse automatique d'absence.
├─ vacationBody string ÉCRITURE SEULE (SECRET)
Corps du texte de la réponse automatique d'absence.
├─ twoFactorEnabled boolean LECTURE SEULE
Par défaut : false
Indique si la 2FA TOTP est active.
├─ members string[] FACULTATIF
Par défaut : []
Adresses e-mail des membres (pour @type="List").
├─ createdAtUtc datetime LECTURE SEULE
Horodatage de création en UTC.
{
  "@type": "User",
  "email": "anna.weber@muster-logistik.de",
  "displayName": "Anna Weber (IT-Leitung)",
  "password": "SicheresStartPasswort!2026",
  "role": "TenantAdmin",
  "isActive": true,
  "quotaMb": 15360,
  "outboundDailyLimit": 1000,
  "forwardAddress": null,
  "forwardKeepOriginal": true,
  "vacationEnabled": false,
  "vacationSubject": "Abwesenheitsnotiz: Anna Weber",
  "vacationBody": "Ich bin ab Montag wieder erreichbar."
}
# Benutzerpostfach (15 GiB Quota) anlegen:
nextxch-cli create Account/User \
  --set email=anna.weber@muster-logistik.de \
  --set displayName="Anna Weber" \
  --set password="SicheresStartPasswort!2026" \
  --set quotaMb=15360

# Verteilerliste anlegen:
nextxch-cli create Account/List \
  --set email=team@muster-logistik.de \
  --set displayName="Disposition & Einkauf" \
  --set members=anna.weber@muster-logistik.de,dispo@muster-logistik.de
curl -fsS -X POST "https://mail.firma.de/api/admin/cli/objects/Account/User" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"anna.weber@muster-logistik.de","displayName":"Anna Weber","password":"SicheresStartPasswort!2026","quotaMb":15360}'
await http.PostAsJsonAsync("/api/admin/cli/objects/Account/User", new
{
    email = "anna.weber@muster-logistik.de",
    displayName = "Anna Weber",
    password = "SicheresStartPasswort!2026",
    quotaMb = 15360
});
Objet de collection (CRUD)

Alias

Clé : address RBAC : TenantAdmin+
7 propriétés

Alias e-mail secondaire ou alias de confidentialité masqué jetable, acheminé vers une boîte aux lettres Account principale ou une liste de distribution.

GET /api/admin/cli/objects/Alias Liste tous les alias et e-mails masqués
POST /api/admin/cli/objects/Alias Crée un nouvel alias acheminé vers une boîte aux lettres cible
PATCH /api/admin/cli/objects/Alias/{address} Active/suspend un alias ou met à jour sa cible
DELETE /api/admin/cli/objects/Alias/{address} Supprime l'alias
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ address string PRIMARY KEY
Adresse e-mail de l'alias.
├─ target string OBLIGATOIRE
Adresse e-mail du compte principal cible.
├─ principalId guid LECTURE SEULE
UUID du principal cible résolu.
├─ description string FACULTATIF
Par défaut : ""
Note sur l'usage ou libellé du service externe (également affiché dans l'éditeur du Webmail).
├─ isMasked boolean FACULTATIF
Par défaut : false
Marque l'alias comme alias masqué de confidentialité.
├─ isEnabled boolean FACULTATIF
Par défaut : true
Lorsque false, le courrier adressé à cet alias est rejeté sans libérer l'adresse.
├─ createdAtUtc datetime LECTURE SEULE
Horodatage de création en UTC.
{
  "address": "shop-8f2k@muster-logistik.de",
  "target": "anna.weber@muster-logistik.de",
  "description": "Fachbuchhandlung / Online-Bestellung",
  "isMasked": true,
  "isEnabled": true
}
# Masked-Email-Alias anlegen:
nextxch-cli create Alias \
  --set address=shop-8f2k@muster-logistik.de \
  --set target=anna.weber@muster-logistik.de \
  --set description="Fachbuchhandlung" \
  --set isMasked=true

# Alias bei Spam-Aufkommen mit 1 Befehl stummschalten:
nextxch-cli update Alias shop-8f2k@muster-logistik.de --set isEnabled=false
curl -fsS -X POST "https://mail.firma.de/api/admin/cli/objects/Alias" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"address":"vertrieb@muster-logistik.de","target":"anna.weber@muster-logistik.de","description":"Vertrieb & Angebote"}'
await http.PostAsJsonAsync("/api/admin/cli/objects/Alias", new
{
    address = "vertrieb@muster-logistik.de",
    target = "anna.weber@muster-logistik.de",
    description = "Vertrieb & Angebote",
    isEnabled = true
});
Objet de collection (CRUD)

ApiKey

Clé : id RBAC : TenantAdmin+
11 propriétés

Clé API à portée limitée, immuable et restreinte par RBAC et CIDR (smk_<id>_<secret>) pour /api/v1/send, SCIM 2.0 ou nextxch-cli.

GET /api/admin/cli/objects/ApiKey Liste les clés API actives (secret omis)
POST /api/admin/cli/objects/ApiKey Génère une nouvelle clé d'API et renvoie apiKey (smk_...) une seule fois
DELETE /api/admin/cli/objects/ApiKey/{id} Révoque immédiatement la clé d'API
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ id guid LECTURE SEULE
Par défaut : UUIDv7
UUID unique de la clé (utilisé pour la révocation via DELETE).
├─ apiKey string CRÉATION UNIQUEMENT
Par défaut : smk_<id>_<secret>
Jeton Bearer complet renvoyé UNE SEULE fois lors de la création.
├─ label string OBLIGATOIRE
Libellé lisible (par ex. "Ansible GitOps" ou "Shopware 6").
├─ role string FACULTATIF
Par défaut : TenantAdmin
Portée des privilèges (User est strictement limité à POST /api/v1/send).
Enum :"User""ComplianceAuditor""TenantAdmin""ServerAdmin"
├─ senderAddress string FACULTATIF
Adresse d'expéditeur autorisée (obligatoire lorsque role="User").
├─ defaultCategory string FACULTATIF
Par défaut : transactional
Catégorie d'envoi par défaut pour cette clé.
Enum :"critical""transactional""notification""marketing"
├─ allowedCidrs string[] FACULTATIF
Par défaut : [] (Alle IPs)
Réseaux CIDR source IPv4/IPv6 autorisés.
├─ expiresInDays integer ÉCRITURE SEULE (SECRET)
Par défaut : null (Kein Ablauf)
Expiration automatique facultative, en jours à compter de la création.
├─ expiresAtUtc datetime LECTURE SEULE
Horodatage d'expiration UTC calculé.
├─ outboundDailyLimit integer FACULTATIF
Par défaut : 10000
Limite quotidienne d'envoi sortant pour cette clé.
├─ isActive boolean LECTURE SEULE
Par défaut : true
Statut actif.
{
  "label": "Shopware 6 ERP Rechnungsversand",
  "role": "User",
  "senderAddress": "rechnung@muster-logistik.de",
  "defaultCategory": "transactional",
  "allowedCidrs": ["10.20.10.15/32", "2a01:4f8:120:44aa::/64"],
  "expiresInDays": 180,
  "outboundDailyLimit": 10000
}
# CIDR-geschützten Key für Ansible / nextxch-cli erstellen:
nextxch-cli create ApiKey \
  --set label="GitLab CI / Ansible" \
  --set role=TenantAdmin \
  --set allowedCidrs=10.20.0.0/16 \
  --set expiresInDays=90

# Schlüssel widerrufen:
nextxch-cli delete ApiKey 0195f2a0-8b14-7000-9a10-001122334455
curl -fsS -X POST "https://mail.firma.de/api/admin/cli/objects/ApiKey" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"label":"Shopware 6","role":"User","senderAddress":"rechnung@muster-logistik.de","allowedCidrs":["10.20.10.15/32"],"expiresInDays":180}'
var res = await http.PostAsJsonAsync("/api/admin/cli/objects/ApiKey", new
{
    label = "Shopware 6",
    role = "User",
    senderAddress = "rechnung@muster-logistik.de",
    allowedCidrs = new[] { "10.20.10.15/32" },
    expiresInDays = 180
});
var created = await res.Content.ReadFromJsonAsync<JsonElement>();
string oneTimeSecretKey = created.GetProperty("apiKey").GetString()!;
Objet de collection (CRUD)

Route

Clé : domain RBAC : ServerAdmin+
10 propriétés

Routage SMTP sortant par domaine ou exception de certificat TLS (par ex. StartTlsIgnoreCert pour les hôtes MX partenaires aux certificats non concordants, ou connecteur smart host).

GET /api/admin/cli/objects/Route Liste toutes les routes sortantes personnalisées
POST /api/admin/cli/objects/Route Crée une route de domaine personnalisée ou une substitution TLS
PATCH /api/admin/cli/objects/Route/{domain} Met à jour l'hôte cible, le port ou le mode TLS
DELETE /api/admin/cli/objects/Route/{domain} Supprime la route personnalisée
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ domain string PRIMARY KEY
Domaine du destinataire (par ex. "neff.me" ou "*.partner-holding.eu").
├─ targetHost string FACULTATIF
Par défaut : "" (Auto DNS-MX)
Nom d'hôte ou IP du relais. Laissez vide pour résoudre automatiquement le MX de destination via DNS.
├─ targetPort integer FACULTATIF
Par défaut : 25
Port TCP de destination (25, 587 ou 465).
├─ tlsMode string FACULTATIF
Par défaut : StartTlsOptional
Mode TLS sortant et de validation X.509 pour ce domaine.
Enum :"StartTlsOptional""StartTlsRequired""StartTlsIgnoreCert""ImplicitTls""ImplicitTlsIgnoreCert""None"
├─ username string FACULTATIF
Nom d'utilisateur SMTP AUTH facultatif.
├─ password string ÉCRITURE SEULE (SECRET)
Mot de passe SMTP AUTH facultatif.
├─ hasPassword boolean LECTURE SEULE
Par défaut : false
Indique si un mot de passe est enregistré.
├─ clearPassword boolean ÉCRITURE SEULE (SECRET)
Par défaut : false
Définir sur true pour effacer le mot de passe enregistré.
├─ description string FACULTATIF
Note d'audit de l'administrateur.
├─ enabled boolean FACULTATIF
Par défaut : true
Activer ou suspendre la route.
{
  "domain": "neff.me",
  "targetHost": "",
  "targetPort": 25,
  "tlsMode": "StartTlsIgnoreCert",
  "username": null,
  "description": "Shared-Hosting-MX mit abweichendem Zertifikatsnamen (Auto DNS-MX)",
  "enabled": true
}
# Partner-Domain mit fehlerhaftem MX-Zertifikat per Auto-DNS-MX freigeben:
nextxch-cli create Route \
  --set domain=neff.me \
  --set tlsMode=StartTlsIgnoreCert \
  --set description="TLS NameMismatch auf Partner-MX erlauben"

# Internes ERP-Subnetz an lokalen Connector routen:
nextxch-cli create Route \
  --set domain=erp.firma.intern \
  --set targetHost=10.40.2.15 \
  --set targetPort=25
curl -fsS -X POST "https://mail.firma.de/api/admin/cli/objects/Route" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"domain":"neff.me","targetHost":"","targetPort":25,"tlsMode":"StartTlsIgnoreCert","enabled":true}'
await http.PostAsJsonAsync("/api/admin/cli/objects/Route", new
{
    domain = "neff.me",
    targetHost = "",
    targetPort = 25,
    tlsMode = "StartTlsIgnoreCert",
    enabled = true
});
Politique singleton

TransactionalSend (Singleton)

Clé : singleton RBAC : TenantAdmin+
11 propriétés

Pilote la montée en charge IP progressive sur 28 jours, les catégories de messages et les quotas horaires/quotidiens par fournisseur destinataire (Google, Microsoft, Yahoo/AOL, FAI allemands, iCloud, *).

GET /api/admin/cli/objects/TransactionalSend/singleton Récupère la montée en charge IP active & la politique de quotas fournisseurs
PATCH /api/admin/cli/objects/TransactionalSend/singleton Met à jour le jour de montée en charge, le préréglage ou les quotas fournisseur
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ enabled boolean FACULTATIF
Par défaut : true
Active la gouvernance des catégories et des quotas fournisseurs.
├─ warmingEnabled boolean FACULTATIF
Par défaut : false
Active la montée en charge progressive quotidienne du préchauffage d'IP.
├─ startAtWarmingDay integer ÉCRITURE SEULE (SECRET)
Par défaut : 1
Saute au jour N du calendrier de préchauffage ou le réinitialise (1..365).
├─ currentWarmingDay integer LECTURE SEULE
Jour actif actuel du programme de montée en charge.
├─ currentWarmingPercent integer LECTURE SEULE
Pourcentage actuellement actif des quotas du fournisseur cible.
├─ warmingDailyRampPercents integer[] FACULTATIF
Par défaut : [5,7,10,...,100]
Tableau des pourcentages quotidiens pour la montée en charge du préchauffage.
├─ maxDeferHours integer FACULTATIF
Par défaut : 48
Fenêtre d'anticipation maximale en heures pour l'étalement DeferQueue.
├─ defaultCategory string FACULTATIF
Par défaut : transactional
Catégorie de repli lorsqu'elle est omise dans les appels API.
Enum :"critical""transactional""notification""marketing"
├─ preset string ÉCRITURE SEULE (SECRET)
Sélecteur de préréglage en une étape.
Enum :"warming_28d""warming_14d""production_100"
├─ categories CategoryPolicy[] FACULTATIF
Par défaut : 4 Kategorien
Tableau d'objets de politique de catégorie.
├─ providers ProviderQuota[] FACULTATIF
Par défaut : 6 Provider-Gruppen
Tableau de définitions de quotas par fournisseur destinataire.
{
  "enabled": true,
  "warmingEnabled": true,
  "startAtWarmingDay": 1,
  "maxDeferHours": 48,
  "defaultCategory": "transactional",
  "preset": "warming_28d"
}
# 28-Tage IP-Warming aktivieren:
nextxch-cli update TransactionalSend --set preset=warming_28d

# Direkt auf Warming-Tag 14 vorspringen:
nextxch-cli update TransactionalSend --set startAtWarmingDay=14
curl -fsS -X PATCH "https://mail.firma.de/api/admin/cli/objects/TransactionalSend/singleton" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"preset":"warming_28d","enabled":true}'
await http.PatchAsJsonAsync(
    "/api/admin/cli/objects/TransactionalSend/singleton",
    new { preset = "warming_28d", enabled = true });
Politique singleton

ReputationShield (Singleton)

Clé : singleton RBAC : TenantAdmin+
20 propriétés

Protection optionnelle de la réputation sortante : liste de suppression automatique des rebonds/plaintes, désabonnement en un clic RFC 8058, disjoncteur de rebonds, temporisation adaptative 421 vis-à-vis des FAI et webhooks HMAC-SHA256.

GET /api/admin/cli/objects/ReputationShield/singleton Récupère la configuration actuelle de Reputation Shield
PATCH /api/admin/cli/objects/ReputationShield/singleton Met à jour les indicateurs de protection ou applique un préréglage
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ preset string ÉCRITURE SEULE (SECRET)
Applique un profil de protection complet en une seule étape.
Enum :"off""recommended""strict_bulk"
├─ enableSuppressionPreCheck boolean FACULTATIF
Par défaut : false
Filtre les adresses supprimées avant l'envoi.
├─ enforceSuppressionOnSmtpRelay boolean FACULTATIF
Par défaut : false
Applique aussi la liste de suppression lors de la soumission SMTP.
├─ allSuppressedBehavior string FACULTATIF
Par défaut : reject_422
Mode de réponse lorsque tous les destinataires sont supprimés.
Enum :"reject_422""accept_noop"
├─ autoSuppressHardBounces boolean FACULTATIF
Par défaut : false
Supprime automatiquement les rebonds permanents 5xx (hard bounces).
├─ hardBounceSuppressionDays integer FACULTATIF
Par défaut : 180
Jours de conservation des suppressions pour rebond définitif.
├─ autoSuppressConsecutiveSoftBounces boolean FACULTATIF
Par défaut : false
Fait passer les rebonds temporaires 4xx répétés dans la liste de suppression.
├─ softBounceEscalationThreshold integer FACULTATIF
Par défaut : 5
Rebonds temporaires consécutifs avant suppression.
├─ autoSuppressFblComplaints boolean FACULTATIF
Par défaut : false
Analyse automatiquement les rapports d'abus ARF RFC 5965 entrants.
├─ enableOneClickUnsubscribe boolean FACULTATIF
Par défaut : false
Injecte des en-têtes List-Unsubscribe RFC 8058 signés DKIM.
├─ oneClickUnsubscribeCategories string[] FACULTATIF
Par défaut : ["marketing","notification"]
Catégories recevant les en-têtes de désabonnement RFC 8058.
├─ enableBounceCircuitBreaker boolean FACULTATIF
Par défaut : false
Suspend les envois non critiques si le taux de rebonds/plaintes sur 24 h augmente fortement.
├─ maxHardBouncePercent number FACULTATIF
Par défaut : 5.0
Plafond du taux de rebonds permanents sur 24 h, en pourcentage.
├─ maxComplaintPercent number FACULTATIF
Par défaut : 0.1
Plafond du taux de plaintes ARF sur 24 h, en pourcentage.
├─ allowCriticalWhenTripped boolean FACULTATIF
Par défaut : true
Autorise les e-mails 2FA critiques même lorsque le disjoncteur est déclenché.
├─ enableAdaptiveIspBackoff boolean FACULTATIF
Par défaut : false
Temporisation automatique lorsque le MX distant renvoie une limitation 421/451.
├─ ispCooldownMinutes integer FACULTATIF
Par défaut : 15
Durée de refroidissement en minutes.
├─ webhookEnabled boolean FACULTATIF
Par défaut : false
Active les webhooks signés HMAC-SHA256.
├─ webhookUrl string FACULTATIF
URL HTTPS de destination pour les webhooks.
├─ webhookSecret string ÉCRITURE SEULE (SECRET)
Secret HMAC-SHA256 pour X-Nextxch-Signature.
{
  "preset": "recommended",
  "enableSuppressionPreCheck": true,
  "autoSuppressHardBounces": true,
  "hardBounceSuppressionDays": 180,
  "enableOneClickUnsubscribe": true,
  "oneClickUnsubscribeCategories": ["marketing", "notification"],
  "enableBounceCircuitBreaker": true,
  "maxHardBouncePercent": 5.0,
  "maxComplaintPercent": 0.1,
  "allowCriticalWhenTripped": true,
  "enableAdaptiveIspBackoff": true,
  "webhookEnabled": true,
  "webhookUrl": "https://erp.firma.de/webhooks/nextxch"
}
# Empfohlenen Reputationsschutz aktivieren:
nextxch-cli update ReputationShield --set preset=recommended

# Webhook-Ziel konfigurieren:
nextxch-cli update ReputationShield \
  --set webhookEnabled=true \
  --set webhookUrl=https://erp.firma.de/webhooks/nextxch \
  --set webhookSecret=whsec_9f8e7d6c5b4a
curl -fsS -X PATCH "https://mail.firma.de/api/admin/cli/objects/ReputationShield/singleton" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"preset":"recommended"}'
await http.PatchAsJsonAsync(
    "/api/admin/cli/objects/ReputationShield/singleton",
    new { preset = "recommended" });
Politique singleton

SpamGuard (Singleton)

Clé : singleton RBAC : ServerAdmin+
15 propriétés

Défense entrante multicouche : vérification SPF/DKIM/DMARC/ARC, Smart Greylisting (/24 IPv4 & /64 IPv6), classifieur ML en ligne (FTRL-Proximal), zones DNSBL, listes d'autorisation/de blocage et analyse TCP ClamAV.

GET /api/admin/cli/objects/SpamGuard/singleton Récupère les seuils SpamGuard & les compteurs d'apprentissage
PATCH /api/admin/cli/objects/SpamGuard/singleton Met à jour sur place les scores, DNSBL, listes d'autorisation ou ClamAV
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ enableEmailAuthChecks boolean FACULTATIF
Par défaut : true
Vérifie SPF, DKIM, DMARC et ARC en entrée.
├─ enableGreylisting boolean FACULTATIF
Par défaut : true
Greylisting intelligent adaptatif pour les expéditeurs inconnus qui écrivent pour la première fois.
├─ greylistRetryDelaySeconds integer FACULTATIF
Par défaut : 240
Délai minimal avant nouvelle tentative, en secondes (par défaut : 240 s).
├─ junkScoreThreshold number FACULTATIF
Par défaut : 4.5
Seuil de score pour le classement dans le dossier Courrier indésirable.
├─ rejectScoreThreshold number FACULTATIF
Par défaut : 9.0
Seuil de score pour le rejet SMTP 550 définitif.
├─ tagSubjectOnJunk boolean FACULTATIF
Par défaut : false
Ajouter un préfixe à l'objet des messages indésirables.
├─ subjectTagPrefix string FACULTATIF
Par défaut : "[SPAM] "
Chaîne du préfixe d'objet.
├─ enableStructuralHeuristics boolean FACULTATIF
Par défaut : true
Évalue les heuristiques structurelles MIME/HTML.
├─ dnsblZones string[] FACULTATIF
Par défaut : ["bl.spamcop.net","ix.dnsbl.manitu.net"]
Zones DNSBL interrogées.
├─ dnsblHitScore number FACULTATIF
Par défaut : 3.5
Score ajouté par résultat DNSBL.
├─ allowedSendersOrDomains string[] FACULTATIF
Par défaut : []
Liste d'autorisation explicite d'adresses e-mail ou de domaines d'expéditeurs.
├─ blockedSendersOrDomains string[] FACULTATIF
Par défaut : []
Liste de blocage explicite d'adresses e-mail ou de domaines d'expéditeurs.
├─ clamAvEnabled boolean FACULTATIF
Par défaut : false
Active l'analyse ClamAV via le démon TCP INSTREAM.
├─ clamAvHost string FACULTATIF
Par défaut : "127.0.0.1"
Hôte ou IP du démon ClamAV.
├─ clamAvPort integer FACULTATIF
Par défaut : 3310
Port TCP du démon ClamAV.
{
  "enableEmailAuthChecks": true,
  "enableGreylisting": true,
  "greylistRetryDelaySeconds": 240,
  "junkScoreThreshold": 4.5,
  "rejectScoreThreshold": 9.0,
  "enableStructuralHeuristics": true,
  "dnsblZones": ["bl.spamcop.net", "ix.dnsbl.manitu.net"],
  "dnsblHitScore": 3.5,
  "allowedSendersOrDomains": ["datev.de", "partner-spedition.de"],
  "blockedSendersOrDomains": [],
  "clamAvEnabled": false,
  "clamAvHost": "127.0.0.1",
  "clamAvPort": 3310
}
# Partner-Domains auf die Allowlist setzen:
nextxch-cli update SpamGuard \
  --set allowedSendersOrDomains=datev.de,partner-spedition.de

# Schwellenwerte anpassen:
nextxch-cli update SpamGuard --set junkScoreThreshold=4.5 --set rejectScoreThreshold=9.0
curl -fsS -X PATCH "https://mail.firma.de/api/admin/cli/objects/SpamGuard/singleton" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"junkScoreThreshold":4.5,"rejectScoreThreshold":9.0,"allowedSendersOrDomains":["datev.de"]}'
await http.PatchAsJsonAsync(
    "/api/admin/cli/objects/SpamGuard/singleton",
    new { junkScoreThreshold = 4.5, rejectScoreThreshold = 9.0 });
Politique singleton

SmtpQueue (Singleton)

Clé : singleton RBAC : ServerAdmin+
19 propriétés

Contrôle la file d'attente de remise MX sortante, la concurrence des workers, les intervalles de nouvelle tentative, la limitation de débit par FAI, la validation TLS sortante, le smart host global et les limites ESMTP entrantes.

GET /api/admin/cli/objects/SmtpQueue/singleton Récupère tous les paramètres de file d'attente, TLS et ESMTP
PATCH /api/admin/cli/objects/SmtpQueue/singleton Met à jour les workers de la file d'attente, le calendrier de nouvelles tentatives ou les limites ESMTP en 0 ms
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ outboundQueuePaused boolean FACULTATIF
Par défaut : false
Suspend la remise MX sortante tout en continuant d'accepter le courrier.
├─ outboundWorkerConcurrency integer FACULTATIF
Par défaut : 8
Nombre de workers de remise sortante en parallèle.
├─ maxDeliveryAttempts integer FACULTATIF
Par défaut : 10
Nombre maximal de tentatives de remise avant le rebond définitif.
├─ retryScheduleMinutes integer[] FACULTATIF
Par défaut : [5,15,60,240,720]
Calendrier d'espacement des nouvelles tentatives, en minutes.
├─ maxConcurrentConnectionsPerMxDomain integer FACULTATIF
Par défaut : 4
Nombre maximal de connexions simultanées par domaine MX de destination.
├─ strictThrottleDomains string[] FACULTATIF
Par défaut : ["t-online.de","gmx.de","web.de","yahoo.com"]
Domaines soumis à une limitation stricte de la concurrence.
├─ strictThrottleMaxConcurrency integer FACULTATIF
Par défaut : 2
Nombre maximal de connexions simultanées pour strictThrottleDomains.
├─ outboundConnectTimeoutSeconds integer FACULTATIF
Par défaut : 30
Délai de connexion TCP/TLS sortante en secondes.
├─ outboundBindAddress string FACULTATIF
Par défaut : "" (Auto)
Adresse IP source locale de liaison facultative.
├─ outboundUseStartTls boolean FACULTATIF
Par défaut : true
Utiliser STARTTLS sur les connexions MX sortantes.
├─ requireOutboundTls boolean FACULTATIF
Par défaut : false
Interrompre la remise si le MX distant ne propose pas TLS.
├─ outboundTlsCertValidation string FACULTATIF
Par défaut : Opportunistic
Mode global de validation des certificats X.509 sortants.
Enum :"Opportunistic""IgnoreErrors""Strict"
├─ outboundSmartHost string FACULTATIF
Par défaut : ""
Hôte de relais amont global facultatif.
├─ outboundSmartPort integer FACULTATIF
Par défaut : 587
Port TCP du smart host global.
├─ maxMessageSizeMb long FACULTATIF
Par défaut : 50
Limite ESMTP SIZE globale en MiB.
├─ maxRecipientsPerTransaction integer FACULTATIF
Par défaut : 100
Nombre maximal de commandes RCPT TO par transaction.
├─ maxConnectionsPerIp integer FACULTATIF
Par défaut : 20
Nombre maximal de connexions entrantes simultanées par IP.
├─ tarpitDelayMs integer FACULTATIF
Par défaut : 1500
Délai de tarpit en ms en cas d'erreurs d'authentification ou de commande.
├─ requireTlsForAuth boolean FACULTATIF
Par défaut : true
Exige le chiffrement TLS avant d'autoriser SMTP AUTH.
{
  "outboundQueuePaused": false,
  "outboundWorkerConcurrency": 8,
  "maxDeliveryAttempts": 10,
  "retryScheduleMinutes": [5, 15, 60, 240, 720],
  "maxConcurrentConnectionsPerMxDomain": 4,
  "strictThrottleDomains": ["t-online.de", "gmx.de", "web.de", "yahoo.com"],
  "strictThrottleMaxConcurrency": 2,
  "outboundUseStartTls": true,
  "requireOutboundTls": false,
  "outboundTlsCertValidation": "Opportunistic",
  "maxMessageSizeMb": 50,
  "maxRecipientsPerTransaction": 100,
  "tarpitDelayMs": 1500,
  "requireTlsForAuth": true
}
# Outbound-TLS auf Opportunistic (RFC 7435) setzen & MaxMessageSize auf 75 MiB erhöhen:
nextxch-cli update SmtpQueue \
  --set outboundTlsCertValidation=Opportunistic \
  --set maxMessageSizeMb=75
curl -fsS -X PATCH "https://mail.firma.de/api/admin/cli/objects/SmtpQueue/singleton" \
  -H "Authorization: Bearer smk_..." \
  -H "Content-Type: application/json" \
  -d '{"outboundTlsCertValidation":"Opportunistic","maxMessageSizeMb":75}'
await http.PatchAsJsonAsync(
    "/api/admin/cli/objects/SmtpQueue/singleton",
    new { outboundTlsCertValidation = "Opportunistic", maxMessageSizeMb = 75 });
Politique singleton

TransportRules & Server (Singletons)

Clé : singleton RBAC : ServerAdmin+
11 propriétés

Clause de non-responsabilité juridique à l'échelle de l'organisation (insérée avant la signature DKIM), script Sieve entrant global, seuils d'auto-ban Fail2Ban et ServerHostName canonique.

GET /api/admin/cli/objects/TransportRules/singleton Récupère les paramètres de clause de non-responsabilité, de Sieve global et de bannissement automatique
PATCH /api/admin/cli/objects/TransportRules/singleton Met à jour les règles de transport sur place
GET /api/admin/cli/objects/Server/singleton Récupère le ServerHostName, le moteur de stockage, la profondeur de la file d'attente et l'état de la licence
PATCH /api/admin/cli/objects/Server/singleton Met à jour le ServerHostName canonique en 0 ms
{ } Schéma d'objet & propriétés typées application/json · camelCase
├─ outboundDisclaimerEnabled boolean FACULTATIF
Par défaut : false
Ajoute un pied de page d'entreprise obligatoire aux messages sortants.
├─ outboundDisclaimerExternalOnly boolean FACULTATIF
Par défaut : true
Ajouter le pied de page uniquement lors d'envois vers des domaines externes.
├─ outboundDisclaimerText string FACULTATIF
Par défaut : ""
Pied de page juridique en texte brut.
├─ outboundDisclaimerHtml string FACULTATIF
Par défaut : ""
Pied de page légal HTML.
├─ inboundSystemSieve string FACULTATIF
Par défaut : ""
Script Sieve RFC 5228 global exécuté avant les scripts des utilisateurs.
├─ autoBanThreshold integer FACULTATIF
Par défaut : 8
Échecs de connexion avant bannissement automatique de l'IP.
├─ autoBanDurationMinutes integer FACULTATIF
Par défaut : 30
Durée du bannissement IP automatique en minutes.
├─ serverHostName string FACULTATIF
Par défaut : mail.<domain>
FQDN canonique pour EHLO, TLS et Autodiscover (objet : Server).
├─ storageProvider string LECTURE SEULE
Par défaut : Sqlite | SqlServer | Postgres
Moteur de stockage actif (objet : Server).
├─ activeQueueCount integer LECTURE SEULE
Par défaut : 0
Nombre actuel de messages dans la file d'attente sortante (objet : Server).
├─ licenseState string LECTURE SEULE
Par défaut : EnterpriseActive
État de la licence (objet : Server).
{
  "outboundDisclaimerEnabled": true,
  "outboundDisclaimerExternalOnly": true,
  "outboundDisclaimerText": "-- \nMuster Logistik GmbH · Industriestraße 18 · 70565 Stuttgart · HRB 774819",
  "outboundDisclaimerHtml": "<p style='font-size:11px;color:#64748b'><strong>Muster Logistik GmbH</strong> &middot; 70565 Stuttgart &middot; HRB 774819</p>",
  "inboundSystemSieve": "require [\"fileinto\"];\nif header :contains \"X-Spam-Flag\" \"YES\" { fileinto \"Junk\"; }",
  "autoBanThreshold": 8,
  "autoBanDurationMinutes": 30
}
# Pflicht-Impressum & Auto-Ban aktualisieren:
nextxch-cli update TransportRules \
  --set outboundDisclaimerEnabled=true \
  --set autoBanThreshold=8

# Kanonischen Server-Hostnamen abfragen oder setzen:
nextxch-cli get Server
nextxch-cli update Server --set serverHostName=mail.muster-logistik.de
curl -fsS -X GET "https://mail.firma.de/api/admin/cli/objects/Server/singleton" \
  -H "Authorization: Bearer smk_..."
var status = await http.GetFromJsonAsync<JsonElement>("/api/admin/cli/objects/Server/singleton");
Console.WriteLine($"Host: {status.GetProperty("serverHostName")}, Queue: {status.GetProperty("activeQueueCount")}");