Campaign Transmission Webhook
The Campaign Transmission Webhook is an endpoint that will be implemented on the integrating aggregator side. This endpoint is where Optitext will send SMS campaigns, after orchestration, to the aggregator at which point responsibility of transmitting the campaigns falls on the aggregator.
Overview
This document is intended for third-party developers building APIs that will receive requests from the OptitextAPI AggregatorService into their Campaign Transmission Webhook. The AggregatorService is a messaging client that forwards campaign message data to external aggregator Campaign Transmission Webhook for SMS/messaging delivery.
Request Format
HTTP Method and Headers
Optitext API will send POST requests to your endpoint with the following headers:
POST /your-endpoint-url
Content-Type: application/json
X-API-Key: {your-provided-api-key}
x-hub-signature: {hmac-signature}
User-Agent: {system-user-agent}
Request Body Structure
The request body contains a JSON payload with the following structure:
{
"batchId": "string",
"metadata": {
"appId": "int",
"brandId": "string",
"campaignId": "string",
"campaignType": "int",
"channelId" :"int",
"engagementId": "string",
"isHashed": "boolean",
"scheduledTime": "long",
"tenantId": "int"
},
"recipients": [
{
"message": "string"
"mobileNumber": "string",
"customerId": "string"
}
],
}Field Descriptions
| Field | Type | Required | Description |
|---|---|---|---|
batchId | string | Optional | Unique identifier for the message batch |
metadata.appId | int | ||
metadata.campaignId | string | Optional | Campaign identifier |
metadata.campaignType | int | ||
metadata.channelId | int | ||
metadata.engagementId | string | Optional | Unique identifier for the customer engagement |
metadata.isHashed | bool | ||
metadata.scheduledTime | number | Yes | Unix timestamp (milliseconds) for when the message should be sent |
metadata.tenantId | int | ||
recipients | array | Optional | Array of message recipients |
recipients[].message | string | Optional | The message content to send |
recipients[].mobileNumber | string | Optional | Recipient's mobile phone number |
recipients[].customerId | string | Optional | Unique customer identifier |
Sample Request
{
"batchId": "batch-123",
"metadata": {
"appId": 123,
"brandId": "betcash",
"campaignId": "232522",
"campaignType": 1,
"channelId": 493,
"engagementId": "aaabbbccc",
"isHashed": false,
"scheduledTime": 1704106200000,
"tenantId": 1
},
"recipients": [
{
"message": "Hello! Your order is ready for pickup.",
"mobileNumber": "+1234567890",
"customerId": "customer-001"
},
{
"message": "Hello! Your order is ready for pickup.",
"mobileNumber": "+1234567891",
"customerId": "customer-002"
}
]
}Authentication
API Key Authentication
Each request includes an X-API-Key header containing your unique API key. Validate this key on every request to ensure authenticity.
HMAC Signature Verification
All requests include an x-hub-signature header containing an HMAC signature for request validation. This signature is critical for security and must be verified.
Signature Format
The signature follows the format: {algorithm}={hash}
Examples:
sha256=a1b2c3d4e5f6...sha1=f6e5d4c3b2a1...sha512=1a2b3c4d5e6f...
Signature Generation Process
- The entire JSON request body is used as the payload
- HMAC is computed using your shared secret key
- The hash is converted to lowercase hexadecimal
- The algorithm prefix is added
Verification Steps
- Extract the algorithm and hash from the
x-hub-signatureheader - Recompute the HMAC using the request body and your secret key
- Compare the computed hash with the received hash
- Important: Use a constant-time comparison to prevent timing attacks
Supported Algorithms
| Algorithm | Hash Length | Use Case |
|---|---|---|
| SHA256 | 64 characters | Default, recommended |
| SHA1 | 40 characters | Legacy support |
| SHA512 | 128 characters | Enhanced security |
Sample Verification (Python)
import hmac
import hashlib
def verify_signature(payload, signature_header, secret_key):
"""Verify HMAC signature from AggregatorService"""
if not signature_header or '=' not in signature_header:
return False
algorithm, received_hash = signature_header.split('=', 1)
# Compute HMAC
if algorithm == 'sha256':
computed_hmac = [hmac.new](http://hmac.new)(
secret_key.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
)
elif algorithm == 'sha1':
computed_hmac = [hmac.new](http://hmac.new)(
secret_key.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha1
)
elif algorithm == 'sha512':
computed_hmac = [hmac.new](http://hmac.new)(
secret_key.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha512
)
else:
return False
computed_hash = computed_hmac.hexdigest().lower()
# Constant-time comparison
return [hmac.compare](http://hmac.compare)_digest(computed_hash, received_hash)Response Requirements
Success Responses
Return any 2xx HTTP status code to indicate successful processing:
200 OK- Standard success response
Response Body (Optional)
You may optionally return a JSON response body, but it's not required for successful processing:
{
"message": "Message processed successfully",
"processedAt": "2024-01-01T12:30:00Z",
"batchId": "batch-123"
}Response Headers
No specific response headers are required, but standard headers are recommended:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 01 Jan 2024 12:30:00 GMT
Error Handling
Retryable Errors
The AggregatorService will retry requests for the following HTTP status codes:
| Status Code | Use Case | Retry Behaviour |
|---|---|---|
429 Too Many Requests | Rate limit exceeded, should retry with backoff | Will retry |
500 Internal Server Error | Server internal error, temporary issue | Will retry |
503 Service Unavailable | Service temporarily unavailable | Will retry |
Note: Network connectivity issues (HttpRequestException) and request timeouts (TaskCanceledException) will also trigger retries through the Google Cloud Functions retry mechanism.
Non-Retryable Errors (Client Errors)
Return appropriate 4xx status codes for client errors. These will NOT be retried:
| Status Code | Use Case | Retry Behaviour |
|---|---|---|
400 Bad Request | Invalid request format or parameters | No retry |
401 Unauthorized | Authentication failed or missing credentials | No retry |
403 Forbidden | Access denied, insufficient permissions | No retry |
404 Not Found | Endpoint or resource not found | No retry |
405 Method Not Allowed | HTTP method not supported | No retry |
406 Not Acceptable | Request format not acceptable | No retry |
409 Conflict | Request conflicts with current state (e.g., unique constraints for referential integrity) | No retry |
410 Gone | Resource no longer available | No retry |
422 Unprocessable Entity | Request validation failed or business logic error | No retry |
Important: All other non-2xx status codes not explicitly listed as retryable will be treated as non-retryable errors and will cause the campaign to abort.
Error Response Body
For error responses, include a descriptive error message:
{
"error": "Invalid message format",
"message": "The 'recipients' field is required",
"code": "MISSING_RECIPIENTS"
}Security Considerations
HMAC Signature Validation
- Always verify the HMAC signature before processing requests
- Use constant-time comparison to prevent timing attacks
- Reject requests with invalid or missing signatures
- Store your HMAC secret key securely (environment variables, key management systems)
API Key Security
- Validate the API key on every request
- Use secure key storage mechanisms
- Implement rate limiting based on API key
- Log authentication failures for monitoring
Network Security
- Use HTTPS endpoints only
- Implement appropriate firewall rules
- Consider IP allowlisting if possible
- Monitor for suspicious traffic patterns
Retry Behaviour
AggregatorService Retry Logic
The AggregatorService implements the following retry behaviour based on HTTP status codes and exception types:
Retryable Conditions
Requests will be retried for:
- HTTP 429 Too Many Requests (rate limit exceeded)
- HTTP 500 Internal Server Error (temporary server issue)
- HTTP 503 Service Unavailable (temporary service outage)
- Network connectivity issues (
HttpRequestException) - Request timeouts (
TaskCanceledException)
Non-Retryable Conditions
Requests will NOT be retried for:
- HTTP 400 Bad Request (invalid payload or parameters)
- HTTP 401 Unauthorized (authentication failed)
- HTTP 403 Forbidden (access denied)
- HTTP 404 Not Found (endpoint or resource not found)
- HTTP 405 Method Not Allowed (HTTP method not supported)
- HTTP 406 Not Acceptable (request format not acceptable)
- HTTP 409 Conflict (request conflicts with current state)
- HTTP 410 Gone (resource no longer available)
- HTTP 422 Unprocessable Entity (business logic errors)
- All other non-2xx status codes not explicitly listed as retryable
Retry Configuration
- Maximum retries: Handled by Google Cloud Functions (typically 3 attempts)
- Retry interval: Exponential backoff (~10s, 20s, 30s+)
- Dead letter handling: Failed messages go to a dead letter queue after max attempts
- Timeout: Requests may timeout and be retried
Recommendations for Your API
- Implement idempotency using
batchIdorengagementIdto handle duplicate requests - Use circuit breakers to prevent cascading failures
- Implement graceful degradation for partial service outages
- Return appropriate HTTP status codes to control retry behaviour
Testing and Validation
Webhook Testing
You can test your endpoint using the provided test utilities:
- Signature Generation Script: Use
scripts/signature/generate_[signature.py](http://signature.py)to create test signatures - Mock Service: Reference
scripts/mock-service/for testing examples
Test Scenarios
Implement test cases for:
- Valid Request Processing
- Proper JSON structure
- Valid HMAC signature
- Correct API key
- Authentication Failures
- Invalid API key (return 401/403)
- Missing/invalid HMAC signature (return 401/403)
- Payload Validation
- Missing required fields (return 400)
- Invalid data types (return 400)
- Business logic errors (return 422)
- Error Conditions
- Internal server errors (return 5xx)
- Service unavailable (return 503)
- Edge Cases
- Empty recipients array
- Very large message payloads
- Special characters in message content
Sample Test Request
curl -X POST https://your-endpoint.com/webhook \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-H "x-hub-signature: sha256=a1b2c3d4..." \
-d '{
"batchId": "test-batch-123",
"engagementId": "test-engagement-456",
"metadata": {
"campaignId": "test-campaign",
"numberOfCustomers": 1,
"numberOfDroppedCustomers": 0,
"region": "US",
"senderId": "test-sender"
},
"recipients": [{
"content": {"message": "Test message"},
"mobileNumber": "+1234567890",
"customerId": "test-customer"
}],
"scheduledTime": 1704106200000
}'Configuration Requirements
When registering your endpoint with OptitextAPI, provide:
- Endpoint URL: Your HTTPS webhook URL
- API Key: Unique key for request authentication
- HMAC Secret: Shared secret for signature verification
- HMAC Algorithm: Preferred algorithm (sha256 recommended)
- Sender ID: Your unique sender identifier
Support and Troubleshooting
Common Issues
- Signature Verification Failures
- Ensure you're using the raw request body for HMAC computation
- Verify the algorithm matches (sha256, sha1, sha512)
- Use constant-time comparison for security
- Authentication Errors
- Validate API key format and storage
- Check for typos in header names (
X-API-Key,x-hub-signature)
- Retry Loops
- Return 2xx status codes for successful processing
- Use 4xx codes to prevent retries for permanent failures
- Implement idempotency to handle duplicate requests
Monitoring Recommendations
- Log all incoming requests with relevant metadata
- Monitor signature verification success rates
- Track response times and error rates
- Alert on unusual traffic patterns or authentication failures
For additional support or questions about this integration, please refer to the OptitextAPI documentation or contact the development team.
Updated about 15 hours ago
