Table of Contents
< All Topics

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

MethodSecurity LevelImplementation EffortBest For
API Key OnlyStandard ⭐⭐⭐MinimalDevelopment, testing, quick integrations
API Key + SignatureEnhanced ⭐⭐⭐⭐⭐ModerateProduction, high-security environments
API Key + IP WhitelistEnhanced ⭐⭐⭐⭐MinimalServer-to-server integrations

Understanding Your Credentials

When you create an API key, you receive two distinct credentials:

API Key (Public Identifier)

Formatpk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PurposeIdentifies your API key in requests
Sent To ServerYes, in every request header
Can Be RetrievedYes, visible in dashboard (prefix only for security)

API Secret (Private Key)

Formatsk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PurposeSigns requests for enhanced authentication
Sent To ServerNever – only used locally to generate signatures
Can Be RetrievedNo – 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

HeaderFormatDescription
X-TimestampUnix seconds (e.g., 1703001234)When the request was created
X-API-Signature64-character hex stringHMAC-SHA256 signature of the request

Signature Generation Algorithm

Follow these steps to generate a valid signature:

  1. Hash your API Secret: Generate SHA256 hash of the secret
    secret_hash = SHA256("sk_live_your_secret")
  2. Get current timestamp: Unix time in seconds (not milliseconds!)
    timestamp = 1703001234
  3. Prepare the payload: Concatenate timestamp + “.” + JSON body
    payload = "1703001234.{"to":"+919876543210","message":"Hello"}"
  4. 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:

  1. Navigate to External API ManagementAPI Keys
  2. Edit your API key
  3. In the Security section, add allowed IP addresses
  4. 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

EnvironmentKey Name ExampleRate Limit
DevelopmentDev – Local Testing10/min
StagingStaging – QA Testing30/min
ProductionProduction – Order System60/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.

Leave a Reply

Your email address will not be published. Required fields are marked *