| name | sendook |
| description | Give AI agents their own email inboxes using the Sendook API. Use when building email agents, sending/receiving emails programmatically, managing inboxes, handling attachments, organizing threads, managing custom domains, or setting up real-time notifications via webhooks. Supports instant inbox creation and full email lifecycle management. |
Sendook Email SDK
Open-source email infrastructure for AI agents — instant inbox creation, sending/receiving, webhooks, custom domains, and more.
Quick Start
npm install @sendook/node
import Sendook from "@sendook/node";
const client = new Sendook(process.env.SENDOOK_API_KEY);
const inbox = await client.inbox.create({
name: "support",
email: "support@yourdomain.com",
});
await client.inbox.message.send({
inboxId: inbox.id,
to: ["user@example.com"],
subject: "Welcome!",
text: "Thanks for signing up.",
});
curl -X POST https://api.sendook.com/v1/inboxes \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "support", "email": "support@yourdomain.com"}'
curl -X POST https://api.sendook.com/v1/inboxes/{inbox_id}/messages/send \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": ["user@example.com"], "subject": "Welcome!", "text": "Thanks for signing up."}'
Resource Hierarchy
Organization
└── Inbox
├── Thread
│ └── Message
│ └── Attachment
├── Domain
├── Webhook
└── API Key
- Organization — Top-level account, owns all resources
- Inbox — An email address that can send and receive messages
- Thread — A conversation grouping related messages
- Message — An individual email (sent or received)
- Attachment — A file attached to a message (base64 encoded)
Inboxes
Create and manage email inboxes. Each inbox gets a unique email address.
Create Inbox
const inbox = await client.inbox.create();
const inbox = await client.inbox.create({
name: "notifications",
email: "notifications@yourdomain.com",
});
curl -X POST https://api.sendook.com/v1/inboxes \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "notifications", "email": "notifications@yourdomain.com"}'
Response 201 Created:
{
"id": "inbox_abc123",
"name": "notifications",
"domainId": "dom_xyz789",
"email": "notifications@yourdomain.com",
"createdAt": "2025-01-15T10:30:00Z"
}
List Inboxes
const inboxes = await client.inbox.list();
curl https://api.sendook.com/v1/inboxes \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Get Inbox
const inbox = await client.inbox.get("inbox_abc123");
curl https://api.sendook.com/v1/inboxes/inbox_abc123 \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Delete Inbox
await client.inbox.delete("inbox_abc123");
curl -X DELETE https://api.sendook.com/v1/inboxes/inbox_abc123 \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Messages
Send, receive, and manage email messages.
Send Message
await client.inbox.message.send({
inboxId: "inbox_abc123",
to: ["recipient@example.com"],
subject: "Hello from Sendook",
text: "Plain text body",
html: "<h1>Hello</h1><p>HTML body</p>",
});
curl -X POST https://api.sendook.com/v1/inboxes/inbox_abc123/messages/send \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["recipient@example.com"],
"subject": "Hello from Sendook",
"text": "Plain text body",
"html": "<h1>Hello</h1><p>HTML body</p>"
}'
Response 202 Accepted — message queued for delivery.
Send with Attachments
import { readFileSync } from "fs";
await client.inbox.message.send({
inboxId: "inbox_abc123",
to: ["recipient@example.com"],
subject: "Report attached",
text: "Please find the report attached.",
attachments: [
{
content: readFileSync("report.pdf").toString("base64"),
name: "report.pdf",
contentType: "application/pdf",
},
],
});
Send with CC/BCC
await client.inbox.message.send({
inboxId: "inbox_abc123",
to: ["primary@example.com"],
cc: ["cc@example.com"],
bcc: ["bcc@example.com"],
subject: "Team update",
text: "Here's the latest update.",
});
Reply to Message
await client.inbox.message.reply({
inboxId: "inbox_abc123",
messageId: "msg_def456",
text: "Thanks for your email!",
html: "<p>Thanks for your email!</p>",
});
curl -X POST https://api.sendook.com/v1/inboxes/inbox_abc123/messages/msg_def456/reply \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Thanks for your email!"}'
List Messages
const messages = await client.inbox.message.list("inbox_abc123");
const results = await client.inbox.message.list("inbox_abc123", "invoice");
curl https://api.sendook.com/v1/inboxes/inbox_abc123/messages \
-H "Authorization: Bearer $SENDOOK_API_KEY"
curl "https://api.sendook.com/v1/inboxes/inbox_abc123/messages?query=invoice" \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Get Message
const message = await client.inbox.message.get("inbox_abc123", "msg_def456");
curl https://api.sendook.com/v1/inboxes/inbox_abc123/messages/msg_def456 \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Response:
{
"id": "msg_def456",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello",
"text": "Plain text body",
"html": "<p>HTML body</p>",
"labels": [],
"threadId": "thread_ghi789",
"createdAt": "2025-01-15T10:35:00Z"
}
Threads
Threads group related messages into conversations.
List Threads
const threads = await client.inbox.thread.list("inbox_abc123");
curl https://api.sendook.com/v1/inboxes/inbox_abc123/threads \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Get Thread
const thread = await client.inbox.thread.get("inbox_abc123", "thread_ghi789");
curl https://api.sendook.com/v1/inboxes/inbox_abc123/threads/thread_ghi789 \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Response:
{
"id": "thread_ghi789",
"subject": "Hello",
"messages": [
{
"id": "msg_def456",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello",
"text": "Original message",
"createdAt": "2025-01-15T10:35:00Z"
},
{
"id": "msg_jkl012",
"from": "recipient@example.com",
"to": ["sender@example.com"],
"subject": "Re: Hello",
"text": "Reply message",
"createdAt": "2025-01-15T10:40:00Z"
}
]
}
Domain Management
Register and verify custom sending domains for branded email addresses.
Create Domain
const domain = await client.domain.create({ name: "yourdomain.com" });
curl -X POST https://api.sendook.com/v1/domains \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "yourdomain.com"}'
Get DNS Records
After creating a domain, retrieve the required DNS records to configure.
const records = await client.domain.dns({ domainId: "dom_xyz789" });
curl https://api.sendook.com/v1/domains/dom_xyz789/dns \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Response:
[
{ "type": "MX", "name": "yourdomain.com", "value": "mx.sendook.com" },
{ "type": "TXT", "name": "yourdomain.com", "value": "v=spf1 include:sendook.com ~all" },
{ "type": "CNAME", "name": "sendook._domainkey.yourdomain.com", "value": "dkim.sendook.com" }
]
Verify Domain
After adding DNS records, verify the domain.
await client.domain.verify({ domainId: "dom_xyz789" });
curl -X POST https://api.sendook.com/v1/domains/dom_xyz789/verify \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Get Domain
const domain = await client.domain.get({ domainId: "dom_xyz789" });
Delete Domain
await client.domain.delete({ domainId: "dom_xyz789" });
Webhooks
Receive real-time notifications when email events occur. See references/webhooks.md for detailed payload documentation.
Create Webhook
const webhook = await client.webhook.create({
url: "https://your-app.com/webhooks/email",
events: ["message.received", "message.delivered", "message.bounced"],
});
curl -X POST https://api.sendook.com/v1/webhooks \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/email",
"events": ["message.received", "message.delivered", "message.bounced"]
}'
Supported Events
| Event | Description |
|---|
inbox.created | New inbox created |
inbox.deleted | Inbox deleted |
inbox.updated | Inbox settings changed |
message.received | New email received in inbox |
message.sent | Email sent from inbox |
message.delivered | Email confirmed delivered |
message.bounced | Email delivery failed |
message.complained | Recipient marked email as spam |
message.rejected | Email rejected by recipient server |
List Webhooks
const webhooks = await client.webhook.list();
Get Webhook
const webhook = await client.webhook.get("wh_mno345");
Test Webhook
Send a test event to verify your endpoint is working.
await client.webhook.test("wh_mno345");
curl -X POST https://api.sendook.com/v1/webhooks/wh_mno345/test \
-H "Authorization: Bearer $SENDOOK_API_KEY"
Delete Webhook
await client.webhook.delete("wh_mno345");
Express.js Webhook Handler
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhooks/email", (req, res) => {
const { event, data } = req.body;
switch (event) {
case "message.received":
console.log(`New email from ${data.from}: ${data.subject}`);
break;
case "message.bounced":
console.log(`Bounce for ${data.to}: ${data.subject}`);
break;
case "message.complained":
console.log(`Spam complaint from ${data.to}`);
break;
}
res.status(200).send("OK");
});
API Keys
Create and manage API keys for authentication.
Create API Key
const key = await client.apiKey.create({ name: "production" });
curl -X POST https://api.sendook.com/v1/api_keys \
-H "Authorization: Bearer $SENDOOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "production"}'
List API Keys
const keys = await client.apiKey.list();
Get API Key
const key = await client.apiKey.get({ apiKeyId: "key_pqr678" });
Delete API Key
await client.apiKey.delete({ apiKeyId: "key_pqr678" });
Error Handling
try {
await client.inbox.message.send({
inboxId: "inbox_abc123",
to: ["recipient@example.com"],
subject: "Hello",
text: "Body",
});
} catch (error) {
if (error.response) {
console.error(error.response.status, error.response.data);
} else if (error.request) {
console.error("No response:", error.request);
} else {
console.error("Error:", error.message);
}
}
Common Status Codes
| Status | Meaning |
|---|
200 | Success |
201 | Resource created |
202 | Accepted (async operations like send) |
400 | Bad request — invalid parameters |
401 | Unauthorized — invalid or missing API key |
404 | Resource not found |
429 | Rate limit exceeded |
500 | Internal server error |
Error response shape:
{
"message": "Inbox not found",
"code": "not_found",
"details": {}
}
Best Practices
- Deliverability: Always configure SPF, DKIM, and DMARC records for custom domains. Use
client.domain.dns() to get the required records, then verify with client.domain.verify().
- Webhooks: Return
200 immediately from webhook handlers and process events asynchronously. Use a message queue for heavy processing.
- Attachments: Base64 encoding increases file size by ~33%. Keep attachments under 10MB.
- Rate Limits: The API allows ~1,000 requests/minute and ~50,000 requests/day. Implement exponential backoff on
429 responses.
- Search: Use
client.inbox.message.list(inboxId, query) with regex patterns to search across to/from/cc, subject, and body fields.
- Threading: Use
client.inbox.thread.get() to retrieve full conversation history instead of fetching individual messages.
API Reference
| Method | Description |
|---|
client.inbox.create(options?) | Create a new inbox |
client.inbox.list() | List all inboxes |
client.inbox.get(inboxId) | Get inbox details |
client.inbox.delete(inboxId) | Delete an inbox |
client.inbox.message.send(options) | Send an email |
client.inbox.message.reply(options) | Reply to a message |
client.inbox.message.list(inboxId, query?) | List/search messages |
client.inbox.message.get(inboxId, messageId) | Get a message |
client.inbox.thread.list(inboxId) | List threads |
client.inbox.thread.get(inboxId, threadId) | Get thread with messages |
client.domain.create(options) | Register a custom domain |
client.domain.get(options) | Get domain details |
client.domain.dns(options) | Get required DNS records |
client.domain.verify(options) | Verify domain DNS configuration |
client.domain.delete(options) | Delete a domain |
client.webhook.create(options) | Create a webhook |
client.webhook.list() | List all webhooks |
client.webhook.get(webhookId) | Get webhook details |
client.webhook.test(webhookId) | Send test event to webhook |
client.webhook.delete(webhookId) | Delete a webhook |
client.apiKey.create(options) | Create an API key |
client.apiKey.list() | List all API keys |
client.apiKey.get(options) | Get API key details |
client.apiKey.delete(options) | Delete an API key |