Webhook events reference
Every event Funkel can send, payload shape, and when each one fires.
Funkel can push events to any HTTPS endpoint you control. Each event is an HTTP POST with a JSON body and a signed header. This page is the canonical list of events and their payloads.
Envelope
Every event shares the same outer shape:
{
"event": "lead.discovered",
"timestamp": "2026-05-04T12:34:56Z",
"data": { /* event-specific fields */ }
}Headers
Content-Type: application/jsonX-LeadPilot-Event:the event name (e.g.lead.discovered)X-LeadPilot-Signature:sha256=<hex>, see Verifying webhook signatures
Note · The signature header still uses the legacy LeadPilot prefix while we finish the rename to Funkel. Match on the header name; don’t hard-code “Funkel” in a prefix check.
Events
lead.discovered
An agent has found a lead matching one of its signals.
{
"lead_id": "uuid",
"agent_id": "uuid",
"agent_name": "Founders posting about Outreach",
"first_name": "Mira",
"last_name": "Jensen",
"company": "Acme",
"headline": "Head of GTM at Acme",
"profile_url": "https://www.linkedin.com/in/mira-jensen",
"signal_key": "competitor_post_engagement"
}lead.approved
A discovered lead has been approved into a campaign (manual or auto).
{
"lead_id": "uuid",
"campaign_id": "uuid",
"approved_at": "2026-05-04T12:34:56Z"
}action.executed
A scheduled workflow action (invite, message, follow-up) has been sent.
{
"action_id": "uuid",
"action_type": "message",
"lead_id": "uuid",
"campaign_id": "uuid"
}reply.received
A lead has replied to one of your campaigns. Fires once per lead, on the first reply.
{
"lead_id": "uuid",
"campaign_id": "uuid",
"conversation_id": "uuid",
"first_name": "Mira",
"last_name": "Jensen",
"message_preview": "Sure, 15 min Thursday works…"
}message.received
An incoming message landed on a connected LinkedIn sender (any message, including from leads not in your campaigns).
{
"conversation_id": "uuid",
"lead_id": "uuid | null",
"campaign_id": "uuid | null",
"sender_name": "Mira Jensen",
"sender_provider_id": "ACoAA...",
"message": "...",
"is_first_reply": true
}campaign.completed
Every lead in a campaign has reached a terminal state (replied, exhausted workflow, or canceled).
campaign.paused
A campaign has been paused, manually or by the system.
Delivery and retries
Webhooks are best-effort. Funkel attempts delivery once with a 10-second timeout. Non-2xx responses are logged but not retried in v1. Make your endpoint idempotent, process by data.lead_id or data.action_id, and you’ll be safe even if delivery is occasionally repeated in future versions.