Connect FormDSignal to your CRM
Every morning's leads can be pushed straight into the system your team works in, so nobody copies from an email. You set one URL on your account page, we POST the day's matching leads there as JSON at the same time as the digest, and you map the fields once. Pro and API plans; about ten minutes with Zapier or Make, no developer needed.
- Get a URL that accepts POST requests from your CRM or automation tool (each section below shows where).
- Paste it into Account → Delivery → Webhook URL, click Save delivery settings, then Send test. A sample lead arrives in your tool within a few seconds.
- In your tool, map the fields you want (name, score, headline, phone, link) to your CRM's fields, using the test payload as the example. From the next morning, real leads flow automatically.
Zapier (HubSpot, Salesforce, Pipedrive, Close, Monday, Airtable, Slack…)
- In Zapier, create a Zap. Trigger: Webhooks by Zapier → Catch Hook. Copy the webhook URL it shows.
- Paste it into Account → Delivery → Webhook URL, save, click Send test. Back in Zapier click Test trigger; the sample lead appears.
- Add a Looping by Zapier step (Create Loop from Line Items) over
leadsso each lead becomes its own CRM record. Inside the loop add your CRM action, e.g. HubSpot → Create Company or Salesforce → Create Lead, and mapentityName→ name,headline→ description,score→ a custom "Lead score" property,contact.phone→ phone,url→ website or a notes field. - Add a filter before the action if you only want some leads, e.g.
score≥ 70. Turn the Zap on.
Executive changes and adviser registrations arrive in the same payload under execChanges and advisers; add a loop for each if you subscribe to those verticals.
Make (Integromat)
- Create a scenario starting with Webhooks → Custom webhook, click Add, copy the URL.
- Paste it into Account → Delivery, save, click Send test while Make shows "waiting for data" so it learns the structure.
- Add an Iterator over
leads[], then your CRM module (HubSpot CRM → Create a Company, Salesforce → Create a Record, Pipedrive → Create an Organization). Map the fields as in the Zapier section. Schedule: "immediately as data arrives".
HubSpot
HubSpot does not accept arbitrary inbound webhooks on its own, so use Zapier or Make as above (five minutes), or, on Operations Hub Professional, a Workflow → Custom code action behind a Zapier catch hook. Recommended object mapping: Form D fund launches and company raises → Companies (name, phone, city, state, a custom "FormDSignal score" number property and "FormDSignal link" URL property); the relatedPersons array → Contacts associated to the company; executive changes → a Deal or a Task on the company; adviser registrations → Companies with the CRD as a custom property.
Salesforce
Simplest: Zapier or Make with Salesforce → Create Lead (Company = entityName, Description = headline + summary, Phone = contact.phone, Lead Source = "FormDSignal", a custom Score__c number field). If you have a developer, an Apex REST endpoint can receive the payload directly; verify the signature as shown under "Your own endpoint" and upsert on accessionNo as the external id so a re-send never duplicates.
Pipedrive
Zapier or Make with Pipedrive → Create Organization then Create Person for each of relatedPersons, and optionally Create Deal titled with the headline. Put the score in a custom numeric field on the organization so you can sort the pipeline by it.
Google Sheets
Zapier: Webhooks → Catch Hook, then Looping by Zapier over leads, then Google Sheets → Create Spreadsheet Row with one column per field. Make: Custom webhook → Iterator → Google Sheets → Add a Row. Or skip the webhook entirely: the account page has a CSV download of the last 30 days, and the API returns CSV for a Sheets IMPORTDATA formula on the API plan.
Your own endpoint
We POST one JSON body per digest with Content-Type: application/json, a User-Agent of FormDSignal-Webhook/1, an X-FormDSignal-Event header (daily, weekly, sample or test) and a signature header. Respond with any 2xx within 10 seconds; do your processing asynchronously. Failures are logged and the records appear again in the next digest, so treat accessionNo (Form D and 8-K) and crd + regDate (advisers) as idempotency keys.
Payload
{
"kind": "daily",
"sentAt": "2026-10-27T12:30:04.112Z",
"account": "[email protected]",
"count": 2,
"leads": [
{
"accessionNo": "0002156237-26-000001",
"filedDate": "2026-10-24",
"kind": "fund_launch",
"kindLabel": "New fund",
"score": 88,
"entityName": "Harbor Credit Fund II LP",
"sponsorName": "Harbor Capital",
"headline": "Harbor Capital files a $400M private credit fund",
"summary": "Original Form D for a $400M 3(c)(7) private credit vehicle; first sale has not yet occurred.",
"strategy": "private_credit",
"stage": "pre_first_close",
"totalOffering": "$400M",
"location": "New York, NY",
"state": "NY",
"flags": [
"3c7_institutional",
"pre_first_close"
],
"personas": [
"fund_admin",
"audit_tax"
],
"relatedPersons": [
{
"name": "Jane Doe",
"title": "Managing Partner"
}
],
"whyNow": [
{
"persona": "fund_admin",
"hook": "Pre-first-close 3(c)(7) credit fund with no administrator on record."
}
],
"contact": {
"phone": "212-555-0100",
"street1": "1 Example Plaza",
"city": "New York",
"state": "NY",
"zip": "10001"
},
"edgarUrl": "https://www.sec.gov/Archives/edgar/data/2156237/000215623726000001/",
"url": "https://www.formdsignal.com/filings/0002156237-26-000001"
}
],
"execChanges": [
{
"accessionNo": "0001193125-26-250001",
"filedDate": "2026-10-24",
"companyName": "Example Corp",
"ticker": "EXMP",
"score": 85,
"vacancy": true,
"topRole": "CFO",
"topEventType": "departure",
"topPerson": "John Roe",
"headline": "Example Corp (EXMP): CFO John Roe resigned; interim named",
"url": "https://www.formdsignal.com/exec-moves/0001193125-26-250001"
}
],
"advisers": []
}Verify the signature
Header: X-FormDSignal-Signature: t=<unix seconds>,v1=<hex> where v1 = HMAC-SHA256(secret, t + "." + raw body). The secret is on your account page under Delivery ("Create secret" the first time; "Regenerate" rotates it). Reject if t is older than five minutes.
// Node (Express) — use the raw body, not the parsed JSON
import { createHmac, timingSafeEqual } from "node:crypto"
app.post("/formdsignal", express.raw({ type: "application/json" }), (req, res) => {
const [, t, v1] = /t=(\d+),v1=([0-9a-f]+)/.exec(req.get("X-FormDSignal-Signature") || "") || []
const expected = createHmac("sha256", process.env.FORMDSIGNAL_SECRET).update(`${t}.${req.body}`).digest("hex")
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300
if (!fresh || !v1 || !timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) return res.status(401).end()
const payload = JSON.parse(req.body)
res.status(200).end() // acknowledge first…
queue.add(payload) // …then process
})# Python (Flask)
import hmac, hashlib, time, re
from flask import request, abort
@app.post("/formdsignal")
def hook():
m = re.match(r"t=(\d+),v1=([0-9a-f]+)", request.headers.get("X-FormDSignal-Signature", ""))
if not m or abs(time.time() - int(m[1])) > 300: abort(401)
expected = hmac.new(SECRET.encode(), f"{m[1]}.".encode() + request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, m[2]): abort(401)
payload = request.get_json()
return "", 200Common questions
Nothing arrived after "Send test". The page tells you the HTTP status your endpoint returned. 404 means the URL is wrong; 401/403 means your tool expects a token in the URL (Zapier and Make do not); a timeout means the endpoint is slow or private (localhost and internal IPs are rejected). Can I get only high-score leads? Yes: set the score floor and filters on the account page; the webhook sends exactly what the digest sends. Can I have both email and webhook? Yes, always both. Multiple destinations? One URL per account; fan out inside Zapier or Make. Questions: [email protected].