Capitolo 14 Client, groupware & CLI Admin: #pApiAuth

Riferimento completo dei campi API & CLI: tutti i 12 tipi di oggetto, endpoint REST & POST /api/v1/send

Questo riferimento documenta ogni proprietà JSON, tipo di dato, valore enum, impostazione predefinita e ruolo RBAC minimo di tutti i 12 oggetti di gestione (/api/admin/cli/objects/{type} & nextxch-cli), nonché l'invio di e-mail transazionali (POST /api/v1/send). Usare la casella di filtro istantaneo sopra la tabella per cercare qualsiasi proprietà, come dkimSigningMode, tlsMode, allowedCidrs o warmingEnabled.

Suggerimento operativo & nota sull'architettura

Introspezione live nel terminale o via HTTP: ogni nextxch espone questo schema completo in JSON leggibile dalle macchine su GET /api/admin/cli/schema (con header SHA-256 X-Nextxch-Schema-Version). Nel terminale, eseguire in qualsiasi momento nextxch-cli describe <Type> (ad es. nextxch-cli describe Domain o nextxch-cli describe SmtpQueue).

Recapito transazionale

TransactionalSendRequest (POST /api/v1/send)

Chiave: Idempotency-Key RBAC: User+
13 proprietà

Endpoint HTTP JSON ad alto throughput per il recapito da web app, ERP, CRM e codici 2FA, con priorità per categoria, IP warming, controllo preliminare delle soppressioni e idempotenza di 24 ore.

