Integration Postbacks
Some integrations (LeadProsper today) post results back to WaveRunner. Each integration record has its own postback URL, shown on the record's page under Integrations > My Integrations. This page is for the vendor or engineer configuring that callback.
Endpoint
POST /webhooks/integrations/{token}/ (the full URL is shown on the integration page)
| Authentication | The URL token identifies the record. When the record has a postback secret, send it as X-Waverunner-Secret (the header name is the same on every host). |
| Body | JSON object, at most 1 MiB. LeadProsper sends its campaign-trigger payload as is. |
| Rate limit | 600 requests per minute per source IP. |
| Retries | Sent once by the vendor; the platform reconciles missed postbacks hourly when a read token is configured. |
Responses
| Status | Body | Meaning |
|---|---|---|
200 |
{"status": "accepted", "activity_id": "<uuid>"} |
Stored and queued; the activity id appears in the record's activity log. |
200 |
{"status": "skipped"} |
The record is switched off, or its provider is inactive on the platform; nothing was stored beyond a skipped activity row. |
400 |
{"code": "invalid_payload", "message": "Invalid payload.", "details": null} |
Not a JSON object. |
401 |
{"code": "unauthorized", "message": "Unauthorized.", "details": null} |
The secret header is missing or wrong. |
404 |
{"code": "not_found", "message": "Not found.", "details": null} |
Unknown or rotated token. The endpoint never confirms whether a token exists. |
413 |
{"code": "payload_too_large", "message": "Payload too large.", "details": null} |
Body over 1 MiB. |
429 |
{"detail": "Request was throttled. Expected available in 60 seconds.", "status_code": 429} with Retry-After |
Over the per-IP rate. |
Example
The body below is the LeadProsper campaign-trigger payload the integration expects. Replace the URL with the one on your integration page and the secret with the record's postback secret.
curl -X POST "https://api.waverunner.ai/webhooks/integrations/in_your_token/" \
-H "Content-Type: application/json" \
-H "X-Waverunner-Secret: your_postback_secret" \
-d '{"our_ref": "7f3c9a2e-1b4d-4c8e-9f60-2a5b7d9e1c34", "request_uuid": "c1d2e3f4", "lead_id": "123456", "status": "SOLD", "sell_price": "4.50", "buyer_id": "88", "buyer_name": "Example Buyer"}'
import requests
response = requests.post(
"https://api.waverunner.ai/webhooks/integrations/in_your_token/",
headers={"X-Waverunner-Secret": "your_postback_secret"},
json={
"our_ref": "7f3c9a2e-1b4d-4c8e-9f60-2a5b7d9e1c34",
"request_uuid": "c1d2e3f4",
"lead_id": "123456",
"status": "SOLD",
"sell_price": "4.50",
"buyer_id": "88",
"buyer_name": "Example Buyer",
},
)
print(response.status_code, response.json())
const response = await fetch(
'https://api.waverunner.ai/webhooks/integrations/in_your_token/',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Waverunner-Secret': 'your_postback_secret',
},
body: JSON.stringify({
our_ref: '7f3c9a2e-1b4d-4c8e-9f60-2a5b7d9e1c34',
request_uuid: 'c1d2e3f4',
lead_id: '123456',
status: 'SOLD',
sell_price: '4.50',
buyer_id: '88',
buyer_name: 'Example Buyer',
}),
}
);
console.log(response.status, await response.json());
Response:
{
"status": "accepted",
"activity_id": "0b6f2d4a-5e1c-4f7a-8d3b-9c2e6a1f7b40"
}
Rotating the URL
Rotate inbound token on the integration page mints a new URL; the old one answers 404 immediately. Update the vendor's callback to the new URL straight after rotating.