---
title: Webhooks
description: HMAC-signed webhook integrations with Agent Community services.
---

# Webhooks

Agent Community services exchange event notifications through HMAC-SHA256-signed webhook requests. This page documents the signing scheme so integration partners can verify payloads.

## Who this is for

Webhook integrations are currently **partner-scoped** — internal agent services (e.g. the enrichment pipeline) and document-signing callbacks. There is no self-serve webhook subscription product yet. If you're building an integration that needs event callbacks, contact [hello@agentcommunity.org](mailto:hello@agentcommunity.org).

## Signature scheme

Every request signed with this scheme carries an HMAC-SHA256 signature computed over the raw request body with a shared secret, sent in the `x-webhook-signature` header as `sha256=<hex digest>`.

Verification steps:

1. Read the raw request body (before any JSON parsing) — the signature is computed over the exact bytes sent, so parsing first and re-serializing will break verification.
2. Compute `HMAC-SHA256(secret, body)` and hex-encode it.
3. Constant-time-compare the result, prefixed with `sha256=`, against the `x-webhook-signature` header value. Reject the request if they don't match.

```ts
import { createHmac, timingSafeEqual } from 'crypto'

function verifyWebhookSignature(
  body: string,
  signatureHeader: string | null,
  secret: string,
): boolean {
  if (!signatureHeader?.startsWith('sha256=')) return false

  const expected = `sha256=${createHmac('sha256', secret).update(body).digest('hex')}`

  if (expected.length !== signatureHeader.length) return false
  return timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}
```

This mirrors the reference implementation in this repo (`lib/webhooks/hmac.ts`) — used both to verify inbound callbacks (e.g. the enrichment pipeline notifying us that a member profile is ready) and to sign outbound requests to partner services.

## Replay handling

There is **no timestamp header** in this signing scheme — it authenticates the payload, not the delivery time. Replay protection, where needed, is the receiver's responsibility. Today's live consumer of this scheme (the enrichment-complete callback) applies idempotency at the data layer: before queuing a follow-up action it checks for an existing record keyed to the same source event, so a duplicate delivery is a safe no-op rather than a duplicate side effect.

A separate webhook receiver (DocuSeal document-signing callbacks) uses its own verification method — not this `x-webhook-signature` scheme — and deduplicates deliveries by submission ID and event type in Cloudflare Workers KV with a Postgres backstop. It's mentioned here only to avoid confusion: not every webhook endpoint on this site uses the scheme documented above.

## Events

Live usage today includes internal agent-service callbacks (enrichment pipeline → site) and document-signing status updates. Outbound event feeds for members are on the roadmap and will be announced in [/lists](https://agentcommunity.org/lists).