POST /api/v1/send Accoda e firma l'e-mail transazionale (UTF-8 Multipart/Alternative + allegati)
POST /api/v1/unsubscribe/{token} Endpoint pubblico One-Click-Unsubscribe RFC 8058 (token firmato DKIM)
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ from string FACOLTATIVO
Predefinito: Key senderAddress
Indirizzo del mittente. Per le chiavi Role=User viene impostato automaticamente sul senderAddress associato alla chiave.
├─ to string[] OBBLIGATORIO
Array di indirizzi e-mail dei destinatari principali (almeno 1 obbligatorio).
├─ cc string[] FACOLTATIVO
Predefinito: []
Destinatari in copia facoltativi.
├─ bcc string[] FACOLTATIVO
Predefinito: []
Destinatari facoltativi in copia nascosta.
├─ replyTo string FACOLTATIVO
Indirizzo Reply-To facoltativo.
├─ subject string OBBLIGATORIO
Oggetto dell'e-mail (UTF-8).
├─ textBody string FACOLTATIVO
Corpo in testo semplice (text/plain). È obbligatorio almeno uno tra textBody e htmlBody.
├─ htmlBody string FACOLTATIVO
Corpo HTML (text/html). Fornirlo insieme a textBody per una recapitabilità di 10/10.
├─ category string FACOLTATIVO
Predefinito: transactional
Seleziona la priorità della coda, l'esclusione dall'IP warming e il bucket di quota del provider destinatario.
Enum:"critical""transactional""notification""marketing"
├─ overflowMode string FACOLTATIVO
Predefinito: DeferQueue
Comportamento al raggiungimento della quota oraria del provider di destinazione (accodamento graduale vs. HTTP 429 + Retry-After).
Enum:"DeferQueue""Reject429"
├─ idempotencyKey string FACOLTATIVO
Predefinito: Header Idempotency-Key
Chiave di deduplicazione deterministica (ad es. ID fattura) che impedisce invii duplicati entro 24 ore.
├─ headers Record<string, string> FACOLTATIVO
Predefinito: {}
Dizionario di X-header personalizzati RFC 5322.
├─ attachments SendAttachmentDto[] FACOLTATIVO
Predefinito: []
Array di { 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();
Oggetto collezione (CRUD)

IntegrationKey (API v1)

Chiave: id RBAC: TenantAdmin+
9 proprietà

Chiave di integrazione con scope per /api/v1. Una chiave agisce per un utente oppure, come integrazione del tenant, per l'utente indicato nell'header X-Nextxch-Act-As. Senza lo scope l'API risponde 403; un utente di un altro tenant restituisce 404. Facoltativamente OAuth 2.0 client credentials (token valido 1 ora).

POST /api/admin/integrations/keys Crea una chiave (admin, mostrata una sola volta)
PUT /api/admin/integrations/keys/{id} Modifica ambiti
DELETE /api/admin/integrations/keys/{id} Revoca una chiave (e i relativi token OAuth)
POST /api/v1/oauth/token OAuth 2.0 client_credentials: key id + key per un access token
GET /api/v1/me Chiave, ambiti e utente che agisce
GET /api/v1/openapi.json Documento OpenAPI 3.1 dell'installazione
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ label string OBBLIGATORIO
Etichetta, ad es. CRM o ERP.
├─ mode string OBBLIGATORIO
Predefinito: user
user: agisce sempre per userEmail. tenant: agisce per X-Nextxch-Act-As (solo utenti del proprio tenant).
Enum:"user""tenant"
├─ userEmail string FACOLTATIVO
Utente per mode=user.
├─ scopes string[] OBBLIGATORIO
Autorizzazioni; .write include .read. crypto.read e crypto.manage (S/MIME, PGP) sono riservati.
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 FACOLTATIVO
Integrazione del tenant limitata agli utenti di questo dominio.
├─ allowedCidrs string[] FACOLTATIVO
Predefinito: []
Reti IP consentite.
├─ expiresInDays integer FACOLTATIVO
Scadenza in giorni (vuoto: illimitata).
├─ senderAddress string FACOLTATIVO
Mittente per mail.send (POST /api/v1/send).
├─ apiKey string SOLA LETTURA
smk_<id>_<secret>, solo nella risposta di creazione; viene memorizzato solo l'hash.
{
  "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");
Oggetto collezione (CRUD)

CalendarEvent (API v1)

Chiave: hrefName RBAC: User+
12 proprietà

Eventi con serie ed eccezioni, partecipanti, sale, inviti, risposte, disponibilità, calendari condivisi e globali e link di invito. Ambiti calendar.read e calendar.write. Orari in UTC (ISO-8601), fusi orari come nomi IANA.

GET /api/v1/calendars Calendari propri, condivisi, globali e di risorse
GET /api/v1/calendar/events?from&to&calendarId&limit&cursor Occorrenze in una finestra temporale (serie espanse)
POST /api/v1/calendar/events Crea un evento (invita persone e sale)
GET /api/v1/calendar/events/{hrefName} Evento con serie ed eccezioni
PUT /api/v1/calendar/events/{hrefName} Modifica; scope=occurrence|following per parti di una serie
DELETE /api/v1/calendar/events/{hrefName} Elimina, facoltativamente con annullamenti (notify=true)
POST /api/v1/calendar/events/{hrefName}/respond Rispondi a un invito
POST /api/v1/calendar/events/{hrefName}/invite-link Link di invito firmato
GET /api/v1/calendar/events/{hrefName}/ics iCalendar grezzo
POST /api/v1/calendar/freebusy Disponibilità di persone e sale
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ title string OBBLIGATORIO
Titolo.
├─ startUtc datetime OBBLIGATORIO
Inizio in UTC.
├─ endUtc datetime OBBLIGATORIO
Fine in UTC.
├─ timeZone string FACOLTATIVO
Predefinito: Europe/Berlin
Fuso orario IANA dell'evento (serie, visualizzazione).
├─ allDay boolean FACOLTATIVO
Predefinito: false
Tutto il giorno.
├─ attendees Attendee[] FACOLTATIVO
Predefinito: []
{ email, name, role }; sale e risorse accettano o rifiutano secondo le proprie regole.
├─ sendInvitations boolean FACOLTATIVO
Predefinito: false
Invia inviti iMIP.
├─ recurrence Ricorrenza FACOLTATIVO
{ freq, interval, byDay, byMonthDay, count, until }.
├─ showAs string FACOLTATIVO
Predefinito: busy
Mostra come.
Enum:"free""tentative""busy""oof""workingElsewhere"
├─ isPrivate boolean FACOLTATIVO
Predefinito: false
Privato (dettagli solo per il proprietario).
├─ calendarId string FACOLTATIVO
Calendario di destinazione ownerId:slug con diritti di scrittura.
├─ teamsMeeting boolean FACOLTATIVO
Crea una riunione 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()!;
Oggetto collezione (CRUD)

Rooms & Booking (API v1)

Chiave: id RBAC: User+
6 proprietà

Sale e risorse con disponibilità (rooms.read) e pagine di prenotazione come Calendly (booking.read, booking.write): pagina di prenotazione, tipi di evento, slot liberi, lettura e cancellazione delle prenotazioni. Una sala si prenota tramite un evento con la sala come partecipante.

GET /api/v1/rooms?startUtc&endUtc&category Sale e risorse, con disponibilità per la finestra temporale
GET /api/v1/rooms/{email}/availability Orari occupati di una sala
GET /api/v1/booking/profile Pagina di prenotazione con orari e tipi di evento
GET /api/v1/booking/event-types Tipi di evento
POST /api/v1/booking/event-types Crea un tipo di evento
GET /api/v1/booking/event-types/{id}/slots?from&to Orari di inizio liberi (max. 42 giorni)
GET /api/v1/booking/bookings?status&from&to Prenotazioni
POST /api/v1/booking/bookings/{id}/cancel Annulla una prenotazione (l'ospite viene informato)
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ slots[].startUtc datetime SOLA LETTURA
Inizio della disponibilità in UTC.
├─ slots[].endUtc datetime SOLA LETTURA
Fine (inizio più durata).
├─ slots[].staff object[] SOLA LETTURA
Personale disponibile per i tipi di evento di team.
├─ status string SOLA LETTURA
Stato della prenotazione.
Enum:"confirmed""cancelled"
├─ guest object SOLA LETTURA
{ name, email, phone, timeZone }.
├─ free boolean SOLA LETTURA
Sala libera nella fascia oraria.
{
  "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());
Oggetto collezione (CRUD)

Contact, Directory & Sharing (API v1)

Chiave: hrefName RBAC: User+
9 proprietà

Contatti personali con tutti i campi e foto (contacts.read, contacts.write, If-Match per la protezione dai conflitti), elenco indirizzi globale (directory.read) e condivisioni e delega (sharing.manage, delega = concessione sulla casella di posta con sendAs o sendOnBehalf).

GET /api/v1/contacts?q Cerca contatti
POST /api/v1/contacts Crea un contatto
PUT /api/v1/contacts/{hrefName} Modifica un contatto (i campi null restano invariati)
DELETE /api/v1/contacts/{hrefName} Elimina un contatto
GET /api/v1/directory?q&type Elenco indirizzi globale
GET /api/v1/sharing/grants Concessioni proprie
PUT /api/v1/sharing/grants Imposta o rimuove un'autorizzazione
GET /api/v1/sharing/shared-with-me Condivisi con me
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ givenName string FACOLTATIVO
Nome.
├─ familyName string FACOLTATIVO
Cognome.
├─ organization string FACOLTATIVO
Azienda.
├─ emails TypedValue[] FACOLTATIVO
Predefinito: []
{ type: work|home|other, value }.
├─ phones TypedValue[] FACOLTATIVO
Predefinito: []
{ type: cell|work|home|fax, value }.
├─ addresses Address[] FACOLTATIVO
Predefinito: []
{ type, street, city, region, postalCode, country }.
├─ birthday string FACOLTATIVO
yyyy-MM-dd o --MM-dd.
├─ photo string SOLO SCRITTURA (SEGRETO)
data:image/jpeg;base64,... (max. 512 KB), vuoto rimuove.
├─ permissions string[] FACOLTATIVO
Diritti di una concessione (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" }
});
Oggetto collezione (CRUD)

Webhook (API v1)

Chiave: id RBAC: User+
4 proprietà

Eventi come JSON POST firmato: calendar.event.created, calendar.event.updated, calendar.event.deleted, calendar.invitation.replied, booking.created, booking.rescheduled, booking.cancelled. Firma X-Nextxch-Signature: v1=hex(HMAC-SHA256(secret, X-Nextxch-Timestamp + "." + body)). Nuovi tentativi dopo 30 s, 2 min, 10 min, 1 h e 6 h.

GET /api/v1/webhooks Webhook del tenant
POST /api/v1/webhooks Registrare un webhook (secret solo nella risposta)
DELETE /api/v1/webhooks/{id} Elimina un webhook
GET /api/v1/webhooks/{id}/deliveries Recapiti recenti
POST /api/v1/webhooks/{id}/test Invia un evento di prova
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ url string OBBLIGATORIO
Destinazione HTTPS (gli indirizzi interni sono bloccati).
├─ events string[] OBBLIGATORIO
Eventi; la chiave richiede gli scope .read corrispondenti.
Enum:"calendar.event.created""calendar.event.updated""calendar.event.deleted""calendar.invitation.replied""booking.created""booking.rescheduled""booking.cancelled""*"
├─ secret string SOLA LETTURA
whsec_..., solo alla creazione; memorizzato crittografato.
├─ id (Ereignis) string SOLA LETTURA
evt_... stabile per ogni modifica, per la deduplicazione.
{
  "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();
});
Oggetto collezione (CRUD)

Tenant

Chiave: slug RBAC: ServerAdmin+
11 proprietà

Confine organizzativo multi-tenant che isola domini, principal, quote di archiviazione (GiB) e la conservazione di conformità GoBD WORM predefinita.

GET /api/admin/cli/objects/Tenant Elenca tutti i tenant
GET /api/admin/cli/objects/Tenant/{slugOrId} Restituisce il tenant tramite slug o UUID
POST /api/admin/cli/objects/Tenant Crea un nuovo tenant
PATCH /api/admin/cli/objects/Tenant/{slugOrId} Aggiorna direttamente le quote del tenant
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ id guid SOLA LETTURA
Predefinito: UUIDv7
Identificatore UUIDv7 interno immutabile.
├─ slug string PRIMARY KEY
Chiave tenant univoca e sicura per URL (ad es. "default", "acme").
├─ name string OBBLIGATORIO
Nome visualizzato leggibile dell'organizzazione.
├─ isActive boolean FACOLTATIVO
Predefinito: true
Attiva o sospende il tenant.
├─ maxDomains integer FACOLTATIVO
Predefinito: 100
Numero massimo di domini consentiti in questo tenant.
├─ maxPrincipals integer FACOLTATIVO
Predefinito: 10000
Numero massimo di account e liste di distribuzione.
├─ quotaGb integer FACOLTATIVO
Predefinito: 100
Limite di archiviazione del tenant in GiB.
├─ outboundDailyLimit integer FACOLTATIVO
Predefinito: 25000
Numero massimo di e-mail in uscita al giorno per l'intero tenant.
├─ defaultLockMode string FACOLTATIVO
Predefinito: Normal
Modalità di blocco GoBD WORM predefinita per i domini appena creati.
Enum:"Normal""Governance""Compliance""LegalHold"
├─ defaultRetentionDays integer FACOLTATIVO
Predefinito: 0
Periodo di conservazione obbligatorio predefinito in giorni (3650 = 10 anni GoBD).
├─ createdAtUtc datetime SOLA LETTURA
Timestamp di creazione in 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);
Oggetto collezione (CRUD)

