Skip to main content

Postchi API Documentation

Base URL: http://localhost:3000

Authentication

All API endpoints except /auth/register and /auth/login require authentication via JWT token.

Include the token in the Authorization header:

Authorization: Bearer YOUR_ACCESS_TOKEN

Register

Create a new user and organization.

Endpoint: POST /v1/auth/register

Request Body:

{
"email": "user@example.com",
"password": "SecurePass123",
"name": "John Doe",
"organizationName": "My Company"
}

Password Requirements:

  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number

Response: 201 Created

{
"success": true,
"data": {
"user": {
"id": "clx1...",
"email": "user@example.com",
"name": "John Doe",
"role": "ADMIN"
},
"organization": {
"id": "clx2...",
"name": "My Company",
"slug": "my-company-1699..."
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1...",
"refreshToken": "eyJhbGciOiJIUzI1..."
}
}
}

Login

Authenticate an existing user.

Endpoint: POST /v1/auth/login

Request Body:

{
"email": "user@example.com",
"password": "SecurePass123"
}

Response: 200 OK

{
"success": true,
"data": {
"user": {
"id": "clx1...",
"email": "user@example.com",
"name": "John Doe",
"role": "ADMIN"
},
"organization": {
"id": "clx2...",
"name": "My Company",
"slug": "my-company-1699..."
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1...",
"refreshToken": "eyJhbGciOiJIUzI1..."
}
}
}

Get Current User

Get the authenticated user's information.

Endpoint: GET /v1/auth/me

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"user": {
"id": "clx1...",
"email": "user@example.com",
"name": "John Doe",
"role": "ADMIN"
},
"organization": {
"id": "clx2...",
"name": "My Company",
"slug": "my-company-1699..."
}
}
}

Domains

Create Domain

Add a new domain for sending emails. This will generate DNS records that need to be added to your domain's DNS settings.

Endpoint: POST /v1/domains

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"domain": "example.com"
}

Response: 201 Created

{
"success": true,
"data": {
"id": "clx3...",
"domain": "example.com",
"verificationStatus": "PENDING",
"verificationToken": "postchi-verify-a1b2c3...",
"dnsRecords": {
"verification": {
"type": "TXT",
"name": "example.com",
"value": "postchi-verify-a1b2c3..."
},
"spf": {
"type": "TXT",
"name": "example.com",
"value": "v=spf1 include:_spf.mail.postchi.io ~all"
},
"dkim": {
"type": "TXT",
"name": "default._domainkey.example.com",
"value": "v=DKIM1; k=rsa; p=MIIBIjANBg..."
},
"dmarc": {
"type": "TXT",
"name": "_dmarc.example.com",
"value": "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
}
}
}
}

List Domains

Get all domains for your organization.

Endpoint: GET /v1/domains

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": [
{
"id": "clx3...",
"domain": "example.com",
"verificationStatus": "VERIFIED",
"verificationToken": "postchi-verify-a1b2c3...",
"dnsRecords": { ... }
}
],
"count": 1
}

Get Domain

Get a specific domain by ID.

Endpoint: GET /v1/domains/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"id": "clx3...",
"domain": "example.com",
"verificationStatus": "PENDING",
"verificationToken": "postchi-verify-a1b2c3...",
"dnsRecords": { ... }
}
}

Verify Domain

Verify DNS records for a domain.

Endpoint: POST /v1/domains/:id/verify

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"verified": true,
"checks": {
"ownership": {
"verified": true,
"found": true,
"expected": "postchi-verify-a1b2c3...",
"actual": "postchi-verify-a1b2c3..."
},
"spf": {
"verified": true,
"found": true,
"expected": "v=spf1 record",
"actual": "v=spf1 include:_spf.mail.postchi.io ~all"
},
"dkim": {
"verified": true,
"found": true,
"expected": "v=DKIM1 record",
"actual": "v=DKIM1; k=rsa; p=MIIBIjANBg..."
},
"dmarc": {
"verified": true,
"found": true,
"expected": "v=DMARC1 record",
"actual": "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
},
"mx": {
"verified": true,
"found": true,
"expected": "MX records",
"actual": "mail.example.com (priority: 10)"
}
}
}
}

Delete Domain

Delete a domain.

Endpoint: DELETE /v1/domains/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"message": "Domain deleted successfully"
}

Error Responses

All endpoints may return error responses in the following format:

Response: 4xx or 5xx

{
"status": "error",
"message": "Error description"
}

Common error codes:

  • 400 - Bad Request (validation error)
  • 401 - Unauthorized (missing or invalid token)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 409 - Conflict (resource already exists)
  • 429 - Too Many Requests (rate limit exceeded)
  • 500 - Internal Server Error

Rate Limiting

  • Auth endpoints: 5 requests per 15 minutes per email/IP
  • API endpoints: 100 requests per minute per API key/token

cURL Examples

Complete Workflow

# 1. Register
curl -X POST http://localhost:3000/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "Test1234",
"name": "Test User",
"organizationName": "Test Org"
}'

# Save the accessToken from response

# 2. Login (alternative to register)
curl -X POST http://localhost:3000/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "Test1234"
}'

# 3. Get current user
curl http://localhost:3000/v1/auth/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# 4. Create a domain
curl -X POST http://localhost:3000/v1/domains \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{"domain": "example.com"}'

# 5. List domains
curl http://localhost:3000/v1/domains \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# 6. Verify domain DNS
curl -X POST http://localhost:3000/v1/domains/DOMAIN_ID/verify \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# 7. Delete domain
curl -X DELETE http://localhost:3000/v1/domains/DOMAIN_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Messages

Send Email

Send a transactional email. The email will be queued for asynchronous delivery.

Endpoint: POST /v1/messages

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"from": {
"email": "sender@example.com",
"name": "Sender Name"
},
"to": [
{
"email": "recipient@example.com",
"name": "Recipient Name"
}
],
"subject": "Welcome to Postchi",
"html": "<h1>Hello!</h1><p>This is a test email.</p>",
"text": "Hello! This is a test email.",
"replyTo": {
"email": "support@example.com",
"name": "Support"
},
"tags": ["welcome", "onboarding"],
"metadata": {
"userId": "12345",
"campaignId": "welcome-2024"
}
}

Field Descriptions:

  • from.email (required): Must be from a verified domain
  • to (required): Array of recipients (max 1000)
  • cc (optional): Carbon copy recipients
  • bcc (optional): Blind carbon copy recipients
  • subject (required): Email subject (max 998 characters)
  • html (optional): HTML email body
  • text (optional): Plain text email body (either html or text required)
  • replyTo (optional): Reply-to address
  • headers (optional): Custom email headers
  • tags (optional): Array of tags for categorization (max 10)
  • metadata (optional): Custom metadata for tracking

Response: 202 Accepted

{
"success": true,
"data": {
"id": "msg_abc123",
"status": "queued",
"message": "1 email(s) queued for delivery"
}
}

Get Message Status

Get the status and delivery information for a specific message.

Endpoint: GET /v1/messages/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"id": "msg_abc123",
"from": "sender@example.com",
"to": "recipient@example.com",
"subject": "Welcome to Postchi",
"status": "SENT",
"sentAt": "2024-11-07T10:30:00.000Z",
"events": [
{
"type": "QUEUED",
"createdAt": "2024-11-07T10:29:00.000Z"
},
{
"type": "PROCESSING",
"createdAt": "2024-11-07T10:29:30.000Z"
},
{
"type": "SENT",
"createdAt": "2024-11-07T10:30:00.000Z",
"metadata": {
"smtpMessageId": "<abc123@example.com>"
}
}
]
}
}

List Messages

Get a list of messages with filtering and pagination.

Endpoint: GET /v1/messages

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Query Parameters:

  • status (optional): Filter by status (QUEUED, PROCESSING, SENT, FAILED, BOUNCED)
  • domain (optional): Filter by domain
  • from (optional): Filter by sender email
  • to (optional): Filter by recipient email
  • limit (optional): Number of results (default: 50, max: 100)
  • offset (optional): Pagination offset (default: 0)

Example:

GET /v1/messages?status=SENT&limit=20&offset=0

Response: 200 OK

{
"success": true,
"data": [
{
"id": "msg_abc123",
"from": "sender@example.com",
"to": "recipient@example.com",
"subject": "Welcome to Postchi",
"status": "SENT",
"createdAt": "2024-11-07T10:29:00.000Z",
"sentAt": "2024-11-07T10:30:00.000Z",
"domain": {
"domain": "example.com"
}
}
],
"pagination": {
"total": 150,
"limit": 20,
"offset": 0
}
}

