AgentSkillsCN

smtp2go-api

通过SMTP2GO API发送事务邮件与短信。支持身份验证、/email/send与/email/mime端点,提供模板管理、附件(Base64/URL)、交付事件的Webhook、统计信息以及退订功能。当您需要从Cloudflare Workers发送邮件、构建通知、追踪交付状态、处理退回邮件时,可使用此功能。有效避免身份验证错误与附件编码问题。

SKILL.md
--- frontmatter
name: smtp2go-api
description: 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.

SMTP2GO API Integration

Build email and SMS delivery with the SMTP2GO transactional API.

Quick Start

typescript
// 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: 'noreply@yourdomain.com',
    to: ['recipient@example.com'],
    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

RegionBase URL
Globalhttps://api.smtp2go.com/v3
UShttps://us-api.smtp2go.com/v3
EUhttps://eu-api.smtp2go.com/v3
AUhttps://au-api.smtp2go.com/v3

Authentication

Two methods supported:

typescript
// 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

typescript
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:

typescript
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:

typescript
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

typescript
// Convert file to base64
const fileBuffer = await file.arrayBuffer();
const base64 = btoa(String.fromCharCode(...new Uint8Array(fileBuffer)));

const email = {
  sender: 'noreply@example.com',
  to: ['user@example.com'],
  subject: 'Document attached',
  text_body: 'Please find the document attached.',
  attachments: [{
    filename: 'report.pdf',
    fileblob: base64,
    mimetype: 'application/pdf',
  }],
};

URL Reference (Cached 24h)

typescript
const email = {
  sender: 'noreply@example.com',
  to: ['user@example.com'],
  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

typescript
const email = {
  sender: 'noreply@example.com',
  to: ['user@example.com'],
  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

typescript
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

typescript
const email = {
  sender: 'noreply@example.com',
  to: ['user@example.com'],
  subject: 'Welcome aboard!',
  template_id: 'template-uuid-here',
  template_data: {
    name: 'John',
    company: 'Acme Corp',
  },
};

Template Syntax: HandlebarsJS with {{ variable }} placeholders.

Template Endpoints

EndpointMethodDescription
/template/addPOSTCreate new template
/template/editPOSTUpdate existing template
/template/deletePOSTRemove template
/template/searchPOSTList/search templates
/template/viewPOSTGet template details

Webhooks

Configure webhooks to receive real-time delivery notifications.

Event Types

Email Events:

EventDescription
processedEmail queued for delivery
deliveredSuccessfully delivered
openRecipient opened email
clickLink clicked
bounceDelivery failed
spamMarked as spam
unsubscribeUser unsubscribed
resubscribeUser resubscribed
rejectBlocked (suppression/sandbox)

SMS Events:

EventDescription
sendingProcessing
submittedSent to provider
deliveredConfirmed delivery
failedDelivery failed
rejectedNetwork blocked
opt-outRecipient opted out

Webhook Payload (Email)

typescript
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

typescript
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

EndpointMethodDescription
/webhook/viewPOSTList webhooks
/webhook/addPOSTCreate webhook
/webhook/editPOSTUpdate webhook
/webhook/removePOSTDelete 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.

typescript
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

EndpointMethodDescription
/stats/email_summaryPOSTCombined statistics
/stats/email_bouncesPOSTBounce summary (30 days)
/stats/email_cyclePOSTEmail cycle data
/stats/email_historyPOSTHistorical data
/stats/email_spamPOSTSpam reports
/stats/email_unsubsPOSTUnsubscribe data

Activity Search

POST /activity/search (Rate limited: 60/min)

Search for email events:

typescript
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

typescript
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: 'blocked@example.com',
  }),
});

Suppression Endpoints

EndpointMethodDescription
/suppression/addPOSTAdd to suppression list
/suppression/viewPOSTView suppressions
/suppression/removePOSTRemove from list

SMS

POST /sms/send

typescript
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

EndpointMethodDescription
/sms/sendPOSTSend SMS
/sms/receivedPOSTView received SMS
/sms/sentPOSTView sent SMS
/sms/summaryPOSTSMS statistics

Response Codes

CodeStatusDescription
200OKSuccess
400Bad RequestInvalid parameters
401UnauthorizedInvalid/missing API key
402Request FailedValid params, request failed
403ForbiddenInsufficient permissions
404Not FoundResource not found
429Too Many RequestsRate limited
5xxServer ErrorSMTP2GO server issue

Error Response Format

typescript
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 permission
  • E_ApiResponseCodes.NON_VALIDATING_IN_PAYLOAD - Invalid JSON/email format
  • E_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:

typescript
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

typescript
// wrangler.jsonc
{
  "name": "email-service",
  "vars": {
    "SMTP2GO_REGION": "api"  // or "us-api", "eu-api", "au-api"
  }
}

// .dev.vars
SMTP2GO_API_KEY=api-XXXXXXXXXXXX
typescript
// 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: 'noreply@yourdomain.com',
        to: ['user@example.com'],
        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:

  1. Sender Domain (Recommended): Add and verify domain in SMTP2GO dashboard for SPF/DKIM alignment
  2. Single Sender Email: Verify individual email address

Unverified senders are rejected with 400 error.

Common Patterns

Contact Form Handler

typescript
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: 'website@yourdomain.com',
      to: ['support@yourdomain.com'],
      reply_to: email,
      subject: `Contact form: ${name}`,
      text_body: `From: ${name} <${email}>\n\n${message}`,
      html_body: `
        <p><strong>From:</strong> ${name} &lt;${email}&gt;</p>
        <hr>
        <p>${message.replace(/\n/g, '<br>')}</p>
      `,
    }),
  });

  if (!response.ok) {
    throw new Error('Failed to send email');
  }

  return response.json();
}

Webhook Handler

typescript
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

IssueCauseSolution
401 UnauthorizedMissing/invalid API keyCheck API key in header or body
400 sender not verifiedUnverified sender domainVerify domain in SMTP2GO dashboard
429 Too Many RequestsRate limit exceededImplement exponential backoff
Attachment too largeOver 50MB totalCompress or use URL references
Template variables not replacedWrong syntaxUse {{ variable }} Handlebars syntax
Webhook not receiving eventsTimeout/errorsCheck endpoint returns 200 within 10s

References


Last Updated: 2026-02-06 API Version: v3.0.3