<catalog> and <namespace> (or <project> and <dataset> on BigQuery) with the names Autumn sent you. Examples on the other v2.6 pages are written as plain SQL against <namespace>.v2_6_<object>; this page shows how each engine spells them.
Addressing tables
- ClickHouse
- BigQuery
The Glue catalog mounts as a database, and the whole
<namespace>.v2_6_<object> is one table name with a literal dot in it. Backtick both parts:`<catalog>.<namespace>`.`v2_6_customers` does not resolve, and neither does the single-quoted form.Types
Every v2.6 column is one of five types. There are no array, map or struct columns.events.timestamp is the one native timestamp.
Timestamps
Columns typedlong (epoch ms) (created_at, started_at, expires_at, current_period_end, as_of and the like) count milliseconds since 1970-01-01 UTC. Convert them before you display or compare with dates:
- ClickHouse
- BigQuery
JSON columns
Objects and arrays are stored as JSON text in string columns, in the same shape the API returns. Columns typedstring (JSON) in the Schema Reference hold either:
- an object, such as
v2_6_customers.metadata = '{"region":"eu"}'orv2_6_features.display = '{"singular":"message","plural":"messages"}', or - an array, such as
v2_6_features.event_names = '["message_sent","message_edited"]',v2_6_invoices.plan_ids = '["pro","seats"]',v2_6_plan_items.price_tiersorevents.deductions.
- ClickHouse
- BigQuery
Nullable(String), and ClickHouse won’t build an array from a nullable value, so wrap them in ifNull(col, '[]') before JSONExtract(..., 'Array(...)') or JSONExtractArrayRaw.Price tiers end with
"to": "inf" (a string) on the last tier, and a number on the others. Read to as text, then convert the numeric ones.Nulls in ClickHouse
On Iceberg tables, ClickHouse can raiseNOT_FOUND_COLUMN_IN_BLOCK for WHERE col IS NULL. If you hit it, write coalesce(col, '') = '' for strings and coalesce(col, 0) = 0 for numbers instead. BigQuery handles IS NULL normally.
Rebuild a customer
Each part ofPOST /v1/customers.get comes from one table. Resolve the customer’s internal id once, then read each part with it. Join on org_id and env as well when you query more than one environment.
An entity (
POST /v1/entities.get) works the same way with the entity’s view: scope = 'entity' and its internal_entity_id.
The balances part, in each engine:
- ClickHouse
- BigQuery
Trials a customer has used
trials_used[] has no table of its own. A trial was used when a subscription had a trial and did not stay pending. The API also counts trials used by other customers with the same fingerprint:
What the lakehouse doesn’t have
Three parts of the customer are fetched from Stripe or computed when you call the API, so they are in no lakehouse version:rewards.discounts[], invoice_previews[], and the payment method and billing details.
Examples
Active subscriptions with their plan
Customers close to running out
Usage per day, by plan
events.deductions records which plan each unit of usage came from:
- ClickHouse
- BigQuery
Cross-database joins
Join lakehouse tables against your own data in the same engine by qualifying both sides. Join on your own customer id (customer_id), which is the id you share with Autumn, and filter early by env and time range on large tables such as events.
Important notes
- Join on internal ids. Inside the lakehouse, join on
internal_*ids,customer_plan_idandbalance_id, never oncustomer_id,plan_idorentity_id. Your own ids can be renamed, can be null (customer_id), are shared by every version of a plan (plan_id), or are unique only within one customer (entity_id). - Filter
env.liveandsandboxrows sit in the same tables. - Pick a balance view. See Working with balances.