Your data, in your own bucket.
Every night SegmentHub writes events, profiles, visitors and campaign statistics to storage you own, in a format your warehouse loads without a conversion step. The bucket, the credentials, the storage bill and the retention policy are all yours — the same principle as bringing your own mail relay and your own ad credentials.
acme/events/
dt=2026-09-05/
part-00001.parquet
part-00002.parquet
_load.sql
_manifest.json
dt=2026-09-06/
part-00001.parquet
_manifest.json
acme/profiles/
acme/email_campaign_stats/
Six datasets, every day
Each with a fixed, published column contract — because once a partition is loaded, those names are in someone's models and dashboards.
events
by event time
profiles
by last seen
visitors
by last seen
campaign_stats
on-site
email_campaign_stats
daily rows
push_campaign_stats
daily rows
Your own attribute names and event parameters travel in a JSON column rather than widening the table per account, so every tenant loads into the same fixed schema and a new attribute never breaks yesterday's model.
Three formats, one layout your tools already understand
Parquet
Snappy-compressed columnar files with the best storage and scan economics. Arrays stay real arrays; JSON columns are strings your warehouse parses on read.
NDJSON
Gzipped, one object per line, with nested structure intact. Loads everywhere, and a human can decompress a part and read it — which matters more than it sounds at 3am.
CSV
Gzipped and flattened, for the spreadsheet and the legacy loader. Escaping is done properly, because attribute values routinely contain commas, quotes and newlines.
Hive-style partitions
Files land under dt=YYYY-MM-DD/, which BigQuery, Athena, Redshift Spectrum, Spark and DuckDB all discover as a partition column with no configuration. Data files start with part- and metadata with an underscore, so a wildcard load can never accidentally parse the manifest as data.
The manifest is the commit marker
Parts are written first and the manifest last, so "is this partition safe to load" is one cheap test. It carries the schema, per-file row counts, byte counts and checksums, and a line saying where the rows came from — which is the answer to the first question anyone asks about a light-looking day.
A partition is a unit of work that can be redone
The scheduled export, a ninety-day backfill, a retry after a bucket outage and a re-export you queue by hand are all the same code path reaching the same ledger row. That is why none of them can double a row in your warehouse.
Backfill on request
Events are re-read from the platform's own event archive rather than teed off the live stream, so any day the archive still holds can be exported today — and they are partitioned by when each event happened, not when it was archived.
Replace, never append
A re-run deletes the stale parts a shorter run leaves behind before it writes the manifest, so "replace the partition" is true rather than approximately true. Today is never exported: its partition is not closed yet.
Never at the cost of the platform
The sweep runs every fifteen minutes on its own worker pool with a per-account ceiling, so one tenant catching up on three months cannot slow the indexing your audience queries depend on.
Into the warehouse, not just into a bucket
BigQuery loads natively. Each day targets its own table partition and replaces it, so a retry corrects a day rather than doubling it. The schema is passed explicitly rather than auto-detected — a day where one column happened to be empty would otherwise land as a different type and break the loads weeks later.
Snowflake, Athena and Trino get ready-to-run SQL. Every partition can carry a generated script with a COPY INTO, a LOAD DATA and a CREATE EXTERNAL TABLE, written from the same schema the files were written with, so the two cannot disagree.
An existing table is left alone. You may have clustered it, added columns or set a retention policy, and none of that is ours to overwrite.
The connection test writes and deletes a real object. It is the only honest check — a read-only key passes everything else and then fails at 03:00 on a partition nobody is watching.
Partition ledger
One row per dataset and day, not per attempt. A failed partition backs off and retries on its own; the panel can queue any date range for re-export. Illustrative interface values.
Answer "can we get our data out?" with a bucket path.
Pair it with the reporting module for day-to-day questions and the REST API for everything in between.
Set up your first export