Konfiguration
Drei Wege, einen Client zu erzeugen, alle Optionen und was er ablehnt, bevor eine Anfrage gesendet wird.
Optionen
import os from openemail import AsyncOpenEmail, OpenEmail, init, openemail init(os.environ['OPENEMAIL_API_KEY'])openemail.me.ping() billing = OpenEmail(os.environ['BILLING_API_KEY']) pinned = OpenEmail('oe_live_...', base_url='https://api.openemail.uk') background = AsyncOpenEmail()| Einstiegspunkt | Was Sie damit erhalten |
|---|---|
| init(...) | Konfiguriert den gemeinsamen Client und gibt ihn zurück. openemail ist ab diesem Zeitpunkt in jedem Modul dieser Client, und alles, was Sie weglassen, wird aus der Umgebung gelesen. |
| openemail | Der gemeinsame Client. Wird er vor init verwendet, baut er sich beim ersten Aufruf aus OPENEMAIL_API_KEY und OPENEMAIL_BASE_URL selbst auf. |
| OpenEmail(...) | Ein separater Client, für einen zweiten Schlüssel neben dem gemeinsamen oder um die Instanz zu erzeugen, die Ihr eigenes Modul exportiert. Alles, was Sie weglassen, wird wie bei init aus der Umgebung gelesen, und create_client ist dieselbe Klasse unter einem anderen Namen. |
| AsyncOpenEmail(...) | Ein separater asynchroner Client mit denselben Optionen, bei dem jede Methode mit await aufgerufen wird. Der gemeinsame openemail ist synchron, diesen hier erzeugen Sie also selbst. |
| get_client() und reset_client() | get_client gibt den gemeinsamen Client selbst zurück und erzeugt ihn aus der Umgebung, wenn init nicht gelaufen ist. reset_client verwirft ihn, sodass die nächste Verwendung einen neuen erzeugt. |
import httpxfrom openemail import init init( 'oe_live_...', base_url='https://api.openemail.uk', timeout=30, max_retries=2, http_client=httpx.Client(proxy='http://proxy.internal:3128'), headers={'X-Team': 'billing'}, user_agent='billing-service/1.4', disable_update_notice=True,)| Option | Standard | Hinweise |
|---|---|---|
| api_key | OPENEMAIL_API_KEY | Wird von init und OpenEmail aus der Umgebung gelesen. Muss mit oe_live_ oder oe_test_ beginnen. |
| access_token | OPENEMAIL_ACCESS_TOKEN | Ein OAuth-Zugriffstoken oder eine Funktion, die eines zurückgibt, anstelle von api_key. Siehe OAuth-Zugriffstoken weiter unten. |
| base_url | https://api.openemail.uk | Oder OPENEMAIL_BASE_URL. Ein abschließender Schrägstrich wird entfernt, und init und OpenEmail setzen https:// vor einen blanken Host bzw. http:// vor einen Host auf diesem Rechner: localhost, eine 127.x.x.x-Adresse oder ::1. Anmeldedaten werden nie über einfaches http an einen anderen Host gesendet, und 0.0.0.0 oder [::] löst beim Erzeugen des Clients eine Ausnahme aus, weil das Adressen sind, auf denen ein Server lauscht, und keine, an die man Anfragen sendet. |
| timeout | 30 | In Sekunden, pro Versuch, nicht pro Aufruf. Umfasst das Lesen des Bodys, nicht nur der Header. 0 deaktiviert es. files.upload lässt mindestens 600 Sekunden zu, sofern der Aufruf kein eigenes timeout übergibt. |
| max_retries | 2 | Zusätzliche Versuche nach dem ersten, bei Aufrufen, die sich gefahrlos wiederholen lassen. Wird am Client gesetzt, nicht pro Aufruf. |
| http_client | ein neuer httpx.Client | Übergeben Sie einen eigenen für einen Proxy, eigene TLS-Einstellungen, einen gemounteten Transport oder ein Test-Double: einen httpx.Client an OpenEmail, einen httpx.AsyncClient an AsyncOpenEmail. Das Schließen des Clients lässt einen von Ihnen übergebenen offen. |
| headers | {} | Werden bei jeder Anfrage gesendet. |
| user_agent | openemail-python/<version> | Werden bei jeder Anfrage gesendet. |
| disable_update_notice | False | Überspringt die einmal pro Prozess erfolgende Prüfung auf eine neuere Version auf PyPI. Die Prüfung läuft nur, wenn die Ausgabe an ein Terminal geht, und OPENEMAIL_DISABLE_UPDATE_NOTICE schaltet sie ebenfalls ab. |
Was er vor dem Senden ablehnt
Diese lösen ValueError aus (oder TypeError, wo die Tabelle es angibt), bevor irgendeine Anfrage gesendet wird, statt beim ersten Versand als verwirrender Fehler aufzutauchen. Die Meldung nennt, was falsch war und was stattdessen zu übergeben ist.
| Abgelehnt | Warum |
|---|---|
| Gar kein Schlüssel | Weder api_key noch OPENEMAIL_API_KEY wurde gesetzt, es gibt also nichts, womit authentifiziert werden könnte. |
| Ein Session-Cookie oder Session-Token | Nur oe_live_ und oe_test_ authentifizieren hier, und die API sieht es genauso. Die Prüfung ist ein Präfix und nichts weiter, ein widerrufener Schlüssel scheitert daher erst auf der Leitung. |
| Eine base_url, die keine http- oder https-URL ist | Anderes lässt sich nicht abrufen, der Client lehnt es daher schon beim Erzeugen ab, statt bei der ersten Anfrage zu scheitern. |
| Eine base_url auf 0.0.0.0 oder [::] | Eine Adresse, auf der ein Server lauscht, und keine, an die man Anfragen sendet. Die Meldung nennt stattdessen 127.0.0.1 oder [::1] mit demselben Port. |
| Anmeldedaten über einfaches http | Wird beim Aufruf abgelehnt, bevor die Anfrage hinausgeht, es sei denn, der Server läuft auf diesem Rechner. Jeder im Netzwerk könnte sie mitlesen. |
| Eine leere oder nur aus Punkten bestehende id bei einer beliebigen Methode | Wird beim Aufruf der Methode ausgelöst. Ein Pfadsegment aus Punkten wird von jedem URL-Parser entfernt, die Anfrage träfe also einen anderen Endpunkt. |
| Ein http_client der falschen Art | Ein TypeError beim Erzeugen des Clients. OpenEmail nimmt einen httpx.Client entgegen und AsyncOpenEmail einen httpx.AsyncClient. |
| Ein Body-Wert, den JSON nicht abbilden kann | Ein TypeError, der seinen Typ nennt. JSON-Typen werden durchgereicht, und ein datetime, ein date oder ein set wird für Sie umgewandelt. |
Es gibt keine Option test_mode und wird auch keine geben. Das Schlüsselschema ist Teil der Zugangsdaten und kein Hinweis, der Modus ist also eine Eigenschaft des Schlüssels. client.mode liest das Präfix und entscheidet nichts.
Ein Client, mehrere Schlüssel
Erzeugen Sie den Client einmal und teilen Sie ihn. Eine frische Instanz pro Anfrage wirft den Verbindungspool und die Konfiguration ohne Gegenwert weg, und kein Zustand darauf ist aufruferspezifisch.
Ein Client lässt sich gefahrlos zwischen Threads teilen. close() oder das Ende eines with-Blocks schließt den Verbindungspool, den er geöffnet hat, und ein von Ihnen übergebener http_client bleibt offen, damit Sie ihn selbst schließen.
Für den Fall, der sonst eine Instanz pro Schlüssel erzwingen würde, etwa einen Job, der im Namen mehrerer Workspaces sendet, übergeben Sie api_key beim Aufruf. Er ersetzt den Authorization-Header für diese Anfrage und hinterlässt nichts am Client.
from openemail.types import EmailSend workspace_key = 'oe_live_...' message: EmailSend = {'from': sender, 'to': recipient, 'subject': subject, 'text': text} client.emails.send(message) client.emails.send(message, api_key=workspace_key) client.threads.list(folder='inbox', api_key=workspace_key)client.webhooks.list(api_key=workspace_key)Jede Methode außerhalb von temp_mail nimmt ihn als Schlüsselwortargument entgegen, neben timeout. Er wird vor dem Senden der Anfrage nach derselben Regel geprüft, die der Konstruktor verwendet, ein Tippfehler löst daher einen ValueError aus, der api_key= on this call nennt, statt eines 401 zu Anmeldedaten, die Sie dann erst suchen müssten. Ein wiederholter Aufruf behält den Schlüssel, den er bekommen hat.
timeout wird in Sekunden angegeben und ersetzt das Timeout des Clients für diesen einen Aufruf, bei jedem seiner Versuche.
client.mode beschreibt den Schlüssel, mit dem der Client KONSTRUIERT wurde, und folgt keiner Überschreibung. Sobald ein Client mehrere Schlüssel bedient, gibt es keinen einzelnen Modus zu melden, lesen Sie ihn daher am übergebenen Schlüssel ab.
Ein Endpunkt, den keine Methode kapselt
client.raw.request sendet eine Anfrage mit den Anmeldedaten, der Basis-URL, dem Timeout und der Retry-Policy des Clients und gibt das geparste JSON zurück. Es nimmt method, query, body, api_key und timeout entgegen. Ein GET wird wiederholt, alles andere einmal gesendet, sofern Sie nicht repeatable=True übergeben. idempotent=True fügt einen Idempotency-Key hinzu, und zwar den, den Sie als idempotency_key übergeben, oder einen frischen.
result = client.raw.request('/something-new', method='POST', body={'name': 'Invoices'})Der Pfad muss mit genau einem / beginnen. Alles andere, etwa //host/x, löst eine Ausnahme aus, bevor eine Anfrage gesendet wird, ebenso ein Pfad, dessen fertige URL den Ursprung der Basis-URL verlässt, damit die mitgeführten Anmeldedaten nie einen anderen Host erreichen.
Wegwerf-Postfächer
create_temp_mail() erzeugt einen Client ohne API-Schlüssel, und create_async_temp_mail() ist sein asynchrones Gegenstück. Er legt Postfächer anonym an, und jeder Lesezugriff sendet das Postfach-Token, das create zurückgegeben hat, oder das neuere, das extend zurückgegeben hat, entweder pro Aufruf als inbox_token oder einmalig als create_temp_mail(inbox_token=...).
from openemail import create_temp_mail temp_mail = create_temp_mail() inbox = temp_mail.create()messages = temp_mail.list_messages(inbox['id'], inbox_token=inbox['token']) print(messages['items'], messages['expiresAt'])OAuth-Zugriffstoken
Noch nicht verfügbarEine App, die jemand über OAuth verbunden hat, etwa ein Kommandozeilenwerkzeug oder ein Agent, hält ein Zugriffstoken statt eines API-Schlüssels. Übergeben Sie es als access_token, entweder das Token selbst oder eine Funktion, die es zurückgibt und auf AsyncOpenEmail auch async sein darf. Die Funktion wird einmal pro Aufruf aufgerufen, und die Wiederholungen dieses Aufrufs verwenden, was sie zurückgab. Erneuern Sie das Token also darin, wenn es bald abläuft, dann muss der Client nie neu gebaut werden.
from openemail import OpenEmail client = OpenEmail(access_token=session.fresh_access_token) me = client.me.get() if me['object'] == 'oauth_token': print(me['clientId'], me['expiresAt'])| Fall | Was passiert |
|---|---|
| api_key und access_token zusammen oder keins von beiden | Der Konstruktor löst ValueError aus. Ist keins von beiden gesetzt, nennt die Meldung OPENEMAIL_API_KEY und OPENEMAIL_ACCESS_TOKEN. |
| Ein Wert, der kein Token ist | Ein Token hat 1 bis 512 Zeichen und beginnt nicht mit oe_, die Prüfung, die is_access_token vornimmt. Ein String, der sie nicht besteht, löst im Konstruktor eine Ausnahme aus, und eine Funktion, die so einen zurückgibt, lässt den Aufruf scheitern, bevor irgendetwas gesendet wird. |
| OPENEMAIL_ACCESS_TOKEN | Gelesen von init, OpenEmail und dem gemeinsamen openemail, wenn Sie keinen der beiden Zugänge übergeben und OPENEMAIL_API_KEY nicht gesetzt ist. Ein Schlüssel in der Umgebung hat also Vorrang. |
| Eine Funktion, die eine Ausnahme auslöst | Der Aufruf löst genau diesen Fehler unverändert aus, und es wird nichts gesendet. |
| Eine Funktion auf OpenEmail, die ein Awaitable zurückgibt | Ein ValueError, da der synchrone Client nicht darauf warten kann. Auf AsyncOpenEmail darf die Funktion async sein. |
| Ein api_key pro Aufruf | Ersetzt das Token für diese eine Anfrage, und die Funktion wird nicht aufgerufen. |
| mode | Unter einem Token immer live. |
| create_temp_mail() | Sendet keinen Zugang, egal was in der Umgebung steht. |
| me.get() und me.ping() | Für ein Token antwortet get mit object gleich oauth_token, id und roleId gleich None, der clientId der verbundenen App und expiresAt, dem Zeitpunkt, an dem die Freigabe der Person für die App ausläuft. ping antwortet mit kind gleich oauth, keyId gleich None und der clientId. KeyResource und PingResource sind Unions, prüfen Sie also object oder kind, bevor Sie clientId oder expiresAt lesen. |
Ein Token handelt für eine Person und liest ihre Mails so, wie sie es kann. Halten Sie es also wie einen Schlüssel auf einem Server.
Bestätigungscodes
Noch nicht verfügbarVor einer heiklen Änderung, etwa dem Löschen einer Domain oder dem Ändern eines Webhooks, verlangt die API von einem Zugriffstoken den Bestätigungscode, den die Web-App von der Person verlangen würde. Der Aufruf löst einen OpenEmailApiError aus, dessen is_step_up_required True ist, und es wurde nichts geändert. Fordern Sie einen Code an, bestätigen Sie den, den die Person Ihnen gibt, und rufen Sie dann erneut auf. Ein API-Schlüssel wird nie gefragt.
from openemail import OpenEmailApiError try: client.domains.delete(domain_id)except OpenEmailApiError as error: if not error.is_step_up_required: raise challenge = client.security.begin_step_up() if challenge['method'] == 'email': prompt = f'Enter the code we emailed to {challenge.get("sentTo")}: ' else: prompt = 'Enter the code from your authenticator app, or a backup code: ' client.security.verify_step_up({'code': input(prompt)}) client.domains.delete(domain_id)| Methode | Was es tut |
|---|---|
| security.step_up_status() | Ob die App gerade bestätigt ist (elevated, elevatedUntil), wie der nächste Code geprüft wird (method, email oder totp) und minutes, die Länge des Zeitfensters. Sie sendet nichts und meldet keine Pause. |
| security.begin_step_up(body=None) | Eröffnet eine Abfrage. Bei email geht ein sechsstelliger Code an die Adresse, mit der sich die Person anmeldet, und sentTo zeigt sie maskiert. Bei totp liest sie einen aus ihrer Authenticator-App ab oder nimmt einen Wiederherstellungscode. Eine noch offene Abfrage mit verbleibenden Versuchen wird wiederverwendet, außer Sie übergeben {'resend': True}, und eine gesperrte oder abgelaufene wird durch einen einfachen Aufruf ersetzt. Jede App darf pro Person 5 Abfragen pro Stunde und 20 in 24 Stunden eröffnen, und die nächste löst einen 429 step_up_throttled aus. |
| security.verify_step_up({'code': code}) | Prüft den Code und schaltet heikle Änderungen für diese App 60 Minuten lang frei, bis elevatedUntil, über REST und über die MCP-Tools, die dieselben Änderungen vornehmen. Nach 10 falschen Codes in 24 Stunden von dieser App oder 20 von allen Apps der Person zusammen lösen dieser Aufruf und begin_step_up einen 429 step_up_locked aus, mit einer Meldung, die sagt, wann die Bestätigung wieder möglich ist. |
Der Client fragt nie selbst nach einem Code und wiederholt den Aufruf nicht von sich aus, und weder begin_step_up noch verify_step_up wird automatisch wiederholt, weil ein erneuter Versuch nach einer verlorenen Antwort eine zweite E-Mail senden oder einen zweiten Versuch verbrauchen könnte. Die beiden Methoden brauchen keinen Scope, und ein API-Schlüssel bekommt bei jeder von ihnen 400 step_up_not_applicable. STEP_UP_ERROR_CODES benennt jeden Grund, aus dem eine Bestätigung scheitern kann, und die Fehlerseite der API sagt, was jeweils zu tun ist.