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