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.
| Integration | Mode | Availability | Setup details |
|---|---|---|---|
| BigQuery | batch | Console setup | Extract on an explicit sync. |
| Snowflake | batch | API setup | API setup; console form planned. Requires the connector to be enabled by your operator. |
| Databricks | batch | API setup | API setup; console form planned. Requires the connector to be enabled by your operator. |
| Redshift | batch | API setup | API setup; console form planned. Requires the connector to be enabled by your operator. |
| Postgres | batch | Console setup | Read-only extraction on an explicit sync. |
| MySQL | batch | API setup | API setup; console form planned. Requires the connector to be enabled by your operator. |
| Google Cloud Storage | batch | Console setup | Ingest objects on an explicit sync. |
| Amazon S3 | batch | Console setup | Ingest objects on an explicit sync. |
| Azure Blob Storage | batch | Planned | Planned for self-service setup. |
| File upload | batch | Console setup | Upload CSV or Parquet, then sync. |
| HTTPS ingestion API | streaming | Console setup | Ingestion is asynchronous; real-time state updates require an active streaming scoring deployment. |
| Kafka | streaming | Planned | Planned as a supported self-service bridge deployment. |
| Google Pub/Sub intake | streaming | API setup | Create a stream source for a subscription you own through the API guide. The console source catalog does not list it yet. |
| Google Pub/Sub bridge | streaming | Planned | Planned as a supported self-service bridge deployment. To read a subscription you own, use the Google Pub/Sub intake. |
| Amazon Kinesis | streaming | Planned | Planned as a supported self-service bridge deployment. |
| Segment | streaming | Planned | Destination implementation exists; public catalog availability is pending. |
| RudderStack | streaming | Planned | Destination implementation exists; public catalog availability is pending. |
| Stripe events | streaming | API setup | Configure the webhook adapter through the API guide. |
| Shopify events | streaming | API setup | Configure the webhook adapter through the API guide. |
| Amplitude events | streaming | API setup | Configure the webhook adapter through the API guide. |
| Mixpanel events | streaming | API setup | Configure the webhook adapter through the API guide. |
| Python SDK | streaming | Planned | Planned. Send events over the HTTPS ingestion API today. |
| TypeScript SDK | streaming | Planned | Planned 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:
- Create the source (
POST /sources) with a least-privilege credential; credentials are envelope-encrypted, write-only, and never readable again. - Test the connection (
POST /sources/{id}/test-connection) — named checks with actionable failure detail. - 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. - Map columns (
PUT /sources/{id}/mapping) to entity / event type / timestamp / attributes; preview withmapping:previewbefore committing. - Sync (
POST /sources/{id}/syncs) — land, profile, normalize; verifyrows_rejectedand 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'sdetails(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.
| Connector | What Tessera stores | Notes |
|---|---|---|
| Amazon S3 | An IAM access key ID and secret access key | Static keys only |
| BigQuery | A service account key (JSON), or nothing with cross_project_grant | The cross-project grant stores no key |
| Cloud Storage | Nothing | Access is your bucket grant to the per-project service account |
| PostgreSQL | A username and password, plus an SSH tunnel private key when a tunnel is on | |
| MySQL | A username and password, plus an SSH tunnel private key when a tunnel is on | |
| Redshift | A database username and password, and optionally an access key pair for the unloaded objects | The two halves of the key pair go together |
| Snowflake | An RSA private key that Tessera generates | You register the matching public key |
| Databricks | A personal access token | Tessera does not renew it |
To rotate a credential without a gap:
- Create the new credential at the provider while the old one still works.
- Send it with
PATCH /sources/{id}. The body'scredentialsobject 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. - Run
POST /sources/{id}/connection-diagnosticsand confirm that its credential checks pass. - 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.