Aller à la documentation
Python

Configuration

Trois façons de construire un client, toutes les options, et ce qu'il refuse avant qu'une requête ne parte.

Options

clients.py
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()
Point d'entréeCe que vous obtenez
init(...)Configure le client partagé et le renvoie. openemail est ce client à partir de là, dans chaque module, et tout ce que vous omettez est lu depuis l'environnement.
openemailLe client partagé. Utilisé avant init, il se construit lui-même à partir de OPENEMAIL_API_KEY et OPENEMAIL_BASE_URL au premier appel.
OpenEmail(...)Un client distinct, pour une deuxième clé à côté de la clé partagée, ou pour construire l'instance qu'exporte votre propre module. Tout ce que vous omettez est lu depuis l'environnement, comme le fait init, et create_client est la même classe sous un autre nom.
AsyncOpenEmail(...)Un client asynchrone distinct, avec les mêmes options, dont chaque méthode s'appelle avec await. Le openemail partagé est synchrone : celui-ci, vous le construisez donc vous-même.
get_client() et reset_client()get_client renvoie le client partagé lui-même, en le construisant à partir de l'environnement si init n'a pas été exécuté. reset_client l'oublie, si bien que l'utilisation suivante en construit un nouveau.
options.py
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,)
OptionPar défautRemarques
api_keyOPENEMAIL_API_KEYLu depuis l'environnement par init et OpenEmail. Doit commencer par oe_live_ ou oe_test_.
access_tokenOPENEMAIL_ACCESS_TOKENUn jeton d'accès OAuth, ou une fonction qui en renvoie un, à la place d'api_key. Voir la section Jetons d'accès OAuth ci-dessous.
base_urlhttps://api.openemail.ukOu OPENEMAIL_BASE_URL. Une barre oblique finale est retirée, et init et OpenEmail placent https:// devant un hôte nu, ou http:// devant un hôte de cette machine : localhost, une adresse 127.x.x.x ou ::1. Un identifiant n'est jamais envoyé en http simple à un autre hôte, et 0.0.0.0 ou [::] lève une exception à la création du client, car ce sont des adresses sur lesquelles un serveur écoute, pas des adresses auxquelles envoyer des requêtes.
timeout30En secondes, par tentative, pas par appel. Couvre la lecture du corps, pas seulement celle des en-têtes. 0 le désactive. files.upload accorde au moins 600 secondes, sauf si l'appel passe son propre timeout.
max_retries2Tentatives supplémentaires après la première, sur les appels qu'il est sûr de répéter. Se règle sur le client, pas par appel.
http_clientun nouveau httpx.ClientPassez le vôtre pour un proxy, vos propres paramètres TLS, un transport monté ou un double de test : un httpx.Client à OpenEmail, un httpx.AsyncClient à AsyncOpenEmail. Fermer le client laisse ouvert celui que vous avez passé.
headers{}Envoyés à chaque requête.
user_agentopenemail-python/<version>Envoyés à chaque requête.
disable_update_noticeFalseIgnore la vérification, une fois par processus, d'une version plus récente sur PyPI. La vérification ne s'exécute que lorsque la sortie va vers un terminal, et OPENEMAIL_DISABLE_UPDATE_NOTICE la désactive aussi.

Ce qu'il refuse avant d'envoyer

Ces cas lèvent ValueError, ou TypeError là où le tableau l'indique, avant l'envoi de toute requête, au lieu de ressortir en échec déroutant à votre premier envoi. Le message dit ce qui n'allait pas et quoi passer à la place.

