API Reference
The Broadcaster API gives your AI agents real-world communication channels — email, web, and more — with a single integration.
https://api.broadcaster.aiv1Quickstart
Get your agent sending and receiving email in under 60 seconds.
1. Install the SDK
pip install broadcasternpm install broadcaster2. Create an inbox & send an email
Get your API key from the Dashboard and replace br_live_... below.
from broadcaster import Broadcaster
client = Broadcaster(api_key="br_live_...")
agent = client.agents.create(
name="Support",
slug="support"
)
print(agent.email) # support@broadcaster.ai
# Send an email
agent.send(
to="user@example.com",
subject="Hello!",
body="Hello from my AI agent!"
)import { Broadcaster } from "broadcaster";
const client = new Broadcaster({
apiKey: "br_live_..."
});
const agent = await client.agents.create({
name: "Support",
slug: "support"
});
console.log(agent.email); // support@broadcaster.ai
// Send an email
await agent.send({
to: "user@example.com",
subject: "Hello!",
body: "Hello from my AI agent!"
});3. Receive emails
Set a webhook URL on your agent, and Broadcaster will POST inbound emails to your endpoint as structured JSON.
curl -X PATCH https://api.broadcaster.ai/v1/agents/ag_abc123 \
-H "Authorization: Bearer br_live_..." \
-H "Content-Type: application/json" \
-d '{ "webhook_url": "https://myapp.com/webhook" }'📋 Copy for Cursor / Claude
Copy this complete block into Cursor, Claude, or any AI coding assistant for a working Broadcaster integration with setup, error handling, and API reference.
"""
Broadcaster.ai — Complete API Reference for AI Coding Assistants
Copy this block into Cursor, Claude, or any AI coding assistant.
Base URL: https://api.broadcaster.ai/v1
Auth: Authorization: Bearer $BROADCASTER_API_KEY
=== AGENTS (Email Inboxes) ===
POST /agents — Create agent (name, slug?, webhook_url?, client_id? for idempotency)
GET /agents — List agents
GET /agents/:id — Get agent
PATCH /agents/:id — Update agent
DELETE /agents/:id — Delete agent
=== MESSAGES ===
POST /agents/:id/messages/send — Send email (to, subject, body, cc?, bcc?, reply_to?)
GET /agents/:id/messages — List messages (direction?, channel?, page?, per_page?)
GET /messages/:id — Get message by ID
POST /agents/:id/messages/:msgId/reply — Reply (proper email threading)
POST /agents/:id/messages/:msgId/reply-all — Reply all (all To/CC recipients)
POST /agents/:id/messages/:msgId/forward — Forward (to, body?)
POST /agents/:id/messages/:msgId/labels — Add labels (labels: string[])
DELETE /agents/:id/messages/:msgId/labels — Remove labels
=== THREADS ===
GET /agents/:id/threads — List conversation threads (label?)
GET /agents/:id/threads/:threadId — Get thread with all messages
=== DRAFTS (Human-in-the-Loop) ===
POST /agents/:id/drafts — Create draft (to, subject?, body, in_reply_to_id?)
GET /agents/:id/drafts — List drafts (status?: draft|approved|sent|discarded|all)
GET /agents/:id/drafts/:draftId — Get draft
PATCH /agents/:id/drafts/:draftId — Update draft
POST /agents/:id/drafts/:draftId/send — Approve and send
DELETE /agents/:id/drafts/:draftId — Discard
=== ALLOW/BLOCK LISTS ===
POST /agents/:id/lists — Add entry (direction, list_type, entry, reason?)
GET /agents/:id/lists — List entries (direction?, list_type?)
DELETE /agents/:id/lists/:entryId — Remove entry
=== SAFETY (Automatic on Inbound) ===
Every inbound email includes in metadata:
- injection_scan: { risk_level, detections[] } — prompt injection detection
- auth_flows: [{ type: otp|verification_link|magic_link|password_reset, value, confidence }]
High-risk emails auto-labeled "⚠️ injection-risk"
=== WEBHOOKS ===
Set webhook_url on agent creation. Payload:
{ event: "message.received", agent_id, message: { id, from, to, subject, body, attachments, labels, thread_id },
safety: { is_suspicious, risk_level, detections }, auth_flows: [...] }
Verify: HMAC-SHA256 of body using webhook_secret (X-Broadcaster-Signature header)
=== WEBSOCKET ===
wss://api.broadcaster.ai/v1/agents/:id/listen?token=YOUR_KEY
=== ERRORS & LIMITS ===
400 validation, 401 unauthorized, 404 not found, 429 rate limited, 502 email delivery failed
Free: 1000 msgs/mo, 1 agent | Pro $9: 5000/mo, 10 agents | Scale $29: 25000/mo, 50 agents
"""
import os, requests
API = "https://api.broadcaster.ai/v1"
KEY = os.getenv("BROADCASTER_API_KEY")
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
# Create agent
agent = requests.post(f"{API}/agents", json={"name": "Support", "slug": "support"}, headers=H).json()
# Send email with CC
requests.post(f"{API}/agents/{agent['id']}/messages/send", json={
"to": "user@example.com", "cc": "manager@example.com",
"subject": "Hello", "body": "From my AI agent", "channel": "email"
}, headers=H)
# Create draft for human review
draft = requests.post(f"{API}/agents/{agent['id']}/drafts", json={
"to": "customer@example.com", "subject": "Re: Your inquiry",
"body": "Thank you for reaching out..."
}, headers=H).json()
# Human approves → send
requests.post(f"{API}/agents/{agent['id']}/drafts/{draft['id']}/send", headers=H)
# List inbound with safety data
msgs = requests.get(f"{API}/agents/{agent['id']}/messages?direction=inbound", headers=H).json()
for msg in msgs["data"]:
meta = msg.get("metadata", {})
if meta.get("injection_scan", {}).get("is_suspicious"):
print(f"⚠️ SUSPICIOUS: {msg['subject']} — {meta['injection_scan']['risk_level']}")
if meta.get("auth_flows"):
for af in meta["auth_flows"]:
print(f"🔑 {af['type']}: {af['value']}")Authentication
Authenticate API requests by including your API key in the Authorization header.
curl https://api.broadcaster.ai/v1/agents \
-H "Authorization: Bearer br_live_xxxxxxxxxxxxxxxx"API Key Format
API keys are prefixed with br_live_ for production and br_test_ for sandbox environments. Keys are shown only once at account creation — store them securely.
Public Endpoints
Some endpoints are publicly accessible without authentication:
POST /v1/accounts— Create accountGET /v1/agents/by-slug/:slug— Public agent lookupPOST /v1/agents/:slug/contact— Contact form
WebSocket Authentication
The WebSocket endpoint at /v1/agents/:id/listen uses query parameter authentication since WebSocket connections don't support custom headers:
wss://api.broadcaster.ai/v1/agents/ag_abc123/listen?token=br_live_xxxxxxxxAccounts
Agents
Messages
Threads
Safety & Auth Flows
Webhooks
WebSocket
Contact
Webhook Guide
When someone emails your agent or submits the web contact form, Broadcaster delivers the message to your webhook URL as an HTTP POST request.
Payload Format
{
"event": "message.received",
"agent_id": "ag_abc123",
"message": {
"id": "msg_abc456",
"channel": "email",
"from": "customer@gmail.com",
"to": "support@broadcaster.ai",
"subject": "Help with my order",
"body": "I haven't received my package yet...",
"body_html": "<p>I haven't received my package yet...</p>",
"attachments": [],
"received_at": "2026-03-26T10:30:00Z"
},
"timestamp": "2026-03-26T10:30:01Z",
"signature": "sha256=xxxxxxxx"
}Signature Verification
Every webhook request includes an HMAC-SHA256 signature in the signature field (and the X-Broadcaster-Signature header). Verify this signature using your agent's webhook_secret to ensure the request is authentic.
import crypto from "crypto";
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");
const sig = signature.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(sig, "hex"),
Buffer.from(expected, "hex")
);
}
// In your webhook handler:
app.post("/webhook", (req, res) => {
const signature = req.headers["x-broadcaster-signature"];
const isValid = verifyWebhook(
req.body,
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send("Invalid signature");
}
// Process the message
const { message } = req.body;
console.log(`New ${message.channel} from ${message.from}`);
res.status(200).send("OK");
});import hmac
import hashlib
import json
def verify_webhook(payload, signature, secret):
expected = hmac.new(
secret.encode("utf-8"),
json.dumps(payload).encode("utf-8"),
hashlib.sha256
).hexdigest()
sig = signature.replace("sha256=", "")
return hmac.compare_digest(sig, expected)
# In your webhook handler (Flask example):
@app.route("/webhook", methods=["POST"])
def webhook():
signature = request.headers.get(
"X-Broadcaster-Signature"
)
is_valid = verify_webhook(
request.json,
signature,
os.environ["WEBHOOK_SECRET"]
)
if not is_valid:
return "Invalid signature", 401
# Process the message
message = request.json["message"]
print(f"New {message['channel']} from {message['from']}")
return "OK", 200Retry Behavior
If your endpoint returns a non-2xx status code or the request times out, Broadcaster retries delivery up to 3 times with increasing delays:
After all 3 attempts fail, the delivery is marked as failed. You can inspect delivery attempts via the webhook delivery logs endpoint.
Best Practices
- Always verify the HMAC signature before processing webhooks
- Return a
200response quickly — process messages asynchronously if needed - Use the
message.idfield for idempotency to handle potential duplicate deliveries - Use the test webhook endpoint to verify your setup during development
Errors
The API returns standard HTTP status codes. Error responses include a JSON body with error and code fields.
{
"error": "Agent not found",
"code": "agent_not_found"
}| Status | Code |
|---|---|
400 | validation_error |
400 | invalid_request |
400 | channel_not_enabled |
401 | unauthorized |
403 | forbidden |
403 | plan_limit_agents |
404 | agent_not_found |
404 | message_not_found |
409 | slug_taken |
429 | rate_limited |
429 | plan_limit_messages |
Rate Limits
API requests are rate limited per account. Message sending is also subject to plan-based monthly quotas.
| Plan | Max Agents | Messages |
|---|---|---|
| Free | 1 | 1,000 / month |
| Pro | 10 | 5,000 / month |
| Scale | 50 | 25,000 / month |
| Enterprise | Unlimited | Unlimited |