Skip to main content

Connect Segment

Planned (Phase 2)

Planned for Phase 2. This guide describes the planned setup; the configuration shown below is validated against the current API schema so it stays accurate.

The destination is implemented and contract-tested in the monorepo (integrations/cdp/segment-destination); the Segment catalog listing is pending the Actions partner review.

Route the events you already collect with Segment into Tessera as a Segment destination (Actions framework, cloud mode) — no new instrumentation. Three actions with default subscriptions do the work:

ActionSubscriptionCanonical event
Track Eventtype = "track"event name → event_type (normalized), properties → attributes
Identify Entitytype = "identify"profile.updated with traits as attributes
Page Viewtype = "page"page.viewed with page properties as attributes, URL in context

alias and group calls are acknowledged and ignored (identity resolution ships in Phase 3), and Segment's messageId becomes the event's idempotency id — Segment's own at-least-once retries can never double-count.

1. Create the credential (least privilege)​

Mint a dedicated ingest-scope API key for Segment under Project Settings → API keys — not your admin key. If the destination is ever compromised it can only write events, never read data or manage the project. The key is pasted once into the destination's Tessera API Key setting (stored as a Segment secret) and only ever sent as the Authorization bearer to https://ingest.staging.tessera.zone/v1/events.

2. Record the source in Tessera​

A streaming source records the integration in your project (timeline attribution + dead-letter scoping):

{
"name": "segment-prod",
"kind": "stream",
"config": {
"subtype": "adapter",
"adapter": "segment",
"entity_type": "user"
}
}

3. Add the destination in Segment​

In the Segment app: Catalog → Destinations → Tessera → connect it to the sources you want routed. Settings:

SettingValue
Tessera API Keythe ingest-scope key from step 1 (secret)
Mapping Templateoptional JSON template (below); empty = default
Ingestion URLleave default unless you run a single-tenant deployment

Enable the three pre-subscribed actions. Batching is on: the destination respects the ingestion limits (500 events / 5 MiB per request) and honors retry_after_seconds on rate limits by retrying — per-event failures show up in Segment's event delivery view without failing whole batches.

4. The mapping template​

Both planned CDP destinations translate calls with the same versioned mapping template (JSON Schema id https://spec.tessera.dev/schemas/cdp-mapping.json, in the monorepo at api/jsonschema/cdp-mapping.json). Paste a template document into the destination's Mapping Template setting — or leave it empty for the default:

{ "version": 1, "entity_type": "user" }

Every knob, with defaults:

{
"version": 1,
"entity_type": "user",
"entity_id_precedence": ["user_id", "anonymous_id"],
"identify_event_type": "profile.updated",
"page_event_type": "page.viewed",
"event_name_rules": {
"lowercase": true,
"space_replacement": "_",
"renames": { "Match Finished": "match.finished" }
},
"attribute_renames": { "order_total": "amount" },
"attribute_drops": ["internal_note"],
"flatten_separator": "_"
}
FieldMeaning
versionAlways 1 (the template document is versioned).
entity_typeThe entity type your events describe (user, account, player, …).
entity_id_precedenceWhich CDP id becomes the entity id: first non-empty of user_id (userId) / anonymous_id (anonymousId). Calls with neither are dropped and counted — never sent with a fabricated id. A numeric id becomes its digits (12 and 12.0 are 12, as on every other connector, see Entities and events); an id that is not an identifier (a fractional number, a boolean, an object) drops the call, counted as dropped_invalid_entity_id, and never falls back to the other id.
identify_event_typeEvent type for identify calls (traits become attributes).
page_event_typeEvent type for page calls (page properties become attributes; the URL rides in the non-model context).
event_name_rulesHow track names become event types: renames win (exact match, target used verbatim); otherwise lowercase (if lowercase) and whitespace runs become space_replacement. Names that still don't match ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*){0,3}$ are dropped with a counted reason.
attribute_renamesRenames applied to flattened keys before normalization.
attribute_dropsFlattened keys to drop deliberately.
flatten_separatorJoins nested property paths: order.total → order_total.

Translation rules that are not configurable:

  • event_id is always the CDP messageId, so CDP-side retries deduplicate downstream — redelivery is safe by construction.
  • The event timestamp is the CDP's event timestamp (timestamp, falling back to originalTimestamp) — never sentAt/receivedAt delivery time, and never invented when absent.
  • CDP context (library, user agent, IP, page URL) maps into the canonical event's non-model context, never into attributes.
  • Attribute keys normalize to ^[a-z][a-z0-9_]{0,63}$ (lowercase, invalid characters become _); key collisions keep the first value; arrays keep at most 64 string/number elements; arrays of objects are dropped.
  • alias and group calls are acknowledged and ignored (identity resolution ships in Phase 3).
Contract review still applies

CDP-sourced attributes pass through schema-contract review like every other source — deny by default: connecting a CDP does not by itself make anything a model input.

5. Test and verify​

Send one track from Segment's Event Tester, then read the entity's timeline (GET /entities/user/{id}/events). Contract-time violations park in the dead-letter view, replayable after a contract fix.

Troubleshooting​

SymptomCauseFix
events missingdestination disabled for the sourceenable per-source routing in Segment
event dropped, delivery view says dropped_no_entity_idneither userId nor anonymousId on the callsend an id; Tessera never fabricates one
event dropped, delivery view says dropped_invalid_entity_idthe first id present (userId, else anonymousId) is not an identifier: a fractional number (12.5), a boolean, an object, or a whole number above 2^53 − 1 sent as a JSON number (the rule)send the id as a string; the other id is never used instead
event dropped, dropped_invalid_event_typeevent name can't normalize to ^[a-z][a-z0-9_.]* formadd an event_name_rules.renames entry for that name
alias/group not appearingacknowledged and ignored in v1expected — identity resolution is Phase 3
duplicated-looking eventsdifferent messageIds upstreamdedupe upstream or supply your own id property