cURL Examples

Sending an Email

# Send a simple email
curl -X POST http://localhost:3000/v1/messages \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"from": {
"email": "hello@example.com",
"name": "My App"
},
"to": [
{
"email": "user@test.com",
"name": "Test User"
}
],
"subject": "Welcome!",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"text": "Welcome! Thanks for signing up.",
"tags": ["welcome"],
"metadata": {
"userId": "123"
}
}'

# Get message status
curl http://localhost:3000/v1/messages/msg_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# List sent messages
curl "http://localhost:3000/v1/messages?status=SENT&limit=10" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Suppression List

The suppression list prevents emails from being sent to specific addresses. This is used for managing bounces, complaints, unsubscribes, and manual blocks.

Add Email to Suppression List

Add a single email address to the suppression list.

Endpoint: POST /v1/suppressions

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"email": "user@example.com",
"reason": "HARD_BOUNCE",
"reasonDetail": "Hard bounce - mailbox does not exist"
}

Field Descriptions:

  • email (required): Email address to suppress
  • reason (required): Reason for suppression - one of: HARD_BOUNCE, SPAM_COMPLAINT, MANUAL, UNSUBSCRIBE
  • reasonDetail (optional): Additional details about the suppression

Response: 201 Created

{
"success": true,
"data": {
"id": "sup_abc123",
"email": "user@example.com",
"reason": "HARD_BOUNCE",
"reasonDetail": "Hard bounce - mailbox does not exist",
"createdAt": "2024-11-07T10:30:00.000Z"
}
}

Bulk Add Emails to Suppression List

Add multiple email addresses to the suppression list at once.

Endpoint: POST /v1/suppressions/bulk

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"emails": [
"bounce1@example.com",
"bounce2@example.com",
"bounce3@example.com"
],
"reason": "HARD_BOUNCE"
}

Response: 201 Created

{
"success": true,
"data": {
"added": 3,
"skipped": 0,
"message": "Added 3 email(s) to suppression list"
}
}

List Suppression Entries

Get a list of suppressed email addresses with filtering and pagination.

Endpoint: GET /v1/suppressions

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Query Parameters:

  • reason (optional): Filter by reason (HARD_BOUNCE, SPAM_COMPLAINT, MANUAL, UNSUBSCRIBE)
  • email (optional): Search for specific email (partial match)
  • limit (optional): Number of results (default: 50, max: 100)
  • offset (optional): Pagination offset (default: 0)

Example:

GET /v1/suppressions?reason=HARD_BOUNCE&limit=20&offset=0

Response: 200 OK

{
"success": true,
"data": [
{
"id": "sup_abc123",
"email": "user@example.com",
"reason": "HARD_BOUNCE",
"reasonDetail": "Hard bounce - mailbox does not exist",
"createdAt": "2024-11-07T10:30:00.000Z"
}
],
"pagination": {
"total": 45,
"limit": 20,
"offset": 0
}
}

Check if Email is Suppressed

Check whether a specific email address is in the suppression list.

Endpoint: GET /v1/suppressions/check/:email

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Example:

GET /v1/suppressions/check/user@example.com

Response: 200 OK

{
"success": true,
"data": {
"email": "user@example.com",
"suppressed": true
}
}

Remove Email from Suppression List

Remove an email address from the suppression list.

Endpoint: DELETE /v1/suppressions/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"success": true,
"message": "Email removed from suppression list"
}
}

cURL Examples

Managing Suppression List

# Add single email to suppression list
curl -X POST http://localhost:3000/v1/suppressions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"email": "bounce@example.com",
"reason": "HARD_BOUNCE",
"reasonDetail": "Hard bounce - mailbox does not exist"
}'

# Bulk add emails
curl -X POST http://localhost:3000/v1/suppressions/bulk \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"emails": ["bounce1@example.com", "bounce2@example.com"],
"reason": "HARD_BOUNCE"
}'

