Webhooks
Get a signed HTTP POST the moment Redditoro drafts a new lead, then pipe it into n8n, Zapier, Slack, a database, or your own service. This is a plain HTTP integration; you don't need MCP or any client to use it.
How it works
Redditoro scans Reddit for your keywords around the clock. Whenever a scan drafts a new lead for a project, we immediately send an HTTP POST to every active webhook URL on that project. The body is JSON and carries the full lead, so your system can act on it without calling our API back.
Webhooks are scoped per project and available on Growth and up. Each webhook has its own signing secret so you can verify that a request really came from us.
Add a webhook
- Switch to the project you want, then open MCP / API access in the dashboard.
- In the Webhooks card, paste your endpoint URL (must be
https://) and click Add. - Copy the signing secret shown once. Store it as an env var; you'll use it to verify every request. You can delete a webhook any time to stop delivery.
Events
| Event | Fires when |
|---|---|
lead.created | A scan drafts a new lead (a matched Reddit post/comment with a generated reply) for the project. |
More events (published, outcome changes) are on the roadmap. Ignore events you don't recognise so new ones never break your handler.
Payload
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Redditoro-Webhooks/1.0
X-Redditoro-Signature: sha256=3b8f...c1
{
"event": "lead.created",
"created_at": "2026-03-14T10:02:00.000Z",
"data": {
"subreddit": "SaaS",
"title": "Best tool to find leads on Reddit?",
"url": "https://www.reddit.com/r/SaaS/comments/abc123/best_tool/",
"leadScore": 4,
"product": "Redditoro",
"reply": "honestly just used redditoro for this, worked well even on..."
}
}Fields
| Field | Type | Description |
|---|---|---|
event | string | "lead.created" |
created_at | string | ISO 8601 timestamp of when we sent it |
data.subreddit | string | null | Subreddit the lead is in (no r/ prefix) |
data.title | string | Title of the Reddit thread |
data.url | string | Full permalink to the post or comment |
data.leadScore | number | Buying-intent score, 1 (cold) to 5 (hot) |
data.product | string | Which of your products it was matched to |
data.reply | string | The AI-drafted reply awaiting your review |
Verify the signature
Every request includes an X-Redditoro-Signature header of the form sha256=<hex>. It's an HMAC-SHA256 of the raw request body keyed with your webhook secret. Recompute it and compare in constant time; reject anything that doesn't match.
import crypto from "crypto";
// Express example. IMPORTANT: verify against the RAW request body, not a
// re-serialised object — re-stringifying changes bytes and breaks the check.
app.post("/hooks/redditoro", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.header("X-Redditoro-Signature") || "";
const expected =
"sha256=" + crypto.createHmac("sha256", process.env.REDDITORO_WEBHOOK_SECRET)
.update(req.body) // req.body is a Buffer (the raw bytes)
.digest("hex");
const ok =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) return res.status(401).send("bad signature");
const event = JSON.parse(req.body.toString());
// ...handle event.data, then:
res.sendStatus(200);
});Python:
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(header, expected)Delivery & retries
- One POST per new lead. A scan that finds several leads sends several requests.
- We wait up to 5 seconds for a response. Return a
2xxquickly and do slow work asynchronously, or you may time out. - Delivery is best-effort with no automatic retries yet: if your endpoint is down or slow, that event is missed. Treat webhooks as a fast notification, not a system of record, and reconcile with the
list_leadstool if you need guaranteed completeness. - Events may arrive out of order and, rarely, more than once. Make your handler idempotent (the post URL is a good dedupe key).
Testing locally
Point a webhook at webhook.site (or an ngrok tunnel to your machine) to inspect real payloads, then run a scan from the dashboard or the run_scan tool to trigger one.
Security checklist
- Only accept
https://endpoints and always verify the signature. - Compare signatures in constant time (
timingSafeEqual/compare_digest). - Keep the secret server-side; rotate it by deleting and re-adding the webhook.
- Don't trust the payload blindly; it's an AI-drafted reply you still review before publishing.