Quickstart
If you can't send and receive a deterministic response, contact support.
1. Create an API key
Sign up and generate an API key from the dashboard.
export TRUNCUS_API_KEY=tr_live_...
Don't have an API key yet? Create one in 10 seconds →
2. Send via curl
curl -X POST https://truncus.co/api/v1/emails/send -H "Authorization: Bearer $TRUNCUS_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" -d '{
"to": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Test message",
"html": "<p>Hello from Truncus.</p>"
}'
3. Install the SDK
npm install @truncus/email
4. Send your first email
import { Truncus } from '@truncus/email'
const truncus = new Truncus({
apiKey: process.env.TRUNCUS_API_KEY
})
const response = await truncus.emails.send({
to: 'user@example.com',
from: 'hello@yourdomain.com',
subject: 'Test message',
html: '<p>Hello from Truncus.</p>'
})
console.log(response)
5. Inspect the response
POST /api/v1/emails/send is asynchronous: it answers as soon as a provider accepts or refuses the message, not when the message is delivered. status is one of sent, queued, scheduled, duplicate, blocked or failed.
{ "status": "sent", "message_id": "msg_8f21", "provider": "ses" }
Need the delivery outcome in the same request? Call POST /api/v1/emails/send_sync with the same body (one recipient, no send_at). It waits up to 25 seconds for the delivery, bounce or complaint event and returns that terminal state: delivered, bounced or complained. If no event arrives in time, it returns pending with the message_id to poll.
curl -X POST https://truncus.co/api/v1/emails/send_sync \
-H "Authorization: Bearer $TRUNCUS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"to": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Test message",
"html": "<p>Hello from Truncus.</p>"
}'
{"message_id":"cmvxr6q4q000120ajvvjpxhq8","global_message_id":"gm_8b96f356-f7e9-472e-bbe4-88a9be1651b5","status":"delivered","delivered_at":"2026-11-01T11:35:11.909Z","smtp_response":"250 2.0.0 Ok: queued as 4jHXnV4rtkz9vNPx","latency_ms":2080}
{ "status": "bounced", "message_id": "msg_9b44", "global_message_id": "gm_3e1f7a20-6c4d-4b19-9a52-d07c8e41b6f3", "bounced_at": "2026-10-24T09:12:04.077Z", "latency_ms": 2310 }
{ "status": "pending", "message_id": "msg_a2f7", "global_message_id": "gm_c47d2e91-0b8a-4f63-b1e5-72a9f3d6e018", "note": "Delivery confirmation not received within 25s. The email was sent successfully. Check status via GET /api/v1/emails/msg_a2f7", "latency_ms": 25004 }
send_sync never infers a status: delivered means the delivery event arrived, and pending says that it has not yet.
6. Configure your domain
Recommended for production. Add SPF, DKIM, and DMARC records for your sending domain.
Dashboard → Domains → Add domain
Each sending domain has a fixed daily sending cap, 500 emails/day by default. Truncus does not raise the cap automatically; contact us if you need a higher limit.
7. Handle delivery state via webhook
app.post('/truncus/webhook', async (req, res) => {
const event = req.body
if (event.event_type === 'bounce') {
if (event.data.bounce_type === 'Permanent') {
// hard bounce — Truncus has suppressed the address; remove it on your side too
await removeEmailAddress(event.email.to)
} else {
// soft bounce — schedule your own retry with a limit
await scheduleRetry(event.email.to)
}
}
if (event.event_type === 'complaint') {
await markAddressSuppressed(event.email.to)
}
res.status(200).send('ok')
})
Every webhook body has event_type, email (id, to, subject, status) and data. For a bounce, data.bounce_type is Permanent, Transient or Undetermined. A suppressed address produces no bounce: the send itself returns blocked with code ALL_RECIPIENTS_SUPPRESSED.
All delivery events are stored and replayable. If your endpoint is down, replay events from the dashboard or via the API.
Rate limits
If you exceed your tier limit:
HTTP 429
{
"error": "rate_limit_exceeded",
"retry_after": 60
}
Back off for retry_after seconds and retry. No silent failures.
Production checklist
- Domain verified (SPF, DKIM, DMARC)
- Webhook endpoint reachable and returning 200
- Retry logic on 429 responses
- Monitoring on
bounceandcomplaintevents andblockedsends