# Mailchimp Webhooks

## When to Use This Skill

* Setting up Mailchimp webhook handlers
* How do I respond to Mailchimp's webhook URL validation (the GET request)?
* How do I secure Mailchimp webhooks (they are not HMAC-signed)?
* Handling audience events: subscribe, unsubscribe, profile, upemail, cleaned, campaign
* Parsing Mailchimp's `application/x-www-form-urlencoded` payloads

## Verification (core)

Mailchimp does NOT sign its webhooks — there is no HMAC and no signature header. You secure the endpoint two ways, both described in Mailchimp's [sync audience data with webhooks](https://mailchimp.com/developer/marketing/guides/sync-audience-data-webhooks/) guide:

1. URL validation (GET): When you save a webhook, Mailchimp sends a `GET` to the URL to confirm it is reachable. Respond `200` — do not require the secret on GET.
2. Shared secret (POST): Put an unguessable secret in the webhook URL's query string (e.g. `https://your.app/webhooks/mailchimp?secret=…`) and compare it on every `POST` with a timing-safe comparison. Always serve the endpoint over HTTPS.

Payloads are `application/x-www-form-urlencoded` with a top-level `type` field and `data[...]` fields.

Node:

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

// Timing-safe compare of the ?secret= query param against your stored secret.
function verifyMailchimpSecret(provided, expected) {
  if (!provided || !expected) return false;
  const a = Buffer.from(provided);
  const b = Buffer.from(expected);
  if (a.length !== b.length) return false;      // avoid throw on length mismatch
  return crypto.timingSafeEqual(a, b);
}

```

Python:

```python
import hmac

# Timing-safe compare of the ?secret= query param against your stored secret.
def verify_mailchimp_secret(provided: str, expected: str) -> bool:
    if not provided or not expected:
        return False
    return hmac.compare_digest(provided, expected)

```

> For complete handlers with GET validation, form parsing, event dispatch, and tests, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/mailchimp-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/mailchimp-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/mailchimp-webhooks/examples/fastapi/)

## Common Event Types

Dispatch on the top-level `type` field.

| `type` | Triggered When | Key `data` fields |
| --- | --- | --- |
| `subscribe` | A contact joins the audience | `id`, `list_id`, `email`, `email_type`, `merges`, `ip_opt`, `ip_signup` |
| `unsubscribe` | A contact leaves the audience | `action` (`unsub`/`delete`), `reason` (`manual`/`abuse`), `id`, `list_id`, `email`, `campaign_id` |
| `profile` | A contact updates their profile | `id`, `list_id`, `email`, `email_type`, `merges`, `ip_opt` |
| `upemail` | A contact changes their email address | `list_id`, `new_id`, `new_email`, `old_email` |
| `cleaned` | An address is cleaned (bounces/spam) | `list_id`, `campaign_id`, `reason` (`hard`/`abuse`), `email` |
| `campaign` | A campaign finishes sending | `id`, `subject`, `status`, `reason`, `list_id` |

> For full event reference, see [Mailchimp's webhook guide](https://mailchimp.com/developer/marketing/guides/sync-audience-data-webhooks/).

## Environment Variables

```bash
# The unguessable secret you append to your webhook URL query string.
# Register the URL as: https://your.app/webhooks/mailchimp?secret=<this value>
MAILCHIMP_WEBHOOK_SECRET=a-long-random-hard-to-guess-string

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 mailchimp --path /webhooks/mailchimp

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mailchimp-webhooks/references/overview.md) - Mailchimp webhook concepts and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mailchimp-webhooks/references/setup.md) - Dashboard configuration and getting the secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/mailchimp-webhooks/references/verification.md) - Securing the endpoint (no signature)