Domain

Chiave: name RBAC: TenantAdmin+
19 proprietà

Dominio di posta ospitato con generazione automatica parallela delle chiavi Dual-DKIM (RSA-2048 + Ed25519), policy di conservazione GoBD WORM, plus-addressing e limiti di frequenza.

GET /api/admin/cli/objects/Domain Elenca tutti i domini
GET /api/admin/cli/objects/Domain/{name} Restituisce la configurazione del dominio & i selettori DKIM
POST /api/admin/cli/objects/Domain Crea il dominio & genera le chiavi DKIM RSA-2048 + Ed25519
PATCH /api/admin/cli/objects/Domain/{name} Aggiorna modalità DKIM, quote o conservazione in 0 ms
GET /api/admin/cli/dns/{name}?live=true Verifica i record DNS live o esporta la zona BIND RFC 1035
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ id guid SOLA LETTURA
Predefinito: UUIDv7
UUIDv7 interno del dominio.
├─ name string PRIMARY KEY
Nome di dominio DNS (ad es. "muster-logistik.de").
├─ tenant string FACOLTATIVO
Predefinito: default
Slug o GUID del tenant proprietario.
├─ isActive boolean FACOLTATIVO
Predefinito: true
Attiva o sospende il dominio.
├─ postmasterAddress string FACOLTATIVO
Predefinito: postmaster@<name>
Indirizzo di contatto postmaster secondo RFC 5321.
├─ dkimSigningMode string FACOLTATIVO
Predefinito: Dual
Modalità di firma DKIM attiva (Dual firma sia con RSA-2048 sia con Ed25519).
Enum:"Rsa2048""Ed25519""Dual"
├─ dkimRsaSelector string FACOLTATIVO
Predefinito: s2026
Selettore DNS RSA-2048.
├─ dkimRsaDnsName string SOLA LETTURA
Predefinito: s2026._domainkey.<name>
FQDN _domainkey RSA-2048 calcolato.
├─ dkimEd25519Selector string FACOLTATIVO
Predefinito: ed2026
Selettore DNS Ed25519.
├─ dkimEd25519DnsName string SOLA LETTURA
Predefinito: ed2026._domainkey.<name>
FQDN _domainkey Ed25519 calcolato.
├─ retentionLockMode string FACOLTATIVO
Predefinito: Normal
Modalità di blocco dell'archivio WORM GoBD per questo dominio.
Enum:"Normal""Governance""Compliance""LegalHold"
├─ retentionDays integer FACOLTATIVO
Predefinito: 0
Durata di conservazione obbligatoria in giorni (3650 = 10 anni GoBD).
├─ quotaGb integer FACOLTATIVO
Predefinito: 50
Limite di storage del dominio in GiB.
├─ maxMessageSizeMb integer FACOLTATIVO
Predefinito: 50
Dimensione massima del messaggio in MiB.
├─ maxAccounts integer FACOLTATIVO
Predefinito: 1000
Numero massimo di caselle di posta in questo dominio.
├─ outboundDailyLimit integer FACOLTATIVO
Predefinito: 5000
Limite giornaliero di invio in uscita per questo dominio.
├─ outboundHourlyLimit integer FACOLTATIVO
Predefinito: 1000
Limite orario di invio in uscita per questo dominio.
├─ usePlusAddressing boolean FACOLTATIVO
Predefinito: true
Attiva il plus addressing RFC 5233 (user+tag@domain).
├─ createdAtUtc datetime SOLA LETTURA
Timestamp di creazione in 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();
Oggetto collezione (CRUD)

