# Mighty Networks Webhooks

Mighty Networks sends an HTTP POST with a JSON body when something happens in your Network: a
member joins or leaves, buys a plan, a post or comment is created, an RSVP changes. Webhooks are
**not signed**. You configure an API key with the webhook in Network settings, and every request
carries it as `Authorization: Bearer <your API key>`. Your endpoint authenticates the request by
comparing that header with the key you stored.

A static shared credential authenticates the sender, not the body (it can't show the body is
untouched), and it gives no replay protection (there is no signature and no timestamp). Serve the endpoint over
HTTPS, rotate the API key if it leaks, and dedupe on `event_id`.

## When to Use This Skill

* How do I receive Mighty Networks webhooks?
* How do I verify the `Authorization: Bearer` header on a Mighty Networks webhook?
* Does Mighty Networks sign webhook payloads? (No: use the configured API key)
* How do I handle `MemberJoined`, `MemberPurchased`, `PostCreated` or `RsvpCreated` events?
* Why does `event_type` say `PostCreatedHook` when the docs say `PostCreated`?

## Verification (core)

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

const sha256 = (s) => crypto.createHash('sha256').update(s, 'utf8').digest();
const hasBearer = (v) => typeof v === 'string' && /^bearer /i.test(v);

function verifyMightyNetworksRequest(headers, secret = process.env.MIGHTY_NETWORKS_WEBHOOK_SECRET) {
  if (!secret) return false;                             // never accept when unconfigured
  const expected = hasBearer(secret) ? secret.slice(7) : secret;  // tolerate a stored "Bearer ..."
  if (!expected) return false; // a configured value of just "Bearer " must not match
  const auth = headers['authorization'];
  if (!expected || !hasBearer(auth)) return false;       // missing header or wrong scheme
  // Hash both sides: equal-length buffers, so timingSafeEqual never throws.
  return crypto.timingSafeEqual(sha256(auth.slice(7)), sha256(expected));
}

```

* Header: `Authorization: Bearer <API key>`. The scheme matches in any case (RFC 9110), the key
  after it is compared exactly: don't trim or alter it.
* Store only the **bare key** in `MIGHTY_NETWORKS_WEBHOOK_SECRET`. If the stored value itself
  starts with `Bearer ` (case-insensitive), strip it, as Hookdeck does.
* Missing header, wrong scheme or mismatch: `401`. A body that isn't a JSON object: `400`.
  Handled: `200`. Any 2xx within 10 seconds counts as delivered.
* Read the body raw (`express.raw({ type: '*/*' })`) and authenticate before parsing it.

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

## Payload and Event Types

```json
{
  "event_id": "abc123-uuid",
  "event_timestamp": "2024-01-15T10:30:00Z",
  "event_type": "PostCreatedHook",
  "payload": { "id": 12345, "title": "Example post title", "space_id": 111 }
}

```

`event_type` carries the event name with a **`Hook` suffix** (`PostCreatedHook`), while the
reference pages name events without it (`PostCreated`). Strip a trailing `Hook` before
dispatching. Dedupe on `event_id`: a non-2xx response is retried up to 5 times over several hours.

| Event | Sent When |
| --- | --- |
| `MemberJoined` | A member joins a space |
| `MemberLeft` | A member leaves a space |
| `MemberPurchased` | A member purchases a plan |
| `MemberPurchaseRequested` | A member requests to purchase a plan |
| `MemberSubscriptionRenewed` | A member's subscription is renewed |
| `MemberSubscriptionCanceled` | A member cancels their subscription |
| `MemberPlanChanged` | A member upgrades or downgrades their plan |
| `MemberRemovedFromPlan` | A member is removed from a plan |
| `PostCreated` | A post is created in a space |
| `PostUpdated` | A post is updated |
| `CommentCreated` | A comment is created on a post or other commentable content |
| `RsvpCreated` | An RSVP is created for an event |
| `EventCreated` | An event is scheduled in a space |
| `EventCanceled` | An event is canceled |
| `ReportedContentCreated` | A post, comment or chat is reported |

[references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mighty-networks-webhooks/references/overview.md) lists all 48 events.

## Environment Variables

```bash
MIGHTY_NETWORKS_WEBHOOK_SECRET=your-api-key   # the API key you configured with the webhook

```

## Setup in Hookdeck

Hookdeck's Mighty Networks source (`MIGHTY_NETWORKS`) checks the `Authorization` header for you.
Paste the API key you configured with the webhook in Mighty Networks.

## Local Development

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

```bash
npx hookdeck-cli listen 3000 mighty-networks --path /webhooks/mighty-networks

```

Use the printed URL as the webhook URL in your Network settings.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mighty-networks-webhooks/references/overview.md) - What Mighty Networks webhooks are, events and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mighty-networks-webhooks/references/setup.md) - Configure the webhook and API key
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mighty-networks-webhooks/references/verification.md) - Bearer key check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/mighty-networks-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests