API
Everything an agent needs to run outreach: prospects, drafts, sending, campaigns, suppression, and its own setup, over HTTP or MCP. Designed for autonomous agents: stable error codes, explicit scopes, and a machine-readable spec you can hand straight to a model. Team management, billing and the audit log stay in the dashboard deliberately; an agent should not be able to invite people or change your plan.
Start here
The full description lives at /api/v1/openapi.json. It needs no authentication, so an agent can discover the surface before it holds a key. Base URL for every call is /api/v1.
Authentication
Create a key in Settings, API. It is shown once and stored only as a hash, so it cannot be recovered later. Send it as a bearer token.
curl https://your-workspace/api/v1/prospects \ -H "Authorization: Bearer bk_live_..."
Every key belongs to one workspace. There is no cross-workspace access and no way for a key to widen its own scope.
Scopes
Grant an agent only what it needs.
prospects:read: Read prospectsprospects:write: Create, update and delete prospectsmessages:read: Read generated emailsmessages:write: Generate and edit emailsmessages:send: Send emails to prospectscampaigns:read: Read campaigns and cadencescampaigns:write: Create and change campaignssuppressions:read: Read the do-not-contact listsuppressions:write: Add to the do-not-contact listanalytics:read: Read performance statssettings:read: Read workspace settings and setup statussettings:write: Change settings and connect provider keys
messages:send is never implied by messages:write. Sending is irreversible, so an agent can draft freely while a human keeps the send button.
A complete run
Add prospects, draft, review, send.
# 1. Add prospects
curl -X POST https://your-workspace/api/v1/prospects \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prospects":[{"companyName":"Acme Roofing","contactName":"Jane Diaz","email":"jane@acme.com"}]}'
# 2. Draft an email
curl -X POST https://your-workspace/api/v1/messages/generate \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prospectId":"<id from step 1>"}'
# 3. Read what is waiting for review
curl https://your-workspace/api/v1/messages \
-H "Authorization: Bearer $API_KEY"
# 4. Send it (requires messages:send)
curl -X POST https://your-workspace/api/v1/messages/<message-id>/send \
-H "Authorization: Bearer $API_KEY"Errors
Every failure returns the same shape with a stable code you can branch on, rather than a message you would have to pattern match.
{
"error": {
"code": "forbidden",
"message": "This key is missing the messages:send scope.",
"required_scope": "messages:send",
"docs": "/docs/api"
}
}unauthorized: key missing, invalid, revoked or expiredforbidden: key lacks the scope named inrequired_scopeinvalid_request: body failed validation; the message names the fieldnot_found: no such record in this workspacequota_exceeded: plan limit reached; check/analyticsconflict: the action contradicts current state, e.g. sending twicerate_limited: back off forRetry-Aftersecondsprovider_error: your AI provider failed; usually worth retrying
Checking budget before a batch
GET /api/v1/analytics returns plan.remaining. An agent should read it before starting a large run rather than discovering a limit halfway through.
{
"data": {
"plan": {
"key": "pro",
"remaining": { "generations": 2840, "emails": 2903, "prospects": 4915 }
}
}
}Guardrails that always apply
The API is not a way around the safety rules. However you call it, the same checks run:
- The do-not-contact list is checked immediately before every send.
- Plan quotas are enforced on generation, sending and prospect count.
- Every sent email gets an unsubscribe link and your postal address.
- Sending requires a connected mailbox, or a from-address on a domain verified in your own Resend account. MastroSDR checks that when the address is saved and again before the first send.
- There is no endpoint to remove someone from the do-not-contact list. An opt-out is permanent as far as any agent is concerned.
MCP
The same capabilities are exposed as Model Context Protocol tools at /api/mcp, so a model can discover them without anyone writing an integration. Authenticate with the same Authorization: Bearer bk_live_… header; the tool list is filtered to the scopes that key actually holds, and every call re-checks the scope server-side.
Tools follow the same separation as the REST surface. draft_email needs messages:write and send_email needs messages:send, which is never implied by it, so you can hand an agent the keyboard without handing it the send button. mark_replied is what stops a cadence: nothing detects replies automatically, so an agent watching an inbox should call it as soon as it sees one.
get_performance and check_suppression are the two worth calling before a batch. The first tells you what allowance is left, the second who must never be contacted.
Rate limits
600 requests per minute per key. A 429 includes Retry-After in seconds. Treat it as a signal to back off rather than to rotate keys.
A 429 on POST /messages/{id}/sendcan also mean the workspace's daily send cap is reached, in which case Retry-After is the time until it resets at midnight UTC, up to a whole day, not seconds. Always read the header rather than assuming a per-minute window.