Agent skill
smtp2go-api
Send transactional emails and SMS via SMTP2GO API. Covers authentication, /email/send and /email/mime endpoints, template management, attachments (base64/URL), webhooks for delivery events, statistics, and suppressions. Use when sending emails from Cloudflare Workers, building notifications, tracking delivery status, handling bounces. Prevents auth errors, attachment encoding issues.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/smtp2go-api
SKILL.md
SMTP2GO API Integration
Build email and SMS delivery with the SMTP2GO transactional API.
Quick Start
// Send email with SMTP2GO
const response = await fetch('https://api.smtp2go.com/v3/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Hello from SMTP2GO',
html_body: '<h1>Welcome!</h1><p>Your account is ready.</p>',
text_body: 'Welcome! Your account is ready.',
}),
});
const result = await response.json();
// { request_id: "uuid", data: { succeeded: 1, failed: 0, email_id: "1er8bV-6Tw0Mi-7h" } }
Base URLs
| Region | Base URL |
|---|---|
| Global | https://api.smtp2go.com/v3 |
| US | https://us-api.smtp2go.com/v3 |
| EU | https://eu-api.smtp2go.com/v3 |
| AU | https://au-api.smtp2go.com/v3 |
Authentication
Two methods supported:
// Method 1: Header (recommended)
headers: {
'X-Smtp2go-Api-Key': 'your-api-key'
}
// Method 2: Request body
body: JSON.stringify({
api_key: 'your-api-key',
// ... other params
})
Get API keys from SMTP2GO dashboard: Sending > API Keys
Core Endpoints
Send Standard Email
POST /email/send
interface EmailSendRequest {
// Required
sender: string; // Verified sender email
to: string[]; // Recipients (max 100)
subject: string;
// Content (at least one required)
html_body?: string;
text_body?: string;
// Optional
cc?: string[]; // CC recipients (max 100)
bcc?: string[]; // BCC recipients (max 100)
reply_to?: string;
custom_headers?: Array<{ header: string; value: string }>;
attachments?: Attachment[];
inlines?: InlineImage[];
// Templates
template_id?: string;
template_data?: Record<string, any>;
// Subaccounts
subaccount_id?: string;
}
interface Attachment {
filename: string;
mimetype: string;
fileblob?: string; // Base64-encoded content
url?: string; // OR URL to fetch from
}
interface InlineImage {
filename: string;
mimetype: string;
fileblob: string;
cid: string; // Content-ID for HTML reference
}
Response:
interface EmailSendResponse {
request_id: string;
data: {
succeeded: number;
failed: number;
failures: string[];
email_id: string;
};
}
Send MIME Email
POST /email/mime
For pre-encoded MIME messages:
const response = await fetch('https://api.smtp2go.com/v3/email/mime', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
mime_email: mimeEncodedString,
}),
});
Attachments
Base64 Encoding
// Convert file to base64
const fileBuffer = await file.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(fileBuffer)));
const email = {
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Document attached',
text_body: 'Please find the document attached.',
attachments: [{
filename: 'report.pdf',
fileblob: base64,
mimetype: 'application/pdf',
}],
};
URL Reference (Cached 24h)
const email = {
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Image attached',
text_body: 'Photo from our event.',
attachments: [{
filename: 'photo.jpg',
url: 'https://cdn.example.com/photos/event.jpg',
mimetype: 'image/jpeg',
}],
};
Inline Images in HTML
const email = {
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Newsletter',
html_body: '<h1>Welcome</h1><img src="cid:logo123" alt="Logo">',
inlines: [{
filename: 'logo.png',
fileblob: logoBase64,
mimetype: 'image/png',
cid: 'logo123', // Reference in HTML as src="cid:logo123"
}],
};
Limits: Maximum total email size: 50 MB (content + attachments + headers)
Templates
Create Template
POST /template/add
const response = await fetch('https://api.smtp2go.com/v3/template/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
template_name: 'welcome-email',
html_body: '<h1>Welcome, {{ name }}!</h1><p>Thanks for joining {{ company }}.</p>',
text_body: 'Welcome, {{ name }}! Thanks for joining {{ company }}.',
}),
});
Send with Template
const email = {
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Welcome aboard!',
template_id: 'template-uuid-here',
template_data: {
name: 'John',
company: 'Acme Corp',
},
};
Template Syntax: HandlebarsJS with {{ variable }} placeholders.
Template Endpoints
| Endpoint | Method | Description |
|---|---|---|
/template/add |
POST | Create new template |
/template/edit |
POST | Update existing template |
/template/delete |
POST | Remove template |
/template/search |
POST | List/search templates |
/template/view |
POST | Get template details |
Webhooks
Configure webhooks to receive real-time delivery notifications.
Event Types
Email Events:
| Event | Description |
|---|---|
processed |
Email queued for delivery |
delivered |
Successfully delivered |
open |
Recipient opened email |
click |
Link clicked |
bounce |
Delivery failed |
spam |
Marked as spam |
unsubscribe |
User unsubscribed |
resubscribe |
User resubscribed |
reject |
Blocked (suppression/sandbox) |
SMS Events:
| Event | Description |
|---|---|
sending |
Processing |
submitted |
Sent to provider |
delivered |
Confirmed delivery |
failed |
Delivery failed |
rejected |
Network blocked |
opt-out |
Recipient opted out |
Webhook Payload (Email)
interface WebhookPayload {
event: string;
time: string; // Event timestamp
sendtime: string; // Original send time
sender: string;
from_address: string;
rcpt: string; // Recipient
recipients: string[];
email_id: string;
subject: string;
bounce?: string; // Bounce type if applicable
client?: string; // Email client (for opens)
'geoip-country'?: string;
}
Webhook Configuration
POST /webhook/add
await fetch('https://api.smtp2go.com/v3/webhook/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
url: 'https://api.yourdomain.com/webhooks/smtp2go',
events: ['delivered', 'bounce', 'spam', 'unsubscribe'],
}),
});
Webhook Endpoints
| Endpoint | Method | Description |
|---|---|---|
/webhook/view |
POST | List webhooks |
/webhook/add |
POST | Create webhook |
/webhook/edit |
POST | Update webhook |
/webhook/remove |
POST | Delete webhook |
Retry Policy: Up to 35 retries over 48 hours. Timeout: 10 seconds.
Statistics
Email Summary
POST /stats/email_summary
Combined report of bounces, cycles, spam, and unsubscribes.
const response = await fetch('https://api.smtp2go.com/v3/stats/email_summary', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({}),
});
Statistics Endpoints
| Endpoint | Method | Description |
|---|---|---|
/stats/email_summary |
POST | Combined statistics |
/stats/email_bounces |
POST | Bounce summary (30 days) |
/stats/email_cycle |
POST | Email cycle data |
/stats/email_history |
POST | Historical data |
/stats/email_spam |
POST | Spam reports |
/stats/email_unsubs |
POST | Unsubscribe data |
Activity Search
POST /activity/search (Rate limited: 60/min)
Search for email events:
const response = await fetch('https://api.smtp2go.com/v3/activity/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
// Filter parameters
}),
});
Note: Returns max 1,000 items. For real-time data, use webhooks instead.
Suppressions
Manage email addresses that should not receive emails.
Add Suppression
POST /suppression/add
await fetch('https://api.smtp2go.com/v3/suppression/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
email: '[email protected]',
}),
});
Suppression Endpoints
| Endpoint | Method | Description |
|---|---|---|
/suppression/add |
POST | Add to suppression list |
/suppression/view |
POST | View suppressions |
/suppression/remove |
POST | Remove from list |
SMS
POST /sms/send
const response = await fetch('https://api.smtp2go.com/v3/sms/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
to: ['+61400000000'], // Max 100 numbers
message: 'Your verification code is 123456',
}),
});
SMS Endpoints
| Endpoint | Method | Description |
|---|---|---|
/sms/send |
POST | Send SMS |
/sms/received |
POST | View received SMS |
/sms/sent |
POST | View sent SMS |
/sms/summary |
POST | SMS statistics |
Response Codes
| Code | Status | Description |
|---|---|---|
| 200 | OK | Success |
| 400 | Bad Request | Invalid parameters |
| 401 | Unauthorized | Invalid/missing API key |
| 402 | Request Failed | Valid params, request failed |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limited |
| 5xx | Server Error | SMTP2GO server issue |
Error Response Format
interface ErrorResponse {
request_id: string;
data: {
error: string;
error_code: string;
field_validation_errors?: Record<string, string>;
};
}
Common error codes:
E_ApiResponseCodes.ENDPOINT_PERMISSION_DENIED- API key lacks permissionE_ApiResponseCodes.NON_VALIDATING_IN_PAYLOAD- Invalid JSON/email formatE_ApiResponseCodes.API_EXCEPTION- General API error
Rate Limiting
- Activity Search: 60 requests/minute
- Email Search (deprecated): 20 requests/minute
- Other endpoints: Configurable per API key
Handling 429:
async function sendWithRetry(payload: any, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch('https://api.smtp2go.com/v3/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify(payload),
});
if (response.status === 429) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
continue;
}
return response.json();
}
throw new Error('Rate limit exceeded after retries');
}
Cloudflare Workers Integration
// wrangler.jsonc
{
"name": "email-service",
"vars": {
"SMTP2GO_REGION": "api" // or "us-api", "eu-api", "au-api"
}
}
// .dev.vars
SMTP2GO_API_KEY=api-XXXXXXXXXXXX
// src/index.ts
export default {
async fetch(request: Request, env: Env) {
const baseUrl = `https://${env.SMTP2GO_REGION}.smtp2go.com/v3`;
// Send transactional email
const response = await fetch(`${baseUrl}/email/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
sender: '[email protected]',
to: ['[email protected]'],
subject: 'Order Confirmation',
template_id: 'order-confirmation-template',
template_data: {
order_id: '12345',
total: '$99.00',
},
}),
});
const result = await response.json();
return Response.json(result);
},
} satisfies ExportedHandler<Env>;
interface Env {
SMTP2GO_API_KEY: string;
SMTP2GO_REGION: string;
}
Sender Verification
Before sending, verify your sender identity:
- Sender Domain (Recommended): Add and verify domain in SMTP2GO dashboard for SPF/DKIM alignment
- Single Sender Email: Verify individual email address
Unverified senders are rejected with 400 error.
Common Patterns
Contact Form Handler
export async function handleContactForm(formData: FormData, env: Env) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const message = formData.get('message') as string;
const response = await fetch('https://api.smtp2go.com/v3/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Smtp2go-Api-Key': env.SMTP2GO_API_KEY,
},
body: JSON.stringify({
sender: '[email protected]',
to: ['[email protected]'],
reply_to: email,
subject: `Contact form: ${name}`,
text_body: `From: ${name} <${email}>\n\n${message}`,
html_body: `
<p><strong>From:</strong> ${name} <${email}></p>
<hr>
<p>${message.replace(/\n/g, '<br>')}</p>
`,
}),
});
if (!response.ok) {
throw new Error('Failed to send email');
}
return response.json();
}
Webhook Handler
export async function handleWebhook(request: Request) {
const payload = await request.json();
switch (payload.event) {
case 'bounce':
// Handle bounce - update user record, retry logic
console.log(`Bounce: ${payload.rcpt} - ${payload.bounce}`);
break;
case 'unsubscribe':
// Update preferences
console.log(`Unsubscribe: ${payload.rcpt}`);
break;
case 'spam':
// Add to suppression, alert team
console.log(`Spam report: ${payload.rcpt}`);
break;
}
return new Response('OK', { status: 200 });
}
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Missing/invalid API key | Check API key in header or body |
| 400 sender not verified | Unverified sender domain | Verify domain in SMTP2GO dashboard |
| 429 Too Many Requests | Rate limit exceeded | Implement exponential backoff |
| Attachment too large | Over 50MB total | Compress or use URL references |
| Template variables not replaced | Wrong syntax | Use {{ variable }} Handlebars syntax |
| Webhook not receiving events | Timeout/errors | Check endpoint returns 200 within 10s |
References
Last Updated: 2026-02-06 API Version: v3.0.3
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?