Handshake Endpoint
The Handshake Endpoint establishes the connection between an integrating aggregator and the SMS API (formerly OptiText API). Only after the handshake completes successfully can campaigns be sent to end customers. On success, your aggregator is registered to send campaigns on behalf of the tenant.
Before using this endpoint, obtain your <BASE_URL> (which includes the Base36-encoded tenant and application identifier), your app-api-key, and your shared secret from the tenant's SMS Configuration (Settings > SMS > SMS Config). You also need the campaign send URL — the webhook on your side that Optimove will call to deliver campaigns.
The handshake is typically called once per integration. Have your Campaign Transmission Webhook ready to receive requests before you complete it.
Handshake Endpoint
Endpoint
POST <BASE_URL>/api/v1/aggregator/handshake
The <BASE_URL> is provided by the tenant during SMS API configuration. It contains the base URL plus a Base36-encoded identifier that combines the tenant ID and application ID.
Headers
The following headers are mandatory for all requests:
app-api-key: The tenant's API Token (the Optimove API key from Settings > SMS > SMS Config).x-hub-signature: HMAC-SHA256 signature of the raw request body, formatted assha256=<signature>.content-type: Must be set toapplication/json.
Generating the Signature
The x-hub-signature header contains an HMAC signature of the raw request body, computed with your shared secret. The format follows the GitHub webhook signature format:
x-hub-signature: sha256=<signature><signature> is the lowercase hexadecimal HMAC-SHA256 hash of the request body.
const crypto = require("crypto");
const rawBody = req.rawBody; // Raw request body string (before JSON parsing)
const secret = process.env.WEBHOOK_SECRET; // Your shared secret
const signature = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const hubSignature = `sha256=${signature}`;import hashlib
import hmac
import os
raw_body = request.body # Raw request body bytes (before JSON parsing)
secret = os.environ["WEBHOOK_SECRET"].encode("utf-8") # Your shared secret
signature = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
hub_signature = f"sha256={signature}"Request Body Parameters
{
"aggregatorApiKey": "string",
"campaignSendUrl": "string",
"senderId": "string"
}| Parameter | Type | Mandatory/Optional | Description | How it Affects Results |
|---|---|---|---|---|
aggregatorApiKey | String | Mandatory | Your aggregator's API key, used to authenticate when sending campaigns. | Optimove includes this value as the X-API-Key on every Campaign Transmission Webhook request. |
campaignSendUrl | String | Mandatory | The webhook URL where Optimove sends campaign requests to your aggregator. | Determines where orchestrated campaigns are delivered for transmission. |
senderId | String | Mandatory | The sender identifier used for campaigns sent through your aggregator. | Sets the default sender associated with campaigns for this tenant/app. |
Response
A successful handshake returns 200 OK:
{
"message": "Handshake completed successfully",
"processedAt": "2024-01-15T10:30:00.000Z"
}Error Codes
| Status Code | Description |
|---|---|
400 | Bad request — missing required fields, authentication headers, or validation errors. |
409 | Conflict — the handshake payload must be unique; a duplicate, unchanged payload was sent. |
500 | Internal server error during handshake processing. |
400 Bad Request — missing identifiers:
{ "message": "API token, appId, or tenantId is missing." }400 Bad Request — validation errors:
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"AggregatorApiKey": ["The AggregatorApiKey field is required."],
"CampaignSendUrl": ["The CampaignSendUrl field is required."]
}
}409 Conflict — duplicate handshake:
{
"title": "Duplicate Handshake",
"message": "Handshake data unchanged for appId: 1, tenantId: 1001. Skipping update.",
"code": 40901,
"traceId": "0HNG7S2VFU9UH:00000005"
}500 Internal Server Error:
{
"message": "An error occurred during handshake.",
"error": "Detailed error message"
}Integration Flow
- Gather prerequisites: Obtain your
<BASE_URL>(includes the Base36-encoded tenant/app ID),app-api-key, and shared secret from the tenant's SMS Configuration (Settings > SMS Configuration). - Prepare your endpoint: Stand up the campaign send URL that will receive campaign requests from Optimove.
- Generate the signature: Compute the HMAC-SHA256 signature of your request body.
- Send the handshake: POST the request to complete the handshake.
- Handle the response: Process the success or error response.
Complete Example
curl -X POST "<BASE_URL>/api/v1/aggregator/handshake" \
-H "Content-Type: application/json" \
-H "app-api-key: <APP_API_KEY>" \
-H "x-hub-signature: sha256=<SIGNATURE>" \
-d '{
"aggregatorApiKey": "<YOUR_AGGREGATOR_API_KEY>",
"campaignSendUrl": "https://your-aggregator.example.com/api/v1/campaigns/send",
"senderId": "YourCompany"
}'{
"message": "Handshake completed successfully",
"processedAt": "2024-01-15T10:30:00.000Z"
}The handshake payload must be unique. Re-sending an identical, unchanged payload returns
409 Conflict. Change at least one field (for example, thecampaignSendUrl) when you need to update a registration.
Security Considerations
- Always use HTTPS so sensitive data is protected in transit.
- Keep your API keys and shared secrets secure; never expose them in client-side code.
- Validate the signature on every request to ensure message integrity.
- Implement logging and monitoring for handshake attempts.
Rate Limiting
The handshake endpoint is not heavily rate-limited, since it is typically called once per integration. Implement reasonable retry logic with exponential backoff for 500-level errors.
Support
If you run into issues with the handshake endpoint, contact the Optimove developer support team with your <BASE_URL> and identifier, redacted request/response examples, the error messages received, and a timestamp.
Next, implement the Campaign Transmission Webhook so Optimove can deliver campaigns to you.