Manage verified domains, IMAP mailboxes, MJML templates, and send transactional or bulk email — all through the same client.
Domains#
Verified sending domains for your company. Domain creation and DNS verification happen in the dashboard; the API surfaces a read-only view for use at send time.
List domains#
/api/mail/companies/{slug}/domainsconst domains = await sentroy.domains.list()
// → Domain[]Get domain#
/api/mail/companies/{slug}/domains/{id}const domain = await sentroy.domains.get("domain-id")Mailboxes#
IMAP mailbox accounts created against your verified domains. Used by the Inbox API to read messages and by Send to authenticate the from-address.
List mailboxes#
/api/mail/companies/{slug}/mailboxesconst mailboxes = await sentroy.mailboxes.list()Templates#
Reusable MJML email templates with multilingual fields and variable placeholders.
List templates#
/api/mail/companies/{slug}/templatesconst templates = await sentroy.templates.list()Get template#
/api/mail/companies/{slug}/templates/{id}const template = await sentroy.templates.get("template-id")LocalizedString shape
Templates support multiple languages. A field can be a plain string (single-language) or an object keyed by language code:
{
"id": "b3f1a2c4-...",
"name": { "en": "Welcome Email", "tr": "Hosgeldin E-postasi" },
"subject": { "en": "Welcome, {{name}}!", "tr": "Hosgeldin, {{name}}!" },
"mjmlBody": { "en": "<mjml>...</mjml>", "tr": "<mjml>...</mjml>" },
"variables": ["name", "company"],
"domainId": "a1b2c3d4-...",
"domainName": "example.com",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-04-10T14:22:00.000Z"
}Use the variables array to know which placeholders the template expects.
Inbox#
Read messages, list IMAP folders, group threads, and manage message state.
List messages#
/api/mail/companies/{slug}/inbox?mailbox=info@example.com&folder=INBOX&page=1&limit=20const messages = await sentroy.inbox.list({
mailbox: "info@example.com",
folder: "INBOX",
page: 1,
limit: 20,
})Get message#
/api/mail/companies/{slug}/inbox/{uid}?mailbox=info@example.comconst message = await sentroy.inbox.get(1234, {
mailbox: "info@example.com",
})List folders#
/api/mail/companies/{slug}/inbox/folders?mailbox=info@example.comconst folders = await sentroy.inbox.listFolders("info@example.com")Get thread#
/api/mail/companies/{slug}/inbox/thread?subject=Re%3A%20Project%20update&mailbox=info@example.comconst thread = await sentroy.inbox.getThread(
"Re: Project update",
"info@example.com",
)Mark as read / unread#
/api/mail/companies/{slug}/inbox/{uid}/readawait sentroy.inbox.markAsRead(1234, { mailbox: "info@example.com" })
await sentroy.inbox.markAsUnread(1234, { mailbox: "info@example.com" })Move message#
/api/mail/companies/{slug}/inbox/{uid}/moveawait sentroy.inbox.move(1234, "Trash", {
from: "INBOX",
mailbox: "info@example.com",
})Delete message#
/api/mail/companies/{slug}/inbox/{uid}?mailbox=info@example.comawait sentroy.inbox.delete(1234, { mailbox: "info@example.com" })Send email#
Single endpoint for transactional and bulk send. Pass either a templateId + variables, or raw html. Recipients can be a string or array of addresses.
Send with a template#
/api/mail/companies/{slug}/sendconst result = await sentroy.send.email({
to: "user@example.com",
from: "info@example.com",
subject: "Welcome!",
domainId: "domain-id",
templateId: "template-id",
variables: {
name: "John",
company: "Acme",
},
})Send in a specific language#
For multilingual templates, pass langto pick which translation of subject and body to use. If omitted, the template's default language wins.
/api/mail/companies/{slug}/sendawait sentroy.send.email({
to: "user@example.com",
from: "info@example.com",
subject: "Hosgeldin!",
domainId: "domain-id",
templateId: "template-id",
lang: "tr",
variables: { name: "Ahmet" },
})Send with raw HTML#
/api/mail/companies/{slug}/sendawait sentroy.send.email({
to: ["user1@example.com", "user2@example.com"],
from: "info@example.com",
subject: "Hello",
domainId: "domain-id",
html: "<h1>Hello World</h1>",
})Send with attachments#
Attachments accept a base64 content string plus filename and MIME type.
/api/mail/companies/{slug}/sendawait sentroy.send.email({
to: "user@example.com",
from: "info@example.com",
subject: "Invoice",
domainId: "domain-id",
html: "<p>Please find your invoice attached.</p>",
attachments: [
{
filename: "invoice.pdf",
content: base64String,
contentType: "application/pdf",
},
],
})Audience#
Manage contacts and audience lists. Build your own newsletter signup, sync customers from another system, or assemble segments for a campaign.
List contacts#
Paginated browsing with optional status and tag filters. Tags are comma-joined on the wire — pass them as an array to the SDK.
/api/mail/companies/{slug}/audience/contacts?page=1&limit=50&status=active&tags=customer,vipconst { contacts, total, page, limit } = await sentroy.audience.contacts.list({
page: 1,
limit: 50,
status: "active",
tags: ["customer", "vip"],
})Search contacts#
Email-prefix autocomplete. Capped at 10 results server-side — use list for paginated browsing.
/api/mail/companies/{slug}/audience/contacts?q=alex@const matches = await sentroy.audience.contacts.search("alex@")Create contact#
/api/mail/companies/{slug}/audience/contactsconst contact = await sentroy.audience.contacts.create({
email: "user@example.com",
name: "Jane Doe",
tags: ["beta-tester"],
metadata: { signupSource: "landing-2026-q2" },
})Update contact#
Pass any subset of fields. Use status to mark unsubscribed/bounced.
/api/mail/companies/{slug}/audience/contacts/{id}await sentroy.audience.contacts.update(contact.id, { tags: ["customer"] })Delete contact#
Soft-delete — sets status: "unsubscribed". The record is preserved so historical mail-log foreign keys keep resolving and the email can't accidentally be re-added.
/api/mail/companies/{slug}/audience/contacts/{id}await sentroy.audience.contacts.delete(contact.id)Audience lists#
Lists are simple groupings; a single contact can belong to many. Use them as the target of a campaign or a form submission.
/api/mail/companies/{slug}/audience/listsconst lists = await sentroy.audience.lists.list()/api/mail/companies/{slug}/audience/listsconst list = await sentroy.audience.lists.create({
name: "Newsletter — May 2026",
description: "Opt-ins from the homepage form",
})/api/mail/companies/{slug}/audience/lists/{id}await sentroy.audience.lists.delete(list.id)List membership#
Membership operations are scoped via the SDK's members(listId) accessor — keeps the list id off every call.
/api/mail/companies/{slug}/audience/lists/{id}/membersconst members = sentroy.audience.lists.members(list.id)
await members.add(contact.id)/api/mail/companies/{slug}/audience/lists/{id}/membersconst inList = await members.list()/api/mail/companies/{slug}/audience/lists/{id}/membersawait members.remove(contact.id)Suppressions#
Suppressed addresses are skipped at send time. Bounces and complaints are added automatically by the mail server — the API is for honoring off-platform opt-outs or removing a stale entry.
List suppressions#
/api/mail/companies/{slug}/suppressions?page=1&limit=50&domainId=domain-id&reason=complaintconst suppressions = await sentroy.suppressions.list({
domainId: "domain-id",
reason: "complaint",
page: 1,
limit: 50,
})Add suppression#
/api/mail/companies/{slug}/suppressionsconst added = await sentroy.suppressions.add({
email: "leaving@example.com",
domainId: "domain-id",
reason: "manual",
})Remove suppression#
Removing a suppression makes the address eligible to receive mail again.
/api/mail/companies/{slug}/suppressions/{id}await sentroy.suppressions.remove(added.id)Webhooks#
Subscribe to delivery events on a per-domain basis. Each delivery is signed with the secret returned at create time — verify the HMAC on your endpoint before trusting the payload.
Create webhook#
/api/mail/companies/{slug}/webhooksconst webhook = await sentroy.webhooks.create({
url: "https://example.com/webhooks/sentroy",
events: ["sent", "bounced", "opened", "clicked", "unsubscribed"],
domainId: "domain-id",
})
console.log(webhook.secret) // Returned ONCE — store it nowEvent types#
| Name | Type | Description |
|---|---|---|
| sent | string | Mail handed off to SMTP successfully |
| bounced | string | Hard or soft bounce reported by remote MTA |
| failed | string | Send pipeline failure (rendering, suppression, quota) |
| opened | string | Tracking pixel hit (requires trackOpens at send time) |
| clicked | string | Link click recorded (requires trackClicks at send time) |
| unsubscribed | string | Recipient clicked the {{unsubscribe_url}} link |
List + scope#
/api/mail/companies/{slug}/webhooksconst all = await sentroy.webhooks.list()
const scoped = await sentroy.webhooks.list("domain-id")Test fire#
Manual dispatch — POST a custom payload at the webhook's current URL. The result and a row in the delivery log are returned for inspection. The mail server's automated event delivery is unaffected; this is a debug tool, not a production retry path.
/api/mail/companies/{slug}/webhooks/{id}/testconst result = await sentroy.webhooks.test(webhook.id, {
event: "sent",
payload: {
mailLogId: "ml_abc",
to: "user@example.com",
subject: "Welcome",
},
})
console.log(result.responseStatus, result.durationMs)List deliveries#
Paginated history of test/replay dispatches recorded for the webhook. Each row carries the full payload, response body, status, and round-trip duration.
/api/mail/companies/{slug}/webhooks/{id}/deliveries?page=1&limit=50&status=failedconst { items, total } = await sentroy.webhooks
.deliveries(webhook.id)
.list({ page: 1, limit: 50, status: "failed" })Get delivery#
/api/mail/companies/{slug}/webhooks/{id}/deliveries/{deliveryId}const delivery = await sentroy.webhooks
.deliveries(webhook.id)
.get(deliveryId)Replay delivery#
Re-fire the recorded payload at the webhook's current URL. Useful for retesting after the receiver fixes a bug. The new row is linked to the original via replayOf.
/api/mail/companies/{slug}/webhooks/{id}/deliveries/{deliveryId}/replayconst result = await sentroy.webhooks
.deliveries(webhook.id)
.replay(deliveryId)Update + delete#
/api/mail/companies/{slug}/webhooks/{id}await sentroy.webhooks.update(webhook.id, { active: false })/api/mail/companies/{slug}/webhooks/{id}await sentroy.webhooks.delete(webhook.id)Logs#
Query the mail log to debug delivery issues, surface per-message status in your own UI, or build a customer-facing activity timeline.
List logs#
/api/mail/companies/{slug}/logs?status=bounced&domainId=domain-id&from=2026-05-01T00:00:00Z&to=2026-05-31T23:59:59Z&page=1&limit=100const logs = await sentroy.logs.list({
status: "bounced",
domainId: "domain-id",
from: "2026-05-01T00:00:00Z",
to: "2026-05-31T23:59:59Z",
page: 1,
limit: 100,
})Get a single entry#
/api/mail/companies/{slug}/logs/{id}const log = await sentroy.logs.get(logs[0].id)
console.log(log.openedAt, log.clickedAt) // tracking timestamps if enabled