Agent skill
Pipeline CRM Webhooks Skill
Receive and verify Pipeline CRM (pipelinecrm.com) webhooks sent by automations' "Call an API" action. Use when setting up a Pipeline CRM webhook endpoint, enabling Request signing and getting the whsec_ secret, debugging webhook-id / webhook-timestamp / webhook-signature (Standard Webhooks) verification, or handling deal.create, deal.update, deal.destroy, person.create, person.update and company.create events.
Install this skill
npx skills add hookdeck/webhook-skills --skill pipeline-crm-webhooks
Pipeline CRM sends a webhook when a deal, person or company is created, updated or deleted. A webhook is an automation with a Call an API action (or a subscription created through the API): a POST of a JSON payload to your URL. Deliveries can be signed with Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). Signing is opt-in per endpoint.
When to Use This Skill
- How do I receive Pipeline CRM webhooks?
- How do I verify a Pipeline CRM webhook signature?
- How do I enable Request signing and get the
whsec_secret in Pipeline CRM? - How do I handle
deal.create,deal.updateorperson.createevents? - Why does Pipeline CRM webhook verification fail on retries or replays?
- Why are my Pipeline CRM deliveries unsigned?
Verification (core)
| Header | Value |
|---|---|
webhook-id | Unique delivery ID, the same on every retry |
webhook-timestamp | Unix seconds when this attempt was sent |
webhook-signature | v1,<base64 HMAC-SHA256>, space-separated when two secrets are active (rotation) |
Signed content is {webhook-id}.{webhook-timestamp}.{raw body}. The key is the base64-decoded part of the secret after whsec_. The examples use the standardwebhooks library (npm, PyPI), which compares in constant time against every v1, entry:
const { Webhook } = require('standardwebhooks');
// rawBody: exact request body string, NOT re-serialised JSON
function verifyPipelineCrmWebhook(rawBody, headers, secret = process.env.PIPELINE_CRM_WEBHOOK_SECRET) {
return new Webhook(secret).verify(rawBody, {
'webhook-id': headers['webhook-id'],
'webhook-timestamp': headers['webhook-timestamp'],
'webhook-signature': headers['webhook-signature'],
}); // returns the parsed event; throws → respond 400
}
Python: from standardwebhooks.webhooks import Webhook, then Webhook(secret).verify(await request.body(), headers).
Hookdeck retries and replays hit the 5-minute window
standardwebhooks (and svix) always enforce a fixed 5-minute timestamp window. Hookdeck retries and replays resend the original request, including the original webhook-timestamp, so the library rejects any retry or replay that arrives more than 5 minutes after Pipeline CRM sent it. If you receive through Hookdeck, or replay old events, use the dependency-free verifier in references/verification.md and drop or widen the window there. Pipeline CRM does not enforce the window for you either, so dedupe on webhook-id instead.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Setup: signing is opt-in
Signing is opt-in: click Enable signing under Request signing on the automation's Call an API action and paste the secret (shown once) into PIPELINE_. Without it, deliveries are unsigned and fail verification (the handlers return 400 for missing signature headers). Steps and rotation are in references/setup.md.
Event Payload
There is no event type string. The body carries event_model (deal, person, company) and event_action (create, update, destroy). Dispatch on {event_:
{ "trigger": "Deal stage changed", "timestamp": "2026-09-03 14:22:07",
"event_model": "deal", "event_action": "update", "event_model_id": 4521,
"event_data": { "id": 4521, "name": "Acme Corporation - Q4 renewal", "deal_stage_id": 4 },
"event_changes": { "deal_stage_id": [3, 4] },
"caused_by_user": { "id": 13, "email": "sam.okafor@northwind.example" } }
Common Event Types
| Event | Fired when |
|---|---|
deal.create / deal.update / deal.destroy | A deal is created, changed (event_changes maps field to [old, new]) or deleted |
person.create / person.update / person.destroy | A person is created, changed or deleted |
company.create / company.update / company.destroy | A company is created, changed or deleted |
Full details: references/overview.md and Pipeline CRM's API docs.
Delivery Semantics
- Return any
2xxwithin a few seconds; the response body is ignored. Enqueue and process asynchronously. - Failures retry with increasing delays, up to 16 attempts over about a day.
3xxis not followed (register the final URL) and410 Goneswitches the endpoint off. - Every attempt of a delivery carries the same
webhook-id; deliveries can arrive out of order, so read the record back from the API where order matters.
Environment Variables
PIPELINE_CRM_WEBHOOK_SECRET=whsec_xxxxx # shown once when you click Enable signing; handlers fail closed (500) without it
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 pipeline-crm --path /webhooks/pipeline-crm
The automation can only be saved after Test reaches your URL and gets a 2xx, so start the receiver first.
Reference Materials
- references/overview.md - Events, payload fields, delivery and retries
- references/setup.md - Automation setup, Enable signing, rotation, Hookdeck
- references/verification.md - Library and manual verification, replay window, debugging