# CLI for AI agents

> Use MailBlastr from AI agent workflows and CI/CD pipelines — non-interactive sends, stdin piping, batch, idempotent retries, scheduling, and webhook feedback loops, all over plain HTTP.

MailBlastr is well suited to AI agent workflows and CI/CD pipelines: it is a plain JSON REST API with no interactive login, so an agent can drive it with `curl` (and `jq`) the moment it has an `mb_` key in its environment. This page covers the agent-specific patterns. See the [CLI](https://www.mailblastr.com/docs/resources/cli) reference for the full command surface and the [CLI Quickstart](https://www.mailblastr.com/docs/resources/cli-quickstart) to send your first email.

If your agent prefers a typed client, the official `mailblastr` Node SDK wraps the same endpoints — see [SDKs](https://www.mailblastr.com/docs/resources/sdks).

## Non-interactive by default

There is nothing to switch on. Every MailBlastr request is a single HTTP call that returns machine-readable JSON to stdout, with no progress indicators and no prompts. Capture the HTTP status to drive exit codes so a failed call fails the agent’s step:

```bash
status=$(curl -s -o /tmp/out.json -w '%{http_code}' -X POST 'https://api.mailblastr.com/emails' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "from": "Acme <onboarding@yourdomain.com>", "to": ["delivered@example.com"], "subject": "Welcome" }')

if [ "$status" -ge 400 ]; then
  jq -r '.message' /tmp/out.json >&2   # structured error: { "name", "message" }
  exit 1
fi
jq -r '.id' /tmp/out.json
```

Errors always come back as a structured envelope with a `name` and `message` field alongside a non-2xx status — see the [error reference](https://www.mailblastr.com/docs/api/errors). An example auth failure:

```json
{ "name": "missing_api_key", "message": "Missing API key in the authorization header." }
```

## Piping content from stdin

Agents generate content on the fly. Build the request body with `jq` and pipe it straight into `curl` with `-d @-` instead of writing temp files — `--rawfile`/`--arg` keep the generated text safely JSON-escaped:

```bash
echo "Your order has shipped." | jq -Rs '{
    from: "Acme <onboarding@yourdomain.com>",
    to: ["delivered@example.com"],
    subject: "Order update",
    text: .
  }' | curl -s -X POST 'https://api.mailblastr.com/emails' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d @-
```

The same `jq -n --rawfile html ./body.html` pattern injects an HTML body from a file — see the [CLI](https://www.mailblastr.com/docs/resources/cli) reference.

## Batch sending

Send up to 100 emails in a single request by POSTing a JSON array to [POST /emails/batch](https://www.mailblastr.com/docs/api/emails-batch). Read the array straight from a file the agent wrote with `-d @-`:

```bash
cat emails.json | curl -s -X POST 'https://api.mailblastr.com/emails/batch' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d @- \
  | jq '.data'
```

## Safe retries

Add an `Idempotency-Key` header to prevent duplicates when an agent retries a failed request. Reusing the same key returns the original result instead of sending twice. The header works on both `/emails` and `/emails/batch`.

```bash
curl -X POST 'https://api.mailblastr.com/emails' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: welcome-user-123' \
  -d '{ "from": "Acme <onboarding@yourdomain.com>", "to": ["delivered@example.com"], "subject": "Welcome", "text": "Hello!" }'
```

## Scheduling

The `scheduled_at` field accepts an ISO 8601 timestamp or natural language like `"tomorrow at 9am ET"` — see [Schedule emails](https://www.mailblastr.com/docs/emails/schedule).

```bash
curl -X POST 'https://api.mailblastr.com/emails' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "from": "Acme <onboarding@yourdomain.com>",
    "to": ["delivered@example.com"],
    "subject": "Your trial ends soon",
    "text": "Your free trial expires in 3 days.",
    "scheduled_at": "tomorrow at 9am ET"
  }'
```

Cancel or reschedule a queued email with [POST /emails/:id/cancel](https://www.mailblastr.com/docs/api/emails-cancel) and [PATCH /emails/:id](https://www.mailblastr.com/docs/api/emails-update):

```bash
# Reschedule
curl -X PATCH 'https://api.mailblastr.com/emails/em_123' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "scheduled_at": "in 2 hours" }'

# Cancel
curl -X POST 'https://api.mailblastr.com/emails/em_123/cancel' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY"
```

## Closing the loop with webhooks

When an agent sends an email, it usually needs to know what happened next — delivered, bounced, opened, or replied to. Register a [webhook](https://www.mailblastr.com/docs/webhooks/overview) pointing at a public URL (use a tunnel such as Tailscale Funnel, ngrok, or localtunnel during development) so MailBlastr streams [signed events](https://www.mailblastr.com/docs/webhooks/verify) to your handler as they occur.

```bash
curl -X POST 'https://api.mailblastr.com/webhooks' \
  -H "Authorization: Bearer $MAILBLASTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "endpoint_url": "https://hostname.tailnet-name.ts.net/api/webhook",
    "events": ["email.delivered", "email.bounced", "email.received"]
  }'
```

> **Note:** Always verify the webhook signature before trusting a payload — the signing secret is returned when you create the webhook. See [Verify webhooks](https://www.mailblastr.com/docs/webhooks/verify).
