Our Meta API Tag for ssGTM is here – open source, built on first principles, and focused on the full customer journey

When we launched Datomni, implementing Meta conversion tracking for performance marketing quickly became one of our most common tasks, driven by our clients’ substantial ad spend on the platform.

Despite working with various conversion API tags from different providers over the years, we hadn’t found a solution that aligned with our vision of reporting conversions to Meta in a unified way across the full conversion journey. That led us to build our own tag, and in this article, we’ll explain the motivations and details behind it.

In this article, we won’t dive deep into the technical details of the Meta Conversion API. If you want more implementation and technical details, check out our Omni CDI documentation for this tag or visit Github for more detailed info.

Why conversion tracking on the main platforms is going server-side 

Before we dive deep, and at the risk of stating the obvious, let’s first understand why we’re looking at server-side tracking for Meta conversions and why it matters.

With increasing privacy regulations and browser restrictions like ITP (Intelligent Tracking Prevention), traditional tracking methods have become less effective. Server-side tracking through tools like Server-Side Google Tag Manager (GTM) offers a robust alternative by:

  • Increasing the accuracy of data collection.
  • Mitigating the impact of ad blockers.
  • Improving page load performance.

Running your key data pipelines on the server also provides:

  • Increased control over data flow.
  • Better monitoring of tagging and event tracking lifecycle.
  • Ability to enrich data pipelines with additional data points (subject to user consent).

That said, let’s clear something up: Just because you’re running things on the server side doesn’t mean you can skip asking users for consent or ignore privacy rules. In fact, it’s the opposite – you need to be even more careful! When user data hits your servers (instead of going directly to, say, Meta), you’re the one responsible for handling it properly.

You can’t just shrug and say “oh, that’s Meta’s servers handling the data” – it’s all on you now.

What we disliked about legacy Meta Conversion API implementations and tags

We’ve tested many different implementations of Meta Conversion API both standard and open source and something was always not right. 

Here’s the list of things that had us especially frustrated when it comes to using paid or open-source tags for Meta Conversion API. 

Note: These observations are based on our experience building full customer journey data infrastructures. They are judgment calls and may not apply to all use cases.

Low data quality

Most existing tags only partially implement Meta Conversion API’s data model, limiting effective user matching. Even for supported data points, we encountered formatting issues that could impact matching quality:

  • Inconsistent text formatting (e.g., non-lowercase city names and zip codes) 
  • Missing standardization in data preparation 
  • Incomplete implementation of Meta’s recommended formats

We found that many tags overlook crucial parameters. For example, no existing solution we tested properly implements the external_id parameter in Facebook Ads – a vital element for cross-source event stitching.

Disregard for development environments leading to data spoiling

Legacy tags often ignore different development environments (e.g., dev, staging, prod), causing data contamination in Meta. Without controls for each environment, testing and production data mix, leading to inaccurate insights, broken tracking, and troubleshooting delays. This lack of environment awareness hurts data integrity and slows down issue resolution.

Discontinuous customer journey

When implementing full-funnel marketing analytics, we found that existing solutions don’t cut it for detailed customer journey reporting in Meta Ads. This limits the ability to optimize ads across the entire customer journey.

While most tags implement Meta’s recommended events, they miss the mark in two key areas:

  1. Non-web data sources
    • Limited ability to ingest CRM data
    • Poor handling of offline conversions
    • Weak integration with non-web touchpoints
  2. Data matching quality
    • Low-quality event matching across sources
    • Insufficient user identification across touchpoints
    • Poor optimization due to incomplete data correlation

The lack of proper data integration and poor matching quality makes it almost impossible to optimize ads effectively based on downstream or offline conversions. This creates a major roadblock for full-funnel marketing optimization.

Incomplete Customer Journey Reporting: A Practical Example

Let’s break down a typical B2B customer journey:

  1. Initial visit
    • User clicks Facebook ad, visits the site, reads blog content
    • Basic event tracking works fine here
  2. Lead generation
    • User comes back via retargeting ad, submits a contact form
    • Tags handle this well
  3. Sales process (where current tags fail)
    • Sales team contacts lead via CRM
    • Multiple calls, offline contract negotiations, deal closes in CRM

Problems
Current tags struggle to:

  • Link CRM deal closure back to the original Facebook ad
  • Match offline data with web visitor data
  • Report deal values to Meta accurately
  • Connect multiple touchpoints to a single customer

