Guides pour les développeurs IA
La surface API de Bird est conçue pour les agents — une opération par outil, du JSON en entrée et en sortie, des résultats vérifiables par la machine — mais un agent fiable nécessite tout de même les bons patterns autour. Les cinq ci-dessous couvrent les modes de défaillance qui cassent réellement les intégrations d'agents : traiter l'acceptation comme une livraison, réessayer à l'aveugle, et parser de la prose au lieu de la structure. Chaque pattern fonctionne de la même manière, que votre agent pilote le serveur MCP ou le bird CLI.
Pattern 1 : Boucler une opération à la fois
Les outils de Bird sont volontairement granulaires — envoyer un message, récupérer un message, lister les domaines, créer un endpoint webhook — et chacun retourne du JSON structuré dont les champs peuvent être vérifiés par l'étape suivante. Construisez la boucle de sorte que la condition de sortie de chaque étape soit lue depuis la sortie de l'étape précédente, jamais inférée :
Exemple de code
loop:
result = run_tool(next_operation) # one operation per call
if result.ok: advance using result.data # e.g. the em_… id, the verified domain
else: branch on the failure category # see Pattern 4Avec le CLI, la catégorie d'erreur est le code de sortie (2 entrée invalide, 3 non trouvé, 4 auth, 1 autre), donc le branchement ne nécessite aucun parsing de message :
Exemple de code
bird email get "$id" --format json > msg.json
case $? in
0) jq .status msg.json ;; # advance
3) echo "wrong id — fix, don't retry" ;;
4) bird auth login ;; # recover, then re-run
esacLa granularité est le point essentiel : un agent qui peut vérifier l'état entre les étapes récupère de n'importe quelle défaillance individuelle ; un agent pilotant une méga-opération unique ne peut que tout recommencer.
Pattern 2 : Un envoi retourne 202 — le résultat arrive plus tard
Faites un POST d'envoi et vous obtenez 202 Accepted avec un identifiant de message. Accepted signifie que Bird a pris en charge le message, pas qu'il a été livré — le résultat final n'arrive jamais dans la réponse. Il arrive sous forme d'événements webhook : email.delivered quand le serveur du destinataire l'accepte, email.bounced quand la livraison échoue définitivement, email.complained, et ainsi de suite.
Un agent qui déclare le succès au 202 manquera silencieusement chaque rebond. Structurez la tâche comme envoi-puis-attente :
Exemple de code
send → 202 + em_… id # record the id, do NOT report "sent successfully"
await webhook event where data.email_id == em_… id:
email.delivered → done
email.bounced → report failure with bounce_type / bounce_descriptionCorrélez sur email_id — les payloads webhook renvoient vos tags et métadonnées aux côtés des champs d'identité, donc votre propre contexte revient sans recherche supplémentaire, et rappelez-vous que les livraisons sont au-moins-une-fois et non ordonnées — dédupliquez sur l'en-tête webhook-id et triez par le timestamp du payload. Si votre agent n'a pas de récepteur webhook, interrogez le message avec GET (ou bird email get) jusqu'à ce que son statut se résolve — plus lent, mais la même règle s'applique : la relecture est la source de vérité, le 202 ne l'est pas.
Pattern 3 : Utilisez le sandbox comme harnais de test
Pendant le développement de la boucle, n'envoyez pas de vrai courrier — envoyez aux adresses magiques du sandbox mail sur messagebird.dev. Le résultat est déterminé par l'adresse (delivered@ livre toujours, bounce@ produit toujours un hard-bounce, complaint@ génère toujours une plainte), mais tout le reste est le vrai pipeline de production : le même 202, la même séquence d'événements, les mêmes livraisons webhook signées, sans aucun indicateur marquant qu'il s'agit d'un test.
Exemple de code
for address in [delivered@, bounce@, suppressed@] @messagebird.dev:
send to address+run42@… # +label correlates the test case
assert the expected terminal event arrives (delivered / bounced / rejected)Cela fait du sandbox le harnais de test idéal pour les agents : des résultats déterministes sur lesquels asserter, zéro risque de réputation, aucune écriture dans les listes de suppression, et des adresses réutilisables à chaque exécution. Un agent qui passe la matrice du sandbox a exercé l'intégralité de son chemin Pattern 2 — envoi, attente, branchement — avant de toucher une vraie boîte de réception.
Pattern 4 : Récupérer grâce à l'enveloppe d'erreur standard
Chaque erreur de l'API Bird a la même structure, donc la récupération d'erreur est un seul chemin de code, pas un par endpoint :
Exemple de code
{
"error": {
"type": "invalid_request",
"code": "E01002",
"name": "InvalidRequest",
"message": "from address is not on a verified sending domain",
"doc_url": "https://bird.com/docs/api/errors/E01002",
"request_id": "req_01krdgeqcxet5s7t44vh8rt9mg"
}
}Chaque champ a un rôle dans la boucle : branchez sur type/code (stable, lisible par la machine), affichez message à l'humain, récupérez doc_url quand l'agent a besoin de la page de documentation pour cette erreur exacte — elle résout vers du Markdown que l'agent peut lire — et loguez request_id pour qu'un humain puisse le transmettre au support Bird. La distinction décisive est réessayable versus non :
Exemple de code
4xx (except 429) → a request bug: fix the input, never retry as-is
429 → back off, then retry (Pattern 5)
5xx / timeout → retry with the same Idempotency-Key (Pattern 5)Le catalogue complet des codes se trouve sur la page des erreurs. Avec le CLI, l'enveloppe arrive sur stderr et le code de sortie la pré-classifie (2/3/4/1 — voir Pattern 1), donc un agent piloté par shell peut brancher avant de parser quoi que ce soit.
Pattern 5 : Réessayer en toute sécurité — Idempotency-Key et Retry-After sur 429
Les réessais sont là où les agents causent des dégâts : un timeout sur un envoi, un réessai aveugle, et le client reçoit deux e-mails. Le support d'idempotence de Bird rend le réessai sûr — générez un Idempotency-Key par opération logique (pas par tentative) et réutilisez-le à chaque réessai :
Exemple de code
key = uuid() # once per logical send
attempt with Idempotency-Key: key
on 5xx / timeout: backoff, retry with SAME key
on 2xx with Idempotency-Replay: true → the first attempt had succeeded; do not treat as a new sendL'en-tête de réponse Idempotency-Replay: true indique une relecture de la réponse originale, afin que votre agent puisse loguer « récupéré » au lieu de « envoyé deux fois ». Les SDK Bird injectent automatiquement une clé sur chaque requête mutante, donc les agents basés sur les SDK bénéficient de cela gratuitement ; avec le CLI, passez --idempotency-key sur les mutations susceptibles d'être réessayées.
La limitation de débit reçoit le traitement inverse : un 429 signifie ralentir, pas essayer plus fort. La réponse contient un en-tête Retry-After — respectez-le comme le plancher de votre backoff plutôt que d'inventer votre propre calendrier :
Exemple de code
on 429: sleep max(Retry-After, backoff(attempt)); retry with the same keyNe réessayez jamais les autres réponses 4xx sans modification — elles sont mises en cache et rejouées par l'idempotence précisément parce que la même requête produira toujours la même erreur. Corrigez la requête (Pattern 4) et utilisez une nouvelle clé, car réutiliser une clé avec un corps différent retourne 409 IdempotencyKeyReuse.
Étapes suivantes
- Serveur MCP — la surface d'outils que ces patterns pilotent, hébergée sur mcp.bird.com ou exécutée localement avec le CLI
- CLI pour les agents — les mêmes opérations pour les agents capables d'utiliser un shell
- Webhooks & événements — sémantiques de livraison, signatures et le catalogue d'événements derrière le Pattern 2
- Idempotence — sémantiques de relecture et modes de défaillance derrière le Pattern 5
- Erreurs — l'enveloppe et le catalogue complet des codes d'erreur
- Sandbox mail — la matrice d'adresses magiques derrière le Pattern 3