# Amazon EventBridge Webhooks

Amazon EventBridge delivers events to an HTTPS endpoint through an **API destination**: a rule or
pipe target that calls your URL with a Connection's credentials. This skill covers Connections that
use **Basic** authorization, where EventBridge populates the `Authorization` header. API Key and
OAuth Connections are not covered.

Requests are **not signed**. There is no signature header and no timestamp. The only authentication
is the `Authorization: Basic base64(username:password)` header, and your endpoint compares it with
the credentials stored in the Connection.

> **A static shared credential authenticates the sender, not the body.** It gives no replay
> protection (no signature, no timestamp). Serve the endpoint over HTTPS only, make handlers
> idempotent, and rotate the Connection credentials if they leak.

## When to Use This Skill

* How do I receive Amazon EventBridge events at my own HTTP endpoint?
* How do I verify the Basic `Authorization` header from an EventBridge API destination?
* Does EventBridge sign webhook payloads? (No: use the Connection's Basic credentials)
* How do I handle `EC2 Instance State-change Notification` or my own `OrderCreated` events?
* Why does my EventBridge API destination get 401s and keep retrying?

## Verification (core)

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyAwsEventbridgeRequest(headers, user = process.env.AWS_EVENTBRIDGE_WEBHOOK_USERNAME,
                                     pass = process.env.AWS_EVENTBRIDGE_WEBHOOK_PASSWORD) {
  if (!user || !pass) return false;                        // never accept when unconfigured
  const m = /^basic +([A-Za-z0-9+/]+={0,2})$/i.exec((headers['authorization'] || '').trim());
  if (!m) return false;
  const decoded = Buffer.from(m[1], 'base64').toString('utf8');
  const i = decoded.indexOf(':');                          // split on the FIRST colon
  if (i === -1) return false;
  const userOk = safeEqual(decoded.slice(0, i), user);     // evaluate both before deciding
  const passOk = safeEqual(decoded.slice(i + 1), pass);
  return userOk && passOk;
}

```

* Header: `Authorization: Basic base64(username:password)`. The scheme is case-insensitive.
* Return `401` for a missing or malformed header or a mismatch.
* Accept **POST, PUT, PATCH, GET and DELETE**: the API destination's HTTP method is configurable
  (anything except CONNECT and TRACE). Handle an empty body, for example on GET.
* Read the body raw (`express.raw({ type: '*/*' })`) and parse JSON only after the credential check.
* Python: `hmac.compare_digest` on bytes (encode both sides; passing `str` raises on non-ASCII).

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

## Event Types

Without an input transformer the body is the event envelope (`version`, `id`, `detail-type`,
`source`, `account`, `time`, `region`, `resources`, `detail`). Dispatch on `source` + `detail-type`:

| `source` | `detail-type` | Sent When |
| --- | --- | --- |
| `aws.ec2` | `EC2 Instance State-change Notification` | An EC2 instance changes state |
| `aws.s3` | `Object Created` | An object is created in a bucket with EventBridge notifications on |
| `com.example.orders` | `OrderCreated` | Your own application puts a custom event (placeholder source) |

These are examples: the rule's event pattern decides which events reach the endpoint. With an
input transformer the body is whatever the template builds, so it may not be an envelope at all.

## Environment Variables

```bash
AWS_EVENTBRIDGE_WEBHOOK_USERNAME=your-connection-username   # Connection > Basic > Username
AWS_EVENTBRIDGE_WEBHOOK_PASSWORD=your-connection-password   # Connection > Basic > Password

```

## Local Development

EventBridge can't reach `localhost` and requires a public HTTPS endpoint. Tunnel with the Hookdeck
CLI (no account required):

```bash
npx hookdeck-cli listen 3000 aws-eventbridge --path /webhooks/aws-eventbridge

```

Use the printed URL as the API destination endpoint.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-eventbridge-webhooks/references/overview.md) - API destinations, envelope fields, request headers, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-eventbridge-webhooks/references/setup.md) - Create the Connection, API destination and rule
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/aws-eventbridge-webhooks/references/verification.md) - Basic auth check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/aws-eventbridge-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests