> ## 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.

# IP Filtering

Configure IP allowlists and denylists with CIDR notation for sources.

IP filtering adds a network-level gate in front of every source. Use an allowlist to only accept requests from known provider IPs, or a denylist to block specific bad actors. Blocked requests return `403 Forbidden` before anything else in the pipeline runs.

## Allowlist vs denylist

- **Allowlist** — "only these IPs are allowed." Best when your provider publishes a fixed IP list (Stripe, Twilio, GitHub webhooks from GitHub.com).
- **Denylist** — "everyone except these IPs." Best for blocking known abusers while keeping the source open otherwise.

You can set both. The denylist is checked first, then the allowlist.

## CIDR notation

Both lists accept single IPs or CIDR ranges for IPv4:

- Single IP: `192.168.1.1`
- /24 subnet: `192.168.1.0/24` (256 addresses)
- /16 subnet: `10.0.0.0/16` (65,536 addresses)

IPv6 CIDR matching is not currently supported. If you need to allow IPv6 traffic, leave the allowlist empty for now or reach out.

## Configure a source

**Collect the IPs**

Grab the current IP list from your provider's documentation. For Stripe, that's [stripe.com/docs/ips](https://stripe.com/docs/ips); for GitHub, it's the `hooks` section of [api.github.com/meta](https://api.github.com/meta).
  **Store them on the source**

The allowlist is stored as a JSON array. Update the source via API:

    ```bash
    curl -X PATCH https://hookstream.io/v1/sources/src_abc \
      -H "X-API-Key: $HOOKSTREAM_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "ip_allowlist": ["54.187.174.169", "54.187.205.235", "54.187.216.72/32"]
      }'
    ```

    Or open the source in the dashboard and paste comma-separated entries into the **IP Filtering** section — the UI serializes them for you.
  **Send a test event**

Send a webhook from a known-good IP. A rejected request returns `403` with reason `"IP is not in allow list"` or `"IP is in deny list"`.

## Pipeline evaluation order

IP filtering runs at the very front of the ingest pipeline, before any other check:

1. **IP filter** — 403 if the client IP is blocked. Client IP is read from `CF-Connecting-IP`, falling back to `X-Forwarded-For`.
2. **Signature verification** — 401 if the signature doesn't match.
3. **Deduplication** — stored as `duplicate` if the dedup window says so.
4. **Schema validation** — 422 if reject mode and the payload fails.
5. **Storage and fan-out** — the event is persisted and pushed into connections.

Running IP filtering first means banned IPs never cost you a hash or a database round trip.

Provider IP lists change. Set a calendar reminder (or a cron job) to re-sync them every quarter so legitimate traffic doesn't start getting 403s.

## Next Steps

**[Signature Verification](/guides/signature-verification)**

Verify cryptographic signatures on top of the IP allowlist.
  **[Sources API](/api/sources)**

Full source schema, including `ip_allowlist` and `ip_denylist`.