API Keys Guide

Learn how to send emails programmatically using API keys. Choose between REST API for simplicity or SMTP for compatibility. Create keys, configure your application, and monitor usage.

Overview

CenterLeap API keys allow you to send and receive emails programmatically from your applications. You can use either the REST API (recommended) or SMTP/IMAP protocols - the same API key works for all.

REST API (Recommended)

Simple HTTP requests with JSON. Perfect for serverless, edge functions, and modern applications.

SMTP Protocol

Standard SMTP for compatibility with existing libraries and legacy systems.

Key Features:

  • Domain or account-scoped: Keys can be tied to an entire domain or a specific email account
  • Secure storage: Keys are hashed and never stored in plaintext
  • Rate limiting: Built-in rate limits to prevent abuse
  • Audit logging: All key usage is tracked and logged
  • Send & receive: Full Access keys can both send and receive emails via API

Permission Levels:

Full Access

Send emails, receive emails, and manage API keys. Default for new keys.

Send Only

Send emails only. Cannot read inbox or manage keys. Ideal for transactional email services.

Key Scoping:

API keys can be scoped at two levels, giving you flexibility between convenience and security.

Domain Key (Universal)

Can send and receive as any email address on the domain. Created from the domain settings page.

e.g., send as hello@, support@, or notifications@yourdomain.com

Account Key (Scoped)

Locked to a single email address. Cannot send as any other address on the domain. Created from the email account settings page.

e.g., only send as bot@yourdomain.com

Which should I use? Use a domain key if your app sends from multiple addresses (e.g., transactional emails from different senders). Use an account key for bots, agents, or services that should only operate as one specific email address.

Use cases: Transactional emails, password resets, notifications, newsletters, automated reports, email client integrations, AI agent inboxes, and any application that needs to send or receive email.

Creating an API Key

Domain Key (Universal)

  1. Go to Dashboard → Domains
  2. Select a verified domain
  3. Click the "API Keys" tab
  4. Choose permission level (Full Access or Send Only)
  5. Click "Create Key"

This key can send as any email address on the domain.

Account Key (Scoped)

  1. Go to Dashboard → Email Accounts
  2. Click the settings icon on an email account
  3. Click the "API Keys" tab
  4. Choose permission level (Full Access or Send Only)
  5. Click "Create Key"

This key is locked to that specific email address and cannot send as other addresses on the domain.

Copy Your Key Immediately!

Your API key is only shown once at creation. Copy it and store it securely. If you lose it, you'll need to create a new key.

Key Format:

bm_live_abc123...def456

Keys start with bm_live_ followed by 40 hexadecimal characters.

REST API (Recommended)

Send emails with simple HTTP requests - no SMTP library needed

The REST API is the easiest way to send emails from any environment, including serverless functions, edge runtimes, and modern applications that don't have native SMTP support.

Base URL & Authentication

https://api.centerleap.com
Authorization: Bearer bm_live_xxx

Send an Email

JavaScript (fetch)

const response = await fetch('https://api.centerleap.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'hello@yourdomain.com',
    to: 'recipient@example.com',
    subject: 'Hello from CenterLeap',
    html: '<p>This email was sent via REST API.</p>',
  }),
});

const { id } = await response.json();
console.log('Email sent:', id);

cURL

curl -X POST 'https://api.centerleap.com/emails' \
  -H 'Authorization: Bearer bm_live_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "from": "hello@yourdomain.com",
    "to": "recipient@example.com",
    "subject": "Hello from CenterLeap",
    "html": "<p>Hello!</p>"
  }'

Deno / Edge Runtime

// Works in any Deno or edge-runtime environment
const response = await fetch('https://api.centerleap.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${Deno.env.get('CENTERLEAP_API_KEY')}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'noreply@yourdomain.com',
    to: 'user@example.com',
    subject: 'Welcome!',
    html: '<h1>Welcome to our app!</h1>',
  }),
});

const { id } = await response.json();

Request Body

FieldTypeDescription
from*stringSender email (must match API key domain)
to*string | string[]Recipient(s), max 50
subject*stringEmail subject line
htmlstringHTML body
textstringPlain text body
ccstring | string[]CC recipients
bccstring | string[]BCC recipients
reply_tostring | string[]Reply-to address(es)
headersobjectCustom email headers
tagsarrayTags for tracking: [{name: "...", value: "..."}]

Response

Success (200)

{
  "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
}

Other Endpoints

GET /emails/:id

Retrieve an email by ID

POST /emails/batch

Send up to 100 emails in one request

GET /domains

List domains for your API key

GET /api-keys

List API keys

Reading Emails

Full Access API keys can read received and sent emails. The API provides two levels of detail:

