• developer

Cold Email API: The Developer Guide to Programmatic Outbound

Want to integrate cold email into your product or workflow? Here's the developer guide to programmatic outbound: endpoints, auth, code examples, and best practices.

SendEmAll Team

SendEmAll Team

The SendEmAll Team

Why developers want a cold email API

You have a CRM with 5,000 contacts. A webhook fires when a prospect hits a lead score threshold. You want that prospect to automatically enter a cold email sequence — no manual CSV upload, no clicking through a UI.

Or you’re building a product that includes outbound as a feature. Your customers define their ICP in your app, and you orchestrate discovery, personalization, and sending behind the scenes.

Or you’re a RevOps engineer tired of duct-taping Zapier workflows between 6 different tools.

In all three cases, you need an API. Not a UI with an API bolted on as an afterthought. A real API built for programmatic access.

What to look for in a cold email API

Not all APIs are created equal. Here’s what separates a real API from a “we technically have endpoints” API.

Must-haves:

CapabilityWhy it matters
Campaign CRUDCreate, update, pause, delete campaigns programmatically
Lead managementAdd, update, remove leads from campaigns via API
Sending triggersStart, pause, resume sending via API calls
Event webhooksGet notified of opens, replies, bounces in real-time
Rate limit documentationKnow your limits before you hit them
AuthenticationAPI key or OAuth, well-documented
Error responsesStructured error codes, not just 500s

Nice-to-haves:

  • SDKs in popular languages (Python, JavaScript, Go)
  • Sandbox/test mode
  • Idempotent endpoints (safe to retry)
  • Batch operations (add 100 leads in one call, not 100 calls)
  • Webhook signature verification

SendEmAll API overview

The SendEmAll API is a REST API. JSON in, JSON out. Authentication via API key passed in the x-user-context header.

Base URL: https://api.sendemall.com/v1

Authentication: Every request requires an API key in the header:

x-user-context: your-api-key-here

Response format:

{
  "success": true,
  "data": { ... },
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-04-06T12:00:00Z"
  }
}

Errors return a structured response:

{
  "success": false,
  "error": {
    "code": "CAMPAIGN_NOT_FOUND",
    "message": "Campaign with ID cmp_xyz does not exist",
    "status": 404
  }
}

Code examples

Creating a campaign

curl:

curl -X POST https://api.sendemall.com/v1/campaigns \
  -H "Content-Type: application/json" \
  -H "x-user-context: your-api-key" \
  -d '{
    "name": "Q2 Series B Outreach",
    "daily_limit": 50,
    "timezone": "America/New_York",
    "schedule": {
      "days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
      "start_hour": 8,
      "end_hour": 17
    },
    "sequences": [
      {
        "step": 1,
        "delay_days": 0,
        "subject": "{{company}} and pipeline visibility",
        "body": "Hi {{first_name}},\n\n{{ai_personalized_opening}}\n\n{{value_prop}}\n\nWorth a quick look?"
      },
      {
        "step": 2,
        "delay_days": 3,
        "subject": "Re: {{company}} and pipeline visibility",
        "body": "Hi {{first_name}},\n\nQuick follow-up on my last note. {{case_study_snippet}}\n\nHappy to share more details if relevant."
      }
    ]
  }'

Python:

import requests

api_key = "your-api-key"
base_url = "https://api.sendemall.com/v1"

campaign = requests.post(
    f"{base_url}/campaigns",
    headers={
        "Content-Type": "application/json",
        "x-user-context": api_key,
    },
    json={
        "name": "Q2 Series B Outreach",
        "daily_limit": 50,
        "timezone": "America/New_York",
        "schedule": {
            "days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
            "start_hour": 8,
            "end_hour": 17,
        },
        "sequences": [
            {
                "step": 1,
                "delay_days": 0,
                "subject": "{{company}} and pipeline visibility",
                "body": "Hi {{first_name}},\n\n{{ai_personalized_opening}}\n\n{{value_prop}}\n\nWorth a quick look?",
            }
        ],
    },
)

campaign_id = campaign.json()["data"]["id"]
print(f"Created campaign: {campaign_id}")

JavaScript (Node.js):

const response = await fetch("https://api.sendemall.com/v1/campaigns", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-user-context": "your-api-key",
  },
  body: JSON.stringify({
    name: "Q2 Series B Outreach",
    daily_limit: 50,
    timezone: "America/New_York",
    schedule: {
      days: ["monday", "tuesday", "wednesday", "thursday", "friday"],
      start_hour: 8,
      end_hour: 17,
    },
    sequences: [
      {
        step: 1,
        delay_days: 0,
        subject: "{{company}} and pipeline visibility",
        body: "Hi {{first_name}},\n\n{{ai_personalized_opening}}\n\n{{value_prop}}\n\nWorth a quick look?",
      },
    ],
  }),
});

const { data } = await response.json();
console.log(`Created campaign: ${data.id}`);

Adding leads to a campaign

curl -X POST https://api.sendemall.com/v1/campaigns/cmp_abc123/leads \
  -H "Content-Type: application/json" \
  -H "x-user-context: your-api-key" \
  -d '{
    "leads": [
      {
        "email": "sarah@example.com",
        "first_name": "Sarah",
        "last_name": "Chen",
        "company": "Acme Corp",
        "title": "VP of Engineering",
        "custom_fields": {
          "tech_stack": "Kubernetes, Go, Postgres",
          "recent_hire_count": 5
        }
      }
    ]
  }'

Batch limit: 100 leads per request. For larger imports, make multiple requests with a 1-second delay between them.

Triggering a send

curl -X POST https://api.sendemall.com/v1/campaigns/cmp_abc123/activate \
  -H "x-user-context: your-api-key"

The campaign starts sending according to its schedule. To pause:

curl -X POST https://api.sendemall.com/v1/campaigns/cmp_abc123/pause \
  -H "x-user-context: your-api-key"

Listening for webhook events

Register a webhook endpoint:

curl -X POST https://api.sendemall.com/v1/webhooks \
  -H "Content-Type: application/json" \
  -H "x-user-context: your-api-key" \
  -d '{
    "url": "https://your-app.com/webhooks/sendemall",
    "events": ["email.opened", "email.replied", "email.bounced"],
    "secret": "your-webhook-secret"
  }'

When an event occurs, SendEmAll sends a POST to your URL:

{
  "event": "email.replied",
  "timestamp": "2026-04-06T14:23:00Z",
  "data": {
    "campaign_id": "cmp_abc123",
    "lead_id": "lead_xyz789",
    "email": "sarah@example.com",
    "sequence_step": 1,
    "reply_sentiment": "positive",
    "reply_preview": "Thanks for reaching out. We're actually evaluating..."
  },
  "signature": "sha256=abc123..."
}

Verify the signature before processing:

import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Rate limits and best practices

Rate limits:

  • 60 requests per minute per API key (general endpoints)
  • 10 requests per minute for campaign creation
  • 100 leads per batch add request
  • Webhook delivery: up to 3 retries with exponential backoff

Best practices:

  1. Use idempotency keys for create operations. Pass an Idempotency-Key header to prevent duplicate campaigns or leads from retried requests.

  2. Handle rate limits gracefully. 429 responses include a Retry-After header. Respect it.

  3. Verify webhook signatures. Always. Unsigned webhook payloads should be rejected.

  4. Don’t poll for status. Use webhooks instead of polling campaign or lead status endpoints. It’s faster and uses zero rate limit budget.

  5. Store the request_id from every API response. If something goes wrong, this ID helps support trace the issue.

  6. Test in sandbox first. Use https://sandbox.api.sendemall.com/v1 to test your integration without sending real emails or consuming credits.

Common integration patterns

CRM-triggered outbound: Lead scores above threshold in your CRM → webhook to your server → API call to add lead to SendEmAll campaign → reply webhook from SendEmAll → update CRM with reply status.

Product-led outbound: User signs up for free tier → after 14 days without converting → API call adds them to a re-engagement campaign → reply webhook pauses the campaign for that user if they respond.

Multi-tool orchestration: Your data warehouse identifies ICP-matching companies → your pipeline pushes them to SendEmAll via API → replies trigger a Slack notification → meetings get logged in your CRM.

For the full API reference, authentication details, and SDKs, visit the developer documentation.

Start building — your API key is available immediately after signup.

Stop emailing strangers. Start closing buyers.

100 signal-qualified leads
Matched to your ICP
Delivered in 48 hours
4.8 / 5
From 200+ outbound teams