Surgio Docs

API Reference

Campaign API

Collect contacts from anywhere using our simple REST API.

Overview

The Campaign API allows you to programmatically add contacts to your campaigns from any source.

Use Cases

  • Newsletter signup forms
  • Landing page lead capture
  • E-commerce customer lists
  • Event registration

Features

  • Simple REST API
  • Automatic duplicate detection
  • Email validation
  • JSON & Form Data support

Authentication

The Campaign API uses API keys for authentication. Include your API key in every request.

Getting your API key

Navigate to Emails → Campaigns, click any campaign, then click the "Integration" button.

Security Warning

Never expose your API key in client-side code or commit it to version control. Use environment variables or server-side requests.

Endpoint

POST
/api/public/contacts

Add a new contact to a campaign.

Required Parameters

email
Subscriber's email address (must be valid format)
campaignId
Your campaign ID from the Integration dialog
apiKey
Your team API key from the Integration dialog

Optional Parameters

name
Subscriber's name (helps personalize emails)

Content Types

application/json
multipart/form-data

Code Examples

Choose your preferred language or framework.

HTML / JavaScript

Simple form with vanilla JavaScript - perfect for static websites.

newsletter-form.html
<!-- Simple HTML Form -->
<form id="newsletter-form">
  <input type="email" name="email" placeholder="Enter your email" required />
  <input type="text" name="name" placeholder="Your name (optional)" />
  <button type="submit">Subscribe</button>
</form>

<script>
  document.getElementById('newsletter-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    formData.append('campaignId', 'YOUR_CAMPAIGN_ID');
    formData.append('apiKey', 'YOUR_API_KEY');

    try {
      const response = await fetch('https://yourdomain.com/api/public/contacts', {
        method: 'POST',
        body: formData
      });
      const data = await response.json();
      
      if (data.success) {
        alert('Successfully subscribed!');
        e.target.reset();
      } else {
        alert('Error: ' + data.error);
      }
    } catch (error) {
      alert('Failed to subscribe. Please try again.');
    }
  });
</script>

Next.js (App Router)

Server-side API route for secure API key handling.

app/api/subscribe/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
  try {
    const { email, name } = await req.json();

    const response = await fetch('https://yourdomain.com/api/public/contacts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email,
        name,
        campaignId: process.env.CAMPAIGN_ID,
        apiKey: process.env.API_KEY,
      }),
    });

    const data = await response.json();
    return NextResponse.json(data);
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to subscribe' },
      { status: 500 }
    );
  }
}
components/newsletter-form.tsx
'use client';

import { useState } from 'react';

export function NewsletterForm() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);

    try {
      const response = await fetch('/api/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, name }),
      });

      const data = await response.json();
      
      if (data.success) {
        alert('Successfully subscribed!');
        setEmail('');
        setName('');
      } else {
        alert('Error: ' + data.error);
      }
    } catch (error) {
      alert('Failed to subscribe');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
        required
      />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Your name (optional)"
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Subscribing...' : 'Subscribe'}
      </button>
    </form>
  );
}

React

Client-side React component with fetch API.

NewsletterForm.tsx
import { useState } from 'react';

export function NewsletterForm() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);

    try {
      const response = await fetch('https://yourdomain.com/api/public/contacts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email,
          name,
          campaignId: 'YOUR_CAMPAIGN_ID',
          apiKey: 'YOUR_API_KEY', // ⚠️ Use backend proxy in production
        }),
      });

      const data = await response.json();
      
      if (data.success) {
        alert('Successfully subscribed!');
        setEmail('');
        setName('');
      } else {
        alert('Error: ' + data.error);
      }
    } catch (error) {
      alert('Failed to subscribe');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
        required
      />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Your name (optional)"
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Subscribing...' : 'Subscribe'}
      </button>
    </form>
  );
}

Vue.js

Vue 3 Composition API example.

NewsletterForm.vue
<script setup>
import { ref } from 'vue';

const email = ref('');
const name = ref('');
const loading = ref(false);

const handleSubmit = async () => {
  loading.value = true;

  try {
    const response = await fetch('https://yourdomain.com/api/public/contacts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        email: email.value,
        name: name.value,
        campaignId: 'YOUR_CAMPAIGN_ID',
        apiKey: 'YOUR_API_KEY', // ⚠️ Use backend proxy in production
      }),
    });

    const data = await response.json();
    
    if (data.success) {
      alert('Successfully subscribed!');
      email.value = '';
      name.value = '';
    } else {
      alert('Error: ' + data.error);
    }
  } catch (error) {
    alert('Failed to subscribe');
  } finally {
    loading.value = false;
  }
};
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input
      v-model="email"
      type="email"
      placeholder="Enter your email"
      required
    />
    <input
      v-model="name"
      type="text"
      placeholder="Your name (optional)"
    />
    <button type="submit" :disabled="loading">
      {{ loading ? 'Subscribing...' : 'Subscribe' }}
    </button>
  </form>
