@zenderock/useinbox-sdk
v0.2.0
Published
Email inboxes for AI agents — send, receive and reply in threads from code.
Maintainers
Readme
@zenderock/useinbox-sdk
Email inboxes for AI agents. Create a mailbox from code, read what arrives as conversations, and reply inside them.
npm install @zenderock/useinbox-sdkQuick start
import { UseInbox } from '@zenderock/useinbox-sdk';
const client = new UseInbox({ apiKey: process.env.USEINBOX_API_KEY! });
// A live address, immediately able to receive mail.
const inbox = await client.inboxes.create({
username: 'support',
displayName: 'Acme Support',
clientId: 'agent-7',
});
console.log(inbox.address); // [email protected]
// Answer everything unread.
for await (const thread of client.threads.iterate(inbox.id, { unreadOnly: true })) {
const full = await client.threads.get(thread.id);
const last = full.messages?.at(-1);
await client.threads.reply(thread.id, {
text: `Thanks for writing about "${thread.subject}". Looking into it.`,
});
await client.threads.markRead(thread.id);
await client.threads.setLabels(thread.id, ['handled']);
}Why reply and not send
inboxes.send starts a new conversation. threads.reply answers an existing one
and attaches the In-Reply-To and References headers that make the recipient's
mail client show your message inside the original thread. Sending a fresh message
with Re: in the subject does not do this — the recipient gets a second, unrelated
conversation, and the next reply comes back detached from the first.
Threads and messages
Listing threads returns previews. Bodies and attachments come from
threads.get(id), which is one call per conversation — deliberately, so a listing
of a busy inbox does not fetch every stored body to render a list.
Errors
import { UseInboxError, UseInboxConnectionError } from '@zenderock/useinbox-sdk';
try {
await client.inboxes.create();
} catch (error) {
if (error instanceof UseInboxError && error.isQuotaExceeded) {
// Plan limit reached — retrying will not help. Delete an unused inbox.
} else if (error instanceof UseInboxConnectionError) {
// Never reached the API. Already retried; safe to retry again later.
}
}Branch on error.code, not on error.message: codes are stable, messages are prose.
Transient failures (429, 5xx, connection errors) are retried automatically with
exponential backoff and jitter — twice by default, configurable with maxRetries.
4xx responses are never retried.
Options
| Option | Default | |
|---|---|---|
| apiKey | — | required |
| baseUrl | https://api.useinbox.email | |
| timeoutMs | 30000 | per attempt |
| maxRetries | 2 | three attempts total |
| fetch | global fetch | inject your own for tests |
Node 18+.