Account (Account/User & Account/List)

Chiave: email RBAC: TenantAdmin+
18 proprietà

Rappresenta una casella di posta utente (@type="User", con IMAP/JMAP/CalDAV/CardDAV/ActiveSync) o una lista di distribuzione (@type="List" con members[]).

GET /api/admin/cli/objects/Account?filter=role=TenantAdmin Elenca/filtra account e liste di distribuzione
POST /api/admin/cli/objects/Account/User Crea una nuova casella di posta utente con cartelle RFC 6154
POST /api/admin/cli/objects/Account/List Crea una nuova lista di distribuzione
PATCH /api/admin/cli/objects/Account/{email} Aggiorna quota, inoltro, risposta automatica o password
DELETE /api/admin/cli/objects/Account/{email} Elimina un account in modo logico (soft delete)
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ id guid SOLA LETTURA
Predefinito: UUIDv7
UUIDv7 interno del principal.
├─ @type string FACOLTATIVO
Predefinito: User
Variante di account: casella di posta User o gruppo di distribuzione List.
Enum:"User""List"
├─ email string PRIMARY KEY
Indirizzo e-mail primario della casella di posta o della lista.
├─ displayName string FACOLTATIVO
Predefinito: Lokalteil
Nome visualizzato in Webmail, nella GAL e nelle intestazioni From.
├─ password string SOLO SCRITTURA (SEGRETO)
Password in chiaro in creazione/aggiornamento (sottoposta immediatamente a hash con Argon2id).
├─ role string FACOLTATIVO
Predefinito: User
Ruolo RBAC amministrativo.
Enum:"User""ComplianceAuditor""TenantAdmin""ServerAdmin"
├─ isActive boolean FACOLTATIVO
Predefinito: true
Attiva o sospende l'account.
├─ quotaMb integer FACOLTATIVO
Predefinito: 5120
Quota di archiviazione della casella di posta in MiB (5120 = 5 GiB).
├─ usedBytes long SOLA LETTURA
Predefinito: 0
Utilizzo attuale dello spazio della casella di posta in byte.
├─ outboundDailyLimit integer FACOLTATIVO
Predefinito: 500
Limite giornaliero di messaggi in uscita per questo account.
├─ forwardAddress string FACOLTATIVO
Indirizzo e-mail di destinazione dell'inoltro compatibile SRS, facoltativo.
├─ forwardKeepOriginal boolean FACOLTATIVO
Predefinito: true
Conserva una copia locale in INBOX quando l'inoltro è attivo.
├─ vacationEnabled boolean FACOLTATIVO
Predefinito: false
Attiva la risposta automatica (fuori sede).
├─ vacationSubject string FACOLTATIVO
Oggetto della risposta automatica fuori sede.
├─ vacationBody string SOLO SCRITTURA (SEGRETO)
Testo del corpo della risposta automatica fuori sede.
├─ twoFactorEnabled boolean SOLA LETTURA
Predefinito: false
Indica se la 2FA TOTP è attiva.
├─ members string[] FACOLTATIVO
Predefinito: []
Indirizzi e-mail dei membri (per @type="List").
├─ createdAtUtc datetime SOLA LETTURA
Timestamp di creazione in 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
});
Oggetto collezione (CRUD)

