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.update or person.create events?
  • Why does Pipeline CRM webhook verification fail on retries or replays?
  • Why are my Pipeline CRM deliveries unsigned?

Verification (core)

HeaderValue
webhook-idUnique delivery ID, the same on every retry
webhook-timestampUnix seconds when this attempt was sent
webhook-signaturev1,<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_CRM_WEBHOOK_SECRET. 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_model}.{event_action}:

{ "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

EventFired when
deal.create / deal.update / deal.destroyA deal is created, changed (event_changes maps field to [old, new]) or deleted
person.create / person.update / person.destroyA person is created, changed or deleted
company.create / company.update / company.destroyA company is created, changed or deleted

Full details: references/overview.md and Pipeline CRM's API docs.

Delivery Semantics

  • Return any 2xx within 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. 3xx is not followed (register the final URL) and 410 Gone switches 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 6, 2026

View on GitHub →