Why Sendara is built for agents
Autonomous agents need an API they can call confidently without a human in the loop. Sendara is shaped for exactly that:
- One endpoint. Every message — email today, SMS and more later — goes through a single
POST /v1/send. The agent learns one call, not a surface area. - Idempotent by design. Each send carries an
idempotency_key; a retry with the same key returns the original result instead of sending again. Agents that retry on timeout never double-send. - Predictable, typed JSON errors. Failures come back as
{ "error": { "code", "message", "status" } }with a stable machine-readablecode— easy for an agent to branch on (from_not_verified,recipient_suppressed, …) without parsing prose. - Simple API-key auth. One header —
Authorization: Bearer sk_live_…. No OAuth dance, no token refresh for the agent to manage. - Sandbox & test sends. A
sk_test_…key simulates delivery (never billed, no real mail) so an agent can rehearse a workflow end to end before it touches a real inbox.
send_email tool below, and it can send mail in a single tool call. Everything else (templates, broadcasts, domains) is additive — see the SDKs and API reference.Give your agent the docs
We publish an LLM-readable dump of these docs at https://sendara.dev/llms.txt. It is a compact, plain-text description of the base URL, auth, the send body, every channel, error codes, and the sandbox — written to fit in a model's context window.
Two ways to use it:
- Drop it in context.Fetch the file and paste it into the agent's system prompt or a project knowledge file (e.g. a
CLAUDE.mdor an OpenAI Assistant instruction). The model then knows how to call Sendara without you hand-writing the spec. - Index it for retrieval.Point a retrieval tool or vector store at the URL so the agent can pull the relevant section on demand — handy when you don't want the full file resident in every turn.
# Fetch the LLM-readable docs and hand them to your agent's context.
curl -s https://sendara.dev/llms.txt -o sendara-llms.txtMinimal send
The simplest possible send, in every official SDK plus raw HTTP. This mirrors /docs/sending and /docs/sdks exactly — construct a client with your API key, then call emails.send. The SDK attaches an idempotency_keyfor you when you don't pass one.
from, so the helper takes from_ (keyword-only). In Go every call takes a context.Context first and params are passed by value. The sender must be on a verified domain (or omit it for the shared sender).Tool / function calling
Expose Sendara to a model as a single send_email tool. The schema below is the Anthropic shape (name, description, input_schema). It maps directly to OpenAI function calling — wrap it as { "type": "function", "function": { name, description, "parameters": input_schema } } and the JSON Schema body is identical. Either way the model returns the tool name and a JSON object of arguments.
{
"name": "send_email",
"description": "Send a transactional email to a single recipient through Sendara. Returns the created message id.",
"input_schema": {
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "Recipient email address."
},
"subject": {
"type": "string",
"description": "Subject line."
},
"html": {
"type": "string",
"description": "HTML body of the email."
},
"from": {
"type": "string",
"description": "Sender address on a verified domain. Omit to use the shared sender."
}
},
"required": ["to", "subject", "html"]
}
}When the model emits a tool call, route its name and arguments into a handler that calls the Sendara SDK and returns the result for the model to read on the next turn.
send_email; add a send_email_template tool (passing templateId + templateVars) only when you want copy to live in Sendara instead of the model's output. See templates.Idempotency & retries
Autonomous agents retry — on a timeout, a tool-runner restart, or a replanned step. Without a stable key, each retry is a fresh send and your user gets duplicate mail. Pass a deterministic idempotency_key derived from the logical action (an order id, a recipient + intent) so a retried call is a guaranteed no-op.
// Derive the key from the action, not from the clock or a random value —
// the same logical send must produce the same key on every retry.
await sendara.emails.send({
from: "[email protected]",
to: "[email protected]",
subject: "Your receipt",
html: "<p>Thanks!</p>",
idempotencyKey: `receipt-${orderId}`, // same order ⇒ at most one email
});409 — keys are bound to the request they first succeeded with. A clean retry of the same send returns the original result. More on the SDK retry behavior (429, 5xx, network) in the SDKs guide.Dry-run safely
Let an agent rehearse without billing or real mail. Hand it a test key (sk_test_…) and every send is simulated — not billed, no real delivery — but still fires your webhooks, so the full workflow runs end to end. Address the simulator inbox to force an outcome: delivered@, bounced@, or complained@ on any domain.
const sandbox = new Sendara(process.env.SENDARA_TEST_KEY!); // sk_test_…
await sandbox.emails.send({
from: "[email protected]",
to: "[email protected]", // simulates a hard bounce + bounced webhook
subject: "Agent dry run",
html: "<p>Not really sent.</p>",
});To have the agent send a real email to one of your own verified test recipients (free, capped per day), set testSend — the SDK forwards it as test_send: true:
await sendara.emails.send({
from: "[email protected]",
to: "[email protected]", // must be a verified test recipient
subject: "Agent UAT — real delivery",
html: "<p>This one actually arrives.</p>",
testSend: true,
});409 conflicts you can branch on by code. See sandbox & test sends for the full flow.Coming next: SMS & voice OTP for agents
Email is the only generally available send channel today. SMS and voice OTP are on the roadmap, not yet released — calling POST /v1/send with channel set to sms or voice currently returns 422 channel_not_enabled. Once those channels ship, the plan is for the same single endpoint to carry them: you'll flip channel to sms or voice with a { phone_number }destination and a one-time code in the payload, and your agent will be able to verify a user over the same API key and the same idempotency guarantees it already uses for email. We're building this email-first; watch the sending guide and llms.txt as the agent SMS path lands.