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,primaryQuoteorquoteWon? - How do I tell
version/userevents apart (theactionfield)? - What does a
productsUploadorDEALROOM_EVENTwebhook 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):
| Type | What DealHub sends |
|---|---|
| Token | Authorization: Bearer <TOKEN> |
| Basic | Authorization: Basic <CREDENTIALS> |
| None | No 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.
| Event | Triggered when |
|---|---|
draftQuote | A quote is created and saved as a draft, or a draft is modified |
primaryQuote | A quote is marked Primary (no event is sent for the quote that lost the flag) |
quotePendingApproval | A quote is submitted for approval, and as each approver approves |
quoteReady | A quote is ready to share with the customer |
quoteRejected | An approver rejects the quote |
quoteRecalled | A user recalls (cancels) the quote |
quotePublished / quoteUnpublished | The quote is published to / unpublished from a DealRoom |
quoteWon | The quote is signed |
version | action: created, activated, reactivated |
user | action: created, modified, deleted |
productsUpload | An SFTP or Open API product upload completes (upload_status: success / error) |
crmImport | A CRM Import request completes |
DEALROOM_EVENT | DealRoom activity; the key of action_summary (e.g. DEAL_SIGN) names it |
Failure WebHook v1 | An 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
- references/overview.md — Events,
actionfields, payload structure,event_info - references/setup.md — Configuring webhooks in DealHub admin, choosing an Authentication Type
- references/verification.md — The Authorization header in detail, the full-value caveat, debugging failures