Customer-managed BigQuery
Customer-managed — “Your BigQuery” — is the self-serve
BigQuery model: you keep the data in your own
Google Cloud project and grant Twelfth read-only access. It’s inbound and
read-only, reached from Settings → Integrations → Google BigQuery.
The connection is configuration only. Twelfth stores the project, dataset and
location — nothing else. There is no secret to upload and none is stored:
access rides a dedicated Google service account that you grant read-only IAM
roles inside your own project. Revoke those roles and Twelfth can no longer read
anything, immediately, without touching Twelfth at all.
Only workspace owners and admins can connect, validate, or change a warehouse
connection, and BigQuery must be enabled for your workspace first (see the
BigQuery overview).
What you’ll need
Before you start, have these to hand:
- Your GCP project id (e.g.
my-project) — the project that will run the
queries and be billed for them. - The BigQuery dataset that holds your serving tables (e.g.
warehouse_au). - The dataset’s location
(e.g.australia-southeast1). This must match the dataset’s real location
exactly. - Permission in Google Cloud to grant two IAM roles — on the dataset and on the
project.
How Twelfth connects
You keep the data in your own Google Cloud project. Twelfth connects with a
dedicated Google service account and you grant that account read-only access.
Twelfth never receives, stores, or asks for a key, password, or JSON credential —
the identity lives on Twelfth’s side and the grant lives on yours.
Set up the connection
The connect page walks through five steps. You can leave and come back — the
saved connection, its last validation result, and your table selection persist.
1 · Where does the warehouse live?
Confirm the page offers Your BigQuery. If it says Twelfth managed, the
connection is already owned and operated by Twelfth and the setup fields are
intentionally unavailable.
2 · Point at the project and dataset
Enter three values and choose Save connection:
| Field | Example | Rules |
|---|---|---|
| GCP project id | my-project |
Lowercase, starts with a letter, 6–30 chars — a valid GCP project id |
| Dataset | warehouse_au |
Letters, numbers and underscores |
| Location | australia-southeast1 |
The dataset’s real BigQuery location |
Saving stores this configuration against your workspace. It does not yet
prove anything can be read — that’s the next step. If you later edit the values,
choose Update connection.
3 · Grant this principal read-only access
Twelfth shows the service-account principal to grant access to — copy it with
the copy button. Grant it exactly two roles:
| Role | Where you grant it | Why |
|---|---|---|
roles/bigquery.dataViewer |
On the dataset | Read the serving tables and their metadata |
roles/bigquery.jobUser |
On the project | Run the query jobs (they run on your project’s billing) |
These are the only two roles Twelfth needs, and both are read-only. dataViewer
lets Twelfth read; jobUser lets Twelfth run the SELECT that does the reading.
Grant dataViewer on the dataset (not the whole project) to keep the scope
tight. If the principal shows as pending, your dedicated service account is
issued when BigQuery is enabled for your workspace — grant access once it
resolves.
Once you’ve entered your project and dataset, the connect page shows Open
project IAM and Open dataset in BigQuery links that deep-link into the
Google Cloud console for your project — grant jobUser (and add the principal)
in IAM, and grant dataViewer from the dataset’s Sharing → Permissions.
To open the console by hand, swap your own ids into these URLs:
# Project IAM — grant jobUser, add the principal
https://console.cloud.google.com/iam-admin/iam?project=YOUR_PROJECT
# Dataset in BigQuery — grant dataViewer via Sharing → Permissions
https://console.cloud.google.com/bigquery?project=YOUR_PROJECT&d=YOUR_DATASET&page=dataset
4 · Prove the grants
Choose Run validation. Twelfth runs three staged checks, cheapest first —
each proves exactly one grant, so a failure points straight at the missing role:
| Check | What it proves | Fails when |
|---|---|---|
| Run query jobs in the project | A dry-run SELECT 1 — the principal can create jobs |
roles/bigquery.jobUser is missing on the project |
| Read the dataset | The dataset’s metadata is readable | roles/bigquery.dataViewer is missing, or the location is wrong |
| Discover serving tables | The expected webapp_* tables are found, with row counts |
The dataset doesn’t hold the serving contract, or earlier grants aren’t in place |
Later checks are skipped once an earlier one fails, so you fix one grant at a
time. The result is saved to the connection, so the page shows your last
validation when you return.
5 · Choose the serving tables
Validation discovers the tables and their row counts. Tick the ones you want
Twelfth to use and choose Save selection. Expand any table to review its
columns, types and descriptions before selecting it. Discovery only ever
shows allowlisted webapp_* tables (see What Twelfth reads),
and your selection can only narrow that list — it can never widen access
beyond the allowlist.
What Twelfth reads
Discovery is deliberately narrow. Even if you grant dataViewer on a dataset
that holds many tables, Twelfth only ever surfaces the serving contract — a
fixed allowlist of webapp_* end tables. Anything outside this list is invisible
to Twelfth and can’t be discovered, selected, or queried.
The serving-contract tables are:
| Table | Holds |
|---|---|
webapp_sku |
SKUs — name, department, category, sub-category |
webapp_outlet |
Outlets and their type |
webapp_supplier |
Suppliers |
webapp_sku_supplier |
SKU ↔ supplier links (and which is primary) |
webapp_sku_user |
SKU ↔ user assignments |
webapp_sku_performance |
Per-SKU metrics over time |
webapp_category_performance |
Per-category metrics over time |
webapp_supplier_performance |
Per-supplier metrics over time |
webapp_user_performance |
Per-user metrics over time |
webapp_recommendation_price_change |
Price-change recommendations |
webapp_recommendation_purchase_order |
Purchase-order recommendations |
webapp_recommendation_stock_transfer |
Stock-transfer recommendations |
If you don’t already produce the webapp_* contract, see
Transaction inbound, which
reads raw transaction and customer data instead.
How Twelfth queries your warehouse
Every read Twelfth makes goes through one bounded gateway, with the same guards
on every query — the connect page’s query console exercises this exact path:
- Read-only, single statement. Only a single
SELECT(or aWITH … SELECT)
is allowed. Anything else — multiple statements,INSERT,UPDATE,DELETE,
CREATE,DROP— is rejected before it ever reaches BigQuery. Twelfth cannot
write to your warehouse. - Cost gate. Every query is dry-run first to estimate bytes processed and
rejected if it exceeds the cap. BigQuery also enforces a hard
maximumBytesBilledserver-side (default 500 MB), so no query can bill
beyond the cap even if the estimate is off. - Row cap and deadline. Results are capped (10,000 rows by default) and each
query has a timeout (60s by default). - Runs on your billing. Because
jobUseris granted on your project, query
jobs run in your project and appear in your own
BigQuery job history
(swap in your project id).
Security and guarantees
- Read-only, always. Only single-statement
SELECTqueries run, enforced
before they reach BigQuery. Twelfth never writes to source systems. - Narrow by design. Discovery and queries are confined to the
webapp_*
serving allowlist; a broaddataViewergrant on a mixed dataset can’t leak
unrelated table names or data into Twelfth. - Your identity, your revocation. Remove the two IAM roles from the service
account and Twelfth loses all access instantly — no Twelfth-side step needed. - Location pinned. The dataset location is stored with the connection, so
reads stay in the region you chose. - One connection per workspace. A workspace holds a single live BigQuery
connection at a time.
Manage or change a connection
Return to Settings → Integrations → Google BigQuery at any time to:
- Update the project, dataset or location (choose Update connection),
then re-run validation. - Re-run validation after changing IAM grants — for example, after granting a
role the last run flagged as missing. - Change the serving-table selection and save it again.
Troubleshooting
When something’s wrong, Run validation tells you exactly which grant is
missing — read the first check that fails, not the last.
| Symptom | Likely cause | Fix |
|---|---|---|
| “Run query jobs in the project” fails | roles/bigquery.jobUser not granted on the project |
Grant roles/bigquery.jobUser to the shown principal, on the project |
| “Read the dataset” fails | roles/bigquery.dataViewer not granted, or the location is wrong |
Grant roles/bigquery.dataViewer on the dataset; confirm the location matches the dataset exactly |
| “Discover serving tables” fails, or finds fewer tables than expected | The dataset doesn’t hold the webapp_* serving contract, or the tables are named differently |
Confirm the dataset is the one that holds your webapp_* end tables |
| The principal shows “pending” | Your dedicated service account hasn’t been issued yet | Ask your Twelfth contact to enable BigQuery for the workspace, then reload |
| A banner says the gateway has no Google credential on this environment | The environment isn’t configured for warehouse access yet | The form still saves configuration; validation and queries become available once it’s configured |
| A query is rejected for processing too many bytes | The query would scan more than the cost cap | Narrow the query (fewer columns, a WHERE filter) |
Related
- BigQuery overview — the two models and three approaches
- Transaction inbound — read raw transaction data instead of the serving contract
- Outbound sync (inquiry only) — push Twelfth’s outputs to your own systems
Google Cloud reference
- BigQuery access control roles — what
dataViewerandjobUsergrant - Granting access to a dataset — Sharing → Permissions
- BigQuery locations — pick the right location value