Inbound Webhooks
Send signed HTTP requests to Recomly to enroll advocates automatically from your existing systems.
Inbound webhooks let your CRM, booking system, or any custom application enroll advocates into a Recomly campaign by sending a single HTTP request. There is no polling, no manual data entry, and no Zapier required.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST | https://api.recomly.com/api/public/webhook/invite | Enroll advocates |
Authentication
Every request must be authenticated with a signing key. Signing keys are created in the dashboard under Integrations → Webhooks. Each key has a unique key ID and a signing secret.
The signing secret is shown exactly once when the key is created. Store it securely — it cannot be retrieved after the creation dialog is closed.
Request headers
| Header | Value |
|---|---|
Content-Type | application/json |
X-Recomly-Key-Id | Your key ID |
X-Recomly-Signature | HMAC-SHA256 signature (see below) |
Signature format
The X-Recomly-Signature header must be formatted as:
t=<unix_timestamp>,v1=<hmac_hex>Where:
<unix_timestamp>is the current Unix timestamp in seconds (e.g.1712345678)<hmac_hex>is an HMAC-SHA256 hex digest computed over the string<unix_timestamp>.<raw_request_body>, using your signing secret as the key
Recomly validates the signature and rejects requests with a timestamp older than 5 minutes to prevent replay attacks.
Payload reference
The request body must be a JSON object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
campaignId | string | Yes | ID of the active campaign to enroll the advocate into |
name | string | Yes | Full name of the advocate (1–200 characters) |
email | string | One of | Email address. At least one of email or phone must be provided. |
phone | string | One of | E.164 phone number (e.g. +15551234567). Required if email is omitted. |
Response
All requests return 200 { "ok": true }, including requests with an invalid key or bad signature — this is intentional and prevents key enumeration. If advocates aren't appearing after a successful send, double-check that the Key ID and signing secret match what's shown in the dashboard under Integrations → Webhooks.
| Status | Meaning |
|---|---|
200 | Request accepted |
400 | Invalid request body — malformed JSON or missing required fields |
500 | Internal server error |
Code examples
curl
curl -X POST https://api.recomly.com/api/public/webhook/invite \
-H "Content-Type: application/json" \
-H "X-Recomly-Key-Id: YOUR_KEY_ID" \
-H "X-Recomly-Signature: t=$(date +%s),v1=$(echo -n "$(date +%s).BODY" | openssl dgst -sha256 -hmac YOUR_SECRET | awk '{print $2}')" \
-d '{"campaignId":"CAMPAIGN_ID","name":"Jane Smith","email":"jane@example.com"}'Node.js
import crypto from 'crypto';
const KEY_ID = process.env.RECOMLY_KEY_ID;
const SECRET = process.env.RECOMLY_SIGNING_SECRET;
const ENDPOINT = 'https://api.recomly.com/api/public/webhook/invite';
async function enrollAdvocate(payload) {
const body = JSON.stringify(payload);
const ts = Math.floor(Date.now() / 1000);
const sig = crypto.createHmac('sha256', SECRET)
.update(`${ts}.${body}`)
.digest('hex');
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Recomly-Key-Id': KEY_ID,
'X-Recomly-Signature': `t=${ts},v1=${sig}`,
},
body,
});
return res.json();
}
// Example
await enrollAdvocate({
campaignId: 'CAMPAIGN_ID',
name: 'Jane Smith',
email: 'jane@example.com',
phone: '+15551234567', // optional
});Python
import hmac, hashlib, time, json, os, urllib.request
KEY_ID = os.getenv('RECOMLY_KEY_ID')
SECRET = os.getenv('RECOMLY_SIGNING_SECRET').encode()
ENDPOINT = 'https://api.recomly.com/api/public/webhook/invite'
def enroll_advocate(payload: dict):
body = json.dumps(payload).encode()
ts = str(int(time.time()))
sig = hmac.new(SECRET, f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
req = urllib.request.Request(
ENDPOINT,
data = body,
method = 'POST',
headers = {
'Content-Type': 'application/json',
'X-Recomly-Key-Id': KEY_ID,
'X-Recomly-Signature': f't={ts},v1={sig}',
},
)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
# Example
enroll_advocate({
'campaignId': 'CAMPAIGN_ID',
'name': 'Jane Smith',
'email': 'jane@example.com',
})