Business Impact

  • No way to optimize Facebook ads for the actual deal closure
  • Can’t track true ROI from acquisition campaigns
  • Hard to build lookalike audiences based on high-value customers
  • No clear view of which ads drive real business results

This shows how traditional tags fail when tracking beyond simple web conversions. The result? Limited full-funnel optimization.

Logging and monitoring issues are causing serious maintenance and customization pain

Implementing Meta Conversion API is straightforward, but maintaining and troubleshooting integrations is challenging. Our experience with existing solutions, particularly open-source tags, reveals operational flaws that make management unnecessarily difficult.

The biggest problem is the lack of structured logging and monitoring. Current solutions typically offer:

  • Minimal or no execution logs
  • Silent failures with no alerts
  • Inconsistent error reporting
  • No standardized debugging process

This is a major headache, especially when tracking complex customer journeys. For example, if a high-value conversion doesn’t register, figuring out why requires visibility into multiple steps:

  • Data reception
  • Payload transformation
  • API submission
  • Response handling

Existing solutions often don’t log these steps, making it almost impossible to trace where the problem is.

Tag architecture flaws

We’ve also identified several structural problems:

  • No standardized execution: Most tags don’t follow a clear, consistent process for handling events, making troubleshooting inconsistent and slow.
  • Limited customization: Changing business needs require major code adjustments due to rigid architectures.
  • Poor error handling: Many implementations fail silently or give vague error messages, leaving teams in the dark until it affects key metrics.

These issues cause real business problems. Teams are reluctant to integrate new data sources or implement advanced tracking because troubleshooting takes too long and maintenance costs are unpredictable

Over-reliance on customized data clients for GTM Server to get that vendor lock-in juice flowing

Let’s be real—one of the most frustrating aspects of server-side GTM setups is how vendors complicate things with custom data clients. These “special” clients usually add no real value. They’re just different for the sake of being different.

Here’s the deal:

  • Vendors create their own custom data clients
  • They make tags work only with their version
  • Now, you’re stuck using their entire ecosystem

It’s like buying a cheap printer, only to realize you’re locked into paying for their overpriced ink forever. Same game, different industry.

This creates real problems:

  • Want to make a simple change? Good luck—better plan for a whole sprint.
  • Adding a new integration? Better dive into endless vendor-specific docs.
  • Your team has to learn multiple vendor systems.
  • Documentation? Not exactly easy to follow.
  • Changes feel like defusing a bomb.
  • What should take hours takes weeks.
  • Everyone’s afraid to touch anything because no one knows what will break.

Introducing Omni Meta Conversion API, a part of the Omni Activation and Omni CDI 

We’re excited to introduce Omni Meta Conversion API, one of the components of our Omni CDI (Customer Data Infrastructure) ecosystem. This solution enables comprehensive Meta conversion tracking across your entire customer journey, without the typical vendor lock-in headaches.

What are the core benefits? Let’s see.

  • Track full customer journeys from first click to final conversion
  • Maintain complete control over your data flows in the tag
  • Integrate any data source seamlessly without relying on tweaks in the data client level

In our tag, we focus on the following values:

  • Modularity: Each layer operates independently
  • Flexibility: Optional features like identity resolution
  • Reliability: Clear validation at each step
  • Maintainability: Structured error handling and logging
  • Scalability: Easy to extend with new features

Remembering that we’re building on the shoulders of giants and writing this from the perspective of wanting to move the thinking about GTM Server tags one step forward, let’s take a closer look at how we’ve approached the problems we’ve identified.

Attention to data quality

When building this tag, we went full nerd mode and broke down every requirement Meta has for data formatting and standardization to maximize matching. From phone country codes that can’t include special characters to first names that must be lowercase and currency symbols that must be uppercase, we covered it all. These formatting requirements aren’t complicated, but there are a lot of them, and not applying them properly can significantly lower Meta’s matching rates.

A good example of it is how we treat email formatting:

// User data preparation functions
function prepEmail(email) {
    if (!email || !isValidEmail(email)) return null;

    const normalized = normalize(removeSpaces(email));
    if (!normalized) return null;
    
    if (isOmniIdentityResolutionEnabled) {
        omniIdentityObject.email = normalized;
    }

    return hash(normalized);
}

One goal we had was to make it easy for you to add custom formatting or filtering functions without rebuilding the entire tag. That’s why all you need to do is add your custom formatting function and apply it to specific properties, just like we did with email above.

We also focused on maximizing data quality, not just formatting values but ensuring they bring real business value.

