{"id":8104,"date":"2026-08-11T22:02:00","date_gmt":"2026-08-11T20:02:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=8104"},"modified":"2026-10-24T03:14:10","modified_gmt":"2026-10-24T01:14:10","slug":"server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/","title":{"rendered":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery"},"content":{"rendered":"<p>Server-side enrichment adds selected backend attributes to a measurement event, such as a previously calculated customer-value segment. Internal CRM fields can then remain outside the browser. Moving processing to a server does not make data anonymous or automatically suitable for advertising platforms.<\/p>\n<h2>Less browser data, explicit boundaries<\/h2>\n<p>A customer number or hashed identifier remains personal or pseudonymized data when a link to an individual is possible. Hashing is not encryption or a general proof of anonymity. Session tokens can be particularly sensitive: access tokens and authentication cookies do not belong in analytics events.<\/p>\n<p>A suitable design separates a measurement identifier from authentication. The server must verify the permitted link to a customer account. A freely editable <code>user_id<\/code> in a public browser request must not select arbitrary CRM records. Correct identifier syntax alone does not establish authorization.<\/p>\n<table>\n<thead>\n<tr>\n<th>Stage<\/th>\n<th>Responsibility<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Browser<\/td>\n<td>Send required event fields and, where permitted, a pseudonymous measurement identifier.<\/td>\n<\/tr>\n<tr>\n<td>Verified backend<\/td>\n<td>Establish identity, event authorization and the permitted enrichment purpose.<\/td>\n<\/tr>\n<tr>\n<td>Enrichment database<\/td>\n<td>Provide a small set of approved attributes with an update timestamp.<\/td>\n<\/tr>\n<tr>\n<td>Destination tag<\/td>\n<td>Send allowed fields in the destination schema under the required consent rules.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<figure class=\"lw-diagram\"><img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/dia-8104-crm-20261024.png\" alt=\"Verified account mapping, minimal Firestore data, destination rules and explicit failure states.\"\/><figcaption>Verified account mapping, minimal Firestore data, destination rules and explicit failure states.<\/figcaption><\/figure>\n<h2>Firestore lookups and BigQuery calculations<\/h2>\n<p>Firestore is suitable for document lookups by key. Response time depends on factors including region, networking and load. Sub-millisecond or zero-delay performance is not guaranteed for an entire ssGTM request. Typical and tail latency need measurement under realistic load.<\/p>\n<p>BigQuery provides analytical calculations, for example a customer-value segment derived from order history. A separate monitored export job writes approved results into Firestore. Connecting the products does not create that export automatically. Calculation freshness, export failures and deletions need explicit handling. The request path then reads prepared values instead of running a complex warehouse query for every event.<\/p>\n<h2>A deliberately limited lookup example<\/h2>\n<p>The following code belongs in a custom server-side variable; it is not a complete template. Three template fields are required: <code>projectId<\/code> as fixed project configuration, <code>authorizedCustomerKey<\/code> from a verified server-side mapping, and <code>lookupAllowed<\/code> as a boolean decision from verified authorization and consent logic. Copying those values directly from browser fields does not meet the prerequisite.<\/p>\n<p>The separate <code>enrichment<\/code> collection contains only approved attributes. The exporter writes <code>updated_at_ms<\/code> as Unix time in milliseconds, not a Firestore Timestamp object. Segment values and the 24-hour freshness limit are example policies that need alignment with the actual data model.<\/p>\n<pre class=\"wp-block-code\"><code>const Firestore = require(&#x27;Firestore&#x27;);\nconst getType = require(&#x27;getType&#x27;);\nconst queryPermission = require(&#x27;queryPermission&#x27;);\nconst getTimestampMillis = require(&#x27;getTimestampMillis&#x27;);\n\n\/\/ Template inputs: trusted server-side mapping, never raw browser fields.\nif (data.lookupAllowed !== true) return {status: &#x27;not_allowed&#x27;};\nconst key = data.authorizedCustomerKey;\nif (getType(key) !== &#x27;string&#x27; || key.length &lt; 16 || key.length &gt; 128) {\n  return {status: &#x27;invalid_key&#x27;};\n}\nconst alphabet = &#x27;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-&#x27;;\nfor (let i = 0; i &lt; key.length; i++) {\n  if (alphabet.indexOf(key.charAt(i)) &lt; 0) return {status: &#x27;invalid_key&#x27;};\n}\nif (getType(data.projectId) !== &#x27;string&#x27; || data.projectId.length === 0) {\n  return {status: &#x27;invalid_project&#x27;};\n}\nconst path = &#x27;enrichment\/&#x27; + key;\nif (!queryPermission(&#x27;access_firestore&#x27;, &#x27;read&#x27;, {projectId: data.projectId, path: path})) {\n  return {status: &#x27;permission_denied&#x27;};\n}\nconst maxAgeMs = 86400000; \/\/ Example freshness policy: 24 hours.\nreturn Firestore.read(path, {\n  projectId: data.projectId\n}).then((result) =&gt; {\n  const record = result.data;\n  if (getType(record) !== &#x27;object&#x27; || record === null) {\n    return {status: &#x27;invalid_record&#x27;};\n  }\n  const timestamp = record.updated_at_ms;\n  const now = getTimestampMillis();\n  if (getType(timestamp) !== &#x27;number&#x27; || !(timestamp &gt; 0) || timestamp &gt; now) {\n    return {status: &#x27;invalid_timestamp&#x27;};\n  }\n  if (now - timestamp &gt; maxAgeMs) return {status: &#x27;stale&#x27;};\n  const segment = record.ltv_segment;\n  if (segment !== &#x27;low&#x27; &amp;&amp; segment !== &#x27;medium&#x27; &amp;&amp; segment !== &#x27;high&#x27;) {\n    return {status: &#x27;invalid_segment&#x27;};\n  }\n  return {status: &#x27;ok&#x27;, attributes: {ltv_segment: segment}};\n}, (error) =&gt; {\n  return {status: error &amp;&amp; error.reason === &#x27;not_found&#x27; ? &#x27;not_found&#x27; : &#x27;lookup_error&#x27;};\n});\n<\/code><\/pre>\n<p><code>Firestore.read<\/code> resolves to an object with <code>id<\/code> and <code>data<\/code>; document fields are under <code>result.data<\/code>. A missing document rejects with <code>reason: 'not_found'<\/code>. The code separates missing, stale, invalid and technically unavailable data. Unknown values do not become a zero margin or a standard customer.<\/p>\n<p>A destination tag may consume explicitly mapped attributes only when <code>status === 'ok'<\/code>. The internal status object is not an outgoing payload. A successful lookup also does not replace destination-specific permission. Each event needs a defined failure policy: send an already permitted base event without extra fields, or suppress forwarding.<\/p>\n<h2>Permissions and execution time<\/h2>\n<p>The built-in ssGTM Firestore API currently documents Native mode and only the <code>(default)<\/code> database. Isolation can use a separate project with its own default database. The template needs only read access through <code>access_firestore<\/code> for the specific project and intended path. Template restrictions complement IAM rather than replacing IAM.<\/p>\n<p>The runtime service needs a dedicated identity with minimal read permissions. The export job uses a separate writing identity. IAM controls these server accesses; browser Security Rules do not protect this path. A collection with few fields and a separate project boundary reduce the data exposed to a compromised service.<\/p>\n<p>A lookup extends the processing time of dependent tags. Whether the browser response also waits depends on the client and its response handling. The documented <code>Firestore.read<\/code> options do not include a custom <code>timeout<\/code>; unknown options are ignored. Sandboxed JavaScript promises have a documented five-second timeout. That limit is not a substitute for a deliberately short response budget. Stricter deadlines may require a separate lookup service or an asynchronous design.<\/p>\n<h2>Destination and operational checks<\/h2>\n<p>GA4 and Meta CAPI have different data models and requirements. A hash does not authorize arbitrary personal information in event parameters. Google offers specifically configured user-provided data features; those differ from ordinary User-ID and custom parameters. Margins and segments should reach only destinations that actually require the information.<\/p>\n<p>Operational checks cover hit rate, missing documents, stale exports, technical errors and latency distribution. Aggregate counters avoid unnecessary CRM contents in logs. Costs include reads, export writes, storage and potentially network traffic. The API cache is scoped to the current request or container execution, rather than acting as a global customer cache.<\/p>\n<h2>Questions and answers<\/h2>\n<h3>Why avoid a zero fallback?<\/h3>\n<p>An unknown value and a genuine zero have different meanings. A fallback can distort analysis. An error status and an omitted extra field preserve the distinction.<\/p>\n<h3>Does server processing guarantee no data leaks?<\/h3>\n<p>No. Browser responses, debug output, logs, exports and outgoing requests remain possible data paths. The concrete configuration and transmitted fields require inspection.<\/p>\n<h3>What has been tested in the example?<\/h3>\n<p>Twenty-nine isolated checks with simulated APIs cover input boundaries, read restrictions, missing and stale records, and the field allowlist. Those checks do not establish GTM sandbox compatibility, real IAM permissions, load behavior or vendor payload validity.<\/p>\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/server-side\/api\">Server-side tagging APIs<\/a><\/li>\n<li><a href=\"https:\/\/firebase.google.com\/docs\/firestore\/best-practices\">Firestore best practices<\/a><\/li>\n<li><a href=\"https:\/\/docs.cloud.google.com\/firestore\/docs\/security\/iam\">Firestore IAM<\/a><\/li>\n<li><a href=\"https:\/\/firebase.google.com\/docs\/firestore\/security\/rules-conditions\">Firestore Security Rules<\/a><\/li>\n<li><a href=\"https:\/\/support.google.com\/analytics\/answer\/9213390?hl=en\">GA4 User-ID<\/a><\/li>\n<li><a href=\"https:\/\/support.google.com\/analytics\/answer\/14077171?hl=en\">GA4 user-provided data<\/a><\/li>\n<li><a href=\"https:\/\/firebase.google.com\/docs\/firestore\/pricing\">Firestore billing<\/a><\/li>\n<li><a href=\"https:\/\/cloud.google.com\/bigquery\/docs\/introduction\">BigQuery overview<\/a><\/li>\n<li><a href=\"https:\/\/docs.cloud.google.com\/bigquery\/docs\">BigQuery documentation<\/a><\/li>\n<li><a href=\"https:\/\/www.edpb.europa.eu\/topics\/ai-and-technology\/anonymisation-pseudonymisation_en\">Pseudonymisation and anonymisation<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/server-side\/permissions\">Server-side template permissions<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/server-side\">Server-side tagging overview<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>How to enrich incoming analytics hits in Server-Side GTM with CRM attributes via Google Cloud Firestore without exposing PII in the browser.<\/p>\n","protected":false},"author":1,"featured_media":23319,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[91333,91279,91285,91407,91267,91114,91061],"class_list":["post-8104","post","type-post","status-publish","format-standard","hentry","category-digital-marketing","tag-bigquery","tag-prywatnosc-danych-pl","tag-first-party-data","tag-google-cloud","tag-google-tag-manager","tag-pii-protection","tag-server-side-gtm"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-11T20:02:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-24T01:14:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-8104-server-side-gtm-data-enrichment-fire-g.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Server-Side GTM: CRM Enrichment with Firestore and BigQuery\",\"datePublished\":\"2026-08-11T20:02:00+00:00\",\"dateModified\":\"2026-10-24T01:14:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/\"},\"wordCount\":897,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-8104-crm-20261024-dark.png\",\"keywords\":[\"BigQuery\",\"Data Privacy\",\"First-Party Data\",\"Google Cloud\",\"Google Tag Manager\",\"PII Protection\",\"Server-Side GTM\"],\"articleSection\":[\"Digital Marketing\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/\",\"name\":\"Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-8104-crm-20261024-dark.png\",\"datePublished\":\"2026-08-11T20:02:00+00:00\",\"dateModified\":\"2026-10-24T01:14:10+00:00\",\"description\":\"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-8104-crm-20261024-dark.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-8104-crm-20261024-dark.png\",\"width\":1200,\"height\":630,\"caption\":\"Server-Side GTM: CRM Enrichment with Firestore and BigQuery\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-marketing\\\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Server-Side GTM: CRM Enrichment with Firestore and BigQuery\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik","description":"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/","og_locale":"en_US","og_type":"article","og_title":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik","og_description":"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-08-11T20:02:00+00:00","article_modified_time":"2026-10-24T01:14:10+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-8104-server-side-gtm-data-enrichment-fire-g.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery","datePublished":"2026-08-11T20:02:00+00:00","dateModified":"2026-10-24T01:14:10+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/"},"wordCount":897,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-8104-crm-20261024-dark.png","keywords":["BigQuery","Data Privacy","First-Party Data","Google Cloud","Google Tag Manager","PII Protection","Server-Side GTM"],"articleSection":["Digital Marketing"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/","name":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-8104-crm-20261024-dark.png","datePublished":"2026-08-11T20:02:00+00:00","dateModified":"2026-10-24T01:14:10+00:00","description":"Enrich Server-Side GTM events with CRM data: compare Firestore and BigQuery, look up attributes and handle missing values.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-8104-crm-20261024-dark.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-8104-crm-20261024-dark.png","width":1200,"height":630,"caption":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/server-side-gtm-data-enrichment-real-time-crm-lookups-via-firestore\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Server-Side GTM: CRM Enrichment with Firestore and BigQuery"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/8104","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=8104"}],"version-history":[{"count":8,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/8104\/revisions"}],"predecessor-version":[{"id":23321,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/8104\/revisions\/23321"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/23319"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=8104"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=8104"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=8104"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}