agentsclimarketplace

Ascend webhooks

Skill hookdeck/webhook-skills/skills/ascend-webhooks

Receive and verify Ascend webhooks. Use when setting up Ascend webhook handlers, debugging Ascend signature verification (X-Ascend-Signature, HMAC-SHA256), or handling insurance payment events like invoice.paid, payout.paid, and refund.paid.From its SKILL.md

Install
npx -y skills add hookdeck/webhook-skills --skill ascend-webhooks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.9 KB, as published. Nobody here has run it

Ascend Webhooks

Ascend (insurance payments / premium financing) sends webhooks so your app is notified when an event happens — for example when an invoice is paid. Ascend POSTs a JSON payload over HTTPS and signs it with an HMAC-SHA256 signature you must verify before trusting the event.

When to Use This Skill

  • How do I receive Ascend webhooks?
  • How do I verify Ascend webhook signatures?
  • How do I parse the X-Ascend-Signature header?
  • How do I handle invoice.paid (or payout / refund) events?
  • Why is my Ascend webhook signature verification failing?

How Ascend Signs Webhooks

Ascend uses a custom Stripe-style HMAC-SHA256 scheme (not Svix, not Standard Webhooks). Two headers are sent:

HeaderExamplePurpose
X-Ascend-Signaturet=1696200697,v1=5257a869e7...Timestamp + HMAC signature
X-Ascend-Request-Timestamp1696200697Same Unix timestamp (redundant)

The signature is verified by:

  1. Parse X-Ascend-Signature into t (timestamp) and v1 (hex HMAC).
  2. Build the signed string as `${t}:${rawBody}` — the timestamp, a colon, then the raw request body.
  3. Compute HMAC-SHA256(signed_string, webhook_secret) and hex-encode it.
  4. Constant-time compare against v1.

Use the raw request body. Re-serializing the parsed JSON (key reordering, whitespace) changes the bytes and breaks the signature. There is no official Ascend SDK, so every framework below verifies manually.

Verification (core)

const crypto = require('crypto');

// Verify Ascend's "t=<timestamp>,v1=<hex>" signature over "<timestamp>:<rawBody>".
function verifyAscendSignature(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.split('=').map((s) => s.trim()))
  );
  const { t: timestamp, v1: signature } = parts;
  if (!timestamp || !signature) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}:${rawBody}`) // colon separator + RAW body
    .digest('hex');

  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature, 'hex'),
      Buffer.from(expected, 'hex')
    );
  } catch {
    return false; // length mismatch = invalid
  }
}

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

Event Payload Structure

Every event has the same top-level shape. Unlike Stripe, data is the resource object directly (there is no data.object wrapper):

{
  "id": "ajskljfaklsjd0912132",
  "type": "invoice.paid",
  "data": {
    "id": "684c8c8e-75eb-4134-925a-cb3a30f23633",
    "status": "paid",
    "payee": "John Doe Trucking",
    "payer_name": "John Doe",
    "total_amount_cents": 600000,
    "invoice_number": "II2DH1HGHJ",
    "paid_at": "2023-10-01T23:51:37.507Z"
  }
}

Common Event Types

EventTriggered When
invoice.createdAn invoice is created
invoice.processing_paymentAn invoice payment is being processed
invoice.paidAn invoice is paid
invoice.voidedAn invoice is voided
invoice.marked_overdueAn invoice is marked overdue
payout.payingA payout is being paid out
payout.paidA payout has been paid
payout.on_holdA payout is placed on hold
payout.canceledA payout is canceled
payout.failedA payout failed
refund.paidA refund has been paid
refund.cancelledA refund was cancelled

Always branch on the type field and handle unknown types gracefully. See references/overview.md for the full list and payloads.

Environment Variables

VariableDescription
ASCEND_WEBHOOK_SECRETThe webhook signing secret provided by Ascend

Setup

Ascend webhook registration is manual — there is no self-serve dashboard. Email [email protected] with your organization, environment (sandbox/production), the events you want, and your HTTPS endpoint URL. Ascend returns a webhook signing secret. See references/setup.md.

Local Development

For local webhook testing, run the Hookdeck CLI via npx — no install required:

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

No account required — the CLI creates a guest account on first run and provides a local tunnel + web UI for inspecting requests.

Reference Materials

Recommended: webhook-handler-patterns

Install webhook-handler-patterns alongside this skill for cross-cutting concerns:

Related Skills

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.