List (Preview)

GET /emails — returns snippet (first 200 characters). Use for inbox listing, notifications, and previews.

Detail (Full Body)

GET /emails/:id — returns full html and text body. Use when the user opens an email.

List Emails (Preview)

GET https://api.centerleap.com/emails?folder=INBOX&page=1&limit=20

Returns a paginated list with snippet (200-char preview) — no full body. For domain-scoped keys, add ?email=bot@yourdomain.com. Account-scoped keys resolve automatically.

List Response

{
  "object": "list",
  "data": [
    {
      "id": "<message-id@example.com>",
      "from": "sender@example.com",
      "to": "bot@yourdomain.com",
      "subject": "Hello from a customer",
      "preview": "First 200 characters of the email body...",
      "date": "2026-03-19T18:00:00Z",
      "is_read": false,
      "is_starred": false,
      "is_sent": false,
      "folder": "INBOX",
      "has_attachments": false
    }
  ],
  "total": 42,
  "has_more": true,
  "page": 1,
  "page_size": 20
}

Get Email (Full Body)

GET https://api.centerleap.com/emails/:id

Returns the complete html and text body. Use the id from the list response.

Detail Response

{
  "object": "email",
  "id": "<message-id@example.com>",
  "from": "sender@example.com",
  "from_name": "John Doe",
  "to": "bot@yourdomain.com",
  "subject": "Hello from a customer",
  "html": "<p>Full HTML body of the email...</p>",
  "text": "Full plaintext body of the email...",
  "date": "2026-03-19T18:00:00Z",
  "folder": "INBOX",
  "is_read": false,
  "is_starred": false,
  "is_sent": false,
  "has_attachments": false
}

Folders

Both endpoints work with any folder. Use ?folder= to switch:

?folder=INBOX

Received emails (default)

?folder=Sent

Sent emails

?folder=Trash

Deleted emails

Typical Flow

JavaScript — List then Read

// Step 1: List inbox (preview only — fast)
const listRes = await fetch('https://api.centerleap.com/emails?folder=INBOX&limit=10', {
  headers: { 'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}` },
});
const { data: emails } = await listRes.json();

// Show previews
for (const email of emails) {
  console.log(`[${email.is_read ? 'READ' : 'NEW'}] ${email.subject}`);
  console.log(`  Preview: ${email.preview}`);
}

// Step 2: User opens an email — fetch full body
const emailId = encodeURIComponent(emails[0].id);
const detailRes = await fetch(`https://api.centerleap.com/emails/${emailId}`, {
  headers: { 'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}` },
});
const fullEmail = await detailRes.json();

// Now you have the complete HTML/text body
console.log(fullEmail.html);  // Full HTML
console.log(fullEmail.text);  // Full plaintext

Update Email

Mark emails as read/unread, starred, or move them between folders. Use the email UID from the list response as the ID. Add ?folder=INBOX to specify the source folder.

Mark Read / Unread / Star

// Mark as read
await fetch('https://api.centerleap.com/emails/12345?folder=INBOX', {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ is_read: true }),
});

// Mark as unread
await fetch('https://api.centerleap.com/emails/12345?folder=INBOX', {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ is_read: false }),
});

// Star an email
await fetch('https://api.centerleap.com/emails/12345?folder=INBOX', {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ is_starred: true }),
});

// Move to a folder
await fetch('https://api.centerleap.com/emails/12345?folder=INBOX', {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ folder: 'Archive' }),
});

Delete Email

Moves the email to Trash.

DELETE https://api.centerleap.com/emails/12345?folder=INBOX

Permission Required

Reading, updating, and deleting emails requires a Full Access API key. Send Only keys cannot access the inbox.

Threading

Emails are automatically grouped into threads. The list and detail endpoints return threading metadata so you can display conversations.

Thread Fields

FieldDescription
thread_idUUID grouping all emails in the same conversation
in_reply_toMessage-ID of the email being replied to
thread_positionOrder in thread (0 = first email)
referencesFull RFC References header chain (detail only)

Sending a Reply

Include in_reply_to when sending a reply. The API automatically assigns the same thread_id as the parent.

JavaScript — Send Reply

// Reply to an email
await fetch('https://api.centerleap.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'bot@yourdomain.com',
    to: 'sender@example.com',
    subject: 'Re: Original Subject',
    html: '<p>Thanks for your email!</p>',
    in_reply_to: '<original-message-id@example.com>',  // Links to parent
  }),
});

Get Thread

GET https://api.centerleap.com/emails/threads/:threadId

Returns all emails in a thread, ordered by date.

Thread Response

