Send from your server
Complete the quickstart and domain setup, then submit an email with the SDK or REST API. A successful request returns the message ID and queued status.
curl https://api.sendpanda.email/v1/emails \
-H "Authorization: Bearer $SENDPANDA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: welcome-account-123" \
-d '{
"from": "Your app <hello@your-verified-domain.com>",
"to": ["your-own-address@example.com"],
"subject": "Hello from SendPanda",
"text": "Your integration is working."
}'
Use html and/or text, or a published template_id with variables. Add cc, bcc, reply_to, tags and string-valued metadata when needed. See the API reference for the request schema.
Prevent duplicate sends
Reuse one idempotency key for retries of the same business event. A timeout can happen after acceptance: retry with the same key and unchanged payload to retrieve the stored message ID. Different content with the same key returns 409.
Keys are project-scoped and retained with message metadata. An idempotency key identifies one event; it is separate from your API key. The SDK retries reads and keyed sends, and never automatically retries an unkeyed send. Read the idempotency guide for examples.
Request limits
- Maximum 50 total unique To, CC and BCC recipients per message.
- Maximum 500 KB JSON request payload.
- Include a nonempty HTML/text body or a published template.
- Use valid mailboxes and a verified sender domain.
- Tags use ASCII letters, numbers, underscores and dashes;
sendpanda_idis reserved. - Unsafe HTML and URL destinations are rejected.
- Account readiness, quotas, rate limits and suppressions apply to every send, including tests.
Search and pagination
GET /emails supports status, recipient, domain (domain ID), after/before (ISO UTC timestamps), and tag (tag name or value substring). Pages contain up to 50 messages plus next_cursor; pass that opaque value as cursor with the same filters to fetch older messages. Timestamp and message ID ordering preserves messages created together. before is exclusive.
GET /usage returns the organisation's UTC calendar-month recipient usage and a projects breakdown. Definitive failures before submission and queued cancellations release their reservations.
const page = await panda.emails.list({ status: "delivered" });
if (page.next_cursor) {
const next = await panda.emails.list({
status: "delivered",
cursor: page.next_cursor,
});
}
Cancel a queued email
Use panda.emails.cancel(id) or POST /v1/emails/{id}/cancel. Cancellation is possible only before the worker claims the message. Messages already submitted to a receiving server cannot be recalled.