Skip to main content

Connect data

Every connector and ingestion path produces the same thing: canonical events about your entities. Pick the paths that match where your data lives — most projects run a batch connector for history plus streaming for what happens next.

Setup availability​

This is the same capability matrix used by the website and console. Console setup means a supported console flow exists. API setup points to the supported guide; it does not imply a console credential form. Operator-controlled connector flags and your project permissions still apply. Planned and preview integrations are listed with their status.

IntegrationModeAvailabilitySetup details
BigQuerybatchConsole setupExtract on an explicit sync.
SnowflakebatchAPI setupAPI setup; console form planned. Requires the connector to be enabled by your operator.
DatabricksbatchAPI setupAPI setup; console form planned. Requires the connector to be enabled by your operator.
RedshiftbatchAPI setupAPI setup; console form planned. Requires the connector to be enabled by your operator.
PostgresbatchConsole setupRead-only extraction on an explicit sync.
MySQLbatchAPI setupAPI setup; console form planned. Requires the connector to be enabled by your operator.
Google Cloud StoragebatchConsole setupIngest objects on an explicit sync.
Amazon S3batchConsole setupIngest objects on an explicit sync.
Azure Blob StoragebatchPlannedPlanned for self-service setup.
File uploadbatchConsole setupUpload CSV or Parquet, then sync.
HTTPS ingestion APIstreamingConsole setupIngestion is asynchronous; real-time state updates require an active streaming scoring deployment.
KafkastreamingPlannedPlanned as a supported self-service bridge deployment.
Google Pub/Sub intakestreamingAPI setupCreate a stream source for a subscription you own through the API guide. The console source catalog does not list it yet.
Google Pub/Sub bridgestreamingPlannedPlanned as a supported self-service bridge deployment. To read a subscription you own, use the Google Pub/Sub intake.
Amazon KinesisstreamingPlannedPlanned as a supported self-service bridge deployment.
SegmentstreamingPlannedDestination implementation exists; public catalog availability is pending.
RudderStackstreamingPlannedDestination implementation exists; public catalog availability is pending.
Stripe eventsstreamingAPI setupConfigure the webhook adapter through the API guide.
Shopify eventsstreamingAPI setupConfigure the webhook adapter through the API guide.
Amplitude eventsstreamingAPI setupConfigure the webhook adapter through the API guide.
Mixpanel eventsstreamingAPI setupConfigure the webhook adapter through the API guide.
Python SDKstreamingPlannedPlanned. Send events over the HTTPS ingestion API today.
TypeScript SDKstreamingPlannedPlanned for Phase 2. Send events over the HTTPS ingestion API today.

Batch connectors extract when you request a sync. Sending events to the HTTPS API is asynchronous ingestion; it does not itself enable real-time scoring. Batch data needs a scoring refresh, while event-driven state updates need an active streaming scoring deployment. Always check the returned state freshness.

What every batch connector shares​

The flow is identical across kinds — the guides differ only in credentials and config:

  1. Create the source (POST /sources) with a least-privilege credential; credentials are envelope-encrypted, write-only, and never readable again.
  2. Test the connection (POST /sources/{id}/test-connection) — named checks with actionable failure detail.
  3. Look at a sample (POST /sources/{id}/sample, admins only) — up to 20 records of the source, read the way a sync reads them but with nothing landed, metered or stored. Long values are clipped, and values that look like credentials (password or token columns, keys, card numbers, the source's own credential) show as [redacted]. Records of entities you have erased are never shown. Postgres, MySQL, BigQuery tables (not views, materialized views or external tables), Amazon S3 and Cloud Storage sources support it; Snowflake, Databricks and Redshift sources do not yet, so run a sync and preview the landed data instead.
  4. Map columns (PUT /sources/{id}/mapping) to entity / event type / timestamp / attributes; preview with mapping:preview before committing.
  5. Sync (POST /sources/{id}/syncs) — land, profile, normalize; verify rows_rejected and the profile report. A row whose entity or event id is present but is not an identifier (12.5, true, NaN) is rejected and counted per column and reason in the sync's details (entity_ids_refused; the reasons).

Numeric ids stored before the canonical rule​

Tessera reads an id sent as a number as its digits (how identifiers become entity ids): a value 12.0 read from a floating-point or NUMERIC column is the id 12. Syncs before this rule stored such an event id as 12.0.

A source's event ids never change spelling. A column-mapped source whose snapshots from before this rule held floating-point or decimal values in its event_id_column keeps spelling its event ids the earlier way (12.0) on every sync: full and incremental syncs, a file uploaded again to the same source and a re-ingested bucket object. Reading its rows again therefore lands no event twice, and no sync is refused for its event ids. Tessera decides this once per source and event-id column, at the first sync since the rule that maps the column (an earlier sync that failed after landing its rows counts too), and keeps the decision for the life of the source. Mapping the event id to another column gets that column its own decision. Each sync of such a source says so in its details: {type: "legacy_event_id_spelling", column, snapshot_id}, where column is the column the decision was made for and snapshot_id the earliest snapshot that held those values. mapping:preview spells event ids the same way. Every other source, including every source created since the rule, spells event ids canonically (12); integer and text event-id columns are spelled the same either way. Entity ids always follow the rule.

The earlier spelling never brings back what the rule refuses: a NaN, infinite, fractional or boolean event id is rejected as invalid_event_id, and so is a whole number above 2^53 - 1 (its earlier spelling went through a floating-point value that several ids share). The sync's details count those rows (entity_ids_refused).

A new source for the same data is a different source: it spells event ids canonically, so its first sync lands those events again beside the ones the old source stored (deleting a source keeps what it landed). Upload a corrected file to the same source instead of creating a new one.

An event label whose label table reads the same number as 12 still attaches to the event 12.0 of a source that keeps the earlier spelling, and only to such a source's events: a text event id 12.0 from any other source is never matched by a label naming 12. That holds for a one-off upload that never synced again, too: each event dataset decides which sources keep the earlier spelling when it is built, and keeps that decision.

A full re-read of a source whose entity id column is numeric lands its events again under the canonical entity (12), next to the earlier entity (12.0), which keeps its own history: event ids derived by Tessera change with the entity, and a kept event id now names two entities. Both forms then need erasure for one customer (data deletion), so ask your operator first: they can count the stored entity and event ids in an earlier form, per source, label and crosswalk (counts only).

The first sync of a label table that was synced before this rule lands its observations again under the canonical id (12 beside the stored 12.000000000): that is what lets those labels join new events. Label stats count both forms, since the earlier ones are kept as stored; the sync's details count the re-landed rows per label as {type: "label_legacy_relanded", label_id, count}. If the sync fails after storing its label rows, the re-landed rows stay stored but no sync counts them (a retry skips rows already stored); the operator's counts include them.

Credential lifetime and rotation​

Every credential you give a batch connector is long-lived: Tessera stores it envelope-encrypted and uses it exactly as given, every time a connection test or a sync starts. Tessera does not refresh, renew or exchange it, and no batch connector accepts a session token or assumes a role on your behalf. A credential that expires on the provider's side stops working on that day until you replace it.

ConnectorWhat Tessera storesNotes
Amazon S3An IAM access key ID and secret access keyStatic keys only
BigQueryA service account key (JSON), or nothing with cross_project_grantThe cross-project grant stores no key
Cloud StorageNothingAccess is your bucket grant to the per-project service account
PostgreSQLA username and password, plus an SSH tunnel private key when a tunnel is on
MySQLA username and password, plus an SSH tunnel private key when a tunnel is on
RedshiftA database username and password, and optionally an access key pair for the unloaded objectsThe two halves of the key pair go together
SnowflakeAn RSA private key that Tessera generatesYou register the matching public key
DatabricksA personal access tokenTessera does not renew it

To rotate a credential without a gap:

  1. Create the new credential at the provider while the old one still works.
  2. Send it with PATCH /sources/{id}. The body's credentials object replaces the stored one entirely, so include every field the connector needs, including an SSH tunnel private key or an unload key pair you still use.
  3. Run POST /sources/{id}/connection-diagnostics and confirm that its credential checks pass.
  4. Revoke the old credential at the provider.

A sync that is already running keeps the credential it started with. The next connection test or sync uses the new one. The credential is never returned by the API: reads show only credentials_set: true. Snowflake differs in one step, because Tessera generates its key pair; see its page.

Connectors are read-only against your systems — no temp tables, no DDL, no writebacks. Every guide follows the same template, and each executable sample runs in CI against a seeded project.