RefuséPourquoi
Aucune cléNi api_key ni OPENEMAIL_API_KEY n'était défini : il n'y a donc rien pour s'authentifier.
Un cookie ou un jeton de sessionSeuls oe_live_ et oe_test_ authentifient ici, et l'API le dit aussi. La vérification porte sur le préfixe et rien de plus : une clé révoquée échoue donc quand même sur le réseau.
Un base_url qui n'est pas une URL http ou httpsRien d'autre ne peut être récupéré : le client le refuse donc dès sa création au lieu d'échouer à la première requête.
Un base_url sur 0.0.0.0 ou [::]Une adresse sur laquelle un serveur écoute, pas une adresse à laquelle envoyer des requêtes. Le message propose à la place 127.0.0.1 ou [::1] avec le même port.
Un identifiant en http simpleRefusé à l'appel, avant que la requête ne parte, sauf si le serveur est sur cette machine. N'importe qui sur le réseau pourrait le lire.
Un id vide ou fait uniquement de points sur n'importe quelle méthodeL'exception est levée à l'appel de la méthode. Un segment de chemin fait de points est supprimé par tous les analyseurs d'URL : la requête atteindrait donc un autre endpoint.
Un http_client du mauvais typeUne TypeError à la création du client. OpenEmail prend un httpx.Client, et AsyncOpenEmail un httpx.AsyncClient.
Une valeur du corps que JSON ne peut pas transporterUne TypeError qui nomme son type. Les types JSON passent tels quels, et un datetime, un date ou un set est converti pour vous.

Il n'y a pas d'option test_mode et il n'y en aura pas. Le schéma de la clé fait partie de l'identifiant plutôt que d'être un indice : le mode est donc une propriété de la clé. client.mode lit le préfixe et ne décide rien.

Un client, plusieurs clés

Construisez le client une fois et partagez-le. Une nouvelle instance par requête jette pour rien le pool de connexions et la configuration, et aucun état qu'il porte n'est propre à un appelant.

Un même client peut être partagé sans risque entre threads. close() ou la fin d'un bloc with ferme le pool de connexions qu'il a ouvert, et un http_client que vous avez passé reste ouvert : c'est à vous de le fermer.

Pour le cas qui imposerait sinon une instance par clé, comme un job qui envoie pour le compte de plusieurs espaces de travail, passez api_key sur l'appel. Il remplace l'en-tête Authorization pour cette requête et ne laisse rien derrière lui sur le client.

per_call_key.py
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)

Toutes les méthodes hors temp_mail l'acceptent comme argument nommé, à côté de timeout. Elle est vérifiée avant l'envoi de la requête, par la règle qu'utilise le constructeur : une faute de frappe lève donc une ValueError qui nomme api_key= on this call plutôt qu'un 401 portant sur un identifiant qu'il faut ensuite aller chercher. Un appel réessayé conserve la clé qu'on lui a donnée.

timeout est en secondes et remplace le timeout du client pour ce seul appel, sur chacune de ses tentatives.

client.mode décrit la clé avec laquelle le client a été CONSTRUIT et ne suit pas une surcharge. Dès qu'un client sert plusieurs clés, il n'y a plus de mode unique à rapporter : lisez-le sur la clé que vous avez passée.

Un endpoint qu'aucune méthode n'enveloppe

client.raw.request envoie une requête en appliquant l'identifiant, l'URL de base, le timeout et la politique de réessai du client, et renvoie le JSON analysé. Cette méthode accepte method, query, body, api_key et timeout. Elle réessaie un GET et envoie tout le reste une seule fois, sauf si vous passez repeatable=True. idempotent=True ajoute un Idempotency-Key, celui que vous passez comme idempotency_key ou un nouveau.

raw_request.py
result = client.raw.request('/something-new', method='POST', body={'name': 'Invoices'})

Le chemin doit commencer par un seul /. Tout autre chemin, comme //host/x, lève une exception avant l'envoi de la requête, de même qu'un chemin dont l'URL finale sort de l'origine de l'URL de base : l'identifiant transporté n'atteint ainsi jamais un autre hôte.

Boîtes jetables

create_temp_mail() construit un client qui ne porte aucune clé API, et create_async_temp_mail() est son équivalent asynchrone. Il crée des boîtes anonymement, et chaque lecture envoie le jeton de boîte renvoyé par create, ou le plus récent renvoyé par extend, soit à chaque appel via inbox_token, soit une fois pour toutes via create_temp_mail(inbox_token=...).

temp_mail.py
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'])

Jetons d'accès OAuth

Pas encore disponible

Une application qu'une personne a connectée en OAuth, comme un outil en ligne de commande ou un agent, détient un jeton d'accès plutôt qu'une clé API. Passez-le comme access_token, soit le jeton lui-même, soit une fonction qui le renvoie et qui, sur AsyncOpenEmail, peut être async. La fonction est appelée une fois par appel, et les nouvelles tentatives de cet appel réutilisent ce qu'elle a renvoyé : renouvelez donc le jeton à l'intérieur quand il approche de son expiration, et le client n'a jamais à être reconstruit.

