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

# Connect Segment to Orbit: CDP ingest end to end

> Wire Segment into Orbit's CDP: create a Source with a write key, point a Segment webhook destination at the inbound URL with its one-time secret, stitch userId/anonymousId into contacts, and trigger segments and journeys from Segment events. Includes first-event checklist and troubleshooting.

# Connect Segment to Orbit end to end

Segment is a first-class Orbit integration with a **bi-directional** CDP loop:

* **Outbound (Orbit → Segment)** — one of your Segment Sources receives a `write_key`, so Orbit events (messages, conversions, contact changes) post into your Segment workspace as they happen.
* **Inbound (Segment → Orbit)** — each Segment Source you mint in Orbit gets a delivery webhook URL with a one-time HMAC secret. A Webhook Destination in your Segment workspace forwards `identify` / `track` / `group` / `alias` calls into Orbit, where they land in your CDP event stream and resolve onto contacts.

This guide runs the numbered setup, the event-to-contract mapping, identity stitching, verification, monitoring, and revocation. The shape mirrors the [HubSpot & Salesforce guide](/guides/hubspot-salesforce-integration); the endpoint detail lives in [`/api-reference/integrations`](/api-reference/integrations).

<Note>
  Segment's inbound path is **not** the OAuth popup used by Zendesk, Calendly, DocuSign, and Jira — it is a Source/write-key flow, like HubSpot's. Manage it from **Settings → Integrations → Segment → Open CDP console**, or fully over the API below.
</Note>

## What you'll set up

1. A Segment inbound Source object with `POST /api/v1/integrations/segment/sources` (name + your Segment `write_key`).
2. The delivery URL — `POST /api/v1/integrations/webhooks/segment?source=<id>` — registered as a **Webhook Destination** in your Segment workspace.
3. The `x-signature` HMAC-SHA1 secret (hex digest of the raw body) Segment's webhook destination emits, matched to the plaintext inbound secret returned **once** at Source creation.
4. `identify` → contact upsert, `track`/`page` → event stream, `group` → company attribution, `alias` → identity merge.
5. Verification in the CDP event debugger, then enrollment into segments and journeys.

***

## Step 1 — Prerequisites

* An **Owner or admin** role, plus an API key with the `integrations:write` scope (this step touches provider credentials).
* Your Segment workspace's **Source write key** (from Segment → Sources → your source → Settings → API Keys).
* Decide which event types you want to forward (`identify`, `track`, `group`, `alias`).

***

## Step 2 — Create the Source

`POST /api/v1/integrations/segment/sources` stores your write key (encrypted) and returns the delivery URL plus a plaintext inbound HMAC secret — **shown exactly once**. Store it in your password manager; rotate later if you lose it.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/integrations/segment/sources \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "prod-webapp", "write_key": "your_segment_source_write_key" }'
```

Response (abridged):

```json theme={null}
{
  "data": {
    "id": "segmentSource_9f3kd2",
    "name": "prod-webapp",
    "webhook_url": "https://api.orbit.devotel.io/api/v1/integrations/webhooks/segment?source=segmentSource_9f3kd2",
    "inbound_secret": "whsk_0123456789abcdef…",
    "inbound_secret_prefix": "whsk_012345"
  },
  "meta": { "request_id": "req_a8123", "timestamp": "2026-09-05T14:02:11.000Z" }
}
```

<Check>
  Confirm the Source exists with `GET /api/v1/integrations/segment/sources` — the list returns `id`, `name`, `webhook_url`, `inbound_secret_prefix`, `last_event_at`, and last-test fields. Keep only the `inbound_secret_prefix` for diagnosis; Orbit never returns the full secret after creation.
</Check>

From the dashboard the same card lives at **Settings → Integrations → Segment → Open CDP console**, where you create the Source with the UI instead of cURL.

***

## Step 3 — Point Segment → Orbit (Webhook Destination)

In your Segment workspace, add a **Webhook Destination** on the Source you want to forward, then paste the `webhook_url` from Step 2 and set the secret to the `inbound_secret` from the same response:

* **URL**: `https://api.orbit.devotel.io/api/v1/integrations/webhooks/segment?source=<segmentSource_id>`
* **Shared secret**: the one-time `inbound_secret` (starts `whsk_…`).

Segment signs every request with `x-signature: <HMAC-SHA1 hex of the raw body>`; Orbit recomputes that digest with the stored secret and constant-time-compares before dispatching. The `?source=<id>` query parameter picks which Source row (and therefore which secret) to verify against — keep it intact.

<Info>
  Never strip the `source` query parameter when registering the destination. Without it Orbit returns `400 MISSING_SOURCE`; an unknown id returns `401 INVALID_SOURCE`.
</Info>

***

## Step 4 — Which events to send, and how they land

Orbit accepts Segment's four core call types and maps them onto its CDP contract. Send only what you mapped — see the full cross-provider matrix in [Integration event field mappings](/guides/integrations-event-field-mappings).

| Segment call     | What Orbit does                                                                                                                                                                                                                                            | Key fields read                                |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `identify`       | Upserts a contact keyed by `userId` (external id on provider `segment`). Traits `email`, `phone`, `first_name`/`firstName`, `last_name`/`lastName` lift to first-class columns; remaining traits land in `attributes`                                      | `userId`, `traits`                             |
| `track` / `page` | Writes an event row into the tenant event stream, joined to an existing contact when `userId` resolves. Anonymous-only tracks are stored with `contact_id = null` so campaign trigger lookups still see them                                               | `event`, `userId`, `anonymousId`, `properties` |
| `group`          | When `userId` resolves, merges `segment_group_id` + `segment_group_traits` into the contact's `attributes`, and promotes `name`/`industry`/`plan`/`employees` to `company_name`/`company_industry`/`company_plan`/`company_employees` for campaign filters | `groupId`, `userId`, `traits`                  |
| `alias`          | Identity resolution — resolves both `userId` (survivor) and `previousId` to contacts and merges them through the same survivorship-policy merge the Identity Resolution console uses; idempotent when both resolve same or unresolved                      | `userId`, `previousId`                         |

Every event is persisted to the tenant event log with `ON CONFLICT (source_id, message_id) DO NOTHING`, so Segment replays are safe. A response of `duplicate: true` means a re-delivery Orbit already recorded.

<Check>
  After the first `identify`, check the CDP event debugger (`Insights → CDP event debugger`) — the event row and the resolved `contact_id` appear within seconds. Full debugger tour: [CDP event debugger & DLQ](/guides/cdp-event-debugger-and-dlq).
</Check>

***

## Step 5 — Identity stitching: userId + anonymousId + alias

Orbit resolves identity the same way it does for every external provider; Segment just supplies the ids. Cross-references: [Identity resolution](/guides/identity-resolution) and [Anonymous identity stitching](/guides/anonymous-identity-stitching).

* **`identify`** — `userId` becomes the contact's external id under provider `segment`, so the same person re-identified across sessions/integrations lands on one contact row. `email`/`phone` are lifted to first-class columns; everything else stays under `attributes`.
* **`track`/`page`** — Orbit joins the event to the existing contact resolved by `userId`; it never creates a contact from a track, so anonymous visitors stay event-only until an `identify` stitches them.
* **`alias`** — Segment's alias semantics (`previousId` folds into `userId`) map to a survivorship-policy merge: the `userId` contact survives, the `previousId` contact folds in with a merge audit record and your tenant's field-level policy (most recent / primary wins) applied. Both sides resolve, or the call is a recorded no-op.
* **`group`** — B2B attribution (account/company) writes `segment_group_id` onto the contact when `userId` resolves.

<Note>
  Because `alias` runs the same merge primitive as the dashboard Identity Resolution console, Segment-driven merges respect the undo window and your survivorship policy exactly the way manual merges do.
</Note>

***

## Step 6 — Use Segment-fed profiles in segments and journeys

Once events land, they feed Orbit's audiences the same way native events do:

1. Verify first with the event debugger (above) — a missing `source` param, a wrong secret, or an unmapped event name all show up there before they poison audiences.
2. Build audiences in **Contacts → Segments** — conditions over the event stream (e.g. `track` event name `Order Completed` in last 30d) or over group-attributed company fields.
3. Enroll Segment-fed audiences into journeys in **Campaigns → Journey builder** — the full walkthrough is [Segment-triggered journeys](/guides/segment-triggered-journeys).

Because `group` promotes `company_*` fields onto contact attributes, B2B tenants can segment on company plan/industry without a separate enrichment step.

***

## Step 7 — Health monitoring

* **Per-Source health** — `GET /api/v1/integrations/segment/sources` returns each Source with `last_event_at`, `last_test_at`, and `last_test_ok`. A Source whose `last_event_at` stalls while Segment delivery is green on their side usually means a rotated secret mismatch.
* **End-to-end test** — `POST /api/v1/integrations/segment/sources/:id/test` fires a synthetic `identify` through the stored write key and records `last_test_ok` on the Source. Run it after creating or rotating a Source before pointing traffic at it.
* **Catalog status** — `GET /api/v1/integrations/{id}/status` reports whether the Segment catalog entry has any Source configured (`connected: false` when none exist).

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/integrations/segment/sources \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

The `data` rows include `inbound_secret_prefix` for "which secret is this?" diagnosis without ever leaking the full secret.

***

## Step 8 — Revoke or rotate

* **Rotate the secret** — `POST /api/v1/integrations/segment/sources/:id/rotate-secret` issues a new plaintext inbound secret (shown once); update the Webhook Destination in Segment and events resume.
* **Delete a Source** — `DELETE /api/v1/integrations/segment/sources/:id` permanently stops accepting inbound events on that URL. Decommission Sources you no longer want honored.
* **Disconnect the integration** — from the dashboard (Segment card → drawer → Revoke) or `DELETE /api/v1/integrations/segment/disconnect`, same pattern as the OAuth SaaS integrations ([revoke flow](/guides/oauth-saas-integrations)).

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/integrations/segment/sources/segmentSource_9f3kd2/rotate-secret \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

***

## First-event checklist

Run this after Step 3, before building audiences:

1. `POST /segment/sources` returned a `webhook_url` you copied verbatim, `?source=` included.
2. The plaintext `inbound_secret` is stored (it is never shown again; rotate if lost).
3. The Segment Webhook Destination is enabled on the right Segment source.
4. Send a manual `identify` with a known `userId` from Segment's debugger.
5. Orbit returns `200` with `received: true` (not `401`/`400`).
6. The event row + resolved `contact_id` appear in the CDP event debugger.
7. `GET /segment/sources` shows `last_event_at` updated.
8. Only then enroll the event into segments/journeys.

***

## Troubleshooting

| Symptom                                | Cause                                                   | Fix                                                                                                                     |
| -------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `400 MISSING_SOURCE`                   | Webhook Destination URL lost the `?source=` query param | Re-copy `webhook_url` verbatim from the Source row.                                                                     |
| `401 INVALID_SOURCE`                   | Source id unknown (deleted, or wrong tenant)            | List `GET /segment/sources`; recreate the Source and re-register the destination.                                       |
| `401 INVALID_SIGNATURE`                | Rotated/mis-set `inbound_secret` on the Segment side    | Rotate with `POST …/rotate-secret`, paste the new value into the Webhook Destination.                                   |
| `400 INVALID_PAYLOAD`                  | Body missing the `type` discriminator                   | Segment forwards `identify`/`track`/`group`/`alias` — check destination filters/warehouse-only sources stripping calls. |
| Segment retries / `503`-style failures | Orbit dispatcher error (5xx)                            | Segment retries within its window; check the event debugger + audit log for `DISPATCH_FAILED` and re-drive once fixed.  |
| `last_test_ok: false` on Source        | Stored write key invalid/decrypt failure                | Re-create the Source with a valid write key; the test endpoint returns the provider error in `data.error`.              |
| Events arrive, no contact attached     | `identify` never ran, or ran anonymous-only             | Send an `identify` with a stable `userId` before/alongside `track`; track never creates contacts.                       |
| Two contact rows for one person        | Split before `identify`+`alias` stitching               | Emit an `alias` call (or run the Identity Resolution merge); alias collapses through the survivorship-policy merge.     |

***

## Related reading

* [Integration event field mappings](/guides/integrations-event-field-mappings) — which fields each inbound event populates across providers.
* [Identity resolution](/guides/identity-resolution) + [Anonymous identity stitching](/guides/anonymous-identity-stitching) — how Orbit merges cross-id visitors into one contact.
* [CDP event debugger & DLQ](/guides/cdp-event-debugger-and-dlq) — confirm the first event lands before building audiences.
* [Segment-triggered journeys](/guides/segment-triggered-journeys) — enroll Segment-fed audiences into flows.
* [HubSpot & Salesforce end to end](/guides/hubspot-salesforce-integration) — the CRM/CDP sibling guide this page mirrors.
* [Integrations API reference](/api-reference/integrations) — connect/status/sources/rotate/disconnect endpoint detail.