Alias

Chiave: address RBAC: TenantAdmin+
7 proprietà

Alias e-mail secondario o alias di privacy mascherato usa e getta, instradato verso una casella di posta Account primaria o una lista di distribuzione.

GET /api/admin/cli/objects/Alias Elenca tutti gli alias e gli indirizzi e-mail mascherati
POST /api/admin/cli/objects/Alias Crea un nuovo alias instradato verso una casella di posta di destinazione
PATCH /api/admin/cli/objects/Alias/{address} Attiva/sospende un alias o ne aggiorna la destinazione
DELETE /api/admin/cli/objects/Alias/{address} Elimina l'alias
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ address string PRIMARY KEY
L'indirizzo e-mail dell'alias.
├─ target string OBBLIGATORIO
Indirizzo e-mail dell'account primario di destinazione.
├─ principalId guid SOLA LETTURA
UUID del principal di destinazione risolto.
├─ description string FACOLTATIVO
Predefinito: ""
Nota sullo scopo o etichetta del servizio esterno (mostrata anche nel compositore di Webmail).
├─ isMasked boolean FACOLTATIVO
Predefinito: false
Contrassegna l'alias come alias di privacy mascherato.
├─ isEnabled boolean FACOLTATIVO
Predefinito: true
Se false, la posta verso questo alias viene rifiutata senza liberare l'indirizzo.
├─ createdAtUtc datetime SOLA LETTURA
Timestamp di creazione in 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
});
Oggetto collezione (CRUD)

ApiKey

Chiave: id RBAC: TenantAdmin+
11 proprietà

Chiave API con ambito immutabile, limitata tramite RBAC e CIDR (smk_<id>_<secret>) per /api/v1/send, SCIM 2.0 o nextxch-cli.

GET /api/admin/cli/objects/ApiKey Elenca le chiavi API attive (secret omesso)
POST /api/admin/cli/objects/ApiKey Genera una nuova chiave API e restituisce apiKey (smk_...) una sola volta
DELETE /api/admin/cli/objects/ApiKey/{id} Revoca immediatamente la chiave API
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ id guid SOLA LETTURA
Predefinito: UUIDv7
UUID univoco della chiave (usato per la revoca tramite DELETE).
├─ apiKey string SOLO CREAZIONE
Predefinito: smk_<id>_<secret>
Token Bearer completo restituito SOLO una volta alla creazione.
├─ label string OBBLIGATORIO
Etichetta leggibile (ad es. "Ansible GitOps" o "Shopware 6").
├─ role string FACOLTATIVO
Predefinito: TenantAdmin
Ambito dei privilegi (User è strettamente limitato a POST /api/v1/send).
Enum:"User""ComplianceAuditor""TenantAdmin""ServerAdmin"
├─ senderAddress string FACOLTATIVO
Indirizzo mittente autorizzato (obbligatorio quando role="User").
├─ defaultCategory string FACOLTATIVO
Predefinito: transactional
Categoria di invio predefinita per questa chiave.
Enum:"critical""transactional""notification""marketing"
├─ allowedCidrs string[] FACOLTATIVO
Predefinito: [] (Alle IPs)
Reti CIDR IPv4/IPv6 di origine consentite.
├─ expiresInDays integer SOLO SCRITTURA (SEGRETO)
Predefinito: null (Kein Ablauf)
Scadenza automatica facoltativa in giorni dalla creazione.
├─ expiresAtUtc datetime SOLA LETTURA
Timestamp di scadenza UTC calcolato.
├─ outboundDailyLimit integer FACOLTATIVO
Predefinito: 10000
Limite giornaliero di invio in uscita per questa chiave.
├─ isActive boolean SOLA LETTURA
Predefinito: true
Stato attivo.
{
  "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()!;
Oggetto collezione (CRUD)

Route

Chiave: domain RBAC: ServerAdmin+
10 proprietà

Instradamento SMTP in uscita per dominio o eccezione per certificato TLS (ad es. StartTlsIgnoreCert per host MX partner con certificati non corrispondenti, oppure connettore smart host).

GET /api/admin/cli/objects/Route Elenca tutte le route in uscita personalizzate
POST /api/admin/cli/objects/Route Crea una route di dominio personalizzata o un override TLS
PATCH /api/admin/cli/objects/Route/{domain} Aggiorna host di destinazione, porta o modalità TLS
DELETE /api/admin/cli/objects/Route/{domain} Elimina la route personalizzata
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ domain string PRIMARY KEY
Dominio del destinatario (ad es. "neff.me" o "*.partner-holding.eu").
├─ targetHost string FACOLTATIVO
Predefinito: "" (Auto DNS-MX)
Nome host o IP del relay. Lasciare vuoto per risolvere automaticamente l'MX di destinazione tramite DNS.
├─ targetPort integer FACOLTATIVO
Predefinito: 25
Porta TCP di destinazione (25, 587 o 465).
├─ tlsMode string FACOLTATIVO
Predefinito: StartTlsOptional
Modalità di convalida TLS e X.509 in uscita per questo dominio.
Enum:"StartTlsOptional""StartTlsRequired""StartTlsIgnoreCert""ImplicitTls""ImplicitTlsIgnoreCert""None"
├─ username string FACOLTATIVO
Nome utente SMTP AUTH facoltativo.
├─ password string SOLO SCRITTURA (SEGRETO)
Password SMTP AUTH facoltativa.
├─ hasPassword boolean SOLA LETTURA
Predefinito: false
Indica se è memorizzata una password.
├─ clearPassword boolean SOLO SCRITTURA (SEGRETO)
Predefinito: false
Impostare a true per cancellare la password memorizzata.
├─ description string FACOLTATIVO
Nota di audit dell'amministratore.
├─ enabled boolean FACOLTATIVO
Predefinito: true
Attiva o sospendi 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
});
Policy singleton

