API Authentication and Security Best Practices
Authentication Overview
The Coext External API provides flexible authentication options to match your security requirements. You can start with simple API key authentication and upgrade to enhanced signature-based authentication when needed.
Authentication Methods Compared
| Method | Security Level | Implementation Effort | Best For |
|---|---|---|---|
| API Key Only | Standard ⭐⭐⭐ | Minimal | Development, testing, quick integrations |
| API Key + Signature | Enhanced ⭐⭐⭐⭐⭐ | Moderate | Production, high-security environments |
| API Key + IP Whitelist | Enhanced ⭐⭐⭐⭐ | Minimal | Server-to-server integrations |
Understanding Your Credentials
When you create an API key, you receive two distinct credentials:
API Key (Public Identifier)
| Format | pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| Purpose | Identifies your API key in requests |
| Sent To Server | Yes, in every request header |
| Can Be Retrieved | Yes, visible in dashboard (prefix only for security) |
API Secret (Private Key)
| Format | sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| Purpose | Signs requests for enhanced authentication |
| Sent To Server | Never – only used locally to generate signatures |
| Can Be Retrieved | No – shown only once at creation time |
🔐 Critical Security Note: Your API Secret is only displayed once when creating the key. Copy it immediately and store it securely. If you lose it, you’ll need to rotate the secret (which invalidates the old one) or create a new API key.
Simple Authentication (Recommended Start)
For most use cases, simple API key authentication is sufficient. Just include your API key in the request header.
Required Headers
Content-Type: application/json
X-API-Key: pk_live_your_api_key_here
Complete Request Example (cURL)
curl -X POST "https://your-domain.com/api/v1/external/messages/text"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_a1b2c3d4e5f6g7h8i9j0"
-d '{
"to": "+919876543210",
"message": "Hello from the API!"
}'
JavaScript (Node.js with Fetch)
const API_KEY = process.env.COEXT_API_KEY;
const BASE_URL = 'https://your-domain.com/api/v1/external';
async function sendTextMessage(to, message) {
const response = await fetch(`${BASE_URL}/messages/text`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({ to, message })
});
return response.json();
}
// Usage
const result = await sendTextMessage('+919876543210', 'Hello!');
console.log('Message ID:', result.data.message_id);
Python (Requests Library)
import os
import requests
API_KEY = os.environ.get('COEXT_API_KEY')
BASE_URL = 'https://your-domain.com/api/v1/external'
def send_text_message(to: str, message: str) -> dict:
headers = {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
payload = {'to': to, 'message': message}
response = requests.post(
f'{BASE_URL}/messages/text',
json=payload,
headers=headers
)
return response.json()
# Usage
result = send_text_message('+919876543210', 'Hello from Python!')
print(f"Message ID: {result['data']['message_id']}")
PHP (cURL)
<?php
$apiKey = getenv('COEXT_API_KEY');
$baseUrl = 'https://your-domain.com/api/v1/external';
function sendTextMessage($to, $message) {
global $apiKey, $baseUrl;
$payload = json_encode(['to' => $to, 'message' => $message]);
$ch = curl_init("$baseUrl/messages/text");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"X-API-Key: $apiKey"
]
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Usage
$result = sendTextMessage('+919876543210', 'Hello from PHP!');
echo "Message ID: " . $result['data']['message_id'];
Enhanced Authentication (HMAC Signatures)
For production environments requiring additional security, you can add HMAC-SHA256 signature verification. When you include both X-Timestamp and X-API-Signature headers, the server validates them.
Additional Headers for Enhanced Auth
| Header | Format | Description |
|---|---|---|
X-Timestamp | Unix seconds (e.g., 1703001234) | When the request was created |
X-API-Signature | 64-character hex string | HMAC-SHA256 signature of the request |
Signature Generation Algorithm
Follow these steps to generate a valid signature:
- Hash your API Secret: Generate SHA256 hash of the secret
secret_hash = SHA256("sk_live_your_secret") - Get current timestamp: Unix time in seconds (not milliseconds!)
timestamp = 1703001234
- Prepare the payload: Concatenate timestamp + “.” + JSON body
payload = "1703001234.{"to":"+919876543210","message":"Hello"}" - Generate HMAC: HMAC-SHA256 of payload using hashed secret
signature = HMAC-SHA256(payload, secret_hash)
JavaScript Implementation
const crypto = require('crypto');
function generateAuthHeaders(apiKey, apiSecret, requestBody) {
// Step 1: Get current timestamp (seconds, not milliseconds!)
const timestamp = Math.floor(Date.now() / 1000);
// Step 2: Hash the API secret
const secretHash = crypto
.createHash('sha256')
.update(apiSecret)
.digest('hex');
// Step 3: Create the payload to sign
const bodyString = JSON.stringify(requestBody);
const payload = `${timestamp}.${bodyString}`;
// Step 4: Generate HMAC-SHA256 signature
const signature = crypto
.createHmac('sha256', secretHash)
.update(payload)
.digest('hex');
return {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
'X-Timestamp': timestamp.toString(),
'X-API-Signature': signature
};
}
// Usage
const apiKey = process.env.COEXT_API_KEY;
const apiSecret = process.env.COEXT_API_SECRET;
const body = { to: '+919876543210', message: 'Hello!' };
const headers = generateAuthHeaders(apiKey, apiSecret, body);
const response = await fetch('https://your-domain.com/api/v1/external/messages/text', {
method: 'POST',
headers,
body: JSON.stringify(body)
});
Python Implementation
import hashlib
import hmac
import json
import time
import os
def generate_auth_headers(api_key: str, api_secret: str, request_body: dict) -> dict:
# Step 1: Get current timestamp
timestamp = int(time.time())
# Step 2: Hash the API secret
secret_hash = hashlib.sha256(api_secret.encode()).hexdigest()
# Step 3: Create the payload
body_string = json.dumps(request_body, separators=(',', ':'))
payload = f"{timestamp}.{body_string}"
# Step 4: Generate HMAC signature
signature = hmac.new(
secret_hash.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return {
'Content-Type': 'application/json',
'X-API-Key': api_key,
'X-Timestamp': str(timestamp),
'X-API-Signature': signature
}
# Usage
api_key = os.environ['COEXT_API_KEY']
api_secret = os.environ['COEXT_API_SECRET']
body = {'to': '+919876543210', 'message': 'Hello!'}
headers = generate_auth_headers(api_key, api_secret, body)
IP Whitelisting
For server-to-server integrations, you can restrict API access to specific IP addresses:
- Navigate to External API Management → API Keys
- Edit your API key
- In the Security section, add allowed IP addresses
- Save changes
💡 Tip: For dynamic IPs, leave the whitelist empty or use a VPN with a static IP. You can also combine IP whitelisting with signature authentication for maximum security.
Common Authentication Errors
INVALID_API_KEY (HTTP 401)
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid"
}
}
Causes: Key doesn’t exist, format incorrect, key was deleted
Solution: Verify API key is correct and active in dashboard
EXPIRED_API_KEY (HTTP 401)
{
"success": false,
"error": {
"code": "EXPIRED_API_KEY",
"message": "This API key has expired"
}
}
Solution: Create a new API key or remove expiration date
INVALID_SIGNATURE (HTTP 401)
{
"success": false,
"error": {
"code": "INVALID_SIGNATURE",
"message": "Request signature verification failed"
}
}
Causes:
- Wrong API secret used
- Body modified after signing
- JSON serialization differs from what was signed
- Timestamp doesn’t match signature payload
TIMESTAMP_EXPIRED (HTTP 401)
{
"success": false,
"error": {
"code": "TIMESTAMP_EXPIRED",
"message": "Request timestamp is too old or too far in the future"
}
}
Causes: Timestamp more than 5 minutes old, or system clock out of sync
Solution: Sync your server clock with NTP
Security Best Practices
1. Never Expose Your API Secret
# ✅ Good: Environment variables
export COEXT_API_KEY="pk_live_..."
export COEXT_API_SECRET="sk_live_..."
# ✅ Good: Read from environment in code
const apiSecret = process.env.COEXT_API_SECRET;
# ❌ Bad: Hardcoded in source code
const apiSecret = "sk_live_actual_secret_here";
# ❌ Bad: Committed to git
// Never add .env files to version control!
2. Rotate Keys Regularly
- Create new keys every 90 days for production
- Update applications with new credentials
- Revoke old keys after confirming new ones work
- Use the “Rotate Secret” feature to generate new secrets
3. Use Separate Keys for Environments
| Environment | Key Name Example | Rate Limit |
|---|---|---|
| Development | Dev – Local Testing | 10/min |
| Staging | Staging – QA Testing | 30/min |
| Production | Production – Order System | 60/min |
4. Monitor and Alert
- Review API logs for failed authentication attempts
- Set up alerts for unusual activity patterns
- Monitor rate limit usage to detect abuse
- Immediately revoke keys if compromise is suspected
5. Always Use HTTPS
All production requests must use HTTPS. Never send API credentials over unencrypted HTTP connections.
Testing Your Authentication
Use the templates endpoint to verify your authentication without sending a message:
curl -X GET "https://your-domain.com/api/v1/external/templates"
-H "Content-Type: application/json"
-H "X-API-Key: pk_live_your_key"
A successful response with template data confirms your API key is working correctly.