</template>

PHP

Server-side PHP with cURL.

subscribe.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = $_POST['email'] ?? '';
    $name = $_POST['name'] ?? '';

    $data = [
        'email' => $email,
        'name' => $name,
        'campaignId' => 'YOUR_CAMPAIGN_ID',
        'apiKey' => 'YOUR_API_KEY',
    ];

    $ch = curl_init('https://yourdomain.com/api/public/contacts');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $result = json_decode($response, true);

    if ($httpCode === 200 && $result['success']) {
        echo json_encode(['success' => true, 'message' => 'Subscribed successfully']);
    } else {
        echo json_encode(['success' => false, 'error' => $result['error'] ?? 'Unknown error']);
    }
}
?>

cURL

Command-line example for testing.

test-api.sh
curl -X POST https://yourdomain.com/api/public/contacts \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "name": "John Doe",
    "campaignId": "YOUR_CAMPAIGN_ID",
    "apiKey": "YOUR_API_KEY"
  }'

Python

Python with requests library.

subscribe.py
import requests

def add_contact(email, name=None):
    url = 'https://yourdomain.com/api/public/contacts'
    
    data = {
        'email': email,
        'name': name,
        'campaignId': 'YOUR_CAMPAIGN_ID',
        'apiKey': 'YOUR_API_KEY'
    }
    
    try:
        response = requests.post(url, json=data)
        response.raise_for_status()
        
        result = response.json()
        
        if result.get('success'):
            print(f'Successfully added {email}')
            return True
        else:
            print(f'Error: {result.get("error")}')
            return False
            
    except requests.exceptions.RequestException as e:
        print(f'Request failed: {e}')
        return False

# Usage
add_contact('user@example.com', 'John Doe')

Google Sheets

Google Apps Script for spreadsheet integration.

Code.gs
function addContactFromSheet() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var lastRow = sheet.getLastRow();
  
  // Get email from column A and name from column B
  var email = sheet.getRange(lastRow, 1).getValue();
  var name = sheet.getRange(lastRow, 2).getValue();
  
  var url = 'https://yourdomain.com/api/public/contacts';
  
  var payload = {
    'email': email,
    'name': name,
    'campaignId': 'YOUR_CAMPAIGN_ID',
    'apiKey': 'YOUR_API_KEY'
  };
  
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload)
  };
  
  try {
    var response = UrlFetchApp.fetch(url, options);
    var result = JSON.parse(response.getContentText());
    
    if (result.success) {
      sheet.getRange(lastRow, 3).setValue('✓ Synced');
      Logger.log('Contact added successfully');
    } else {
      sheet.getRange(lastRow, 3).setValue('✗ Error: ' + result.error);
      Logger.log('Error: ' + result.error);
    }
  } catch (error) {
    sheet.getRange(lastRow, 3).setValue('✗ Failed');
    Logger.log('Request failed: ' + error);
  }
}

// Optional: Add a custom menu
function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Surgio')
    .addItem('Add Latest Contact', 'addContactFromSheet')
    .addToUi();
}

Responses

Success Response

{
  "success": true,
  "recipient": {
    "id": "clx1234567890",
    "email": "user@example.com",
    "name": "John Doe",
    "createdAt": "2024-12-07T12:00:00.000Z"
  }
}

Error Response

{
  "success": false,
  "error": "Invalid email format"
}

Common Errors

Invalid email formatThe provided email is not valid
Missing required fieldsemail, campaignId, or apiKey is missing
Invalid API keyThe API key is incorrect or expired
Campaign not foundThe campaign ID doesn't exist

Best Practices

Security

  • • Never expose API keys in client-side code
  • • Use environment variables
  • • Implement server-side proxy routes
  • • Regenerate keys if compromised

Error Handling

  • • Check response status codes
  • • Show user-friendly messages
  • • Implement retry logic
  • • Log errors for debugging

User Experience

  • • Validate email format client-side
  • • Show loading states
  • • Provide clear feedback
  • • Clear form after success

Performance

  • • Use async/await patterns
  • • Handle timeouts gracefully
  • • Debounce form submissions
  • • Cache API responses when appropriate
Was this page helpful?