TransactionalSend (Singleton)

Chiave: singleton RBAC: TenantAdmin+
11 proprietà

Gestisce il warming IP progressivo di 28 giorni, le categorie di messaggi e le quote orarie/giornaliere per provider del destinatario (Google, Microsoft, Yahoo/AOL, ISP tedeschi, iCloud, *).

GET /api/admin/cli/objects/TransactionalSend/singleton Restituisce il warming IP attivo & la policy di quota dei provider
PATCH /api/admin/cli/objects/TransactionalSend/singleton Aggiorna il giorno di warming, il preset o le quote per provider
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ enabled boolean FACOLTATIVO
Predefinito: true
Attiva la governance delle quote per categoria e per provider.
├─ warmingEnabled boolean FACOLTATIVO
Predefinito: false
Attiva la rampa progressiva giornaliera di IP warming.
├─ startAtWarmingDay integer SOLO SCRITTURA (SEGRETO)
Predefinito: 1
Salta alla pianificazione di warming del giorno N (1..365) o la reimposta.
├─ currentWarmingDay integer SOLA LETTURA
Giorno attivo corrente nel piano di warming.
├─ currentWarmingPercent integer SOLA LETTURA
Percentuale attualmente attiva delle quote del provider di destinazione.
├─ warmingDailyRampPercents integer[] FACOLTATIVO
Predefinito: [5,7,10,...,100]
Array di percentuali giornaliere per la rampa di warming.
├─ maxDeferHours integer FACOLTATIVO
Predefinito: 48
Finestra massima di anticipo in ore per il pacing DeferQueue.
├─ defaultCategory string FACOLTATIVO
Predefinito: transactional
Categoria di fallback se omessa nelle chiamate API.
Enum:"critical""transactional""notification""marketing"
├─ preset string SOLO SCRITTURA (SEGRETO)
Selettore di preset in un unico passaggio.
Enum:"warming_28d""warming_14d""production_100"
├─ categories CategoryPolicy[] FACOLTATIVO
Predefinito: 4 Kategorien
Array di oggetti policy di categoria.
├─ providers ProviderQuota[] FACOLTATIVO
Predefinito: 6 Provider-Gruppen
Array di definizioni di quota per provider destinatario.
{
  "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 });
Policy singleton

ReputationShield (Singleton)

Chiave: singleton RBAC: TenantAdmin+
20 proprietà

Protezione facoltativa della reputazione in uscita: elenco di soppressione automatico per bounce/reclami, One-Click Unsubscribe RFC 8058, circuit breaker per i bounce, backoff adattivo per 421 degli ISP e webhook HMAC-SHA256.

GET /api/admin/cli/objects/ReputationShield/singleton Ottiene la configurazione attuale di Reputation Shield
PATCH /api/admin/cli/objects/ReputationShield/singleton Aggiorna i flag di protezione o applica un preset
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ preset string SOLO SCRITTURA (SEGRETO)
Applica un profilo di protezione completo in un solo passaggio.
Enum:"off""recommended""strict_bulk"
├─ enableSuppressionPreCheck boolean FACOLTATIVO
Predefinito: false
Filtra gli indirizzi soppressi prima dell'invio.
├─ enforceSuppressionOnSmtpRelay boolean FACOLTATIVO
Predefinito: false
Applica l'elenco di soppressione anche alla SMTP submission.
├─ allSuppressedBehavior string FACOLTATIVO
Predefinito: reject_422
Modalità di risposta quando tutti i destinatari sono soppressi.
Enum:"reject_422""accept_noop"
├─ autoSuppressHardBounces boolean FACOLTATIVO
Predefinito: false
Sopprime automaticamente gli hard bounce permanenti 5xx.
├─ hardBounceSuppressionDays integer FACOLTATIVO
Predefinito: 180
Giorni di conservazione per le soppressioni da hard bounce.
├─ autoSuppressConsecutiveSoftBounces boolean FACOLTATIVO
Predefinito: false
Inoltra i ripetuti soft bounce 4xx all'elenco di soppressione.
├─ softBounceEscalationThreshold integer FACOLTATIVO
Predefinito: 5
Soft bounce consecutivi prima della soppressione.
├─ autoSuppressFblComplaints boolean FACOLTATIVO
Predefinito: false
Analizza automaticamente i report di abuso ARF in entrata (RFC 5965).
├─ enableOneClickUnsubscribe boolean FACOLTATIVO
Predefinito: false
Inserisce header List-Unsubscribe RFC 8058 firmati DKIM.
├─ oneClickUnsubscribeCategories string[] FACOLTATIVO
Predefinito: ["marketing","notification"]
Categorie che ricevono header di disiscrizione RFC 8058.
├─ enableBounceCircuitBreaker boolean FACOLTATIVO
Predefinito: false
Sospende gli invii non critici se il tasso di bounce/reclami nelle 24 ore aumenta bruscamente.
├─ maxHardBouncePercent number FACOLTATIVO
Predefinito: 5.0
Soglia massima del tasso di hard bounce nelle 24 ore, in percentuale.
├─ maxComplaintPercent number FACOLTATIVO
Predefinito: 0.1
Soglia massima del tasso di reclami ARF sulle 24 ore, in percentuale.
├─ allowCriticalWhenTripped boolean FACOLTATIVO
Predefinito: true
Consente le e-mail 2FA critiche anche quando il circuit breaker è scattato.
├─ enableAdaptiveIspBackoff boolean FACOLTATIVO
Predefinito: false
Cooldown automatico quando l'MX remoto restituisce un throttling 421/451.
├─ ispCooldownMinutes integer FACOLTATIVO
Predefinito: 15
Durata del cooldown in minuti.
├─ webhookEnabled boolean FACOLTATIVO
Predefinito: false
Attiva i webhook firmati HMAC-SHA256.
├─ webhookUrl string FACOLTATIVO
URL HTTPS di destinazione per i webhook.
├─ webhookSecret string SOLO SCRITTURA (SEGRETO)
Segreto HMAC-SHA256 per 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" });
Policy singleton

SpamGuard (Singleton)

Chiave: singleton RBAC: ServerAdmin+
15 proprietà

Difesa in entrata multilivello: verifica SPF/DKIM/DMARC/ARC, Smart Greylisting (/24 IPv4 & /64 IPv6), classificatore ML online (FTRL-Proximal), zone DNSBL, elenchi consentiti/bloccati e scansione TCP ClamAV.

GET /api/admin/cli/objects/SpamGuard/singleton Restituisce le soglie SpamGuard & i contatori di addestramento
PATCH /api/admin/cli/objects/SpamGuard/singleton Aggiorna sul posto punteggi, DNSBL, elenchi consentiti o ClamAV
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ enableEmailAuthChecks boolean FACOLTATIVO
Predefinito: true
Verifica SPF, DKIM, DMARC e ARC in entrata.
├─ enableGreylisting boolean FACOLTATIVO
Predefinito: true
Smart Greylisting adattivo per mittenti sconosciuti al primo contatto.
├─ greylistRetryDelaySeconds integer FACOLTATIVO
Predefinito: 240
Ritardo minimo tra i tentativi in secondi (predefinito: 240 s).
├─ junkScoreThreshold number FACOLTATIVO
Predefinito: 4.5
Soglia di punteggio per l'archiviazione nella cartella Spam.
├─ rejectScoreThreshold number FACOLTATIVO
Predefinito: 9.0
Soglia di punteggio per il rifiuto SMTP 550 definitivo.
├─ tagSubjectOnJunk boolean FACOLTATIVO
Predefinito: false
Antepone il prefisso dell'oggetto ai messaggi spam.
├─ subjectTagPrefix string FACOLTATIVO
Predefinito: "[SPAM] "
Stringa del prefisso dell'oggetto.
├─ enableStructuralHeuristics boolean FACOLTATIVO
Predefinito: true
Valuta euristiche strutturali MIME/HTML.
├─ dnsblZones string[] FACOLTATIVO
Predefinito: ["bl.spamcop.net","ix.dnsbl.manitu.net"]
Zone DNSBL interrogate.
├─ dnsblHitScore number FACOLTATIVO
Predefinito: 3.5
Punteggio aggiunto per ogni hit DNSBL.
├─ allowedSendersOrDomains string[] FACOLTATIVO
Predefinito: []
Elenco consentiti esplicito di e-mail o domini mittenti.
├─ blockedSendersOrDomains string[] FACOLTATIVO
Predefinito: []
Elenco bloccati esplicito di e-mail o domini mittenti.
├─ clamAvEnabled boolean FACOLTATIVO
Predefinito: false
Attiva la scansione TCP INSTREAM tramite il demone ClamAV.
├─ clamAvHost string FACOLTATIVO
Predefinito: "127.0.0.1"
Host o IP del daemon ClamAV.
├─ clamAvPort integer FACOLTATIVO
Predefinito: 3310
Porta TCP del demone 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 });
Policy singleton