{
  "object": "thread",
  "thread_id": "dc4b68b5-2d24-4220-a95f-1ac284e5a0e2",
  "subject": "Project Update",
  "message_count": 3,
  "data": [
    { "id": "msg-1", "from": "client@example.com", "subject": "Project Update", "is_sent": false, "thread_position": 0 },
    { "id": "msg-2", "from": "bot@yourdomain.com", "subject": "Re: Project Update", "is_sent": true, "thread_position": 1 },
    { "id": "msg-3", "from": "client@example.com", "subject": "Re: Project Update", "is_sent": false, "thread_position": 2 }
  ]
}

Attachments

Send emails with file attachments (base64-encoded), and download attachments from received emails. Max 10 attachments, 25MB each.

Send with Attachments

JavaScript — Send with attachment

const fileContent = btoa('Hello World');  // Base64 encode

const response = await fetch('https://api.centerleap.com/emails', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'bot@yourdomain.com',
    to: 'recipient@example.com',
    subject: 'Report attached',
    html: '<p>Please find the report attached.</p>',
    attachments: [
      {
        filename: 'report.pdf',
        content: fileContent,        // Base64-encoded file
        content_type: 'application/pdf',
      }
    ],
  }),
});

List Attachments

GET https://api.centerleap.com/emails/:id/attachments

Response

{
  "object": "list",
  "data": [
    {
      "id": "attachment-uuid",
      "filename": "report.pdf",
      "content_type": "application/pdf",
      "size_bytes": 245000,
      "is_inline": false,
      "content_id": null
    }
  ]
}

Download Attachment

GET https://api.centerleap.com/emails/:id/attachments/:attachmentId

Returns the raw file content with proper Content-Type and Content-Disposition headers.

JavaScript — Download attachment

