Agent skill

Partnero Webhooks Skill

Receive and verify Partnero webhooks. Use when setting up a Partnero outgoing webhook endpoint, debugging `Signature` header verification failures, or handling affiliate and referral program events like partner.created, transaction.created and reward.approved.

Install this skill

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


Partnero (affiliate, referral and partner program platform) sends an HTTP POST with a JSON body to your endpoint when partners, customers, transactions or rewards change. Every request carries a Signature header: the hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret.

When to Use This Skill

  • How do I receive Partnero webhooks?
  • How do I verify the Partnero Signature header?
  • Why does my Partnero webhook signature verification fail?
  • How do I handle partner.created, transaction.created or reward.approved events?
  • How do I test Partnero outgoing webhooks locally?

Verification (core)

  • Header: Signature (literally that name). Value: bare lowercase hex digest, no prefix.
  • Signed content: the raw body bytes only (no timestamp, no id).
  • Key: the webhook's signing secret as UTF-8 bytes. Compare the whole header value as one digest.
const crypto = require('crypto');

function verifyPartneroSignature(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret) return false;
  if (!/^[0-9a-f]{64}$/i.test(signatureHeader)) return false; // check shape before comparing
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader.toLowerCase()),
    Buffer.from(expected)
  );
}
  • Missing, malformed or mismatched signature: 401. Bad JSON after a valid signature: 400. Otherwise 200.
  • Read the body raw (express.raw({ type: '*/*' }), request.arrayBuffer(), await request.body()) and parse JSON only after the check. Partnero signs PHP json_encode output (escaped \/ and \uXXXX), so JSON.stringify(req.body) never matches.
  • In Python use hmac.compare_digest on bytes; str raises TypeError on non-ASCII input.
  • No timestamp is signed, so there is no replay window to check. Deduplicate on your own keys.

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

Event Types

Body: {event, url, webhook_key, created_at, data}. Dispatch on event; data holds the event-specific fields. No header names the event type.

EventSent When
partner.createdA new partner signs up or is created
partner.updatedPartner information is updated
partner.approvedA partner application is approved
partner.rejectedA partner application is rejected
partner.archivedA partner is archived
partner.deletedA partner is deleted
customer.createdA new customer is created
customer.updatedCustomer information is updated
customer.deletedA customer is deleted
transaction.createdA new transaction is recorded
transaction.updatedA transaction is updated
transaction.deletedA transaction is deleted (refund)
reward.createdA commission/reward is created
reward.approvedA reward is approved for payout
reward.paidA reward is marked as paid

Environment Variables

PARTNERO_WEBHOOK_SECRET=your_webhook_signing_secret  # shown when you create the webhook

Local Development

Partnero can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

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

Use the printed URL as the webhook URL in Partnero (Program Settings > Webhooks).

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →