Zapier, Make and n8n (REST hooks)

ReadyBuyerFinder supports REST hooks: a tool subscribes a URL to an event, and ReadyBuyerFinder posts to that URL each time the event happens.

Zapier app listing: pending. A Zapier app definition exists, but it is not in Zapier's directory yet. Today, use Webhooks by Zapier → Catch Hook and subscribe its URL as shown below. Make (Custom webhook) and n8n (Webhook node) work the same way.

Events

EventWhen
lead.createdAn agent finds a new lead
lead.approvedA lead is approved (by you, auto mode or the MCP server)
reply.receivedA reply comes back from OutreachSender or ReachDak, with its label

Test the connection

curl -H "Authorization: Bearer $KEY" https://readybuyerfinder.com/api/v1/me

Returns { "workspace_id", "name", "plan" }.

Subscribe

curl -X POST https://readybuyerfinder.com/api/v1/hooks \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"target_url":"https://hooks.zapier.com/hooks/catch/123/abc/","event":"lead.approved"}'

Returns 201 { "id", "event", "target_url", "secret" }. Keep the secret: it signs every post. Up to 50 subscriptions per workspace.

List and unsubscribe

curl -H "Authorization: Bearer $KEY" https://readybuyerfinder.com/api/v1/hooks
curl -X DELETE -H "Authorization: Bearer $KEY" https://readybuyerfinder.com/api/v1/hooks/HOOK_ID

DELETE returns 204. If your URL answers 410 Gone, ReadyBuyerFinder removes the subscription by itself.

What we post

{ "type": "lead.approved", "created_at": "2026-10-24T05:30:00.000Z", "data": { "...": "the lead, as in the webhook payload" } }

Headers: Idempotency-Key and X-Surge-Signature: t=<unix>,v1=<hex>, where v1 is the HMAC-SHA256 of t + "." + body, keyed with the subscription's secret. Failed posts are retried like every other delivery.

Verify the signature (Node)

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyReadyBuyerFinder(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes
  const expected = createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Use the raw request body, exactly as received, not a re-serialised object.