// List attachments on an email
const listRes = await fetch(`https://api.centerleap.com/emails/${emailId}/attachments`, {
  headers: { 'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}` },
});
const { data: attachments } = await listRes.json();

// Download the first attachment
const fileRes = await fetch(
  `https://api.centerleap.com/emails/${emailId}/attachments/${attachments[0].id}`,
  { headers: { 'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}` } }
);
const fileBuffer = await fileRes.arrayBuffer();
// Save or process the file...

Real-Time Streaming

Subscribe to real-time email notifications using Server-Sent Events (SSE). No polling required — the server pushes events to your client as emails arrive.

GET https://api.centerleap.com/emails/stream

The connection stays open and sends events whenever a new email is delivered to the inbox. Account-scoped keys automatically filter to the correct mailbox. Domain-scoped keys require ?email=bot@yourdomain.com.

Events

Event Format

event: connected
data: {"email":"bot@yourdomain.com","timestamp":"2026-03-19T18:00:00Z"}

event: new_email
data: {"email":"bot@yourdomain.com","mailbox":"INBOX","uid":12345,"timestamp":"2026-03-19T18:05:00Z"}

event: heartbeat
data: {"timestamp":"2026-03-19T18:05:30Z"}

Code Example

JavaScript (EventSource)

// Subscribe to real-time email notifications
const eventSource = new EventSource(
  'https://api.centerleap.com/emails/stream',
  { headers: { 'Authorization': `Bearer ${process.env.CENTERLEAP_API_KEY}` } }
);

// Note: In Node.js, use the 'eventsource' npm package with custom headers
// import EventSource from 'eventsource';

eventSource.addEventListener('connected', (e) => {
  console.log('Connected:', JSON.parse(e.data));
});

eventSource.addEventListener('new_email', (e) => {
  const { email, mailbox, uid } = JSON.parse(e.data);
  console.log(`New email in ${mailbox}! UID: ${uid}`);
  // Fetch the new email
  // await fetch(`https://api.centerleap.com/emails?folder=${mailbox}&limit=1`, ...)
});

eventSource.addEventListener('heartbeat', () => {
  // Connection is alive
});

eventSource.onerror = () => {
  console.log('Connection lost, will auto-reconnect');
};

cURL (test connection)

curl -N 'https://api.centerleap.com/emails/stream' \
  -H 'Authorization: Bearer bm_live_your_key_here'

SMTP Configuration

Use these settings to configure your application's SMTP client. Your API key is used as the SMTP password:

Host

mail.centerleap.com

Port

587

Encryption

STARTTLS

Username

centerleap

Password

Your API key (bm_live_...)

Authentication Summary

  • Username: centerleap (always the same)
  • Password: Your API key (e.g., bm_live_abc123...)

Environment Variable:

CENTERLEAP_API_KEY=bm_live_your_key_here

Code Examples

Node.js (Nodemailer)

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'mail.centerleap.com',
  port: 587,
  secure: false, // Use STARTTLS
  auth: {
    user: 'centerleap',
    pass: process.env.CENTERLEAP_API_KEY
  }
});

await transporter.sendMail({
  from: 'hello@yourdomain.com',
  to: 'recipient@example.com',
  subject: 'Hello from CenterLeap',
  text: 'This email was sent via API key.',
  html: '<p>This email was sent via <strong>API key</strong>.</p>'
});

Python (smtplib)

import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# Create message
msg = MIMEMultipart('alternative')
msg['Subject'] = 'Hello from CenterLeap'
msg['From'] = 'hello@yourdomain.com'
msg['To'] = 'recipient@example.com'

text = 'This email was sent via API key.'
html = '<p>This email was sent via <strong>API key</strong>.</p>'

msg.attach(MIMEText(text, 'plain'))
msg.attach(MIMEText(html, 'html'))

# Send email
with smtplib.SMTP('mail.centerleap.com', 587) as smtp:
    smtp.starttls()
    smtp.login('centerleap', os.environ['CENTERLEAP_API_KEY'])
    smtp.send_message(msg)

PHP (PHPMailer)

<?php
use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer(true);

$mail->isSMTP();
$mail->Host = 'mail.centerleap.com';
$mail->SMTPAuth = true;
$mail->Username = 'centerleap';
$mail->Password = getenv('CENTERLEAP_API_KEY');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;

$mail->setFrom('hello@yourdomain.com');
$mail->addAddress('recipient@example.com');

$mail->isHTML(true);
$mail->Subject = 'Hello from CenterLeap';
$mail->Body = '<p>This email was sent via <strong>API key</strong>.</p>';

$mail->send();

cURL

# Create email.txt file with email content:
# From: hello@yourdomain.com
# To: recipient@example.com
# Subject: Hello from CenterLeap
#
# This email was sent via API key.

curl --url 'smtp://mail.centerleap.com:587' \
  --ssl-reqd \
  --user "centerleap:$CENTERLEAP_API_KEY" \
  --mail-from 'hello@yourdomain.com' \
  --mail-rcpt 'recipient@example.com' \
  --upload-file email.txt

Rate Limits

To ensure fair usage and prevent abuse, API keys have rate limits:

Per-Key Limit

1,000

emails per hour (default)

Domain Limit

Varies

based on your plan

Monitoring Usage:

View your API key usage and email logs in Dashboard → Activity → API Keys.

Rate Limit Exceeded

If you exceed your rate limit, requests will be rejected with a 429 error. Wait for the limit to reset (hourly) or contact support to increase your limit.

Key Management

Revoking a Key

If a key is compromised or no longer needed, revoke it immediately:

  1. Go to your domain's API Keys tab
  2. Find the key you want to revoke
  3. Click the revoke button
  4. Confirm the action

Revocation is immediate - all pending requests with that key will be rejected.

Key Rotation

To rotate keys safely without downtime:

  1. Create a new API key
  2. Update your application with the new key
  3. Verify the new key is working
  4. Revoke the old key

Key Expiration

You can set an optional expiration date when creating a key. Expired keys are automatically deactivated and cannot be used.

Security Best Practices

Use environment variables

Never hardcode API keys in your source code. Use environment variables like CENTERLEAP_API_KEY.

Never commit keys to version control

Add .env files to your .gitignore. Use secret management tools for CI/CD.

Use different keys per environment

Create separate keys for development, staging, and production environments.

Rotate keys regularly

Rotate your API keys every 90 days as a security best practice.

Revoke unused keys

If a key is no longer in use, revoke it immediately to reduce security exposure.

Monitor key usage

Regularly check your Activity logs for unusual patterns that might indicate compromise.

Troubleshooting

Authentication failed

Possible Causes

  • API key is incorrect or was copied incorrectly
  • Key has been revoked
  • Key has expired
  • Using wrong username (should be "centerleap")

Solutions

  • Double-check you copied the full key
  • Verify the key is active in your dashboard
  • Create a new key if needed

Rate limit exceeded (429)

Possible Causes

  • Exceeded 1,000 emails/hour per key
  • Exceeded domain-level limit

Solutions

  • Wait for the hourly limit to reset
  • Distribute load across multiple keys
  • Contact support to increase limits

Domain not authorized

Possible Causes

  • Sending from a domain not tied to this key
  • Domain is not verified

Solutions

  • Use the correct "from" address matching your key's domain
  • Verify your domain DNS settings

Connection refused

Possible Causes

  • Firewall blocking port 587
  • Incorrect host or port

Solutions

  • Ensure port 587 is open in your firewall
  • Use host: mail.centerleap.com, port: 587
  • Enable STARTTLS encryption

Still having issues? Check your Activity logs for detailed error messages or ask the AI assistant for help.