Agent skill

Amazon EventBridge Webhooks Skill

Receive and authenticate Amazon EventBridge API destination webhooks. Use when setting up an endpoint that EventBridge calls from a rule or pipe, checking the Basic Authorization header, debugging 401s, or handling events by source and detail-type such as EC2 Instance State-change Notification.

Install this skill

npx skills add hookdeck/webhook-skills --skill aws-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)

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/, examples/nextjs/, 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:

sourcedetail-typeSent When
aws.ec2EC2 Instance State-change NotificationAn EC2 instance changes state
aws.s3Object CreatedAn object is created in a bucket with EventBridge notifications on
com.example.ordersOrderCreatedYour 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

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):

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

Use the printed URL as the API destination endpoint.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 4, 2026

View on GitHub →