Agent skill

Pax8 Webhooks Skill

Receive and authenticate Pax8 webhooks. Use when setting up a Pax8 partner Webhooks API endpoint, checking the Authorization header Pax8 sends back, debugging 401s, or handling SUBSCRIPTION, INVOICE, CLIENT, PRODUCT, QUOTE and USER topic events such as SUBSCRIPTION CREATE.

Install this skill

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


Pax8 (cloud marketplace for MSPs) sends webhooks from its partner Webhooks API when events happen on the platform, grouped by topic (for example SUBSCRIPTION, INVOICE) and action (for example CREATE). Payloads are not signed: the docs say HMAC is "not yet" supported. The only credential is the optional Authorization value you set on the webhook, which Pax8 sends back "exactly as you inputed".

Covers partner Webhooks API webhooks only. Paste the Authorization value exactly as set in Pax8, including any "Bearer " or "Basic " prefix.

(Vendor Provisioner webhooks are a different feature with a Pax8-generated credential in a vendor-chosen header; this skill does not cover them.)

When to Use This Skill

  • How do I receive Pax8 webhooks?
  • How do I verify the Authorization header on a Pax8 webhook?
  • Does Pax8 sign webhook payloads? (No: static shared credential only)
  • Why does my Pax8 webhook endpoint return 401?
  • How do I handle SUBSCRIPTION, INVOICE or CLIENT events from Pax8?

Verification (core)

Set an authorization value when you create the webhook (Pax8 stores it encrypted). On every delivery Pax8 sends it back unchanged in the Authorization header. The key is the whole header value, including any Bearer or Basic prefix: do not strip, parse or base64-decode it.

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyPax8Request(headers, expected = process.env.PAX8_AUTHORIZATION) {
  if (!expected) return false;                // never accept when unconfigured
  const received = headers['authorization'];  // full value, e.g. "Bearer my_super_secret"
  return Boolean(received) && safeEqual(received, expected);
}

Return 401 when this fails (missing header or mismatch), 400 for a body that isn't a JSON object, and 200 once handled. Read the body raw (express.raw({ type: '*/*' })) and parse it only after the check. Python and Next.js equivalents are in references/verification.md.

Security limits: a static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS only (Pax8 rejects non-https URLs) and rotate the credential if it leaks (update it with POST /webhooks/{id}/configuration).

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

Topics and Actions

Topics and actions are defined by GET /webhooks/topic-definitions, the authoritative catalog (create calls are rejected for anything not in it).

CategoryExample events (display names from the docs)
SubscriptionSubscription Created, Updated, Cancelled, Commitment Term Updated, ...
InvoiceInvoice Created, Invoice Voided
ClientClient Created, Updated, Deleted
ProductProduct Created, Updated, Deleted
QuoteQuote Pending, Created, Updated, Expired, Deleted, Line Items Added, ...
UserUser Created, Deleted, Marked Active, Marked Inactive, Info Updated, ...

The docs publish these as display names, not wire values. The only topic identifiers they show are SUBSCRIPTION and PRODUCT, and the only action is CREATE; the examples assume INVOICE, CLIENT, QUOTE and USER for the other categories. Check the topic-definitions API for the exact topics and actions on your account. The body is not documented (the samplePayload is elided), so the examples treat any payload as illustrative and read topic and action defensively.

Delivery Behavior

  • POST with a JSON body; can lag up to 5 minutes after the event
  • A response not received within 10 seconds counts as failed
  • Up to 3 attempts (initial + 2 automatic retries), then log status FAILED. The docs give the back-off as "10 s → 30 s → 60 s", three intervals for two retries, so the exact spacing is unclear
  • One email alert after errorThreshold consecutive failures (default 10, max 20)
  • Send a synthetic event with POST /webhooks/{id}/topics/{topic}/test

Answer within 10 seconds and make handlers idempotent, as the docs give no event id.

Environment Variables

PAX8_AUTHORIZATION=Bearer my_super_secret   # the full value you set as "authorization" in Pax8

Local Development

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

No account required. Use the printed URL as the webhook url.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 9, 2026

View on GitHub →