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 as sha256=<signature>.
  • content-type: Must be set to application/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"
    }
  ]
}
ParameterTypeMandatory/OptionalDescriptionHow it Affects Results
messagesArrayMandatory (min 1)Collection of incoming messages.Each entry is one inbound reply to process.
messages[].toStringMandatoryYour brand/sender identifier.Identifies the sender the customer replied to.
messages[].fromStringMandatoryCustomer's phone number.Identifies the replying customer.
messages[].messageStringMandatoryMessage content from the customer.The reply body (for example, stop for an opt-out).
messages[].sentAtDatetimeMandatoryWhen 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 CodeDescription
400Invalid request — missing required fields or validation errors.
401Unauthorized — issues with the base URL identifier or with the app-api-key / x-hub-signature headers.
500Internal 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.