Authentication & API Keys

Authentication & API Keys

Sure Send CRM uses Bearer token authentication to secure API access. This guide covers everything you need to know about generating, managing, and using API tokens.

Overview

The Partner API uses Bearer token authentication. Every API request must include your API token in the Authorization header.

Authentication Method: Bearer Token Header Format: Authorization: Bearer YOUR_API_TOKEN Security: All requests must use HTTPS

Getting Your API Token

Step 1: Log in to Sure Send CRM

Navigate to https://app.suresend.ai and log in to your account.

Step 2: Open Settings

Click on Settings in the main navigation menu.

Step 3: Navigate to API Tokens

Go to Settings > API Tokens or Settings > Integrations > API Tokens.

Step 4: Generate a New Token

  1. Click "Create API Token" or "Generate New Token"
  2. Give your token a descriptive name (e.g., "Production Integration", "Staging Server")
  3. Select the appropriate permissions (if applicable)
  4. Click "Generate"

Step 5: Store Your Token Securely

⚠️ Important: The token is only shown once. Copy it immediately and store it securely.

Recommended Storage:

  • Environment variables
  • Secret management services (AWS Secrets Manager, HashiCorp Vault)
  • Encrypted configuration files

Never:

  • Commit tokens to version control
  • Share tokens via email or chat
  • Store tokens in client-side code
  • Log tokens in application logs

Making Authenticated Requests

Using cURL

curl https://api.suresend.ai/api/partner/people \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Using JavaScript (Node.js)

const axios = require('axios');

const API_TOKEN = process.env.SURESEND_API_TOKEN;
const API_URL = 'https://api.suresend.ai/api/partner';