access_token.py
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'])
CasCe qui se passe
api_key et access_token ensemble, ou aucun des deuxLe constructeur lève ValueError. Sans aucun des deux, le message nomme OPENEMAIL_API_KEY et OPENEMAIL_ACCESS_TOKEN.
Une valeur qui n'est pas un jetonUn jeton fait de 1 à 512 caractères et ne commence pas par oe_, la vérification que fait is_access_token. Une chaîne qui y échoue lève une exception dès le constructeur, et une fonction qui en renvoie une fait échouer l'appel avant tout envoi.
OPENEMAIL_ACCESS_TOKENLu par init, OpenEmail et le openemail partagé quand vous ne passez aucun des deux identifiants et que OPENEMAIL_API_KEY n'est pas défini : une clé dans l'environnement l'emporte donc.
Une fonction qui lève une exceptionL'appel lève cette erreur, inchangée, et rien n'est envoyé.
Une fonction sur OpenEmail qui renvoie un objet awaitableUne ValueError, car le client synchrone ne peut pas l'attendre. Sur AsyncOpenEmail, la fonction peut être async.
Un api_key par appelRemplace le jeton pour cette seule requête, et la fonction n'est pas appelée.
modeToujours live avec un jeton.
create_temp_mail()N'envoie aucun identifiant, quel que soit le contenu de l'environnement.
me.get() et me.ping()Pour un jeton, get répond avec object à oauth_token, id et roleId à None, le clientId de l'application connectée, et expiresAt, le moment où l'autorisation donnée par la personne à l'application expire. ping répond avec kind à oauth, keyId à None et le clientId. KeyResource et PingResource sont des unions : distinguez donc sur object ou kind avant de lire clientId ou expiresAt.

Un jeton agit pour une personne et lit son courrier comme elle le peut : gardez-le donc sur un serveur, comme une clé.

Codes de vérification

Pas encore disponible

Avant un changement sensible, comme supprimer un domaine ou modifier un webhook, l'API demande à un jeton d'accès le code de vérification que l'application web demanderait à la personne. L'appel lève une OpenEmailApiError dont is_step_up_required vaut True, et rien n'a été modifié. Demandez un code, vérifiez celui que la personne vous donne, puis refaites l'appel. On ne le demande jamais à une clé API.

step_up.py
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)
MéthodeCe qu'il fait
security.step_up_status()Si l'application est vérifiée en ce moment (elevated, elevatedUntil), comment le prochain code est contrôlé (method, email ou totp), et minutes, la durée de la fenêtre. N'envoie rien et ne signale pas de pause.
security.begin_step_up(body=None)Ouvre une vérification. Avec email, un code à six chiffres part vers l'adresse avec laquelle la personne se connecte, et sentTo l'affiche masquée. Avec totp, elle en lit un dans son application d'authentification ou utilise un code de récupération. Une vérification encore ouverte à laquelle il reste des essais est réutilisée, sauf si vous passez {'resend': True}, et une vérification verrouillée ou expirée est remplacée par un simple appel. Chaque application peut en ouvrir 5 par heure et 20 en 24 heures pour chaque personne, et la suivante lève un 429 step_up_throttled.
security.verify_step_up({'code': code})Contrôle le code et débloque les changements sensibles pour cette application pendant 60 minutes, jusqu'à elevatedUntil, par REST et par les outils MCP qui font les mêmes changements. Après 10 codes erronés en 24 heures venant de cette application, ou 20 venant de toutes les applications de la personne ensemble, cet appel et begin_step_up lèvent un 429 step_up_locked avec un message qui indique quand la vérification reprend.

Le client ne demande jamais de code et ne refait jamais l'appel de lui-même, et ni begin_step_up ni verify_step_up n'est réessayé automatiquement, car une nouvelle tentative après une réponse perdue pourrait envoyer un deuxième e-mail ou consommer un deuxième essai. Ces méthodes ne demandent aucune portée, et une clé API qui en appelle une reçoit 400 step_up_not_applicable. STEP_UP_ERROR_CODES nomme chaque façon dont une vérification peut échouer, et la page des erreurs de l'API dit quoi faire pour chacune.