Incoming Messages Webhook
The Incoming Messages Endpoint lets aggregators using the SMS API (formerly OptiText API) forward end-customer replies to campaigns for specific tenants and apps. Use it to relay inbound messages — for example, a customer replying STOP — back to Optimove for processing.
This endpoint uses the same custom authentication scheme as the Handshake Endpoint and Delivery Report Endpoint.
Incoming Messages Endpoint
Endpoint
POST <BASE_URL>/api/v1/aggregator/incoming-message
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 Configuration).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-SHA256 signature of the raw request body, computed with your shared secret, formatted as sha256=<signature>.
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
Send incoming messages in the following format:
{
"messages": [
{
"to": "companyAbc",
"from": "44123456789",
"message": "stop",
"sentAt": "2015-12-25T10:30:00"
}
]
}| Parameter | Type | Mandatory/Optional | Description | How it Affects Results |
|---|---|---|---|---|
messages | Array | Mandatory (min 1) | Collection of incoming messages. | Each entry is one inbound reply to process. |
messages[].to | String | Mandatory | Your brand/sender identifier. | Identifies the sender the customer replied to. |
messages[].from | String | Mandatory | Customer's phone number. | Identifies the replying customer. |
messages[].message | String | Mandatory | Message content from the customer. | The reply body (for example, stop for an opt-out). |
messages[].sentAt | Datetime | Mandatory | When the message was sent. | Timestamps the reply. |
Response
A successful request returns 200 OK:
{
"message": "Incoming message request received successfully",
"processedAt": "2024-01-15T10:30:00.000Z"
}Error Codes
| Status Code | Description |
|---|---|
400 | Invalid request — missing required fields or validation errors. |
401 | Unauthorized — issues with the base URL identifier or with the app-api-key / x-hub-signature headers. |
500 | Internal server error during processing. |
400 Bad Request — missing appId:
{ "message": "appId is missing." }400 Bad Request — validation errors:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Messages[0].Message": [
"The Message field is required."
]
},
"traceId": "00-0f8e508ed5f872d59904a67a748007c7-8f0a08114f73fd43-00"
}500 Internal Server Error:
{ "message": "Incoming message request processing failed." }A 401 Unauthorized response returns no response body.
Complete Example
curl -X POST "<BASE_URL>/api/v1/aggregator/incoming-message" \
-H "Content-Type: application/json" \
-H "app-api-key: <APP_API_KEY>" \
-H "x-hub-signature: sha256=<SIGNATURE>" \
-d '{
"messages": [
{
"to": "YourBrand",
"from": "1234567891",
"message": "stop",
"sentAt": "2025-10-01T12:34:56Z"
}
]
}'{
"message": "Incoming message request received successfully",
"processedAt": "2024-01-15T10:30:00.000Z"
}Security Considerations
- Always use HTTPS to protect data 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 incoming message attempts.
Support
If you run into issues, contact the Optimove developer support team with your Base URL, redacted request/response examples, the error messages received, and a timestamp.