Another area we tackled was the payload structure for the Meta Conversion API. GTM Server tags still have room for improvement here, so we built an interface that takes the burden of remembering how to structure the payload off the user. For example, if you select the purchase event, the tag won’t let you save unless you also provide a value that isn’t empty. The tag will also not let you send this event if the value is 0. This ensures the most critical data points are always accurate.

function prepValue(value) {
    if (!value) return null;
    let convertedValue = makeNumber(removeSpaces(cleanValue(value)));
    if (convertedValue === 0 || convertedValue === null) {
        return null;
    }
    return convertedValue;
}

Another example is that if you select website as the value for action_source, you’ll also need to input the URL and user agent. These steps aren’t hard, but they enhance the overall experience of implementing the tag.

Environment specific

Our tag keeps data separate across all environments (dev, staging, prod). This prevents data from mixing and ensures accurate reporting at every stage, avoiding any data spoilage during testing or production.

Integration with external identity resolution and enrichment service

The Omni Meta Conversion API tag features a two-step request that enhances conversion data quality before sending it to Meta. Rather than directly forwarding events to Meta’s servers, the tag first interacts with Omni’s Identity Resolution service to enrich the payload with additional user identification data.

The integration works in the following sequence:

Initial payload construction

  • The tag first builds a basic event payload with standard Meta Conversion API parameters
  • It collects user identifiers (email, phone, external_id, etc.) and hashes them per Meta’s requirements
  • These identifiers are simultaneously stored in an omniIdentityObject

Identity resolution call