SmtpQueue (Singleton)

Chiave: singleton RBAC: ServerAdmin+
19 proprietà

Controlla la coda di recapito MX in uscita, la concorrenza dei worker, gli intervalli dei tentativi, il throttling ISP, la convalida TLS in uscita, lo smart host globale e i limiti ESMTP in entrata.

GET /api/admin/cli/objects/SmtpQueue/singleton Ottiene tutte le impostazioni di coda, TLS ed ESMTP
PATCH /api/admin/cli/objects/SmtpQueue/singleton Aggiorna worker della coda, pianificazione dei tentativi o limiti ESMTP in 0 ms
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ outboundQueuePaused boolean FACOLTATIVO
Predefinito: false
Sospende il recapito MX in uscita continuando ad accettare posta.
├─ outboundWorkerConcurrency integer FACOLTATIVO
Predefinito: 8
Numero di worker paralleli per il recapito in uscita.
├─ maxDeliveryAttempts integer FACOLTATIVO
Predefinito: 10
Numero massimo di tentativi di recapito prima del bounce permanente.
├─ retryScheduleMinutes integer[] FACOLTATIVO
Predefinito: [5,15,60,240,720]
Pianificazione del backoff dei tentativi in minuti.
├─ maxConcurrentConnectionsPerMxDomain integer FACOLTATIVO
Predefinito: 4
Numero massimo di connessioni simultanee per dominio MX di destinazione.
├─ strictThrottleDomains string[] FACOLTATIVO
Predefinito: ["t-online.de","gmx.de","web.de","yahoo.com"]
Domini soggetti a una limitazione rigorosa della concorrenza.
├─ strictThrottleMaxConcurrency integer FACOLTATIVO
Predefinito: 2
Numero massimo di connessioni simultanee per strictThrottleDomains.
├─ outboundConnectTimeoutSeconds integer FACOLTATIVO
Predefinito: 30
Timeout di connessione TCP/TLS in uscita in secondi.
├─ outboundBindAddress string FACOLTATIVO
Predefinito: "" (Auto)
Indirizzo IP sorgente locale di binding, facoltativo.
├─ outboundUseStartTls boolean FACOLTATIVO
Predefinito: true
Usa STARTTLS sulle connessioni MX in uscita.
├─ requireOutboundTls boolean FACOLTATIVO
Predefinito: false
Interrompe il recapito se l'MX remoto non offre TLS.
├─ outboundTlsCertValidation string FACOLTATIVO
Predefinito: Opportunistic
Modalità globale di convalida dei certificati X.509 in uscita.
Enum:"Opportunistic""IgnoreErrors""Strict"
├─ outboundSmartHost string FACOLTATIVO
Predefinito: ""
Host di relay upstream globale facoltativo.
├─ outboundSmartPort integer FACOLTATIVO
Predefinito: 587
Porta TCP dello smart host globale.
├─ maxMessageSizeMb long FACOLTATIVO
Predefinito: 50
Limite ESMTP SIZE globale in MiB.
├─ maxRecipientsPerTransaction integer FACOLTATIVO
Predefinito: 100
Numero massimo di comandi RCPT TO per transazione.
├─ maxConnectionsPerIp integer FACOLTATIVO
Predefinito: 20
Numero massimo di connessioni in entrata simultanee per IP.
├─ tarpitDelayMs integer FACOLTATIVO
Predefinito: 1500
Ritardo tarpit in ms in caso di errori di autenticazione/comando.
├─ requireTlsForAuth boolean FACOLTATIVO
Predefinito: true
Richiede la crittografia TLS prima di consentire 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 });
Policy singleton

