Pour les agents d'IA
Piloter `openemail` depuis Claude Code, Codex ou une tâche de CI : connexion sans surveillance, aide sous forme de données, simulations, scopes manquants et codes de vérification.
Le guide intégré
openemail agents affiche un court guide en Markdown pour un agent d'IA comme Claude Code ou Codex, ou pour un script en CI : comment se connecter sans personne, lire la sortie, trouver les commandes, modifier les choses en toute sécurité et parcourir les listes, que faire quand il manque un code de vérification ou un scope, et cinq recettes à copier. openemail agent est la même commande.
openemail agentsopenemail agents --json | jq -r '.recipes[].commands[]'Avec --json, le guide est un seul document avec schemaVersion, title, intro, sections de { id, title, points }, exitCodes et recipes de { id, title, commands }. Plutôt que de coller ces règles dans chaque prompt, dites une fois à l'agent, dans le fichier d'instructions que votre projet lui donne déjà, de lancer openemail agents avant d'utiliser la CLI.
Se connecter sans personne
- Utilisez une clé API. Définissez
OPENEMAIL_API_KEY, ou passez--api-keyà une seule commande. Créez-la dans Paramètres → Clés API (openemail open api-keys) avec seulement les scopes dont l'agent a besoin. Une clé n'ouvre jamais de navigateur et n'a jamais besoin de code de vérification. - Ou réutilisez une connexion par navigateur qu'une personne a faite une fois sur cette machine avec
openemail login, et choisissez-la avec--profile <name>. La CLI renouvelle ses jetons toute seule. - Rien ne demande de saisie sans terminal. Avec
--json,--no-inputouCI, ou sans terminal attaché, une valeur que la CLI aurait demandée s'arrête avec le code de sortie2et nomme l'option à passer. - Une connexion par navigateur a besoin d'une personne pour l'approuver, donc un
openemail loginsans surveillance s'arrête avec le code de sortie2et le codeunattendedavant d'enregistrer quoi que ce soit, et renvoie versopenemail login --with-token. openemail whoami --jsonaffiche l'espace de travail, le type de connexion et sesscopes.
Lire la sortie
Passez --json à chaque commande. stdout contient alors exactement un document JSON, ou un objet par ligne avec --ndjson, et la progression reste sur stderr. Un échec affiche une ligne {"error":{...}} sur stderr : décidez selon le code de sortie et son code, montrez next à une personne, et n'analysez jamais message, dont la formulation peut changer. La page « Scripts » liste chaque champ et chaque code de sortie.
Les commandes sous forme de données
--help --json affiche l'aide sous forme d'un seul document JSON, construit à partir du même registre de commandes que celui avec lequel la CLI analyse, il correspond donc toujours à la version installée. Il fonctionne à la racine, sur un groupe ou sur une commande, et openemail help <command> --json affiche la même chose.
openemail send --help --jsonopenemail domains delete --help --json | jq '.commands[0] | {scopes, destructive}'openemail help domains --json | jq -r '.commands[0].subcommands[].command'openemail --help --json | jq -r '.commands[].command'Le document
schemaVersionnumber- Change quand un champ change de sens
cli, versionstring- Toujours `openemail`, et la version qui l'a produit
pathstring[]- La commande demandée, vide pour la racine
commandsobject[]- Pour la racine, chaque commande de premier niveau, sinon celle demandée, chacune avec ses sous-commandes
globalFlagsobject[]- Les options que prend chaque commande, sous la même forme que les options d'une commande
subcommandAliasesobject- Chaque alias partagé, comme `ls` ou `rm`, et les verbes qu'il remplace
exitCodesobject[]- Chaque code de sortie sous la forme `{ code, name, meaning }`
Une commande
namestring- Le dernier mot de la commande
commandstring- La commande entière, comme `openemail domains delete`
path, aliasesstring[]- Les mots après `openemail` qui y mènent, et ses autres noms
summary, descriptionstring- Ce qu'elle fait, en une ligne et en détail
usagestring[]- Comment l'appeler
categorystring | null- Sa section dans `openemail --help` pour une commande de premier niveau, sinon `null`
group, runnable, hiddenboolean- Si elle a des sous-commandes, si elle s'exécute seule et si l'aide l'omet
authstring- La connexion dont elle a besoin : `required`, `browser` pour une connexion par navigateur uniquement, `optional` ou `none`
scopesstring[]- Les scopes d'API dont chacune de ses exécutions a besoin
destructiveboolean- Si elle demande d'abord une confirmation, à laquelle `--yes` répond
argumentsobject[]- `name`, `description`, `required` et `variadic` de chaque argument
flagsobject[]- `name`, `short`, `kind`, `required`, `repeatable`, `choices`, `placeholder`, `description` et `hidden` de chaque option
notes, examplesobject[]- Les blocs d'aide supplémentaires sous la forme `{ title, lines }`, et les exemples sous la forme `{ command, note }`
resourceobject | null- Pour une commande de ressource, la méthode du SDK et l'appel REST qui se trouvent derrière, sinon `null`
subcommandsobject[]- Les commandes sous un groupe, sous la même forme
Une ressource
namespacestring- Le namespace du SDK, comme `domains`
sdkMethodstring- La méthode du SDK, comme `openemail.domains.delete`
sdkMethodAllstring | null- Pour une liste, la méthode `listAll` que parcourt `--all --json`
httpMethod, httpPathstring- L'appel REST, comme `DELETE` et `/domains/{id}`
scopesstring[]- Les scopes dont la méthode a besoin
authstring- `apiKey`, ou `none` et `inboxToken` pour une méthode qui n'envoie aucune clé API
returnsobject- `{ shape, type }` : la forme de la réponse, comme `object` ou `page`, et son type SDK
paginatesboolean- Si elle renvoie une page d'une liste
L'arbre complet fait environ un mégaoctet, presque entièrement les 198 commandes de ressources, demandez donc la commande dont vous avez besoin, ou filtrez l'arbre avec jq. Le texte garde ses accents graves et ne contient aucun code de couleur, et les commandes masquées comme security sont incluses avec hidden à true.
Simulations
--dry-run fonctionne sur toutes les commandes sauf mcp serve. Les lectures s'exécutent normalement, puis la première requête qui changerait quelque chose est affichée au lieu d'être envoyée, et la commande se termine avec le code 0 sans rien faire d'autre. Les confirmations sont sautées, puisque rien n'est envoyé, donc un agent peut voir ce que ferait une commande destructrice sans passer --yes.
openemail domains delete <domain-id> --dry-runopenemail send --from [email protected] --to [email protected] --subject "Hi" --text "Hello" --dry-run --json{ "dryRun": true, "request": { "method": "POST", "url": "https://api.openemail.uk/emails", "headers": { "accept": "application/json", "authorization": "Bearer [redacted]", "content-type": "application/json", "idempotency-key": "58e6fb61-ad2e-401e-b141-7a0546c7c749", "user-agent": "openemail-cli/0.0.1 openemail-sdk/0.0.5" }, "body": { "from": "[email protected]", "to": [ "[email protected]" ], "subject": "Hi", "text": "Hello" }, "raw": null }}- Un changement est toute requête sauf
GETetHEAD, un appel d'outil MCP, et les requêtes de connexion et de déconnexion deloginetlogout. Le renouvellement des jetons etdocs asks'exécutent quand même. - Le plan montre la méthode, l'URL complète, les en-têtes avec la valeur de
Authorizationréduite àBearer [redacted], et le corps JSON avec les champs secrets, comme une clé Resend, masqués. Un envoi de fichier ne montre que sa taille et son type de contenu. - Un changement qui reste sur cette machine, comme
profile use,login --with-tokenou l'oubli d'une clé API enregistrée, affiche{"dryRun":true,"local":{"action","profile"}}et n'enregistre rien. - Une commande qui affiche ce qu'elle a lu avant son premier changement le montre d'abord :
readaffiche le fil, puis la requête qui le marquerait comme lu. Passez--no-mark-readpour omettre la seconde. mcp serverefuse--dry-runavec le code de sortie2, car son client décide de ce qu'il envoie. Prévisualisez plutôt un appel d'outil avecopenemail mcp call <tool> --dry-run.
Scopes manquants
Chaque commande connaît les scopes d'API dont elle a toujours besoin, et son aide les liste. Quand il en manque un à une connexion enregistrée, la commande demande une fois la liste actuelle à l'API, donc un accès accordé sur le site web après la connexion compte tout de suite. Si le scope manque toujours, elle s'arrête avec le code de sortie 4 et le code insufficient_scope avant de demander quoi que ce soit ou d'envoyer une requête :
{"error":{"type":"cli_error","code":"insufficient_scope","message":"This sign-in does not have the emails:send permission, which openemail send needs.","hint":null,"next":"Give this app more access in Account settings, Connected apps (openemail open apps, then Edit access), or run openemail login --force and choose more access.","status":null,"requestId":null,"param":null,"docUrl":null,"exitCode":4}}- Pour une connexion par navigateur,
nextdit de donner plus d'accès à l'application dans Compte → Ligne de commande (openemail open cli, puis Modifier l'accès), ou de lanceropenemail login --forceet de choisir plus d'accès. Une connexion par navigateur ne reçoit jamaiskeys:writenikeys:manage, donc pour ceux-là elle renvoie vers une clé API. - Pour une clé API,
nextdit d'utiliser une clé qui a le scope. - Une clé venant de
--api-keyou deOPENEMAIL_API_KEYn'est pas vérifiée à l'avance, et c'est l'API qui décide. Quand l'API refuse un appel pour un scope manquant, l'erreur porte le mêmenext.
Codes de vérification
Une clé API n'a jamais besoin de code de vérification. Une connexion par navigateur en a besoin d'un avant un changement sensible, comme ajouter un webhook, créer une règle, modifier un membre ou retirer un domaine, et un agent ne peut pas le taper. Avant que l'agent ne s'exécute, une personne lance donc openemail verify dans un terminal avec le même profil, ou choisit Autoriser les modifications pendant 60 minutes pour cette connexion dans Compte → Ligne de commande. L'un ou l'autre couvre les 60 minutes suivantes.
openemail verifyopenemail verify --status --jsonverify --status --json indique à l'agent si le profil est vérifié, dans elevated, et jusqu'à quand, dans elevatedUntil. Sans vérification, le changement s'arrête avec le code de sortie 4 et le code step_up_required, et rien n'est modifié :
{"error":{"type":"cli_error","code":"step_up_required","message":"This action needs a verification code, and there is no interactive terminal to ask for one.","hint":null,"next":"Run openemail verify in an interactive terminal first, then run this again within 60 minutes. An API key never needs a code.","status":403,"requestId":"req_9Qm4tV","param":null,"docUrl":"https://openemail.uk/docs/api/errors#step_up_required","exitCode":4}}Par MCP
Un agent qui parle MCP peut utiliser le serveur MCP d'OpenEmail à la place. openemail mcp config --client claude-code, ou codex, cursor et les autres clients qu'il liste, affiche la configuration, et openemail mcp serve est un pont local qui réutilise la connexion par navigateur de cette CLI. Les clés API ne peuvent pas atteindre le serveur MCP. La page « IA et MCP » donne les détails.
Recettes
openemail inbox --unread --limit 20 --jsonopenemail inbox --unread --json | jq -r '.items[].id'openemail read CAHk7pQ2x9LmZ4 --no-mark-read --jsonopenemail send --from [email protected] --to [email protected] --subject "Weekly report" --body-file report.md --idempotency-key weekly-report-39 --jsonopenemail domains create --domain example.com --dry-run --jsonopenemail domains create --domain example.com --jsonopenemail whoami --json | jq '.scopes'