Skip to main content

Connect RudderStack

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/rudderstack-destination); the RudderStack catalog listing is pending their integrations-repo review.

Route events from RudderStack into Tessera as a cloud destination (server-side; no device mode). The destination transformer maps track/identify/page calls onto canonical events with exactly the same mapping as the planned Segment destination — both call the one shared implementation, governed by the same template document — and batches them to POST /v1/events (500 events / 5 MiB per request).

alias and group calls are acknowledged and ignored (identity resolution ships in Phase 3), and RudderStack's messageId becomes the event's idempotency id, so retries never double-count.

1. Create the credential (least privilege)​

Mint a dedicated ingest-scope API key for RudderStack under Project Settings → API keys. Paste it once into the destination's API Key setting — RudderStack stores it as a secret and it is only ever sent as the Authorization bearer to https://ingest.staging.tessera.zone/v1/events.

2. Record the source in Tessera​

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

3. Add the destination in RudderStack​

In the RudderStack dashboard: Destinations → New destination → Tessera, connect your sources, and fill the settings:

SettingValue
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

Delivery semantics: rate limits (429 + retry_after_seconds) and transient errors are retryable — RudderStack redelivers and Tessera deduplicates on messageId; per-event rejections surface individually in the delivery view without failing the batch; intentionally ignored calls (alias/group, calls with no usable entity id) are marked filtered, not failed, with the counted reason.

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​

Use RudderStack's Live Events view to send one track, 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 not connected to the sourceconnect the source in the RudderStack dashboard
event filtered with dropped_no_entity_idneither userId nor anonymousId on the callsend an id; Tessera never fabricates one
event filtered with 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 filtered with dropped_invalid_event_typeevent name can't normalize to a valid event typeadd an event_name_rules.renames entry for that name
alias/group not appearingacknowledged and ignored in v1expected — identity resolution is Phase 3
traits missing on identifytraits nested under context.traits onlysupported — both trait locations map identically