function generateOmniIdentityEnrichment(eventPayload) {
    // Builds identity payload with:
    // - Platform identifier
    // - Anonymous ID (client_id or custom)
    // - Collected identifiers (email, phone)
    // - Event information for valuation
    // - Visit counting flag

Enrichment process

  • The Omni Identity service receives these identifiers and matches them against its identity graph
  • Returns a unified identifier (uuid) and potentially additional user information such as Meta attribution parameters, fbc or fbp.
  • The service can return enriched email addresses if they exist in the identity graph but weren’t in the original payload to enrich it and improve matching.

Payload enhancement

function applyOmniIdentityEnrichment(response, eventPayload) {
    // Enhances the payload with:
    // - Unified ID as external_id (optional)
    // - Additional email hashes and data points (optional)
    // - Maintains original payload structure

Key Benefits:

  • Provides cross-device and cross-browser user identification
  • Enriches Meta conversion data with additional user identifiers
  • Improves conversion attribution accuracy
  • Maintains user privacy through hashing and secure data handling
  • Can be deployed on private infrastructure for data sovereignty

The integration is optional and configurable through tag settings, allowing clients to control the level of enrichment and data sharing.

Clear tag execution flow

To us, one of the most important things in any server-side tag is having a clear execution flow. This ensures the audience understands exactly what’s happening at each step. Here’s a breakdown of the standard flow:

  • Raw data acquisition
  • Data processing
  • Payload assembly
  • Optional enrichment
  • Payload structuring
  • Validation
  • Dispatch

This flow represents each phase of the tag’s execution, ensuring clarity and consistency throughout the process.

Let’s take a closer look at the process.

We start with clean data ingestion from GTM Server-Side data client, supporting various environments. Then all incoming data goes through specific preparation functions, including cleaning, formatting, standarization, normalization, which we’ve already touched upon. Then we’re allowing for optional enrichments happening in real-time, in which case it’s the identity enrichment that we’re going to cover later. These enrichments are optional and can be turned off without the loss of functionality of the tag. we perform payload assembly using a mapping system:

const userDataMappings = {
    'em': () => prepEmail(data.fbEmail),
    'ph': () => prepPhone(data.fbPhone),
    // ... other mappings
};

Before anything is sent out, we perform thorough validation on the most critical elements and check for data consistency. For example, if you select LDU validation, we verify that you have a complete golden record of information.


    // LDU validation
    if (payload.data_processing_options === 'LDU') {
        if (!payload.data_processing_options_country) {
            const msg = 'Missing data_processing_options_country for LDU';
            digestLog('Error', 'Validation', 'Meta Conversion API', {
                message: msg
            });
            return false;
        }
        if (!payload.data_processing_options_state) {
            const msg = 'Missing data_processing_options_state for LDU';
            digestLog('Error', 'Validation', 'Meta Conversion API', {
                message: msg
            });
            return false;
        }
    }

Making tag debugging actually pleasant: our logging system

Every time our tag runs, it generates a unique trace ID that follows the event through its entire lifecycle. Think of it as a tracking number for your tag execution. Here’s how we create it.

const generateTraceId = (eventDataTemp) => {
    if (currentExecutionTraceId) {
        return currentExecutionTraceId;
    }

    tagInitializationTime = getTimestampMillis();
    
    const traceString = [
        eventDataTemp.event_name,
        eventDataTemp.client_id,
        adjustedTimestamp
    ].join('_');

    currentExecutionTraceId = sha256Sync(traceString, { outputEncoding: 'hex' });
    // ...
};

The unique trace ID is used throughout every step — from initialization to payload construction, enrichment, validation, API communication, and completion. Each step is logged with detailed context, including timestamps, process names, and relevant metadata. The system supports both debug console output and adapts to different execution environments (e.g., dev, prod). Error tracking captures the full context for efficient troubleshooting. This approach ensures complete transparency into tag behavior, enabling fast issue resolution and performance optimization.

This logging framework makes tag behavior fully transparent and debuggable while providing the metrics needed for monitoring and optimization

Integrated external monitoring

One of the biggest challenges we faced, especially when working with multiple data structures, was monitoring tag execution at scale. Our tags solve that problem.

The tag integrates seamlessly with two key monitoring systems outlined below via the digestToOmniMonitor function.

Omni Monitoring System
This system is included in all Omni CDI deployments. It provides real-time visibility into tag execution statuses, API performance, data validation results, success/failure rates, and processing times.

Central Error Monitoring
An anonymous error logging system that aggregates issues across implementations. It detects common failure patterns, enables proactive issue resolution, and informs our roadmap. You can opt out of sending your tag’s data to this system, but we recommend enabling it.

Both systems are built on Snowplow with custom schemas designed to match log message structures.

A key feature of both monitors is the unique trace ID for each execution, which matches the trace ID shown in the GTM Server preview console log.

Adaptable with standard GA4 Data Client and Omni Data Client — no vendor lock-in

The Omni Meta Conversion API Tag is designed for maximum flexibility. It integrates easily with existing setups and doesn’t lock you into using specific tools.

  1. Works with GA4 Data Clients
    Fully compatible with Google Analytics 4 data clients, no extra setup required.
  2. Integrates with Omni Data Clients
    If you’re using Omni tools, you can plug Omni Data Client to capture events for this tag.
  3. Supports Any Structured Data Source
    The tag works with any properly formatted data source—CRM, e-commerce platform, or custom pipelines.
  4. No Proprietary Formats Needed
    There’s no need for special data formats. Use standard, widely accepted structures.

Now that we’ve explained what we’ve built and why, let’s look at an example to explore the practical differences between our tag and others.

Conversion journey comparison: traditional vs Omni Meta API Tag

Scenario: multi-channel purchase journey

Sarah, a customer, interacts with a business across four touchpoints:

  1. Call: She calls using a service like CallRail to inquire about a product.
  2. Website Signup: A week later, she visits the website and signs up as a lead using her email and phone number.
  3. CRM Contract: After multiple follow-ups, she signs a $1,500 contract logged in the CRM.
  4. In-Store Purchase: Months later, she makes a $200 in-store purchase using her loyalty card tied to her phone number.

Journey A: traditional server-side implementation

Let’s examine the key, matching-related payload sections of events sent to Meta Conversion API along this journey, assuming a standard server-side implementation. We assume very advanced implementation which actually sends events from all channels to Meta, and that in itself is a rarity due to the reasons mentioned above as well as challenges with the typical data collectors, which we’ve described in the article that launched our own data collector.

The business sends events to the Meta Conversion API:


1. Calling Event
{
    "event_name": "Contact",
    "user_data": {
        "ph": "[hashed_phone_number]"
    }
}

2. Website Event
{
    "event_name": "Lead",
    "user_data": {
        "em": "[hashed_email]",
        "ph": "[hashed_phone_number]",
        "client_ip_address": "123.45.67.89",
        "client_user_agent": "[browser_data]",
        "fbp": "fb.1.1234567890",
        "fbc": "fb.1.1234567890.abcdef"
    }
}

3. CRM Event
{
    "event_name": "Purchase",
    "user_data": {
        "em": "[hashed_email]"
    },
    "custom_data": {
        "value": 1500,
        "currency": "USD"
    }
}

4. In-Store Purchase
{
    "event_name": "Purchase",
    "user_data": {
        "ext_id": "[loyalty_card_number]"
    },
    "custom_data": {
        "value": 200,
        "currency": "USD"
    }
}

Result:

  • Meta cannot provide full-funnel ad optimization at maximum efficiency because:
    • All events lack a common denominator in the form of unified identifier.
    • The CRM and in-store events also lack attribution properties making it hard to link them to the website signup or the original ad click.
  • Outcome:
    • Only the website signup is tied to the ad click, underreporting the $1,700 total conversion value.
    • Meta’s algorithms and reporting are deprived of critical data for optimization and ROI tracking.

Journey B: With Omni Meta API Tag

With the Omni Meta API Tag, the events include unified identifiers and enriched data:

1. Calling Event
{
    "event_name": "Contact",
    "user_data": {
        "ph": "[hashed_phone_number]",
        "external_id": "omni_uuid_123" // Generated real-time by the Omni Meta API Tag integrated with Omni Identity
    }
}

2. Website Event
{
    "event_name": "Lead",
    "user_data": {
        "em": "[hashed_email]",
        "ph": "[hashed_phone_number]",
        "client_ip_address": "123.45.67.89",
        "client_user_agent": "[browser_data]",
        "fbp": "fb.1.1234567890",
        "fbc": "fb.1.1234567890.abcdef", // All client-side properties are added to identity seasoning 
        "external_id": "omni_uuid_123"
    },
    "custom_data": {
        "content_name": "Special Offer Signup"
    }
}

3. CRM Event
{
    "event_name": "Purchase",
    "user_data": {
        "em": "[hashed_email]",
        "ph": "[hashed_phone_number]",
        "external_id": "omni_uuid_123",
        "client_ip_address": "123.45.67.89", 
        "client_user_agent": "[browser_data]",
        "fbp": "fb.1.1234567890",
        "fbc": "fb.1.1234567890.abcdef" // All client-side properties are extracted from identity seasoning
    },
    "custom_data": {
        "value": 1500,
        "currency": "USD"
    }
}

4. In-Store Purchase
{
    "event_name": "Purchase",
    "user_data": {
        "ph": "[hashed_phone_number]",
        "em": "[hashed_email]",
        "external_id": "omni_uuid_123",
        "fbp": "fb.1.1234567890",
        "fbc": "fb.1.1234567890.abcdef"
    },
    "custom_data": {
        "value": 200,
        "currency": "USD"
    }
}

Result:

  • Unified Tracking: All events are linked through the external_id (omni_uuid_123), tying Sarah’s interactions together.
  • Attribution Cookies (fbc/fbp): CRM and in-store events are automatically enriched with cookies from the website interaction using the built-in identity enrichment in the Omni Meta API tag, enabling full-funnel attribution back to the original ad click.
  • Full Attribution: Meta credits the ad campaign with:
    • A website lead.
    • A $1,500 CRM contract.
    • A $200 in-store purchase.
  • Improved Insights: Meta’s reporting reflects the total $1,700 revenue, enabling accurate ROI tracking and better optimization.

Summary – key benefits of the Omni Meta API Tag for ad optimization:

1. Unified Identity Resolution

  • All events are linked via a consistent external_id.
  • Meta can accurately attribute the $1,500 offline purchase to the original ad click.

2. Cross-Channel Data Enrichment

  • Each event payload is enriched with previously collected identifiers.
  • Phone numbers from in-store visits are connected to email addresses from website interactions.
  • Names from CRM are added to purchase events.

3. Improved Match Rates

  • More identifiers per event increase Meta’s match confidence.
  • Consistent hashing across channels prevents duplicate user profiles.

4. Better Attribution

  • Meta can track the full journey from ad click to purchase.
  • More accurate ROAS (Return on Ad Spend) calculations.
  • Improved optimization for similar high-value customers.

Limitations of our tag

While our tag offers robust functionality, there are a few areas we’re working to improve. One limitation is the use of event names selected from a dropdown list, which restricts the ability to dynamically populate them using a single GTM Server variable. This setup is designed to maintain consistency across event names, ensuring they align with predefined dropdown values. Standardizing event names helps streamline tag conditions, unlocking specific data points or making certain sections of the tag required based on the selected event.

Wrapping up: get a test ride of Omni Meta API Tag!

Want to try out the Omni Meta tag? It’s free and open source. All you need to do is download the template and import it into your GTM Server container.

For detailed instructions and access to the GitHub repo, check out the official docs here: Omni Meta API Docs.

Special thanks to Andrii, Oles, Andrzej, Anton, and Piotr for collaborating with Datomni on building this tag, integration between GTM Server and Omni Analytics, and refining our approach to GTM Server tags.

Photo by Luca Bravo on Unsplash