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

# Segment (product events)

> Stream product-usage events from Segment into Zudo so account engagement, indicators, and health scores reflect real product activity.

The Segment integration ingests product-usage events into Zudo as a webhook destination. Events flow into account timelines, daily counts charts, [Indicators](/accounts/indicators), and (optionally) [health scores](/accounts/health-scoring) — without writing custom code.

<Note>
  This integration is **org-level**. Connections are scoped to your organization and managed by an owner. If you don't
  see **Add Connection**, ask your org owner to set this up.
</Note>

## Before you start

You'll need:

* An **owner** role in your Zudo organization
* A Segment workspace with at least one source (e.g. JavaScript, Node.js, mobile SDK) sending Track events
* An idea of which event names matter to your customer-success team — login, feature usage, billing, etc. Zudo only rolls up events on an allowlist, so a tight list keeps cost and noise down.

## How it works

1. Zudo gives you a unique webhook URL and a shared secret.
2. You add a **Webhook** destination in Segment that posts events to that URL, signed with the shared secret.
3. Segment delivers each event in real time.
4. Zudo verifies the HMAC signature, archives the raw event to S3, and inserts a row into the staging table.
5. Every 10 minutes a job aggregates allowlisted events into per-account daily rollups and resolves which Zudo account each event belongs to.

The webhook hot path is intentionally fast (under 100ms p50) — heavy work happens in the background.

## Connect

<Steps>
  <Step title="Create the Connection in Zudo">
    Go to **Settings → Connections**, click **Add Connection**, and select **Segment (product events)**. Click **Create
    Connection** — Zudo generates the webhook URL and shared secret automatically.
  </Step>

  <Step title="Copy the webhook URL and shared secret">
    The next screen shows both values once. **Copy them now** — Zudo will not show the secret again. If you lose it, you
    can delete the connection and recreate it.
  </Step>

  <Step title="Add the Webhook destination in Segment">
    In your Segment workspace:

    1. Open **Connections → Destinations** and click **Add Destination**.
    2. Search for **Webhooks** (the classic destination, not "Webhook (Actions)").
    3. Choose the source you want events to flow from and click **Next**.
    4. Name the destination something memorable like `Zudo Product Events`.

    On the destination's **Settings** tab:

    1. Under **Webhook URLs**, paste the URL Zudo gave you.
    2. Under **Shared Secret**, paste the secret Zudo gave you. This tells Segment to sign each request as
       `X-Signature: HMAC-SHA1(body, secret)`.
    3. Save.
  </Step>

  <Step title="Enable the destination">
    Toggle the destination on. Segment starts delivering events immediately.
  </Step>

  <Step title="Configure the allowlist back in Zudo">
    Go back to **Settings → Connections** and expand the new Segment connection. Under **Event allowlist**, list the event
    names you want rolled up — one per line. Anything outside the allowlist is still archived to S3 (replayable later) but
    won't appear in rollups, indicators, or health scores.
  </Step>

  <Step title="Configure identity rules (optional)">
    Expand **Identity rules** to control how events resolve to Zudo accounts and contacts. The defaults work for most
    setups — see [Identity rules](#identity-rules) below if you need to customize.
  </Step>

  <Step title="Verify">
    Trigger a test event from your app (or use Segment's **Event Tester**). Within \~10 minutes you'll see daily counts on
    account pages, and recent events appear in the activity timeline under the **Events** filter.
  </Step>
</Steps>

## What flows in

Zudo accepts every Segment call type but treats them differently:

| Call type    | Captured event name                        | Used for                                                      |
| ------------ | ------------------------------------------ | ------------------------------------------------------------- |
| **Track**    | `body.event` (e.g. `Item Viewed`, `Login`) | Rollups + indicators (when the event is in your allowlist)    |
| **Group**    | `$group`                                   | Identity resolution (binds users to accounts via `groupId`)   |
| **Identify** | `$identify`                                | Identity resolution (binds users to email / external user id) |
| **Page**     | `$page`                                    | Same as Track if allowlisted                                  |
| **Screen**   | `$screen`                                  | Same as Track if allowlisted                                  |

The full payload is preserved on every event, so you can replay raw S3 archives if you ever change which events you want rolled up.

## Identity rules

When a Segment event arrives, Zudo runs an ordered chain of strategies to figure out which Zudo Account (and optionally Contact) the event belongs to. The first match wins.

The **default** chain works for most orgs and looks like this:

* **Account chain**:
  1. Match `groupId` against `Account.externalId` (the canonical external identifier you've stamped on the account in Zudo)
  2. Match `groupId` against `Account.psId` (your existing internal account id)
  3. Match `groupId` against `Account.vitallyId` (if you've also got Vitally synced)
* **Contact chain** (used when no account match was found):
  1. Match `userId` against `Contact.productExternalUserId`
  2. Match `email` against `Contact.email`

If neither chain hits, the event is still archived and rolled up — just attached to a `null` accountId. Once you map the right ID onto the account, use **Reconcile** on the connection card to back-fill.

### Customize

In the connection's **Identity rules** section you can:

* Reorder strategies with **↑ / ↓**
* Remove unused strategies with **✕**
* Add strategies for new sources (e.g. `from: payload:properties.account_uuid` to match an arbitrary path inside the event payload)
* Match against a **custom trait value** — useful when your customer's account id is stored as a Vitally trait or a Zudo user-defined trait

Strategies are tried in the order shown. Put the most-specific match first so unique IDs win over fuzzy email matching.

## Volume and cost controls

The **rate limit** field on the connection card caps the number of events per minute Zudo will accept from your org. Default is 600/min (\~864K events/day). Events over the cap return `429` to Segment, which retries them with backoff. Raise or lower this based on your contract.

The **event allowlist** is the biggest cost lever — only allowlisted events enter Postgres. Everything is archived to S3 regardless.

## Troubleshooting

<Steps>
  <Step title="Events not arriving">
    Open your Segment destination's **Event Delivery** tab. If you see 401 responses, the shared secret in Segment
    doesn't match what Zudo has on file. Recreate the connection in Zudo and update Segment's Shared Secret.
  </Step>

  <Step title="Events arriving but no rollups">
    Check the connection's **Recent events** debug list. If `inAllowlist` is `false` for your test events, add them to
    the allowlist. Rollups appear after the next aggregation run (every 10 minutes).
  </Step>

  <Step title="Events showing on the wrong account or no account">
    Identity resolution didn't match. Open the recent events list — the `externalGroupId` and `externalUserId` fields
    show exactly what Segment sent. Make sure the value matches one of the strategies in your Identity rules. Either
    correct the data in Segment's Group/Identify calls, or add a strategy that matches what's already there (for example
    a `payload:properties.account_uuid` strategy).
  </Step>

  <Step title="Rate-limit (429) responses">
    Segment retries 429s with backoff, so events aren't lost in normal traffic spikes. If you see sustained 429s, raise
    the **Rate limit** on the connection or tighten the allowlist so fewer events count toward the cap.
  </Step>
</Steps>
