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

# Outbound Signing

Sign outbound webhook deliveries with HMAC for receiver verification.

Outbound signing mirrors [inbound signature verification](/guides/signature-verification) but from the other side: hookstream signs every outbound delivery so your receivers can verify it came from you, not from an attacker who guessed the URL.

## How it works

**You configure a secret on the destination**

Stored in `signing_config` on the destination.
  **Hookstream computes HMAC(secret, body)**

For every outbound request, using the configured algorithm (SHA-256 or SHA-1).
  **The signature is sent in a header**

Default header is `X-hookstream-Signature` with a `sha256=` prefix.
  **Your receiver recomputes and compares**

If the signatures match (constant-time compare), the request is verified.

## Configure outbound signing

Set `signing_config` on an HTTP destination:

```bash
curl -X PATCH https://hookstream.io/v1/destinations/dest_xyz \
  -H "X-API-Key: $HOOKSTREAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "signing_config": {
      "secret": "your_secret_here",
      "algorithm": "sha256",
      "header": "X-hookstream-Signature",
      "prefix": "sha256=",
      "include_timestamp": false,
      "timestamp_header": "X-hookstream-Timestamp"
    }
  }'
```

### Fields

| Field | Default | Notes |
| --- | --- | --- |
| `secret` | — | Required. The shared secret. |
| `algorithm` | `sha256` | `sha256` (recommended) or `sha1`. |
| `header` | `X-hookstream-Signature` | Header the signature lands in. |
| `prefix` | `"{algorithm}="` | Prepended to the hex digest. Set `""` to send raw hex. |
| `include_timestamp` | `false` | When true, the signing payload becomes `{unix_timestamp}.{body}` (dot-separated) and the timestamp is also sent in `timestamp_header`. |
| `timestamp_header` | `X-hookstream-Timestamp` | Only used when `include_timestamp` is true. |

The verification examples below assume `include_timestamp: false`. If you enable timestamps, the receiver must read `X-hookstream-Timestamp`, reconstruct the signing payload as `` `${timestamp}.${rawBody}` ``, and use that string instead of `rawBody` when computing the HMAC.

Use SHA-256. SHA-1 is only there for compatibility with older receivers — new integrations should never pick it.

## Verify the signature on the receiver side

```javascript node
const crypto = require("crypto");

function verifyHookstream(req, rawBody) {
  const signature = req.headers["x-hookstream-signature"] ?? "";
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.HOOKSTREAM_SECRET)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

```python python
import hmac, hashlib, os

def verify_hookstream(request, raw_body: bytes) -> bool:
    signature = request.headers.get("x-hookstream-signature", "")
    expected = "sha256=" + hmac.new(
        os.environ["HOOKSTREAM_SECRET"].encode(),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
```

```go go
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "net/http"
    "os"
)

func verifyHookstream(r *http.Request, rawBody []byte) bool {
    signature := r.Header.Get("X-hookstream-Signature")
    mac := hmac.New(sha256.New, []byte(os.Getenv("HOOKSTREAM_SECRET")))
    mac.Write(rawBody)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expected))
}
```

Use a constant-time comparison (`crypto.timingSafeEqual`, `hmac.compare_digest`, `hmac.Equal`) — not a plain `==` — so an attacker can't learn the signature byte-by-byte via timing side channels.

## Next Steps

**[Destinations API](/api/destinations)**

Full destination schema, including `signing_config`.
  **[Signature Verification](/guides/signature-verification)**

The inbound side — verifying signatures on the events you receive.