# List suppressed emails
curl "http://localhost:3000/v1/suppressions?reason=HARD_BOUNCE&limit=10" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Check if email is suppressed
curl http://localhost:3000/v1/suppressions/check/user@example.com \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Remove email from suppression list
curl -X DELETE http://localhost:3000/v1/suppressions/sup_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in your Postchi account. When an event happens, Postchi will send an HTTP POST request to the webhook URL you configure.

Webhook Events

The following events are available:

  • message.queued - Email has been queued for sending
  • message.sent - Email has been successfully sent
  • message.failed - Email failed to send
  • message.bounced - Email bounced
  • message.delivered - Email was delivered to recipient
  • message.opened - Recipient opened the email
  • message.clicked - Recipient clicked a link in the email
  • message.complained - Recipient marked email as spam
  • message.unsubscribed - Recipient unsubscribed

Webhook Security

All webhook requests include an X-Postchi-Signature header containing an HMAC SHA256 signature. Use your webhook's signing secret to verify the authenticity of webhook requests.

Create Webhook

Create a new webhook endpoint to receive event notifications.

Endpoint: POST /v1/webhooks

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"url": "https://your-app.com/webhooks/postchi",
"events": [
"message.sent",
"message.failed",
"message.bounced"
],
"enabled": true,
"description": "Production webhook for email events"
}

Field Descriptions:

  • url (required): HTTPS URL where webhook events will be sent
  • events (required): Array of events to subscribe to
  • enabled (optional): Whether webhook is enabled (default: true)
  • description (optional): Description of the webhook

Response: 201 Created

{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/postchi",
"events": ["message.sent", "message.failed", "message.bounced"],
"enabled": true,
"description": "Production webhook for email events",
"signingSecret": "whsec_a1b2c3d4e5f6...",
"createdAt": "2024-11-07T10:30:00.000Z"
}
}

List Webhooks

Get all webhooks for your organization.

Endpoint: GET /v1/webhooks

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": [
{
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/postchi",
"events": ["message.sent", "message.failed"],
"enabled": true,
"description": "Production webhook",
"createdAt": "2024-11-07T10:30:00.000Z",
"lastDeliveryAt": "2024-11-07T11:00:00.000Z",
"successCount": 42,
"failureCount": 0
}
],
"count": 1
}

Get Webhook

Get a specific webhook by ID.

Endpoint: GET /v1/webhooks/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://your-app.com/webhooks/postchi",
"events": ["message.sent", "message.failed"],
"enabled": true,
"signingSecret": "whsec_a1b2c3d4e5f6...",
"createdAt": "2024-11-07T10:30:00.000Z"
}
}

Update Webhook

Update webhook configuration.

Endpoint: PATCH /v1/webhooks/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

Request Body:

{
"url": "https://your-app.com/new-webhook-url",
"events": ["message.sent", "message.bounced"],
"enabled": false
}

All fields are optional. Only include fields you want to update.

Response: 200 OK

{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://your-app.com/new-webhook-url",
"events": ["message.sent", "message.bounced"],
"enabled": false,
"updatedAt": "2024-11-07T11:00:00.000Z"
}
}

Delete Webhook

Delete a webhook.

Endpoint: DELETE /v1/webhooks/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"success": true,
"message": "Webhook deleted successfully"
}
}

Regenerate Signing Secret

Regenerate the signing secret for a webhook. Useful if the secret has been compromised.

Endpoint: POST /v1/webhooks/:id/regenerate-secret

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"id": "wh_abc123",
"signingSecret": "whsec_new_secret_xyz...",
"updatedAt": "2024-11-07T11:00:00.000Z"
}
}

Get Webhook Delivery Logs

View delivery attempts for a specific webhook.

Endpoint: GET /v1/webhooks/:id/deliveries

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Query Parameters:

  • status (optional): Filter by status (success or failed)
  • limit (optional): Number of results (default: 50, max: 100)
  • offset (optional): Pagination offset (default: 0)

Example:

GET /v1/webhooks/wh_abc123/deliveries?status=failed&limit=20

Response: 200 OK

{
"success": true,
"data": [
{
"id": "whd_xyz789",
"event": "message.sent",
"statusCode": 200,
"responseTime": 245,
"success": true,
"createdAt": "2024-11-07T11:00:00.000Z"
},
{
"id": "whd_abc456",
"event": "message.failed",
"statusCode": 500,
"responseBody": "Internal Server Error",
"responseTime": 1200,
"success": false,
"createdAt": "2024-11-07T10:55:00.000Z"
}
],
"pagination": {
"total": 42,
"limit": 20,
"offset": 0
}
}