// Make a request
async function getPeople() {
  const response = await axios.get(`${API_URL}/people`, {
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`
    }
  });
  return response.data;
}

Using Python

import os
import requests

API_TOKEN = os.environ.get('SURESEND_API_TOKEN')
API_URL = 'https://api.suresend.ai/api/partner'

# Make a request
def get_people():
    response = requests.get(
        f'{API_URL}/people',
        headers={'Authorization': f'Bearer {API_TOKEN}'}
    )
    return response.json()

Using Ruby

require 'net/http'
require 'json'

API_TOKEN = ENV['SURESEND_API_TOKEN']
API_URL = 'https://api.suresend.ai/api/partner'

# Make a request
def get_people
  uri = URI("#{API_URL}/people")
  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = "Bearer #{API_TOKEN}"

  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end

  JSON.parse(response.body)
end

Using PHP

<?php

$apiToken = getenv('SURESEND_API_TOKEN');
$apiUrl = 'https://api.suresend.ai/api/partner';

// Make a request
function getPeople() {
    global $apiToken, $apiUrl;

    $ch = curl_init("$apiUrl/people");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer $apiToken"
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}
?>

Environment Variables

Store your API token in environment variables for security:

Bash/Zsh

export SURESEND_API_TOKEN="your-api-token-here"

.env File (with dotenv)

# .env
SURESEND_API_TOKEN=your-api-token-here
SURESEND_API_URL=https://api.suresend.ai/api/partner

Don't forget to add .env to your .gitignore:

.env
.env.local
.env.*.local

Docker

docker run -e SURESEND_API_TOKEN=your-token myapp

Kubernetes

apiVersion: v1
kind: Secret
metadata:
  name: suresend-api-token
type: Opaque
stringData:
  token: your-api-token-here

API Client Setup

JavaScript/Node.js Client

Create a reusable API client:

// api-client.js
const axios = require('axios');

class SureSendClient {
  constructor(apiToken) {
    this.apiToken = apiToken;
    this.baseURL = 'https://api.suresend.ai/api/partner';

    this.client = axios.create({
      baseURL: this.baseURL,
      headers: {
        'Authorization': `Bearer ${this.apiToken}`,
        'Content-Type': 'application/json'
      }
    });
  }

  // People endpoints
  async getPeople(params = {}) {
    const response = await this.client.get('/people', { params });
    return response.data;
  }

  async getPerson(id) {
    const response = await this.client.get(`/people/${id}`);
    return response.data;
  }

  async createPerson(data) {
    const response = await this.client.post('/people', data);
    return response.data;
  }

  async updatePerson(id, data) {
    const response = await this.client.put(`/people/${id}`, data);
    return response.data;
  }

  // Events endpoints
  async createEvent(data) {
    const response = await this.client.post('/events', data);
    return response.data;
  }

  // Add more methods as needed
}

module.exports = SureSendClient;

Usage:

const SureSendClient = require('./api-client');

const client = new SureSendClient(process.env.SURESEND_API_TOKEN);

// Use the client
async function example() {
  const people = await client.getPeople({ limit: 10 });
  console.log(people);
}

Python Client

# suresend_client.py
import os
import requests

class SureSendClient:
    def __init__(self, api_token=None):
        self.api_token = api_token or os.environ.get('SURESEND_API_TOKEN')
        self.base_url = 'https://api.suresend.ai/api/partner'
        self.headers = {
            'Authorization': f'Bearer {self.api_token}',
            'Content-Type': 'application/json'
        }

    def _request(self, method, endpoint, **kwargs):
        url = f'{self.base_url}{endpoint}'
        response = requests.request(method, url, headers=self.headers, **kwargs)
        response.raise_for_status()
        return response.json()

    # People endpoints
    def get_people(self, params=None):
        return self._request('GET', '/people', params=params)

    def get_person(self, person_id):
        return self._request('GET', f'/people/{person_id}')

    def create_person(self, data):
        return self._request('POST', '/people', json=data)

    def update_person(self, person_id, data):
        return self._request('PUT', f'/people/{person_id}', json=data)

    # Events endpoints
    def create_event(self, data):
        return self._request('POST', '/events', json=data)

Usage:

from suresend_client import SureSendClient

client = SureSendClient()

# Use the client
people = client.get_people(params={'limit': 10})
print(people)

Error Handling

401 Unauthorized

Cause: Invalid or missing API token

Response:

{
  "error": "Unauthorized"
}

Solutions:

  • Check that your API token is correct
  • Verify the token hasn't been revoked
  • Ensure you're including the Authorization header
  • Check that "Bearer " prefix is included

Example:

try {
  const response = await axios.get(`${API_URL}/people`, {
    headers: { Authorization: `Bearer ${API_TOKEN}` }
  });
} catch (error) {
  if (error.response?.status === 401) {
    console.error('Invalid API token. Please check your credentials.');
    // Notify admin, rotate token, etc.
  }
}

403 Forbidden

Cause: Token doesn't have permission for this resource

Response:

{
  "error": "Forbidden"
}

Solutions:

  • Check token permissions
  • Verify you're accessing resources in your team
  • Contact support to adjust permissions

Token Management

Best Practices

  1. Use environment variables - Never hardcode tokens
  2. One token per environment - Separate tokens for dev, staging, production
  3. Rotate tokens regularly - Generate new tokens periodically
  4. Revoke compromised tokens immediately - Better safe than sorry
  5. Use descriptive names - Know which token is used where
  6. Monitor token usage - Track API calls per token
  7. Limit token scope - Use team-specific tokens when possible

Token Rotation

When to Rotate:

  • Every 90 days (recommended)
  • When an employee leaves
  • When a token is compromised
  • When moving to production
  • After a security audit

How to Rotate:

  1. Generate a new token in the dashboard
  2. Update environment variables in your applications
  3. Deploy the changes
  4. Test the new token
  5. Revoke the old token

Zero-Downtime Rotation:

// Support multiple tokens during rotation
const PRIMARY_TOKEN = process.env.SURESEND_API_TOKEN;
const FALLBACK_TOKEN = process.env.SURESEND_API_TOKEN_OLD;

async function makeRequest(endpoint, data) {
  try {
    return await apiRequest(endpoint, data, PRIMARY_TOKEN);
  } catch (error) {
    if (error.response?.status === 401 && FALLBACK_TOKEN) {
      console.warn('Primary token failed, trying fallback');
      return await apiRequest(endpoint, data, FALLBACK_TOKEN);
    }
    throw error;
  }
}

Revoking Tokens

If a token is compromised:

  1. Immediately revoke in Settings > API Tokens
  2. Generate a new token
  3. Update your applications
  4. Audit recent API calls - Check for suspicious activity
  5. Review access logs - Ensure no unauthorized access

Rate Limiting

API tokens are subject to rate limits. See Rate Limiting for details.

Rate Limit Headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 3600
X-RateLimit-Context: token:global

Handle Rate Limits:

async function makeRequestWithRetry(endpoint, data, retries = 3) {
  try {
    return await axios.post(endpoint, data, {
      headers: { Authorization: `Bearer ${API_TOKEN}` }
    });
  } catch (error) {
    if (error.response?.status === 429 && retries > 0) {
      const retryAfter = error.response.headers['retry-after'] || 60;
      console.log(`Rate limited. Retrying after ${retryAfter}s`);
      await sleep(retryAfter * 1000);
      return makeRequestWithRetry(endpoint, data, retries - 1);
    }
    throw error;
  }
}

Security Best Practices

Storage

Do:

  • Store in environment variables
  • Use secret management services (AWS Secrets Manager, HashiCorp Vault)
  • Encrypt configuration files
  • Use secure credential storage (1Password, LastPass for team sharing)

Don't:

  • Commit to version control
  • Store in plaintext files
  • Share via email/Slack
  • Embed in client-side code
  • Log in application logs

Transmission

Do:

  • Always use HTTPS
  • Use secure channels for sharing (encrypted file sharing)
  • Rotate tokens after sharing

Don't:

  • Send tokens over HTTP
  • Share in public channels
  • Include in URLs or query parameters

Access Control

Do:

  • Limit who has access to tokens
  • Use separate tokens per environment
  • Revoke tokens when no longer needed
  • Audit token usage regularly

Don't:

  • Share one token across all environments
  • Give everyone access to production tokens
  • Leave old tokens active

Code Security

// ✅ Good: Token from environment
const API_TOKEN = process.env.SURESEND_API_TOKEN;

// ❌ Bad: Hardcoded token
const API_TOKEN = 'sk_live_abc123...';

// ✅ Good: Don't log tokens
logger.info('Making API request', { endpoint: '/people' });

// ❌ Bad: Logging tokens
logger.info('Making API request', { token: API_TOKEN });

// ✅ Good: Redact in error messages
catch (error) {
  console.error('API Error:', error.message);
}

// ❌ Bad: Exposing tokens in errors
catch (error) {
  console.error('API Error:', error.config.headers);
}

Testing

Testing with Mock Tokens

For unit tests, use mock tokens:

// test.js
jest.mock('axios');

describe('SureSendClient', () => {
  it('should make authenticated requests', async () => {
    const client = new SureSendClient('mock-token-for-testing');

    axios.get.mockResolvedValue({ data: { people: [] } });

    await client.getPeople();

    expect(axios.get).toHaveBeenCalledWith(
      expect.any(String),
      expect.objectContaining({
        headers: expect.objectContaining({
          Authorization: 'Bearer mock-token-for-testing'
        })
      })
    );
  });
});

Testing with Separate Token

Create a separate API token for testing:

  1. Generate a token named "Testing" or "Development"
  2. Use it only in non-production environments
  3. Limit its scope if possible
  4. Monitor its usage
  5. Rotate regularly
# .env.test
SURESEND_API_TOKEN=test-token-here

Troubleshooting

Token Not Working

Check:

  • ✅ Token is copied correctly (no extra spaces)
  • ✅ Using Bearer prefix in Authorization header
  • ✅ Token hasn't been revoked
  • ✅ Using HTTPS, not HTTP
  • ✅ Token has proper permissions

Authorization Header Issues

Correct formats:

Authorization: Bearer YOUR_TOKEN_HERE
Authorization: Bearer sk_live_abc123xyz789

Incorrect formats:

Authorization: YOUR_TOKEN_HERE           (missing "Bearer")
Authorization: bearer YOUR_TOKEN_HERE     (lowercase "bearer")
Authorization: Token YOUR_TOKEN_HERE      (wrong prefix)

Token Lost or Forgotten

If you lose your token:

  1. It cannot be recovered (only shown once)
  2. Generate a new token
  3. Update your applications
  4. Revoke the old token if found

Next Steps

Need Help?