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
- Click "Create API Token" or "Generate New Token"
- Give your token a descriptive name (e.g., "Production Integration", "Staging Server")
- Select the appropriate permissions (if applicable)
- 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)
endUsing 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/partnerDon't forget to add .env to your .gitignore:
.env
.env.local
.env.*.local
Docker
docker run -e SURESEND_API_TOKEN=your-token myappKubernetes
apiVersion: v1
kind: Secret
metadata:
name: suresend-api-token
type: Opaque
stringData:
token: your-api-token-hereAPI 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
Authorizationheader - 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
- Use environment variables - Never hardcode tokens
- One token per environment - Separate tokens for dev, staging, production
- Rotate tokens regularly - Generate new tokens periodically
- Revoke compromised tokens immediately - Better safe than sorry
- Use descriptive names - Know which token is used where
- Monitor token usage - Track API calls per token
- 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:
- Generate a new token in the dashboard
- Update environment variables in your applications
- Deploy the changes
- Test the new token
- 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:
- Immediately revoke in Settings > API Tokens
- Generate a new token
- Update your applications
- Audit recent API calls - Check for suspicious activity
- 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:
- Generate a token named "Testing" or "Development"
- Use it only in non-production environments
- Limit its scope if possible
- Monitor its usage
- Rotate regularly
# .env.test
SURESEND_API_TOKEN=test-token-hereTroubleshooting
Token Not Working
Check:
- ✅ Token is copied correctly (no extra spaces)
- ✅ Using
Bearerprefix 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:
- It cannot be recovered (only shown once)
- Generate a new token
- Update your applications
- Revoke the old token if found
Next Steps
- Get started with Your First API Call
- Learn about People API
- Set up Webhooks for real-time updates
- Explore Events API for tracking
Need Help?
- API Reference: See all endpoints in the sidebar
- Support: [email protected]
- Security Issues: [email protected]
Updated 9 days ago
