Schedule Emails
Schedule emails for future delivery with timezone support
Schedule emails to be sent at a specific time in the future with full timezone support.
Paid Feature: Email scheduling is available on LAUNCH, CUSTOM, and ENTERPRISE plans. Free tier users can only send immediate emails.
Email Scheduling Features:
- Schedule up to 365 days in advance
- Timezone support (IANA timezone identifiers)
- Works with both raw emails and templates
- Cancel scheduled emails before they're sent
Quick Start
Schedule a Simple Email
curl -X POST https://api.keplars.com/api/v1/send-email/schedule \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scheduled_at": "2026-01-20_10:00:00",
"timezone": "America/New_York",
"email": {
"from": "[email protected]",
"from_name": "Newsletter Team",
"to": ["[email protected]"],
"subject": "Scheduled Newsletter",
"html": "<h1>Your Weekly Update</h1><p>Here is this week newsletter...</p>"
}
}'Schedule with Template
curl -X POST https://api.keplars.com/api/v1/send-email/schedule \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scheduled_at": "2026-01-25_09:00:00",
"timezone": "UTC",
"email": {
"from": "[email protected]",
"from_name": "Customer Success Team",
"to": ["[email protected]"],
"template_id": "019a83de-2fc2-7b5f-bfbc-c951fe1200f7",
"params": {
"customerName": "John Doe",
"eventDate": "February 1st, 2026"
}
}
}'Date Format Examples
{
"scheduled_at": "2026-01-20T15:30:00",
"timezone": "Asia/Kolkata",
"email": { ... }
}{
"scheduled_at": "2026-01-20_15:30:00",
"timezone": "Asia/Kolkata",
"email": { ... }
}Both formats interpret the time in the specified timezone. In the example above, the email will be sent at 3:30 PM India time.
Schedule Request Format
Required Fields
- scheduled_at (string): Date and time when to send the email. Supports two formats:
- ISO 8601:
2026-01-20T10:00:00(standard format) - Simplified:
2026-01-20_10:00:00(easier to read/write) - Time is interpreted in the specified
timezone(or UTC if not provided)
- ISO 8601:
- email (object): Email configuration (same format as Send Emails)
Optional Fields
- timezone (string): IANA timezone identifier (default:
UTC)- Examples:
America/New_York,Europe/London,Asia/Tokyo,Asia/Kolkata,UTC - The
scheduled_attime will be interpreted in this timezone
- Examples:
Email Object
The email object supports all the same fields as the regular send email endpoints:
Raw Email:
{
"from": "[email protected]",
"from_name": "Your Company",
"to": ["[email protected]"],
"cc": ["[email protected]"],
"bcc": ["[email protected]"],
"subject": "Scheduled Email",
"html": "<p>Your content here</p>"
}Template-based Email:
{
"from": "[email protected]",
"from_name": "Support Team",
"to": ["[email protected]"],
"template_id": "template-uuid",
"params": {
"variable1": "value1",
"variable2": "value2"
}
}Sender Information:
fromis mandatory when using custom domains or SMTP credentialsfrom_nameis mandatory for custom domain emails (sets the sender's display name)- For OAuth emails (Gmail/Microsoft),
fromdefaults to the connected email account from_nameis optional for OAuth emails but recommended for better recipient experience
Scheduling Constraints:
- Minimum: 1 minute in the future
- Maximum: 365 days in the future
- Cannot schedule emails in the past
Schedule Response
Success Response
{
"id": "scheduled_msg_1737307200_abc123",
"object": "scheduled_email",
"status": "scheduled",
"scheduled_at": "2026-01-20T10:00:00",
"timezone": "America/New_York",
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Scheduled Newsletter",
"created_at": "2026-01-19T18:00:00Z",
"metadata": {
"estimated_delivery": "Jan 20, 2026 at 10:00 AM (America/New_York)",
"recipients_count": 1
}
}Error Response
{
"error": {
"type": "validation_error",
"message": "scheduled_at must be at least 1 minute in the future"
}
}Cancel Scheduled Email
Cancel a scheduled email before it's sent:
curl -X DELETE https://api.keplars.com/api/v1/send-email/scheduled/{email_id} \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Campaign postponed"
}'Cancel Response
{
"success": true,
"message": "Scheduled email cancelled successfully"
}Important: You can only cancel emails with status scheduled. Once an email is sent, it cannot be cancelled.
List Scheduled Emails
Get all scheduled emails for your workspace:
curl -X GET https://api.keplars.com/api/v1/send-email/scheduled \
-H "Authorization: Bearer YOUR_API_KEY"Filter by Status
curl -X GET "https://api.keplars.com/api/v1/send-email/scheduled?status=scheduled" \
-H "Authorization: Bearer YOUR_API_KEY"Available status values:
scheduled- Waiting to be sentsent- Successfully sentcancelled- Cancelled before sendingfailed- Failed to send
List Response
{
"object": "list",
"data": [
{
"id": "scheduled_msg_1737307200_abc123",
"workspace_id": "workspace-uuid",
"created_by_user_id": "user-uuid",
"scheduled_at": "2026-01-20T10:00:00Z",
"timezone": "America/New_York",
"status": "scheduled",
"created_at": "2026-01-19T18:00:00Z"
}
],
"count": 1
}Code Examples
// Schedule email
async function scheduleEmail(scheduledAt, timezone, emailData) {
const response = await fetch('https://api.keplars.com/api/v1/send-email/schedule', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
scheduled_at: scheduledAt,
timezone: timezone,
email: emailData
})
});
return response.json();
}
// Schedule using simplified format (recommended for readability)
const result = await scheduleEmail(
'2026-01-21_09:00:00',
'America/New_York',
{
from: '[email protected]',
from_name: 'Onboarding Team',
to: ['[email protected]'],
subject: 'Welcome to our platform!',
html: '<h1>Welcome!</h1><p>We are excited to have you.</p>',
}
);
console.log('Scheduled email ID:', result.id);
// Cancel scheduled email
async function cancelScheduledEmail(emailId, reason) {
const response = await fetch(`https://api.keplars.com/api/v1/send-email/scheduled/${emailId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ reason })
});
return response.json();
}
// Cancel if needed
await cancelScheduledEmail(result.id, 'Campaign postponed');
// List all scheduled emails
async function listScheduledEmails(status) {
const url = new URL('https://api.keplars.com/api/v1/send-email/scheduled');
if (status) url.searchParams.append('status', status);
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`
}
});
return response.json();
}
const scheduledEmails = await listScheduledEmails('scheduled');
console.log(`You have ${scheduledEmails.count} scheduled emails`);interface EmailData {
to: string[];
cc?: string[];
bcc?: string[];
subject?: string;
html?: string;
template_id?: string;
params?: Record<string, any>;
}
interface ScheduleEmailResponse {
id: string;
object: string;
status: string;
scheduled_at: string;
timezone: string;
from: string;
to: string[];
subject: string;
created_at: string;
metadata: {
estimated_delivery: string;
recipients_count: number;
};
}
// Schedule email
async function scheduleEmail(
scheduledAt: string,
timezone: string,
emailData: EmailData
): Promise<ScheduleEmailResponse> {
const response = await fetch('https://api.keplars.com/api/v1/send-email/schedule', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
scheduled_at: scheduledAt,
timezone: timezone,
email: emailData
})
});
if (!response.ok) {
throw new Error(`Failed to schedule email: ${response.statusText}`);
}
return response.json();
}
// Schedule using simplified format (recommended)
const result = await scheduleEmail(
'2026-01-21_09:00:00',
'America/New_York',
{
from: '[email protected]',
from_name: 'Onboarding Team',
to: ['[email protected]'],
subject: 'Welcome to our platform!',
html: '<h1>Welcome!</h1><p>We are excited to have you.</p>',
}
);
console.log('Scheduled email ID:', result.id);
// Cancel scheduled email
async function cancelScheduledEmail(
emailId: string,
reason?: string
): Promise<{ success: boolean; message: string }> {
const response = await fetch(`https://api.keplars.com/api/v1/send-email/scheduled/${emailId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ reason })
});
return response.json();
}
// List all scheduled emails
async function listScheduledEmails(status?: string) {
const url = new URL('https://api.keplars.com/api/v1/send-email/scheduled');
if (status) url.searchParams.append('status', status);
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`
}
});
return response.json();
}
const scheduledEmails = await listScheduledEmails('scheduled');
console.log(`You have ${scheduledEmails.count} scheduled emails`);import requests
from datetime import datetime, timedelta
import pytz
from typing import Dict, List, Optional
API_KEY = "your_api_key_here"
BASE_URL = "https://api.keplars.com/api/v1/send-email"
def schedule_email(
scheduled_at: str,
timezone: str,
email_data: Dict
) -> Dict:
"""Schedule an email for future delivery."""
response = requests.post(
f"{BASE_URL}/schedule",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={
"scheduled_at": scheduled_at,
"timezone": timezone,
"email": email_data
}
)
response.raise_for_status()
return response.json()
# Schedule using simplified format (recommended)
result = schedule_email(
"2026-01-21_09:00:00",
"America/New_York",
{
"from": "[email protected]",
"from_name": "Daily Reports",
"to": ["[email protected]"],
"subject": "Your Daily Report",
"html": "<h1>Daily Report</h1><p>Here is your report...</p>",
}
)
print(f"Scheduled email ID: {result['id']}")
def cancel_scheduled_email(email_id: str, reason: Optional[str] = None) -> Dict:
"""Cancel a scheduled email."""
response = requests.delete(
f"{BASE_URL}/scheduled/{email_id}",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={"reason": reason} if reason else {}
)
response.raise_for_status()
return response.json()
def list_scheduled_emails(status: Optional[str] = None) -> Dict:
"""List all scheduled emails, optionally filtered by status."""
params = {"status": status} if status else {}
response = requests.get(
f"{BASE_URL}/scheduled",
headers={"Authorization": f"Bearer {API_KEY}"},
params=params
)
response.raise_for_status()
return response.json()
scheduled = list_scheduled_emails("scheduled")
print(f"You have {scheduled['count']} scheduled emails")package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
const baseURL = "https://api.keplars.com/api/v1/send-email"
type EmailData struct {
From string `json:"from,omitempty"`
FromName string `json:"from_name,omitempty"`
To []string `json:"to"`
CC []string `json:"cc,omitempty"`
BCC []string `json:"bcc,omitempty"`
Subject string `json:"subject,omitempty"`
HTML string `json:"html,omitempty"`
TemplateID string `json:"template_id,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
}
type ScheduleRequest struct {
ScheduledAt string `json:"scheduled_at"`
Timezone string `json:"timezone"`
Email EmailData `json:"email"`
}
type ScheduleResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Status string `json:"status"`
ScheduledAt string `json:"scheduled_at"`
Timezone string `json:"timezone"`
From string `json:"from"`
To []string `json:"to"`
Subject string `json:"subject"`
CreatedAt string `json:"created_at"`
}
func scheduleEmail(scheduledAt time.Time, timezone string, emailData EmailData) (*ScheduleResponse, error) {
return scheduleEmailByString(scheduledAt.Format("2006-01-02T15:04:05"), timezone, emailData)
}
func scheduleEmailByString(scheduledAt string, timezone string, emailData EmailData) (*ScheduleResponse, error) {
apiKey := os.Getenv("KEPLERS_API_KEY")
reqBody := ScheduleRequest{
ScheduledAt: scheduledAt,
Timezone: timezone,
Email: emailData,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", baseURL+"/schedule", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result ScheduleResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
func cancelScheduledEmail(emailID string, reason string) error {
apiKey := os.Getenv("KEPLERS_API_KEY")
cancelReq := map[string]string{"reason": reason}
jsonData, _ := json.Marshal(cancelReq)
req, err := http.NewRequest("DELETE", baseURL+"/scheduled/"+emailID, bytes.NewBuffer(jsonData))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func main() {
// Schedule using simplified format
// Note: Pass the datetime string directly to scheduleEmail
// Modify scheduleEmail to accept string instead of time.Time for easier usage
result, err := scheduleEmailByString(
"2026-01-21_09:00:00",
"America/New_York",
EmailData{
From: "[email protected]",
FromName: "Reports Team",
To: []string{"[email protected]"},
Subject: "Your Daily Report",
HTML: "<h1>Daily Report</h1><p>Here is your report...</p>",
},
)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Scheduled email ID: %s\n", result.ID)
// Cancel if needed
err = cancelScheduledEmail(result.ID, "Campaign postponed")
if err != nil {
fmt.Printf("Cancel error: %v\n", err)
}
}<?php
class KeplarsEmailScheduler {
private $apiKey;
private $baseURL = 'https://api.keplars.com/api/v1/send-email';
public function __construct($apiKey) {
$this->apiKey = $apiKey;
}
public function scheduleEmail($scheduledAt, $timezone, $emailData) {
$data = [
'scheduled_at' => $scheduledAt,
'timezone' => $timezone,
'email' => $emailData
];
return $this->makeRequest('POST', '/schedule', $data);
}
public function cancelScheduledEmail($emailId, $reason = null) {
$data = $reason ? ['reason' => $reason] : [];
return $this->makeRequest('DELETE', "/scheduled/{$emailId}", $data);
}
public function listScheduledEmails($status = null) {
$url = '/scheduled';
if ($status) {
$url .= '?status=' . urlencode($status);
}
return $this->makeRequest('GET', $url);
}
private function makeRequest($method, $endpoint, $data = null) {
$ch = curl_init($this->baseURL . $endpoint);
$headers = [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($data && ($method === 'POST' || $method === 'DELETE')) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API request failed with status {$httpCode}: {$response}");
}
return json_decode($response, true);
}
}
// Usage example
$apiKey = getenv('KEPLERS_API_KEY');
$scheduler = new KeplarsEmailScheduler($apiKey);
// Schedule using simplified format (recommended)
$result = $scheduler->scheduleEmail(
'2026-01-21_09:00:00',
'America/New_York',
[
'from' => '[email protected]',
'from_name' => 'Reports Team',
'to' => ['[email protected]'],
'subject' => 'Your Daily Report',
'html' => '<h1>Daily Report</h1><p>Here is your report...</p>'
]
);
echo "Scheduled email ID: {$result['id']}\n";
// Cancel if needed
$scheduler->cancelScheduledEmail($result['id'], 'Campaign postponed');
// List scheduled emails
$scheduled = $scheduler->listScheduledEmails('scheduled');
echo "You have {$scheduled['count']} scheduled emails\n";
?>Common Use Cases
1. Weekly Newsletter
Schedule a newsletter to send every Monday at 9 AM:
// Using simplified format for clarity
await scheduleEmail(
'2026-01-27_09:00:00', // Next Monday
'America/New_York',
{
from: '[email protected]',
from_name: 'Weekly Newsletter',
to: subscribers,
template_id: 'weekly-newsletter-template',
params: { week: '2026-01-20' },
}
);2. Birthday Wishes
Schedule birthday emails in advance:
// Schedule for specific date and time
await scheduleEmail(
'2026-02-15_09:00:00',
'UTC',
{
from: '[email protected]',
from_name: 'Celebrations Team',
to: ['[email protected]'],
template_id: 'birthday-template',
params: { name: 'John', age: 30 },
}
);3. Event Reminders
Send reminders before an event:
// Send reminder a day before the event
await scheduleEmail(
'2026-02-28_14:00:00', // 24 hours before event
'Europe/London',
{
from: '[email protected]',
from_name: 'Event Reminders',
to: ['[email protected]'],
subject: 'Event Tomorrow!',
html: '<p>Reminder: Your event is tomorrow at 2 PM.</p>',
}
);Timezone Support
Email scheduling supports all IANA timezone identifiers. Common timezones:
| Region | Timezone Identifier |
|---|---|
| US Eastern | America/New_York |
| US Pacific | America/Los_Angeles |
| UK | Europe/London |
| Central Europe | Europe/Paris |
| India | Asia/Kolkata |
| Japan | Asia/Tokyo |
| Australia (Sydney) | Australia/Sydney |
| UTC | UTC |
Find the full list of IANA timezones at IANA Time Zone Database
Error Codes
| Error Type | Description |
|---|---|
validation_error | Invalid request format or constraints violated |
template_access_denied | Template not found or no access permission |
schedule_error | Failed to schedule email (system error) |
cancellation_error | Failed to cancel (not found or wrong status) |
Best Practices
- Always use timezone parameter - Specify the timezone for clarity, even if it's UTC
- Schedule well in advance - Don't schedule emails too close to the current time (minimum 1 minute)
- Test with templates - Test your templates before scheduling bulk emails
- Monitor scheduled emails - Regularly check the list of scheduled emails
- Cancel when needed - Cancel campaigns that are no longer relevant
Next Steps
- Send Emails - Learn about immediate email sending
- Email Templates - Create reusable templates
- Webhooks - Get notifications when scheduled emails are sent
- Examples - See integration examples for your programming language
Schedule emails effortlessly with Keplars Mail Service. Perfect for newsletters, campaigns, reminders, and automated communications.