TransportRules & Server (Singletons)

Chiave: singleton RBAC: ServerAdmin+
11 proprietà

Disclaimer legale a livello di organizzazione (inserito prima della firma DKIM), script Sieve globale in entrata, soglie di auto-ban Fail2Ban e ServerHostName canonico.

GET /api/admin/cli/objects/TransportRules/singleton Restituisce le impostazioni di disclaimer, Sieve globale e auto-ban
PATCH /api/admin/cli/objects/TransportRules/singleton Aggiorna le regole di trasporto sul posto
GET /api/admin/cli/objects/Server/singleton Restituisce ServerHostName, motore di storage, profondità della coda e stato della licenza
PATCH /api/admin/cli/objects/Server/singleton Aggiorna il ServerHostName canonico in 0 ms
{ } Schema degli oggetti e proprietà tipizzate application/json · camelCase
├─ outboundDisclaimerEnabled boolean FACOLTATIVO
Predefinito: false
Aggiunge un piè di pagina aziendale obbligatorio ai messaggi in uscita.
├─ outboundDisclaimerExternalOnly boolean FACOLTATIVO
Predefinito: true
Aggiunge il piè di pagina solo per l'invio a domini esterni.
├─ outboundDisclaimerText string FACOLTATIVO
Predefinito: ""
Piè di pagina legale in testo semplice.
├─ outboundDisclaimerHtml string FACOLTATIVO
Predefinito: ""
Piè di pagina legale HTML.
├─ inboundSystemSieve string FACOLTATIVO
Predefinito: ""
Script Sieve globale RFC 5228 eseguito prima degli script utente.
├─ autoBanThreshold integer FACOLTATIVO
Predefinito: 8
Accessi non riusciti prima del ban automatico dell'IP.
├─ autoBanDurationMinutes integer FACOLTATIVO
Predefinito: 30
Durata del ban IP automatico in minuti.
├─ serverHostName string FACOLTATIVO
Predefinito: mail.<domain>
FQDN canonico per EHLO, TLS e Autodiscover (oggetto: Server).
├─ storageProvider string SOLA LETTURA
Predefinito: Sqlite | SqlServer | Postgres
Motore di storage attivo (oggetto: Server).
├─ activeQueueCount integer SOLA LETTURA
Predefinito: 0
Numero attuale di messaggi nella coda in uscita (oggetto: Server).
├─ licenseState string SOLA LETTURA
Predefinito: EnterpriseActive
Stato della licenza (oggetto: 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")}");