Webhook Payload Format

When an event occurs, Postchi will send a POST request to your webhook URL with the following format:

{
"id": "evt_abc123",
"event": "message.sent",
"timestamp": "2024-11-07T11:00:00.000Z",
"data": {
"messageId": "msg_xyz789",
"rfcMessageId": "<1234567890.abc@example.com>",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Welcome to Postchi",
"status": "SENT",
"sentAt": "2024-11-07T11:00:00.000Z"
}
}

Verifying Webhook Signatures

To verify webhook authenticity, compare the HMAC signature:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return signature === expectedSignature;
}

// In your webhook handler:
app.post('/webhooks/postchi', (req, res) => {
const signature = req.headers['x-postchi-signature'];
const payload = JSON.stringify(req.body);

if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

// Process webhook...
res.status(200).send('OK');
});

cURL Examples

Managing Webhooks

# Create webhook
curl -X POST http://localhost:3000/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"url": "https://your-app.com/webhooks/postchi",
"events": ["message.sent", "message.failed"],
"description": "Production webhook"
}'

# List webhooks
curl http://localhost:3000/v1/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Get webhook
curl http://localhost:3000/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Update webhook
curl -X PATCH http://localhost:3000/v1/webhooks/wh_abc123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"enabled": false
}'

# Regenerate signing secret
curl -X POST http://localhost:3000/v1/webhooks/wh_abc123/regenerate-secret \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Get delivery logs
curl "http://localhost:3000/v1/webhooks/wh_abc123/deliveries?status=failed" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Delete webhook
curl -X DELETE http://localhost:3000/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Batch Sending

Batch sending allows you to send the same template to multiple recipients with personalized variables. Batches are processed asynchronously and provide real-time progress tracking.

Key Features

  • Template-based only: All batch sends must use a pre-created template
  • Idempotency required: All batch requests must include an Idempotency-Key header
  • Max 100 recipients per batch: For larger sends, split into multiple batches
  • Progress tracking: Real-time counters for queued, sent, delivered, and failed messages
  • Automatic suppression checking: Suppressed emails are automatically skipped

Create Batch Send

Send a template to multiple recipients with personalized variables.

Endpoint: POST /v1/batches

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN
Idempotency-Key: your-unique-key-here
Content-Type: application/json

Request Body:

{
"name": "Monthly Newsletter - January 2025",
"description": "January newsletter for active users",
"templateId": "tmpl_abc123",
"recipients": [
{
"to": "user1@example.com",
"variables": {
"firstName": "John",
"accountType": "premium",
"expiryDate": "2025-02-01"
}
},
{
"to": "user2@example.com",
"variables": {
"firstName": "Jane",
"accountType": "free",
"expiryDate": "2025-01-15"
}
}
],
"from": {
"email": "newsletter@example.com",
"name": "Company Newsletter"
},
"replyTo": "support@example.com"
}

Field Descriptions:

  • name (optional): Human-readable name for the batch
  • description (optional): Description of the batch purpose
  • templateId (required): ID of the template to use
  • recipients (required): Array of recipients (min: 1, max: 100)
    • to (required): Recipient email address
    • variables (optional): Template variables for personalization
  • from (optional): Override template's from settings
    • email (optional): Override from email
    • name (optional): Override from name
  • replyTo (optional): Override reply-to address

Idempotency-Key Requirements:

  • Required header for all batch requests
  • Must be between 1-255 characters
  • Same key + same payload = returns cached response
  • Same key + different payload = returns 409 Conflict error
  • Keys expire after 24 hours

Response: 201 Created

{
"success": true,
"data": {
"id": "batch_xyz789",
"status": "PROCESSING",
"totalCount": 2,
"queuedCount": 2,
"sentCount": 0,
"deliveredCount": 0,
"failedCount": 0,
"progressPercentage": 0,
"createdAt": "2025-01-15T10:00:00.000Z",
"startedAt": "2025-01-15T10:00:00.000Z",
"messages": [
{
"id": "msg_abc123",
"to": "user1@example.com",
"status": "QUEUED"
},
{
"id": "msg_def456",
"to": "user2@example.com",
"status": "QUEUED"
}
]
}
}

