IntegrationsDatabasesGoogle Bigtable

Google Bigtable

Datafly Signal writes first-party events as rows into a Google Cloud Bigtable table — high-throughput, low-latency NoSQL storage suited to time-series workloads.

Why route Google Bigtable through Datafly Signal

  • The data lands in infrastructure you own. Signal writes to your Google Bigtable directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
  • Personal data is hashed on the way in. Email, phone (shaped to E.164 first), first, last and full name and postcode are normalised (trimmed and lower-cased) and hashed with SHA-256 inside your own deployment, so the table holds email_sha256, phone_sha256 and the other digests rather than the raw values. Storing a raw value is a deliberate edit to the installed blueprint.
  • Only what the blueprint maps is stored. The properties Signal writes are exactly the fields the blueprint maps. Identify traits and client properties the blueprint does not map are never written, so a stray email in an event property cannot land in Bigtable in plain text.
  • Presets for Retail, Insurance, Travel, Media & Streaming, B2B / SaaS, Automotive and General. Each vertical preset names one field per property in the matching event spec, with canonical names (transaction_id, value, items), legacy aliases (order_id, revenue, products) read automatically, numbers typed as numbers and currency codes upper-cased.
  • Client IP and user agent are recorded as event context. Both come from the original visitor request rather than from your server, so geography and device analysis done in the warehouse reflects the actual visitor.
  • One consent decision governs every destination. Google Bigtable is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.

Prerequisites

Before configuring Google Bigtable in Signal, you need a GCP project with a Bigtable instance, a table with a column family, and a service account.

Create a GCP Account and Project

  1. Sign up at cloud.google.com if you don’t already have an account.
  2. Create a new project or select an existing one in the GCP Console.
  3. Note the Project ID.

Enable the Bigtable API

  1. Go to APIs & Services > Library.
  2. Search for Cloud Bigtable API and Cloud Bigtable Admin API.
  3. Click Enable for both.

Create a Bigtable Instance

  1. Go to the Bigtable console.
  2. Click Create instance.
  3. Enter an Instance name (e.g. datafly-events) and Instance ID.
  4. Choose the Storage type:
    • SSD — lowest latency (recommended for real-time workloads).
    • HDD — lower cost for large-volume storage.
  5. Configure clusters — select a region and the number of nodes (minimum 1 for development, 3+ for production).
  6. Click Create.

Bigtable pricing is based on the number of nodes and storage. A single-node development instance is suitable for testing but should not be used in production.

Create a Table with Column Family

  1. In the Bigtable console, click on your instance.
  2. Go to Tables > Create table.
  3. Enter a Table ID (e.g. events).
  4. Add a Column family named d (Signal’s default; set column_family if you choose another name).
  5. Click Create.

Alternatively, use the cbt CLI tool:

cbt -project your-project -instance datafly-events createtable events
cbt -project your-project -instance datafly-events createfamily events d

Create a Service Account

  1. Go to IAM & Admin > Service Accounts > Create Service Account.
  2. Enter a name (e.g. datafly-signal-bigtable).
  3. Grant the Bigtable User role (roles/bigtable.user).
  4. Click Done.

Generate a Service Account Key

  1. Click on the service account.
  2. Go to Keys > Add Key > Create new key > JSON.
  3. The key file will download. Store it securely.
⚠️

Store the JSON key file securely. Do not commit it to version control.

Configuration

FieldTypeRequiredDescription
project_idstringYesThe Google Cloud project ID that contains the Bigtable instance.
instance_idstringYesThe Bigtable instance ID.
tablestringYesThe Bigtable table ID to write rows into. Also accepts table_id.
column_familystringNoThe column family to write event columns to. Defaults to d; it must exist in the table.
row_key_layoutselectNotimestamp_first (default), visitor_first or hashed; see Schema. Choose before the first write.
service_account_jsonsecretYesThe full JSON key file content for a service account with roles/bigtable.user.

Signal Setup

Quick Setup

  1. Navigate to Integrations in the sidebar.
  2. Open the Integration Library tab.
  3. Find Google Bigtable or filter by Database.
  4. Click Install, select a variant if available, and fill in the required fields.
  5. Click Install Integration to create the integration with a ready-to-use default blueprint.

API Setup

curl -X POST http://localhost:8084/v1/admin/integration-catalog/google_bigtable/install \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Google Bigtable",
    "variant": "default",
    "config": {
      "project_id": "datafly-analytics",
      "instance_id": "datafly-events",
      "table": "events",
      "column_family": "d",
      "service_account_json": "{\"type\": \"service_account\", ...}"
    },
    "delivery_mode": "server_side"
  }'

Schema

Each event is written as one row with a single mutation. Every cell is in the configured column family with the event time as its cell timestamp. The row key depends on row_key_layout:

