# Maxio Webhooks

## When to Use This Skill

* How do I receive Maxio (Chargify) webhooks?
* How do I verify the `X-Chargify-Webhook-Signature-Hmac-Sha-256` header?
* Why does my Maxio webhook signature verification fail?
* How do I parse Maxio's form-encoded `payload[...]=...&event=...` body?
* Handling `signup_success`, `payment_success`, `payment_failure`, `renewal_success`, `subscription_state_change`

## How Maxio Webhooks Differ

* The body is **`application/x-www-form-urlencoded`**, not JSON: `payload[...]=...&event=...`.
* The signature is the **hex HMAC-SHA256 of the raw form body**, keyed with the **Site Shared Key** used as a raw string (not decoded).
* Verify the **raw body first**, then parse the form. Parsing and re-encoding changes the bytes.
* There is no timestamp in the signature, so use idempotency to handle replays and retries.

## Verification (core)

```javascript
const crypto = require('crypto');

function verifyMaxio(rawBody, signatureHeader, sharedKey) {
  if (!signatureHeader || !sharedKey) return false;
  const expected = crypto.createHmac('sha256', sharedKey).update(rawBody).digest('hex');
  const provided = signatureHeader.trim().toLowerCase();
  if (provided.length !== expected.length) return false;
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
}
// Header: X-Chargify-Webhook-Signature-Hmac-Sha-256
// Docs test vector: key "123", body "payload[chargify]=testing&event=test"
//   -> 19826d51b9f866b26eda1f154de192593360f8d0bcb63df8a28540a5dcf733f1

```

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/maxio-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/maxio-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/maxio-webhooks/examples/fastapi/).

## Common Event Types

| Event | Triggered When |
| --- | --- |
| `signup_success` | A customer signs up for a subscription |
| `payment_success` | A payment succeeds |
| `payment_failure` | A payment attempt fails |
| `renewal_success` | A subscription renews |
| `subscription_state_change` | A subscription changes state |
| `test` | You send a test webhook from the Maxio UI |

The event name arrives in the form field `event`; the data is in nested `payload[...]` fields. See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/maxio-webhooks/references/overview.md).

## Environment Variables

```bash
# Site Shared Key from your Maxio site webhook settings (see references/setup.md)
MAXIO_SHARED_KEY=your_site_shared_key

```

## Local Development

```bash
npx hookdeck-cli listen 3000 maxio --path /webhooks/maxio

```

No account required: the CLI creates a guest account and gives you a tunnel and web UI.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/maxio-webhooks/references/overview.md) — Webhook concepts and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/maxio-webhooks/references/setup.md) — Dashboard configuration and shared key
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/maxio-webhooks/references/verification.md) — Signature details and gotchas