> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squasher.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Signed webhooks

> Push security and audit events to Squasher over HTTPS, signed with HMAC SHA-256.

Use a signed webhook when a tool pushes events instead of letting Squasher pull them. Squasher
checks the signature, the timestamp, and the event id of each delivery before it stores
anything. Events that pass are mapped to OCSF 1.9.0 attributes, the same as pulled events.

> [!NOTE]
>
> Push sources are in preview. The API is not final.

## Create a push source

Call the `pushSources.create` operation with the connector id. Squasher returns the push URL
and the signing secret. The secret is shown only in this response.

```bash theme={null}
curl -X POST "https://api.squasher.ai/v1/projects/$SQUASHER_PROJECT_ID/push-sources" \
  -H "Authorization: Bearer $SQUASHER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"connector_id": "generic.signed_webhook"}'
```

If the vendor makes the secret, send it in `secret`.

## Sign a delivery

For the `generic.signed_webhook` connector:

1. Send a JSON object or a JSON array of OCSF events. Each event needs `metadata.uid`.
2. Put the current Unix time in seconds in the `x-webhook-timestamp` header.
3. Compute HMAC SHA-256 over `<timestamp>.<raw body>` with the secret. Send
   `sha256=<hex digest>` in the `x-webhook-signature` header.

```bash theme={null}
BODY='{"class_uid":3002,"category_uid":3,"activity_id":1,"type_uid":300201,"metadata":{"uid":"evt-1"}}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SQUASHER_PUSH_SECRET" -hex | sed 's/^.* //')
curl -X POST "$SQUASHER_PUSH_URL" \
  -H "Content-Type: application/json" \
  -H "x-webhook-timestamp: $TS" \
  -H "x-webhook-signature: sha256=$SIG" \
  -d "$BODY"
```

## What Squasher rejects

| Case | Status |
| - | - |
| No signature, or a signature that does not match | `401` |
| A timestamp more than 5 minutes from the receive time | `401` |
| Every event id in the delivery was already received in the last 24 hours | `409` |
| A delivery with some new and some repeated ids | `200`; only new events are stored |

## Rotate the secret

Call `pushSources.rotateSecret`. The previous secret still verifies for 24 hours, so the
sender can switch without lost events.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.