LayoutRow keyUse
timestamp_first (default)yyyymmddhhmmss#anonymous_id#message_id (client event time, UTC)The original key, for a table your readers already scan by time. At high write rates every write lands on the node holding the current second.
visitor_firstanonymous_id#reverse_ms#message_id, where reverse_ms is 9999999999999 minus the event’s Unix milliseconds, zero-paddedSpreads writes across visitors; a prefix scan reads one visitor’s events newest first.
hashedthe first four hex digits of SHA-256(anonymous_id), then the timestamp_first keySpreads writes over 65,536 prefixes while staying time-ordered within each.

Choose the layout before the first write: changing it later leaves the table’s keys in two layouts. The cells:

Column qualifierContents
event_typetrack, page, identify, and so on.
event_nameSignal event name (snake_case), for example purchase.
anonymous_idFirst-party visitor identifier.
user_idLogged-in user identifier; absent when not known.
propertiesThe fields the blueprint mapped, as JSON bytes (below).
contextThe event context as JSON bytes: page, device, campaign, consent, IP and user agent.
org_id, pipeline_idSignal identifiers.
message_idThe event’s message ID.
received_atTime Signal received the event (RFC 3339, UTC), when known.

A redelivered event writes the same row key and cell timestamps, so it overwrites itself.

⚠️

Google advises against row keys that start with a timestamp: sequential writes land on one node and create a hotspot. For a new table at event volume, choose visitor_first or hashed.

What the properties hold

The blueprint decides what properties contains, and only mapped fields are written (identify traits and unmapped client properties never are):

  • The event’s own fields from the preset sit at the top level, for example transaction_id, value, currency, items, insurance_type or policy_term_months.
  • The preset’s global block sits under @root: copies of the envelope (event_name, event_type, user_id, anonymous_id, event_id, message_id, event_timestamp, page_url, page_referrer, ip_address, user_agent, locale, timezone, source_id), utm_source, utm_medium, utm_campaign, the consent flags consent_analytics and consent_marketing, and the hashed identity email_sha256, phone_sha256, first_name_sha256, last_name_sha256, name_sha256.
  • identify adds name_sha256 and postal_code_sha256 at the top level.
  • An event the preset does not list stores its envelope and the @root block only.

For example, read the latest rows for one day with cbt (with the timestamp_first layout):

cbt -project your-project -instance datafly-events read events prefix=20260923 count=10

Mapped fields stay inside properties; they are not lifted to top-level fields. What else a row carries outside the blueprint (the whole event context) is under review in #1414.

Stored event context

Alongside the fields your blueprint maps (where email, phone, names and address are stored as normalised SHA-256 by default), each document carries the event’s collected context exactly as Signal received it. The blueprint does not filter or hash the context: this is deliberate (decision 2026-09-23), so the context is complete for attribution, fraud and debugging queries, and it is listed here so you can decide what to keep.

StoredWhat it contains
Visitor requestIP address, user agent, Accept-Language, locale and timezone
PageURL including its query string, path, title and referrer
CampaignUTM source, medium, campaign, term and content
ConsentThe visitor’s consent state
LocationGeoIP country, region and city, and the network (ASN)
DeviceScreen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them
Vendor IDsCookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID)
Session and librarySession ID, number and engagement time; the collector’s name and version
Partner attributesProfile attributes from partner enrichment (partner_attrs)

They are stored as the document’s context field (Bigtable: the context column), using the event context’s JSON field names, beside anonymous_id and user_id. Click and collected IDs outside the context are stored only where the blueprint maps them.

The IP address, device and advertising IDs, and cookie and click identifiers are personal data under UK and EU GDPR. To keep any of them out of storage, strip, mask or hash the field for every integration with the organisation data layer, apply URL redaction to page URLs, or remove it in the installed blueprint; and set the storage’s retention to match your privacy notice. See Personal data by integration.

Bigtable is a first-party destination under your control, gated by the same consent state as every other destination. Each stored event also records the visitor’s consent at collection time: the preset writes consent_analytics and consent_marketing under @root in the properties (the properties cell), and the full consent object is in the context. Use them in views or queries if your governance needs a per-row consent filter.

Testing

  1. Enable the integration in Signal and trigger a test event on your website.
  2. Use the cbt CLI to read recent rows:
cbt -project your-project -instance datafly-events read events count=10
  1. Or use the Bigtable console’s Query tab to inspect rows.
  2. In Signal, check the Live Events view to confirm delivery status shows as successful.

Troubleshooting

ProblemSolution
Events not appearing in the tableVerify the project ID, instance ID, table ID, and column family are correct.
Permission denied (403)The service account lacks the Bigtable User role. Add it in IAM & Admin > IAM.
Table not foundThe table does not exist in the specified instance. Verify the table ID.
Column family not foundThe column family name does not match. List column families with cbt -project ... -instance ... ls <table>.
Invalid service account JSONEnsure you pasted the complete JSON key file content, including all fields.
High latencyCheck the number of nodes in your Bigtable cluster. Add more nodes for higher throughput.
Row key hotspottingThe timestamp_first layout starts every key with the event time. For a new table choose visitor_first or hashed; on an existing table, watch the Key Visualizer and add nodes.

Visit Google Bigtable documentation for full API reference, schema design best practices, and monitoring guides.

See also