Zum Inhalt springen
Doku

Leitfaden

OAuth-App entwickeln

Lassen Sie Nutzer Ihre App per OAuth 2.1 und PKCE mit ihrem Emailit-Workspace verbinden, einschließlich Client-Registrierung, Token-Erneuerung, Widerruf und Fehlern.

Aktualisiert am 1. Okt. 2026

Emailit betreibt unter https://api.emailit.com einen OAuth-2.1-Autorisierungsserver. Nutzen Sie ihn, wenn Sie eine Integration entwickeln, etwa ein CRM, ein No-Code-Tool oder einen KI-Client, die im Namen von Emailit-Nutzern handelt. Ihre Nutzer melden sich im Browser an und erlauben den Zugriff, und Sie erhalten Tokens für die Workspaces, die sie auswählen, ohne je mit ihren API-Schlüsseln umzugehen. Denselben Ablauf verwendet auch der MCP-Server.

So funktioniert es

  1. Registrieren Sie einen Client per dynamischer Client-Registrierung oder hosten Sie ein Client-ID-Metadatendokument.

  2. Leiten Sie den Nutzer zur Autorisierungs-URL weiter und übergeben Sie dabei eine PKCE-Code-Challenge.

  3. Der Nutzer meldet sich bei Emailit an, wählt die Workspaces, die Ihre App nutzen darf, und bestätigt den angeforderten Scope.

  4. Emailit leitet zurück an Ihre Redirect-URI, mit einem einmalig gültigen Autorisierungscode.

  5. Tauschen Sie den Code ein gegen ein Zugriffstoken mit 15 Minuten Laufzeit und ein Refresh-Token.

  6. Rufen Sie die API auf und verwenden Sie dabei das Zugriffstoken; erneuern Sie es, wenn es abläuft.

Endpunkte

Methode Pfad Zweck
GET /.well-known/oauth-authorization-server Metadaten des Autorisierungsservers (RFC 8414)
GET /.well-known/oauth-protected-resource Metadaten der geschützten Ressource für die REST-API
GET /.well-known/oauth-protected-resource/mcp Metadaten der geschützten Ressource für den MCP-Server
POST /oauth/register Dynamische Client-Registrierung (RFC 7591)
GET /oauth/authorize Anmeldung und Zustimmung im Browser
POST /oauth/token Code oder Refresh-Token eintauschen
POST /oauth/revoke Freigabe mit ihrem Refresh-Token widerrufen (RFC 7009)
GET /oauth/grants Freigaben auflisten (die des angemeldeten Nutzers oder die eines Workspaces per API-Schlüssel)
POST /oauth/grants/:id/revoke Freigabe widerrufen
PUT /oauth/grants/:id/workspaces Ändern, welche Workspaces eine Freigabe nutzen kann (durch den Nutzer, der die App verbunden hat)

Alle Pfade liegen auf https://api.emailit.com.

Server ermitteln

Terminal
curl https://api.emailit.com/.well-known/oauth-authorization-server
JSON
{
  "issuer": "https://api.emailit.com",
  "authorization_endpoint": "https://api.emailit.com/oauth/authorize",
  "token_endpoint": "https://api.emailit.com/oauth/token",
  "registration_endpoint": "https://api.emailit.com/oauth/register",
  "revocation_endpoint": "https://api.emailit.com/oauth/revoke",
  "jwks_uri": "https://api.emailit.com/.well-known/jwks.json",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
  "client_id_metadata_document_supported": true,
  "authorization_response_iss_parameter_supported": true,
  "scopes_supported": ["sending", "full"]
}

MCP-Clients beginnen stattdessen mit den Metadaten der geschützten Ressource. Eine nicht authentifizierte Anfrage an https://api.emailit.com/mcp gibt 401 mit WWW-Authenticate: Bearer resource_metadata="https://api.emailit.com/.well-known/oauth-protected-resource/mcp" zurück, und dieses Dokument verweist auf den Autorisierungsserver:

JSON
{
  "resource": "https://api.emailit.com/mcp",
  "authorization_servers": ["https://api.emailit.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["sending", "full"]
}

Scopes

Scope Gewährt
sending Senden und Weiterleiten von E-Mails sowie Verschieben, Stornieren und erneutes Senden von Sendungen. Derselbe Zugriff wie ein reiner Sende-Schlüssel.
full Alle Endpunkte der REST-API und alle MCP-Tools. Derselbe Zugriff wie ein API-Schlüssel mit Vollzugriff.

full umfasst bereits alles, was sending erlaubt. Fordern Sie sending an, wenn Ihre App nur sendet, andernfalls full; auch ein durch Leerzeichen getrenntes sending full wird akzeptiert. Wenn Sie scope weglassen, verwendet Emailit alle Scopes, die der Client registriert hat.

Client registrieren

Sie können einen Client auf zwei Arten registrieren. Beide funktionieren mit allen Abläufen auf dieser Seite.

Dynamische Client-Registrierung

Senden Sie eine Registrierungsanfrage. Eine Authentifizierung ist nicht nötig, und jede IP-Adresse kann bis zu 20 Clients pro Stunde registrieren.

Terminal
curl https://api.emailit.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme CRM",
    "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "scope": "full",
    "token_endpoint_auth_method": "client_secret_basic",
    "client_uri": "https://crm.acme.com",
    "logo_uri": "https://crm.acme.com/logo.png"
  }'
Feld Erforderlich Beschreibung
client_name Ja Wird auf der Zustimmungsseite angezeigt. Bis zu 200 Zeichen.
redirect_uris Ja 1 bis 10 Redirect-URIs. Siehe Regeln für Redirect-URIs.
grant_types Nein Muss authorization_code enthalten; darf refresh_token enthalten. Standardwert: beide.
response_types Nein Nur code.
scope Nein Durch Leerzeichen getrennte Scopes. Standardwert: sending full.
token_endpoint_auth_method Nein none (Standard) für öffentliche Clients wie Desktop-, Mobil- und Browser-Apps; client_secret_basic oder client_secret_post für serverseitige Apps.
client_uri Nein Die Startseite Ihrer App.
logo_uri Nein Logo, das auf der Zustimmungsseite angezeigt wird.

Die Antwort mit 201 gibt die Metadaten zurück und ergänzt eine client_id. Vertrauliche Clients erhalten zusätzlich ein client_secret, das nur einmal angezeigt wird:

JSON
{
  "client_id": "3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73",
  "client_id_issued_at": 1790847000,
  "client_name": "Acme CRM",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "full",
  "token_endpoint_auth_method": "client_secret_basic",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "client_secret": "Ky7WI2z5HxoT6q1z19tuiIev_U1zX0_FKhIIfh0OBco",
  "client_secret_expires_at": 0
}

client_secret_expires_at ist immer 0: Secrets laufen nicht ab. Jeder Client, ob öffentlich oder vertraulich, muss PKCE verwenden.

Client-ID-Metadatendokument

Wenn Ihre App eine statische JSON-Datei hosten kann, können Sie die Registrierung überspringen. Veröffentlichen Sie ein Dokument unter einer HTTPS-URL und verwenden Sie diese URL als Ihre client_id:

https://crm.acme.com/oauth/client.json
{
  "client_id": "https://crm.acme.com/oauth/client.json",
  "client_name": "Acme CRM",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none",
  "scope": "full"
}
  • client_id im Dokument muss exakt der URL des Dokuments entsprechen.
  • token_endpoint_auth_method muss none sein. Es handelt sich um öffentliche Clients, die auf PKCE setzen.
  • HTTPS-Redirect-URIs müssen auf demselben Host liegen wie das Dokument.
  • Emailit ruft das Dokument während der Autorisierung ab, mit einem Timeout von 5 Sekunden und ohne Weiterleitungen zu folgen, und speichert es 5 Minuten im Cache.
  • Die Zustimmungsseite zeigt statt client_name den Host des Dokuments an, zum Beispiel crm.acme.com.

KI-Clients verwenden diese Methode: ChatGPT, Claude und Grok identifizieren sich mit ihren eigenen Client-ID-Metadatendokumenten und verbinden sich daher ohne vorherige Registrierung. Für Grok akzeptiert Emailit außerdem Weiterleitungen zu console.x.ai.

Regeln für Redirect-URIs

  • URIs mit https:// sind erlaubt.
  • http:// ist nur für Loopback-Hosts erlaubt: 127.0.0.1, localhost und [::1]. Bei Loopback-URIs darf sich der Port zum Zeitpunkt der Autorisierung unterscheiden, Host, Pfad und Query müssen aber übereinstimmen. localhost und 127.0.0.1 sind unterschiedliche Hosts.
  • Private Schemata wie cursor://, vscode:// oder com.acme.crm:// sind für native Apps erlaubt.
  • URIs mit file, ftp, data, javascript, blob, about und vbscript sowie jede URI mit einem #fragment werden abgelehnt.
  • Abgesehen von Loopback-Ports muss die gesendete redirect_uri exakt einer registrierten entsprechen.

Nutzer zu Emailit weiterleiten

Erzeugen Sie für jede Autorisierung einen PKCE-Verifier und eine Challenge sowie einen zufälligen state:

JavaScript
import { createHash, randomBytes } from 'node:crypto';

const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
// Store codeVerifier and state in the user's session.

Leiten Sie dann den Browser des Nutzers zum Autorisierungsendpunkt weiter:

Text
https://api.emailit.com/oauth/authorize
  ?response_type=code
  &client_id=3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73
  &redirect_uri=https%3A%2F%2Fcrm.acme.com%2Foauth%2Femailit%2Fcallback
  &scope=full
  &state=Jq3k9V0n2xR7bLm1
  &code_challenge=zsrvXVr2pbLDHbccAx_NY9osQ6nwqTnDZlIeFWKQOsg
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fapi.emailit.com
Parameter Erforderlich Beschreibung
response_type Ja code
client_id Ja Ihre Client-ID oder die URL Ihres Metadatendokuments.
redirect_uri Ja Eine Ihrer registrierten Redirect-URIs.
code_challenge Ja Base64url-kodierter SHA-256-Hash des Code-Verifiers.
code_challenge_method Empfohlen S256. plain wird nicht unterstützt.
scope Empfohlen sending oder full. Muss ein Scope sein, den der Client registriert hat.
state Empfohlen Ein Zufallswert, den Sie beim Callback prüfen.
resource Nein Die Ressource, die Sie aufrufen möchten (RFC 8707): https://api.emailit.com für die REST-API oder https://api.emailit.com/mcp für MCP. Die Tokens funktionieren in jedem Fall für beide.

Öffnen Sie diese URL im Browser des Nutzers. Rufen Sie sie nicht von Ihrem Backend aus ab.

Was der Nutzer sieht

  1. Bei Emailit anmelden. Der Nutzer gibt seine E-Mail-Adresse und sein Passwort ein. Wenn er für die Zwei-Faktor-Authentifizierung eine Authenticator-App nutzt, gibt er anschließend deren Code oder einen Wiederherstellungscode ein.
  2. Workspaces wählen. Die Seite zeigt Ihr Logo, Ihren Client-Namen (oder den Host Ihres Metadatendokuments) und die angeforderten Scopes. Der Nutzer wählt All my workspaces, was auch Workspaces einschließt, die er später erstellt oder denen er später beitritt, oder Only these workspaces mit den angehakten Workspaces, und legt fest, in welchem Workspace Ihre App startet.
  3. Zugriff erlauben. Er wählt Allow access oder Deny.

Eine Freigabe kann mehrere Workspaces umfassen. Der Nutzer kann die Liste später in der Weboberfläche unter Account > Connected apps ändern, und Ihre App sieht die Änderung bei ihrer nächsten Anfrage.

Nach der Bestätigung leitet Emailit zu Ihrer Redirect-URI weiter:

Text
https://crm.acme.com/oauth/emailit/callback?code=XvRKyG5Pkplrd3xkRNaEayKkpkebEMdF&state=Jq3k9V0n2xR7bLm1&iss=https%3A%2F%2Fapi.emailit.com

Prüfen Sie, ob state mit dem gespeicherten Wert übereinstimmt und ob iss gleich https://api.emailit.com ist. Der Code ist 10 Minuten gültig und kann einmal verwendet werden. Wählt der Nutzer Deny, enthält die Weiterleitung stattdessen error=access_denied.

Code gegen Tokens eintauschen

Senden Sie eine POST-Anfrage an den Token-Endpunkt als application/x-www-form-urlencoded (JSON wird ebenfalls akzeptiert). Senden Sie dieselbe redirect_uri und den ursprünglichen Code-Verifier:

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://crm.acme.com/oauth/emailit/callback \
  -d code_verifier="$CODE_VERIFIER"