Batch Statuses:

  • PENDING - Created but not started
  • PROCESSING - Currently sending
  • COMPLETED - All messages processed
  • FAILED - Batch failed
  • CANCELLED - Manually cancelled
  • PAUSED - Temporarily paused

Get Batch Progress

Get real-time progress for a batch send.

Endpoint: GET /v1/batches/:id

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Response: 200 OK

{
"success": true,
"data": {
"id": "batch_xyz789",
"status": "PROCESSING",
"totalCount": 100,
"queuedCount": 100,
"sentCount": 75,
"deliveredCount": 70,
"failedCount": 5,
"bouncedCount": 0,
"progressPercentage": 75.0,
"createdAt": "2025-01-15T10:00:00.000Z",
"startedAt": "2025-01-15T10:00:00.000Z",
"completedAt": null
}
}

Progress Fields:

  • totalCount: Total recipients in batch
  • queuedCount: Messages successfully queued
  • sentCount: Messages successfully sent
  • deliveredCount: Messages delivered to recipient
  • failedCount: Messages that failed to send
  • bouncedCount: Messages that bounced
  • progressPercentage: Percentage complete (0-100)

List Batches

Get a paginated list of all batches for your organization.

Endpoint: GET /v1/batches

Headers:

Authorization: Bearer YOUR_ACCESS_TOKEN

Query Parameters:

  • limit (optional): Number of results (default: 20, max: 100)
  • offset (optional): Pagination offset (default: 0)

Example:

GET /v1/batches?limit=10&offset=0

Response: 200 OK

{
"success": true,
"data": [
{
"id": "batch_xyz789",
"status": "COMPLETED",
"totalCount": 100,
"queuedCount": 100,
"sentCount": 95,
"deliveredCount": 92,
"failedCount": 5,
"bouncedCount": 3,
"progressPercentage": 100.0,
"createdAt": "2025-01-15T10:00:00.000Z",
"startedAt": "2025-01-15T10:00:00.000Z",
"completedAt": "2025-01-15T10:15:00.000Z"
}
],
"pagination": {
"total": 25,
"limit": 10,
"offset": 0
}
}

cURL Examples

Batch Sending

# Create a batch send with idempotency
curl -X POST http://localhost:3000/v1/batches \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: batch-newsletter-2025-01" \
-d '{
"name": "January Newsletter",
"templateId": "tmpl_abc123",
"recipients": [
{
"to": "user1@example.com",
"variables": {
"firstName": "John",
"accountType": "premium"
}
},
{
"to": "user2@example.com",
"variables": {
"firstName": "Jane",
"accountType": "free"
}
}
],
"from": {
"email": "newsletter@example.com",
"name": "Company Newsletter"
}
}'

# Get batch progress
curl http://localhost:3000/v1/batches/batch_xyz789 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# List all batches
curl "http://localhost:3000/v1/batches?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# Retry with same idempotency key (returns cached response)
curl -X POST http://localhost:3000/v1/batches \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: batch-newsletter-2025-01" \
-d '{
"name": "January Newsletter",
"templateId": "tmpl_abc123",
"recipients": [
{
"to": "user1@example.com",
"variables": {"firstName": "John"}
}
]
}'

Best Practices for Batch Sending

  1. Use unique idempotency keys - Include date, campaign ID, or UUID

    • Good: batch-welcome-2025-01-15-uuid123
    • Bad: batch-1
  2. Split large sends - Max 100 recipients per batch

    # For 1000 recipients, create 10 batches
    for i in {1..10}; do
    curl -X POST .../batches \
    -H "Idempotency-Key: campaign-2025-batch-$i" \
    -d '{ recipients: [...] }'
    done
  3. Monitor progress - Poll the progress endpoint

    # Poll every 5 seconds
    while true; do
    curl .../batches/$BATCH_ID | jq '.data.progressPercentage'
    sleep 5
    done
  4. Handle errors gracefully - Check for 409 Conflict on retries

    # Will return 409 if payload differs with same key
    curl -X POST .../batches \
    -H "Idempotency-Key: same-key" \
    -d '{ different: "payload" }'
  5. Clean template variables - Remove recipients from suppression list first

    • Check suppression list before sending
    • Template variables are automatically substituted