<catalog> and <namespace> with the names Autumn assigned you.
Find your catalog and namespace
Autumn sends you both names when your Lakehouse is provisioned (see Connecting). If you need to rediscover them in ClickHouse Cloud, the catalog mounts as a database — list databases to find it:- ClickHouse
`<catalog>`.`<namespace>.v2_3_<object>` (the <namespace>.v2_3_<object> part is a single literal table name — see below).Identifier syntax
- ClickHouse
The Glue catalog mounts as a database. The whole A common mistake is
<namespace>.v2_3_<object> is the table name — the dot is literal, so backtick both parts:`<catalog>.<namespace>`.`v2_3_features` — that won’t resolve, because <namespace>.v2_3_features is a single identifier, not database.table. The single-quoted '<namespace>.v2_3_features' form does not resolve in ClickHouse Cloud either — use backticks on both parts.Timestamps
Allnumber (epoch ms) columns (created_at, started_at, expires_at, current_period_*, *_resets_at, …) are epoch-milliseconds. Convert before use. The only native timestamp is events.timestamp (the unversioned events table — see Schema → Events).
- ClickHouse
fromUnixTimestamp64Milli(toInt64(created_at)) also works and preserves millisecond precision. events.timestamp is already a DateTime — use it directly.JSON columns
metadata, config, processors, properties, deductions, display, and the discounts arrays are stored as JSON strings.
- ClickHouse
Nullable columns: don’t use IS NULL
Examples
Fetch one customer
- ClickHouse
Active subscriptions joined to their plan
Join on the internal ids, not the external ones (see below).- ClickHouse
A customer’s balances for a feature
Use the pooled (customer-level) rows wherecoalesce(entity_id, '') = ''.
- ClickHouse
Current-period overage
Overage is derived, not stored, and there are two figures — pick deliberately (see Working with balances → Overage):- Displayed (
max(0, Σusage − Σgranted)— sum first, floor once): what the balance header / dashboard shows. This query computes this one, so it reproduces the dashboard to within sync-lag drift. - Billable (
Σ max(0, usage − granted)— floor per row, then sum): what Autumn invoices (cusEntToInvoiceOverage). To get it, swap theoverageexpression as noted in the query.
v2_3_breakdowns over the same row set: rows whose product is an active subscription (plus always-live one_off rows), restricted to each entitlement’s current cycle (earliest upcoming reset).
Swap in your
<catalog> / <namespace> and the feature id. Known simplifications: it omits rollover / unused terms (zero for most customers, non-zero in general) and is pooled-only (coalesce(entity_id,'') = ''), so per-entity overage under config.disable_pooled_balance is not counted. For those customers, sum the per-entity rows instead.- ClickHouse
Invoice totals by status
- ClickHouse
plan_ids array with ARRAY JOIN:Event volume per day by subtype
- ClickHouse
Reconstruct a full customer (API shape)
This rebuilds the entireGET /customers/:id response — scalars, subscriptions, purchases, balances (with per-plan breakdowns), flags, and invoices — from the warehouse in a single query, returned as one JSON object. It’s the most useful query if you want the API’s customer view without calling the API.
ClickHouse-specific (uses groupArray, Tuple/Map casts, and the JSON type — ClickHouse 24.8+). Customer-level (pooled) balances and flags use entity_id = ''; per-entity rows are excluded from the customer envelope. FORMAT PrettyJSONEachRow emits one pretty-printed JSON object.
- ClickHouse
Cross-database joins
You can join your Lakehouse tables against your own data living elsewhere in the same engine.- ClickHouse
Qualify each side fully — the Iceberg catalog table and your own ClickHouse table:
The catalog connection is best for ad-hoc and bounded queries. For very large scans, filter early (by
env, time range, or id) or materialize a subset into a native table first.Important notes
Use internal ids, not external ids
This is the single most important rule for reliable queries.- Each plan version is its own row in
v2_3_plans, with its owninternal_id. The only identifier shared across versions is the externalplan_id. - External ids (
plan_id,customer_id,feature_id,entity_id, …) are mutable — you can rename them in Autumn at any time — and a single external id can map to multiple versioned rows.
- Join and filter on
internal_idand theinternal_*foreign keys (internal_customer_id,internal_feature_id,internal_product_id,internal_entity_id). These are immutable and globally unique. - Treat external ids as display-only — great for human-readable output, unreliable as join or lookup keys.
Pooled vs per-entity rows
v2_3_balances, v2_3_breakdowns, and v2_3_flags contain two kinds of row:
- Pooled (customer-level) —
entity_idis null. - Per-entity —
entity_idis set.
coalesce(entity_id, '') = '' to avoid double counting. To analyze a specific entity, filter on its entity_id (or internal_entity_id).
Customers with config.disable_pooled_balance track per-entity rather than pooled — for them, sum the per-entity rows instead of reading the pooled row. See Working with balances for what the aggregated values mean and the active + current-cycle filter you need before trusting them.