Bei client_secret_basic senden Sie Client-ID und Secret im Header Authorization: Basic. Bei client_secret_post senden Sie stattdessen client_id und client_secret als Formularfelder. Senden Sie das Secret nicht auf beiden Wegen.

JSON
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9hdXRoLWhzMjU2In0…",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "XvRKyG5Pkplrd3xkRNaEayKkpkebEMdFn0h1VIuqixAOXybMtJfK8w",
  "scope": "full"
}

Das Zugriffstoken ist 15 Minuten gültig (expires_in in Sekunden). Behandeln Sie es als undurchsichtigen String: Parsen Sie es nicht und verlassen Sie sich nicht auf seinen Inhalt.

API aufrufen

Verwenden Sie das Zugriffstoken als Bearer-Token für die REST-API und den MCP-Server, genau wie einen API-Schlüssel:

Terminal
curl https://api.emailit.com/v2/domains \
  -H "Authorization: Bearer $ACCESS_TOKEN"

REST-Anfragen laufen im Standard-Workspace der Freigabe: dem Workspace, in dem der Nutzer starten wollte oder zu dem er später gewechselt hat. MCP-Tools können mit ihrem Argument workspace auch einen anderen erlaubten Workspace ansprechen. Jede Anfrage verwendet den gewährten Scope und die Rolle des Nutzers im Workspace; eine Anfrage außerhalb des Scopes oder eine Admin-Route, die ein Mitglied mit der Rolle Member aufruft, gibt daher 403 zurück. Verlässt der Nutzer einen Workspace, verliert auch die Freigabe den Zugriff darauf.

Tokens erneuern

Bevor das Zugriffstoken abläuft oder wenn eine Anfrage 401 zurückgibt, holen Sie ein neues:

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
  • Rotation. Jede Erneuerung gibt ein neues refresh_token zurück, das 60 Tage gültig ist. Speichern Sie es und verwerfen Sie das alte. Solange Sie innerhalb von 60 Tagen erneuern, läuft die Verbindung nicht ab.
  • Erkennung von Wiederverwendung. Wird ein altes Refresh-Token mehr als eine Minute nach seiner Ersetzung erneut verwendet, gibt Emailit invalid_grant (Refresh token reuse detected) zurück und widerruft die gesamte Freigabe. Der Nutzer muss dann erneut autorisieren. Serialisieren Sie Erneuerungen, damit nie zwei Worker dasselbe Refresh-Token verwenden.
  • Engerer Scope. Sie können scope übergeben, um ein Zugriffstoken mit weniger Scopes als die Freigabe zu erhalten. Mehr können Sie nicht anfordern.

Freigabe widerrufen

Wenn ein Nutzer Emailit von Ihrer App trennt, widerrufen Sie die Freigabe mit ihrem Refresh-Token:

Terminal
curl https://api.emailit.com/oauth/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

Der Endpunkt authentifiziert Ihren Client auf dieselbe Weise wie der Token-Endpunkt und gibt immer 200 mit leerem Body zurück. Das Widerrufen des Refresh-Tokens widerruft die gesamte Freigabe: Ihre Zugriffstokens funktionieren ab der nächsten Anfrage nicht mehr. Zugriffstokens lassen sich nicht einzeln widerrufen; token_type_hint=access_token gibt invalid_request zurück.

Nutzer können den Zugriff Ihrer App auch selbst widerrufen, in der Weboberfläche unter Connected apps oder mit der unten beschriebenen Freigaben-API.

Freigaben verwalten

Die Freigaben-API listet und ändert Freigaben aus Sicht des Nutzers. Sie akzeptiert die Sitzung des Nutzers in der Weboberfläche oder einen API-Schlüssel mit Vollzugriff:

Aufrufer GET /oauth/grants POST /oauth/grants/:id/revoke PUT /oauth/grants/:id/workspaces
Der Nutzer, der die App verbunden hat Seine Freigaben über alle Workspaces Widerruft die gesamte Freigabe Ändert die Workspaces der Freigabe
API-Schlüssel mit Vollzugriff Freigaben, die den Workspace des Schlüssels enthalten Entfernt den Workspace des Schlüssels aus der Freigabe; die Freigabe wird widerrufen, wenn kein Workspace übrig bleibt Nicht erlaubt (403)

Jede Freigabe in der Liste hat diese Form:

JSON
{
  "id": "9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b",
  "client_id": "https://chatgpt.com/oauth/client.json",
  "client": { "name": "ChatGPT", "uri": "https://chatgpt.com", "logo_uri": null },
  "default_workspace": { "id": "w3f9a1c2e", "name": "Acme" },
  "workspace_access": "selected",
  "workspaces": [{ "id": "w3f9a1c2e", "name": "Acme" }],
  "scopes": ["sending", "full"],
  "resource": "https://api.emailit.com/mcp",
  "created_at": "2026-10-01T09:30:12Z",
  "revoked_at": null,
  "revoked_reason": null
}

Um die Workspaces einer Freigabe zu ändern, senden Sie access (all oder selected), bei selected zusätzlich workspace_ids und optional default_workspace_id, das einer der erlaubten Workspaces sein muss:

Terminal
curl -X PUT https://api.emailit.com/oauth/grants/9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b/workspaces \
  -H "Authorization: Bearer $DASHBOARD_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "access": "selected", "workspace_ids": ["w3f9a1c2e"], "default_workspace_id": "w3f9a1c2e" }'

Der Nutzer muss Mitglied jedes ausgewählten Workspaces sein. Eine widerrufene Freigabe kann nicht bearbeitet werden (409); die App muss sich erneut verbinden.

Fehler

OAuth-Endpunkte geben Fehler im OAuth-Format zurück, nicht im Fehlerformat der REST-API:

JSON
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}
Fehler Status Wann
invalid_request 400 Ein Parameter fehlt oder ist ungültig, der Client ist bei der Autorisierung unbekannt, die redirect_uri ist nicht registriert, code_challenge_method ist nicht S256 oder ein Client-Secret wurde sowohl im Header als auch im Body gesendet.
invalid_client 401 Der Token- oder Widerrufsendpunkt kann den Client nicht authentifizieren: unbekannter Client, fehlendes oder falsches Secret.
invalid_grant 400 Der Code ist ungültig, bereits verwendet oder abgelaufen; die redirect_uri oder der PKCE-Verifier stimmt nicht überein; oder das Refresh-Token ist ungültig, abgelaufen, widerrufen oder wiederverwendet.
invalid_scope 400 Der Scope wird nicht unterstützt, wurde für den Client nicht registriert oder geht bei der Erneuerung über die Freigabe hinaus.
unsupported_response_type 400 response_type ist nicht code.
unsupported_grant_type 400 grant_type ist nicht authorization_code oder refresh_token.
access_denied Weiterleitung Der Nutzer hat Deny gewählt.
too_many_requests 429 Mehr als 20 Registrierungen pro Stunde von einer IP-Adresse.
server_error 500 Bei Emailit ist etwas schiefgelaufen. Versuchen Sie es erneut.

Solange client_id und redirect_uri nicht validiert sind, werden Autorisierungsfehler als JSON im Browser angezeigt und nie weitergeleitet. Danach werden Fehler nur bei Loopback- und privaten Redirect-URIs sowie bei Clients mit Metadatendokument an Ihren Callback weitergeleitet (mit error, error_description, state und iss); andere Clients erhalten eine JSON-Fehlerseite. Ein Deny des Nutzers wird immer weitergeleitet.

Sicherheits-Checkliste

  • Bewahren Sie client_secret und Refresh-Tokens auf Ihrem Server auf, verschlüsselt gespeichert. Liefern Sie nie ein Client-Secret in einer Mobil-, Desktop- oder Browser-App aus; verwenden Sie stattdessen einen öffentlichen Client mit PKCE.
  • Erzeugen Sie für jede Autorisierung einen neuen state und Code-Verifier und prüfen Sie state und iss beim Callback.
  • Registrieren Sie exakte Redirect-URIs. Verwenden Sie keine offenen Weiterleitungen (Open Redirects) als Callbacks.
  • Speichern Sie jede Freigabe zusammen mit dem Workspace, zu dem sie gehört, und reagieren Sie auf invalid_grant, indem Sie den Nutzer auffordern, sich erneut zu verbinden.
  • Fordern Sie sending an, wenn Sie nur E-Mails senden.
Freigaben, Token-Laufzeiten und Widerruf.
Die Endpunkte, die Ihre Tokens aufrufen können.
Für eigene Skripte sind Schlüssel einfacher.
Der OAuth-Ablauf in der Praxis.

War diese Seite hilfreich?

Danke für Ihr Feedback.

Danke, wir lesen jede Nachricht.