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.