AI builder guides
Bird's API surface is agent-shaped: one operation per tool, JSON in and out, and machine-checkable outcomes. A reliable agent still needs the right patterns around it. These five patterns cover the failure modes that break agent integrations: treating acceptance as delivery, retrying without context, and parsing prose instead of structure. Each pattern works the same whether your agent drives the MCP server or the bird CLI.
Pattern 1: Loop one operation at a time
Bird's tools are deliberately granular: send a message, get a message, list domains, or create a webhook endpoint. Every tool returns structured JSON whose fields the next step can check. Build the loop so each step's exit condition comes from the previous step's output:
Code example
loop:
result = run_tool(next_operation) # one operation per call
if result.ok: advance using result.data # for example, the em_… ID or verified domain
else: branch on the failure category # see Pattern 4With the CLI, the failure category is the exit code, so the branch needs no message parsing. See the full table in CLI:
Code example
bird email get "$id" --format json > msg.json
case $? in
0) jq .status msg.json ;; # advance
3) echo "wrong ID: fix the value instead of retrying" ;;
4) bird auth login ;; # recover, then re-run
esacThe granularity is the point: an agent that can check state between steps recovers from any single failure; an agent driving one mega-operation can only start over.
Pattern 2: A send returns 202; the outcome arrives later
POST a send and you get 202 Accepted with a message ID. Accepted means Bird took the message and delivery is pending. The final outcome arrives as webhook events: email.delivered when the recipient's server accepts it, email.bounced when delivery fails permanently, email.complained, and so on.
An agent that declares success at 202 silently misses every bounce. Structure the task as send-then-await instead:
Code example
send → 202 + em_… ID # record the ID; delivery is still pending
await webhook event where data.email_id == em_… id:
email.delivered → done
email.bounced → report failure with bounce_type / bounce_descriptionCorrelate on email_id. Webhook payloads echo your tags and metadata alongside the identity fields, so your own context comes back without an extra lookup. Deliveries are at-least-once and unordered; deduplicate on the webhook-id header and sort by the payload timestamp. If your agent has no webhook receiver, poll the message with GET (or bird email get) until its status resolves. Polling is slower, but the read-back remains the source of truth.
Pattern 3: Use the sandbox as your test harness
While developing the loop, use the mail sandbox's magic addresses on messagebird.dev instead of real mailboxes. The address determines the outcome (delivered@ always delivers, bounce@ always hard-bounces, and complaint@ always complains). Everything else uses the production pipeline: the same 202, event sequence, and signed webhook deliveries, with no flag marking the message as a test.
Code example
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)The sandbox provides deterministic outcomes, zero reputation risk, no suppression-list writes, and reusable addresses across runs. An agent that passes the sandbox matrix has exercised the complete Pattern 2 path (send, await, and branch) before touching a real inbox.
Pattern 4: Recover against the standard error envelope
Every Bird API error has the same shape, so one error-recovery path works across endpoints:
Code example
{
"error": {
"type": "validation_error",
"code": "E04006",
"name": "DomainNotVerified",
"message": "The from address uses a domain that is not verified in this workspace.",
"doc_url": "https://bird.com/docs/api/errors/E04006",
"request_id": "req_01krdgeqcxet5s7t44vh8rt9mg"
}
}Each field has a job in the loop. Branch on type/code (stable and machine-readable), show message to the human, and fetch doc_url when the agent needs the page for that exact error. The URL resolves to Markdown the agent can read. Log request_id so a human can give it to Bird support. Then separate retryable errors from request errors:
Code example
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)The full code catalog lives on the errors page. With the CLI, the envelope arrives on stderr and the exit code pre-classifies it (see Pattern 1 and the full table in CLI). A shell-driving agent can therefore branch before parsing anything.
Pattern 5: Retry safely with Idempotency-Key and Retry-After
Retries can duplicate work when a send times out and the agent tries again. Bird's idempotency support makes retries safe. Generate one Idempotency-Key per logical operation and reuse it on every attempt:
Code example
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 sendThe Idempotency-Replay: true response header marks a replay of the original response, so your agent can log "recovered" instead of "sent twice". The Bird SDKs inject a key automatically on every mutating request, so SDK-based agents get this for free; with the CLI, pass --idempotency-key on mutations that might be retried.
A 429 means the agent must slow down. The response carries a Retry-After header; use it as the minimum backoff instead of inventing a separate schedule:
Code example
on 429: sleep max(Retry-After, backoff(attempt)); retry with the same keyDo not retry other 4xx responses unchanged. Idempotency caches and replays them because the same request produces the same error. Fix the request (Pattern 4) and use a new key; reusing a key with a different body returns 409 IdempotencyKeyReuse.
Next steps
- MCP server: the tool surface these patterns drive, hosted at mcp.bird.com or run locally with the CLI
- CLI for agents: the same operations for shell-capable agents
- Webhooks & events: delivery semantics, signatures, and the event catalog behind Pattern 2
- Idempotency: replay semantics and failure modes behind Pattern 5
- Errors: the envelope and the full error-code catalog
- Mail sandbox: the magic-address matrix behind Pattern 3