# AWS SNS Webhooks

## When to Use This Skill

* How do I receive AWS SNS messages at an HTTP/HTTPS endpoint?
* How do I confirm an SNS subscription (SubscriptionConfirmation / SubscribeURL)?
* How do I verify an SNS message signature?
* Why is my SNS signature verification failing?
* How do I handle SNS `Notification` and `UnsubscribeConfirmation` messages?

## How SNS Delivery Differs From HMAC Webhooks

SNS is not a Standard Webhooks / shared-secret HMAC provider. Instead:

* SNS POSTs a JSON envelope with `Content-Type: text/plain`. The
  `x-amz-sns-message-type` header tells you the type without parsing the body:
  `SubscriptionConfirmation`, `Notification`, or `UnsubscribeConfirmation`.
* Authenticity is proven with an RSA signature over specific envelope fields
  (not the raw body, and not an HMAC). You fetch AWS's public X.509
  certificate from `SigningCertURL` and RSA-verify the base64 `Signature`.
* New HTTP subscriptions require a handshake: the first message is a
  `SubscriptionConfirmation` — you must GET its `SubscribeURL` (or call
  `ConfirmSubscription` with `Token`) before SNS sends any notifications.

## Verification (core)

Node ships the AWS-official [`sns-validator`](https://www.npmjs.com/package/sns-validator)
(handles SigV1/SigV2, the `sns.*.amazonaws.com` cert-host check, cert fetch, and
RSA verify). Pass the parsed message object:

```javascript
const MessageValidator = require('sns-validator');
const validator = new MessageValidator(); // defaults enforce sns.<region>.amazonaws.com certs over HTTPS

// message = JSON.parse(rawBody). SNS signs specific envelope fields, not the raw body.
validator.validate(message, (err, msg) => {
  if (err) return res.status(400).send('Invalid signature');
  // msg is verified. Branch on msg.Type / the x-amz-sns-message-type header.
});

```

Python has no AWS webhook SDK — verify manually. Build the canonical string in
byte-sorted field order, one `Key\nValue\n` pair per field that is present
(`Message`, `MessageId`, `Subject`?, `Timestamp`, `TopicArn`, `Type` for a
Notification; add `SubscribeURL` and `Token` for a SubscriptionConfirmation),
then RSA-verify with the cert from `SigningCertURL` (SHA1 for `SignatureVersion`
1, SHA256 for 2). See [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-sns-webhooks/references/verification.md)
(includes the `UnsubscribeConfirmation` field-set nuance).

> For complete handlers with subscription confirmation, event dispatch, and tests, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/aws-sns-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/aws-sns-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/aws-sns-webhooks/examples/fastapi/)

## Message Types

SNS delivers three envelope types (read from the `x-amz-sns-message-type` header):

| `Type` | Sent when | What to do |
| --- | --- | --- |
| `SubscriptionConfirmation` | You subscribe an HTTP/S endpoint | GET the `SubscribeURL` to confirm |
| `Notification` | A message is published to the topic | Read `Subject` / `Message` and process |
| `UnsubscribeConfirmation` | The subscription is deleted | Verify; optionally re-subscribe if unexpected |

The application payload you care about is the `Message` string inside a
`Notification` (often itself JSON your publisher chose). SNS does not define
business event names — those live in your `Message` body.

> Full message formats: [Parsing message formats](https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html)

## Environment Variables

```bash
# Optional allowlist: reject messages whose TopicArn is not one you expect.
AWS_SNS_TOPIC_ARN=arn:aws:sns:us-east-1:123456789012:MyTopic

```

There is no signing secret — SNS signatures are verified with AWS's public
certificate, so no shared secret is configured. Restrict trust by validating the
`TopicArn` (and, optionally, the certificate host) instead.

## Local Development

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

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-sns-webhooks/references/overview.md) - SNS message types, envelope fields, raw delivery
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-sns-webhooks/references/setup.md) - Create a topic and HTTP/S subscription, confirm it
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-sns-webhooks/references/verification.md) - Signature verification, SDK + manual, gotchas