Guides
Webhooks
Receive new funding signals at your endpoint the moment they happen, no polling.
Overview
A webhook is an HTTP POST that Fundup AI sends to a URL you control the moment a company raises funding, so the signal comes to you in real time instead of you polling for it.
The recommended pattern: webhook + endpoint
Let the webhook tell you when something happens, then call the REST API on demand only for the companies that matter, to pull key contacts, the full profile, tech stack, and more. Used this way you act on fresh signals instantly and stay comfortably within rate limits.
Set up a webhook
Webhooks are configured in the dashboard, not via the API. Go to Settings → Integrations → Webhooks → Connect and provide:
Configuration
- Endpoint URL: a public HTTP/HTTPS URL that accepts
POSTrequests. HTTPS is strongly recommended. - Authentication key: optional. Leave it blank and one is generated for you. It is sent on every request as
Authorization: Bearer <key>so you can verify the call really came from Fundup AI. - Test endpoint: sends a representative sample event so you can confirm connectivity before going live.
After connecting, choose which events you want to receive (below). You can change the auth key, the URL, and the event preferences at any time from the same screen.
Event types
Two notification preferences control what is delivered. Both send the same payload shape (event: "trigger_published"); they differ only in which companies trigger a call.
ICP Match Alerts
Default: ONFires when a newly funded company matches your ICP at 60% or above. The match score is included as userContext.icpMatch.
All Funding Alerts
Default: OFFFires for every new funding announcement, not just your ICP matches. For these, userContext.icpMatch is null.
Headers & delivery
Each event is delivered as a single JSON POST with these headers:
POST https://your-domain.com/webhook
Content-Type: application/json
User-Agent: Fundup-AI-Webhook/1.0
Authorization: Bearer YOUR_WEBHOOK_AUTH_KEY
Delivery behavior
- Success = any
2xxresponse (200,201, or202). Anything else is treated as a failed delivery. - Timeout: Fundup AI waits up to 10 seconds for a response, so acknowledge quickly and process asynchronously.
- Deduplicated: each funding event is delivered to you at most once. Still, key off
trigger.idon your side to stay idempotent. - Real time: events are sent shortly after a new round is detected, evaluated against recent funding activity.
Payload
Every event uses the trigger_published envelope. Here is a full funding example:
{
"event": "trigger_published",
"timestamp": "2026-05-12T09:31:00Z",
"trigger": {
"id": "trigger_789",
"type": "funding",
"subtype": "Series A",
"publishedAt": "2026-05-12T00:00:00Z",
"evidence": {
"fundingId": "funding_456",
"amount": "$15M",
"amountNumeric": 15000000,
"amountUsd": 15000000,
"currency": "USD",
"stage": "Series A",
"announceDate": "2026-05-12T00:00:00",
"investors": [
{ "name": "Sequoia Capital", "type": "lead" },
{ "name": "Andreessen Horowitz", "type": "participant" }
]
}
},
"company": {
"id": "company_123",
"name": "Sample Tech Corp",
"logo": "https://static.fundup.ai/logos/company_123.png",
"country": "United States",
"industry": "Fintech",
"description": "AI-powered payments infrastructure for SMBs.",
"tags": [ { "name": "SaaS", "slug": "saas" } ],
"websiteUrl": "https://example.com",
"techStack": ["React", "Postgres"],
"techStackCategories": ["Frontend", "Database"]
},
"allTriggers": [
{
"id": "trigger_789",
"type": "funding",
"subtype": "Series A",
"publishedAt": "2026-05-12T00:00:00Z"
}
],
"userContext": {
"icpMatch": 87,
"userNote": "",
"trackingStatus": null
}
}
Field reference
| Field | Type | Description |
|---|---|---|
| event | string | Always trigger_published. |
| timestamp | string | ISO-8601 UTC time the event was sent. |
| trigger.id | string | Unique trigger id, use it to deduplicate. |
| trigger.type | string | Signal type, e.g. funding. |
| trigger.subtype | string | null | For funding, the stage (e.g. Series A). |
| trigger.evidence | object | Signal detail. For funding: fundingId, amount, amountUsd, currency, stage, announceDate, investors[]. |
| company | object | Company summary: id, name, country, industry, tags, websiteUrl, techStack. |
| allTriggers | array | All currently active triggers for this company. |
| userContext.icpMatch | number | null | ICP score 0–100 for ICP alerts; null for All Funding alerts. |
| userContext.trackingStatus | string | null | Your saved status for the company, if any. |
The company.id and trigger.evidence.fundingId are the keys you pass to the REST API (e.g. GET /companies/{id} or /companies/{id}/contacts) to pull deeper detail on demand.
Verifying requests
If you set an authentication key, every request carries it as a bearer token. Reject anything that doesn't match, compare with a constant-time check.
app.post("/webhook", (req, res) => {
const auth = req.get("Authorization") || "";
const expected = `Bearer ${process.env.FUNDUP_WEBHOOK_KEY}`;
if (auth.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(auth), Buffer.from(expected))) {
return res.sendStatus(401);
}
res.sendStatus(200); // acknowledge fast (10s timeout)
const { event, company, trigger, userContext } = req.body;
// enqueue for async processing, then call the REST API for deeper detail:
// GET /api/v1/companies/{company.id}
// GET /api/v1/companies/{company.id}/contacts
});
Best practices
- Respond
2xximmediately, then process asynchronously. You have a 10-second window. - Verify the bearer token on every request and serve your endpoint over HTTPS.
- Deduplicate on
trigger.idso a re-delivery never double-processes. - Fetch detail on demand: use the webhook to know what changed, then call the API only for the companies you care about.