Skip to main content
Browse the docs

DocsIntegrations

Stream the activity log to your security tools

Send every activity log entry to Splunk, Datadog or any HTTPS endpoint as signed NDJSON, and verify that each delivery came from Hourari.

Overview

Hourari can send your workspace's activity log (sign-ins, failed sign-ins, permission changes, invitations, billing events and the rest) to your own security tools as it is written. Your on-call team can then alert on it in the place they already watch, without anyone exporting a file.

Streaming is part of the Enterprise plan, and only the workspace owner can set it up. Owners need to have entered their two-factor code, and to be on an allowed network if your workspace has an IP allowlist.

Add a destination

  1. Open Settings → Activity log and scroll to Stream to your security tools.
  2. Choose Add destination.
  3. Give it a name and enter the Collector URL. It must use https:// and a domain name. An IP address is refused.
  4. If your collector needs its own token, enter it as an Auth header: for Splunk HTTP Event Collector, the header name is Authorization and the value is Splunk <your token>.
  5. Copy the signing secret. It is shown only once; you need it to verify deliveries.

The header value can't be viewed or edited after saving.

Pausing, resuming and removing a destination are each recorded in the activity log, so your other destinations hear about it.

The first deliveries are your whole existing log, oldest first, up to 500 entries every five minutes (about 6,000 an hour), so a long history takes a while to catch up. After that, each delivery carries only what is new.

A workspace can stream to up to five destinations. A destination's URL and header can't be changed after it's added: add a new one and remove the old one.

Rotate the signing secret

If a signing secret is lost or may have leaked, or your security team rotates secrets on a schedule, replace it without removing the destination:

  1. In Stream to your security tools, open the … menu on the destination's row and choose New signing secret.
  2. Confirm, then copy the new secret. Like the first one, it's shown only once.
  3. Give the new secret to your collector within 24 hours.

For 24 hours after you generate it, the previous secret keeps working: each delivery carries two signatures, the new one in X-Hourari-Signature and the previous one in X-Hourari-Signature-Previous, so your collector verifies whichever secret it holds. After that, only X-Hourari-Signature is sent and the previous secret is deleted.

To keep verifying while you switch, have your collector also check X-Hourari-Signature-Previous, which carries the signature made with the previous secret for those 24 hours. A collector that already holds the new secret needs no change: X-Hourari-Signature is always signed with the current one.

Nothing is sent again and no entries are skipped: the destination carries on from where it was. The change is recorded in the activity log as "Audit log destination signing secret replaced", without the secret. Only the workspace owner can do this, with the same two-factor and allowed-network checks as adding a destination.

If you generate another new secret within those 24 hours, the oldest one stops working at once, and the one it replaced becomes the previous secret.

What a delivery looks like

Every five minutes, each destination with new entries receives one POST with Content-Type: application/x-ndjson: one JSON object per line, oldest first. Here is one entry, spread over several lines so it is easier to read:

{
  "id": "7c2e4a91-3b5d-4f0e-9a68-1d2c3e4f5a6b",
  "timestamp": "2026-10-17T14:03:51.204+00:00",
  "event_type": "member.role_changed",
  "actor": {
    "id": "5b1f…",
    "email": "dana@example.com",
    "role": "owner",
    "ip": "203.0.113.24",
    "user_agent": "Mozilla/5.0 …"
  },
  "source": "user",
  "target": { "type": "profile", "id": "8e3a…", "label": "Sam Lee" },
  "changed_fields": ["team_role"],
  "metadata": { "from": "member", "to": "admin" },
  "company_id": "0f9d…"
}
Field Meaning
id The entry's unique id. Use it to ignore an entry you have already stored.
timestamp When it happened.
event_type What happened, such as member.role_changed or auth.login_failed.
actor Who did it: their id, email, role, IP address and browser, where known.
source user for something a person did, service for an automatic job, client-report for an event a browser reported.
target What it was done to.
changed_fields, metadata Which fields changed and, for most of them, from what to what. Salaries, rates and costs are never included: the log records that one changed, not the amount.

The request carries these headers:

Header Value
X-Hourari-Delivery A new id for each attempt.
X-Hourari-Timestamp When this attempt was signed, in Unix seconds.
X-Hourari-Signature sha256= followed by the signature in hex, made with the current signing secret.
X-Hourari-Signature-Previous Only for 24 hours after a rotation: the same, made with the previous secret.

Verify the signature

The signature is an HMAC-SHA256 of the timestamp, a full stop, and the raw request body, keyed with your signing secret, the same scheme as webhooks. To check a delivery:

  1. Read the raw body exactly as received.
  2. Compute the HMAC-SHA256 of <X-Hourari-Timestamp>.<raw body> with your signing secret, in hex.
  3. Compare it with the part of X-Hourari-Signature after sha256=, using a constant-time comparison. If it doesn't match and X-Hourari-Signature-Previous is present, compare it with that one too; accept the delivery if either matches. Only the previous header ever needs the old secret.
  4. Reject it if the timestamp is more than five minutes from your clock.

Respond, retries and failures

Answer with any 2xx status within 10 seconds.

  • Anything else is a failure, including a timeout or a redirect. Redirects are not followed. The same entries are sent again five minutes later, so nothing is skipped.
  • Entries arrive at least once. A delivery you accepted can be sent again if your answer did not reach us. Use each line's id to ignore duplicates.
  • A destination that keeps failing is turned off. After 20 failed deliveries in a row (a little over an hour and a half), it stops, the workspace owner gets one email saying so, and an entry is written to the activity log (and streamed to any other destination). Fix the collector, then switch it back on. It picks up where it stopped, and can fail three more times before it is turned off again.
  • Pausing works the same way. Switching a destination off and on again resumes from the last entry it received.

The destination's row shows when it last received something and, if it is failing, the status code. Hourari never stores or shows what your collector replied.

Removing a destination stops deliveries and deletes its signing secret and header value.

Last checked against the product on .