Agent skill

DealHub Webhooks Skill

Receive and authenticate DealHub webhooks (DealHub CPQ / quote-to-revenue). Use when setting up a DealHub webhook handler, debugging the Authorization header DealHub sends (Token mode "Bearer <TOKEN>" or Basic mode "Basic <CREDENTIALS>"), or handling quote events like draftQuote, primaryQuote, quoteReady and quoteWon, plus version, user, productsUpload and DEALROOM_EVENT events. DealHub does not sign webhooks: there is no signature header or HMAC.

Install this skill

npx skills add hookdeck/webhook-skills --skill dealhub-webhooks


DealHub is a CPQ and quote-to-revenue platform. It sends an HTTP POST with a JSON body to your HTTPS endpoint when quotes, versions, users and DealRooms change.

When to Use This Skill

  • How do I receive DealHub webhooks?
  • How do I verify / authenticate a DealHub webhook?
  • Why is my DealHub webhook Authorization check failing?
  • How do I handle draftQuote, primaryQuote or quoteWon?
  • How do I tell version / user events apart (the action field)?
  • What does a productsUpload or DEALROOM_EVENT webhook look like?

No Signature: Authorization Header Only

DealHub does not sign webhook payloads. There is no signature header, no HMAC and no timestamp. Each webhook is configured with one of three Authentication Types (How to Configure Webhooks):

TypeWhat DealHub sends
TokenAuthorization: Bearer <TOKEN>
BasicAuthorization: Basic <CREDENTIALS>
NoneNo header. Only for endpoints without auth, or with a SAS token embedded in the URL

Caveat: the credential is the full Authorization header value, scheme included. Build the exact string you expect (Bearer <TOKEN> or Basic base64(username:password)) and compare the whole header in constant time. Don't strip the scheme and compare only the token.

Verification (core)

const crypto = require('crypto');

// Token mode: DealHub sends "Bearer <TOKEN>". Basic mode: "Basic <CREDENTIALS>".
const expected = process.env.DEALHUB_WEBHOOK_TOKEN
  ? `Bearer ${process.env.DEALHUB_WEBHOOK_TOKEN}`
  : `Basic ${Buffer.from(
      `${process.env.DEALHUB_WEBHOOK_USERNAME}:${process.env.DEALHUB_WEBHOOK_PASSWORD}`
    ).toString('base64')}`;

function verifyDealhubAuthorization(header, expected) {
  if (!header || !expected) return false;               // fail closed
  const a = Buffer.from(header, 'utf8');                 // the WHOLE header value
  const b = Buffer.from(expected, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b); // length guard first
}

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

DealHub has no SDK with a webhook verification helper, so every example does this compare directly (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). Authenticate before you parse the JSON body.

Common Event Types

The event name is the payload's event field.

EventTriggered when
draftQuoteA quote is created and saved as a draft, or a draft is modified
primaryQuoteA quote is marked Primary (no event is sent for the quote that lost the flag)
quotePendingApprovalA quote is submitted for approval, and as each approver approves
quoteReadyA quote is ready to share with the customer
quoteRejectedAn approver rejects the quote
quoteRecalledA user recalls (cancels) the quote
quotePublished / quoteUnpublishedThe quote is published to / unpublished from a DealRoom
quoteWonThe quote is signed
versionaction: created, activated, reactivated
useraction: created, modified, deleted
productsUploadAn SFTP or Open API product upload completes (upload_status: success / error)
crmImportA CRM Import request completes
DEALROOM_EVENTDealRoom activity; the key of action_summary (e.g. DEAL_SIGN) names it
Failure WebHook v1An async API request fails (documented payload has only error_code / error_description)

Every payload includes an event_info object (webhook_id, event_id, api_version, and request_id for async requests). Use event_info.event_id as your idempotency key. See references/overview.md for payloads.

Environment Variables

# Token mode: the token you entered in DealHub (sent as "Bearer <TOKEN>")
DEALHUB_WEBHOOK_TOKEN=your_shared_secret_token

# Basic mode: the username and password you entered in DealHub
DEALHUB_WEBHOOK_USERNAME=
DEALHUB_WEBHOOK_PASSWORD=

Set one mode. With neither set, the examples reject every request with a 500 rather than accept unauthenticated webhooks.

Local Development

npx hookdeck-cli listen 3000 dealhub --path /webhooks/dealhub

No account required — the CLI creates a guest account on first run and gives you a public HTTPS URL plus a web UI for inspecting requests (including the Authorization header). Use port 8000 for the FastAPI example.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →