# Guide to Tanium Webhooks: Features and Best Practices

Tanium webhooks are how endpoint data leaves Tanium Cloud for the rest of your stack. Tanium Connect can take the answer to a saved question, client status or an event and send it to an HTTP endpoint on a schedule, on demand or when the event fires. Security and IT teams use it to feed a SIEM, a data lake, a CMDB or their own automation with what Tanium knows about every endpoint.

This guide covers how Connect HTTP destinations work, what the request looks like, how to authenticate it with Header Authentication, the method, batching and network settings that trip people up, and the practices that make a Tanium receiver dependable.

## What are Tanium webhooks?

In Tanium Connect, a **connection** pairs a **source** with a **destination**. The source decides what data is sent: usually information from Tanium such as a saved question, question log, client status or event. With an **HTTP** destination, Connect sends that data to a REST endpoint you specify, with the URL, method, headers and authentication you configure.

There's no fixed payload and no Tanium event type. The body is the source's rows, with the columns you choose, relabelled and typed as you like, in JSON by default or another Connect format. A connection runs on a schedule, when you run it manually, or, for an Event source, whenever the configured event is detected.

Tanium does not sign HTTP destination requests. The destination's **Use Authentication** setting offers several methods, and **Header Authentication** is the one built for a receiver you control: you choose an **Auth Header Name** and a **Secret**, and Connect sends that header on every request.

## Tanium webhook features

| Feature | Details |
| --- | --- |
| Configuration | **Modules > Connect > Overview > Connections > Create Connection**, destination **HTTP** |
| Sources | Saved question, question log, client status, event and others |
| Triggers | Cron schedule, manual run, or an Event source with **Listen for this Event** |
| URL | HTTPS only. The TLS certificate is validated, with optional **Trust on First Use** |
| Method | Chosen from a **Method** drop-down. Batched sends are described as `POST` requests |
| Headers | Any extra headers with **+ Add Header**, for example `Content-Type: application/json` |
| Authentication | None, Basic Authentication, OAuth2 Client Credentials, Header Authentication, IBM Cloud IAM API Key |
| Signature and timestamp | None |
| Format | JSON by default, or another Connect format. Form data isn't supported |
| Batching | By records or bytes, creating several requests. A batch size of 0 sends one request |
| Timeout | Configurable per destination, in seconds |
| Network | Egress allow list rule per destination FQDN and port. Two proxy IPs per Tanium Cloud instance |
| Retries | Not documented |
| Logs | The connection's **Logs** tab, with a row per run |

## Payloads

Because the source and columns are yours, Tanium has no event list to document. A saved-question source sending JSON produces rows keyed by the column labels you set in **Configure Output > Columns**. A body might look like this, although the exact shape depends on your format settings:

```json
[
  {
    "Computer Name": "host-1.example.com",
    "IP Address": "10.0.0.5",
    "Operating System": "Windows Server 2022"
  }
]

```

You can change each column's **Destination Label** and force its **Value Type** to String, Numeric or Date/Time, with a default for numbers that can't be coerced and a format for dates. Use that to give your receiver stable, machine-friendly field names rather than display labels with spaces.

Tanium's own receiver examples, in Node.js, Python and Ruby, accept a `POST` with a JSON body and save it to a file. They're a reminder that the receiver only has to accept the data and answer quickly.

> **Inspect Tanium Connect requests as they arrive.** Point an HTTP destination at a [Hookdeck Inspect](https://inspect.hookdeck.com) URL and click **Run and Save** to see exactly what your connection sends, with no account or setup required.

## Setting up Tanium webhooks

Before a connection can reach your endpoint, add a network egress allow list rule for the destination's FQDN and port in your Tanium Account. If the endpoint is inside your network, allow inbound traffic from your Tanium Cloud instance's two proxy IP addresses. Pick a header name and a long random secret, for example `X-Webhook-Secret` with the output of `openssl rand -hex 32`.

1. Go to **Modules > Connect > Overview**, find **Connections** and click **Create Connection**. Enter a name and description.
2. Configure the source. You can't change the source type after the connection is created.
3. Select **HTTP** as the destination and create a **New** destination with a unique name. Tanium recommends one destination per connection, because editing a shared destination affects every connection that uses it.
4. Enter your HTTPS **URL** and choose the **Method**.
5. Under **Use Authentication**, choose **Header Authentication** and enter your **Auth Header Name** and **Secret**.
6. Optionally expand **Advanced** to set a batch size and a timeout.
7. Configure filters, the format and columns, then a schedule or **Listen for this Event**.
8. Click **Save**, or **Run and Save** to send immediately, and check the **Logs** tab.

**With Hookdeck.** Create a Hookdeck source with the **Tanium** type and use its URL as the destination URL. Enter the **API Key Header** (your Auth Header Name) and the **API Key** (your Secret) exactly as you set them, including any prefix. The source compares the whole value.

The Tanium source pre-selects `POST` but doesn't manage the method, because Connect lets you choose it. If your destination uses a method other than `POST`, such as `PUT`, allow that method on the Hookdeck source as well, or requests with it won't be accepted. Basic Authentication, OAuth2 Client Credentials and IBM Cloud IAM aren't covered by Hookdeck's Tanium source.

## Securing Tanium webhooks

Look up your Auth Header Name case-insensitively and compare the whole value with your Secret, in constant time. Don't assume the method is `POST`: accept whichever method you chose on the destination.

```javascript
const crypto = require('crypto');
const express = require('express');

const HEADER = (process.env.TANIUM_WEBHOOK_HEADER || 'X-Webhook-Secret').toLowerCase();
const EXPECTED = process.env.TANIUM_WEBHOOK_SECRET;

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

// Match the Method you chose on the destination.
app.all('/webhooks/tanium', express.raw({ type: '*/*', limit: '10mb' }), (req, res) => {
  if (!['POST', 'PUT', 'PATCH'].includes(req.method)) {
    return res.sendStatus(405);
  }

  const received = req.headers[HEADER]; // Node lower-cases header names
  if (!EXPECTED || typeof received !== 'string' || !safeEqual(received, EXPECTED)) {
    return res.sendStatus(401);
  }

  let rows;
  try {
    rows = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // rows is usually an array of objects keyed by your column labels.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import hmac
import os

def verify_tanium_request(headers) -> bool:
    secret = os.environ.get("TANIUM_WEBHOOK_SECRET")
    if not secret:
        return False  # never accept when unconfigured
    name = os.environ.get("TANIUM_WEBHOOK_HEADER", "X-Webhook-Secret")
    received = headers.get(name)  # case-insensitive in Starlette and Flask
    if not received:
        return False
    return hmac.compare_digest(received.encode("utf-8"), secret.encode("utf-8"))

```

A static header proves the sender knows the secret. It does not cover the body, and there is no timestamp, so a captured request can be replayed and still pass. Connect already requires HTTPS. Use a long random secret, and change it on the destination and in your environment together if it might have leaked.

> **Make Tanium webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks your Header Authentication value on every request, stores every batch durably, and delivers to your service at a rate it can handle.

## Tanium webhook limitations and pain points

### No documented retries

**The Problem:** If your endpoint is unavailable during a scheduled run, you can't rely on Connect to try that data again later.

**Why It Happens:** The HTTP destination page documents a timeout, but not retries or what counts as success. Connections scheduled during a Tanium Cloud maintenance window can also be interrupted or fail.

**Workarounds:**

* Check the connection's **Logs** tab after any incident, and run the connection again manually if needed.
* Schedule connections outside the Tanium Cloud maintenance window.

**How Hookdeck Can Help:** Hookdeck accepts and stores each request as soon as it arrives, then delivers to your service with [retries](/docs/retries) you configure. Any batch that failed can be replayed without re-running the connection.

### Large bursts from big sources

**The Problem:** A saved question across thousands of endpoints produces a large body, or a rapid series of batched requests, that can overwhelm a small receiver.

**Why It Happens:** With a batch size of 0, Connect sends everything in one request. With batching, it sends one request per batch until all the data is sent, and a single record larger than the byte limit still goes in full.

**Workarounds:**

* Set a batch size so each request stays within your server's body limit.
* Raise the body size limit on your receiver to match.

**How Hookdeck Can Help:** Hookdeck can deliver batches to your service with a [max delivery rate](/docs/destinations#set-a-max-delivery-rate), so a large run arrives at a pace your service can absorb.

### Method and network mismatches

**The Problem:** A connection fails with every request, even though the URL and secret are right.

**Why It Happens:** The method on the destination doesn't match what the receiver accepts, or the destination FQDN and port aren't in your network egress allow list. A destination missing from the allow list shows a warning in Connect.

**Workarounds:**

* Choose `POST` unless your receiver needs something else, and make the receiver accept the method you chose.
* Add an egress allow list rule for every destination FQDN and port.

**How Hookdeck Can Help:** Hookdeck logs every request it receives, so you can tell a request that never arrived (an egress problem) from one that arrived with an unexpected method or header.

### No event type or delivery ID

**The Problem:** Several connections sending to one endpoint are hard to tell apart, and a re-run sends the same rows again.

**Why It Happens:** The body is the source's rows. Tanium adds no event type, run ID or delivery ID to it.

**Workarounds:**

* Give each connection its own endpoint path or its own destination header.
* Upsert by a stable column, such as the computer name or ID, rather than appending.

**How Hookdeck Can Help:** Use one Hookdeck source per connection, or [filters](/docs/filters) on the columns each connection sends, to route data to the right destination.

## Best practices

### Use one destination per connection

Tanium recommends it, and it keeps headers, secrets and methods from changing under another connection when you edit one.

### Relabel columns for machines

Set **Destination Label** values such as `computer_name` and force **Value Type** where it matters, so your receiver gets stable keys and types.

### Treat each run as a snapshot

Write rows as the current state of each endpoint, keyed by a stable identifier, so re-runs and repeated batches are harmless. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Acknowledge fast and process asynchronously

Verify the header, store the body, respond within your destination's timeout, and load the rows from a background job. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

## Conclusion

Tanium Connect sends the rows of a saved question, event or other source to an HTTP destination with the method, headers and format you choose, unsigned and with no Tanium-defined event type. Use Header Authentication with a long random secret and compare it whole in constant time, make sure your receiver and any Hookdeck source accept the method you picked, set a batch size, and add the destination to your egress allow list.

[Hookdeck Event Gateway](https://hookdeck.com) checks the header, keeps every batch, and paces, retries and replays delivery to your service.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Tanium webhooks reliably in minutes.