Call Us NowRequest a Quote
Back to Blog
DPDP
July 25, 2024
15 min read

Architecting a DPDP-Native Pipeline for Low-Volume B2B Ads with Kotlin Multiplatform, Meta CAPI & Google OCI

Induji Technical Team

Induji Technical Team

Content Strategy

Architecting a DPDP-Native Pipeline for Low-Volume B2B Ads with Kotlin Multiplatform, Meta CAPI & Google OCI

Key Takeaways

  • The Low-Volume Problem: Standard AI-bidding on Meta and Google fails for high-ticket B2B due to sparse conversion data (~5-20 conversions/month), leading to inefficient ad spend and poor optimization.
  • The Solution is a Pipeline: A server-side, first-party data pipeline is the only reliable way to feed high-quality, downstream conversion signals (like MQL, SQL, Closed-Won) from your ERP back to ad platforms.
  • Kotlin Multiplatform (KMP) as the Core: Use KMP to create a unified business logic core for handling conversion event modeling, data enrichment, deduplication, and orchestration of API calls to both Meta CAPI and Google's Ads API. This ensures consistency and reduces code duplication.
  • DPDP-Native by Design: Engineering compliance is non-negotiable. This architecture embeds DPDP principles like consent validation, purpose limitation, and data minimization directly into the data flow, creating a machine-checkable audit trail.
  • ERP as the Source of Truth: Browser-based 'Lead' conversions are vanity metrics. True B2B success is measured by revenue. The pipeline must trigger events based on state changes within your ERP (e.g., ERPNext, SAP), associating real monetary values with each stage of the funnel.

The B2B Advertiser's Dilemma: High Value, Low Volume

In the world of B2B marketing, the value of a single conversion can be immense, often representing a multi-year, six-figure contract. However, the frequency of these conversions is incredibly low. While an e-commerce brand might see thousands of daily purchases, a specialized B2B SaaS or industrial equipment manufacturer may only secure 10-20 qualified leads per month from their entire digital advertising effort.

This creates a fundamental conflict with the operational model of modern ad platforms like Google Ads and Meta Ads. Their powerful AI-driven bidding strategies—Smart Bidding, Value-Based Bidding (VBB), and Advantage+—are data-hungry algorithms. They thrive on a high volume of conversion signals to learn, predict, and optimize. When fed a starvation diet of a few conversions per month, these algorithms falter. They struggle to exit the "learning phase," cannot accurately identify patterns in high-value audiences, and ultimately resort to broad, inefficient targeting that wastes significant budget.

The solution is not to abandon these powerful platforms. It is to architect a system that speaks their language: data. Specifically, a robust, server-side, first-party data pipeline that translates deep-funnel business outcomes from your ERP into the conversion signals the ad platforms need to succeed. This article provides the definitive blueprint for building such a system, with Kotlin Multiplatform at its core and DPDP compliance engineered from the ground up.

Blueprint for a Unified, DPDP-Native Conversion Pipeline

A resilient pipeline must be event-driven, decoupled, and auditable. It moves beyond simple browser-pixel tracking to a sophisticated server-to-server communication model that you, the Data Fiduciary, fully control.

At a high level, the architecture consists of the following components:

  1. Data Capture Layer (Frontend): Your website (e.g., built on Next.js 15) or mobile app. Its primary roles are to capture user interaction identifiers (gclid, _fbp, _fbc) and, critically, to obtain and record explicit user consent as mandated by the DPDP Act 2023.
  2. API Gateway: A single, secure entry point for all incoming tracking events.
  3. Event Bus (e.g., Kafka): Decouples the data capture from processing. The frontend sends a raw event (e.g., form_submitted) to the gateway, which places it on a Kafka topic. This makes the system resilient to processing delays or failures.
  4. Kotlin Multiplatform (KMP) Core: This is the brain of the operation. A service running on a JVM backend (like Ktor or Spring Boot) consumes events from Kafka. It handles data validation, enrichment, consent verification, and business logic.
  5. System of Record (ERP/CRM): The source of truth for lead value (e.g., ERPNext, Salesforce). The initial lead is pushed here. Later, as the lead progresses (MQL, SQL, Won), the ERP triggers events (e.g., via webhooks) back into our pipeline.
  6. Ad Platform Connectors: Logic within the KMP Core responsible for formatting and sending conversion data to the specific endpoints for Meta's Conversions API (CAPI) and Google Ads' Offline Conversion Import (OCI).
  7. Consent Ledger: An immutable database (or even a blockchain) that stores consent records, timestamped and linked to a user identifier, providing a machine-checkable audit trail for DPDP compliance.

Architectural diagram showing data flow from a Next.js frontend, through a Kafka event bus to a Kotlin Multiplatform middleware. The middleware interacts with an ERP system and a DPDP Consent Ledger before sending conversion data to Meta CAPI and Google Ads API endpoints.

The Role of Kotlin Multiplatform as the "Conversion Logic" Hub

Using Kotlin Multiplatform (KMP) for the central processing logic is a strategic choice that offers immense advantages in maintainability, consistency, and type safety. While the main pipeline service will run on a JVM backend, the core data models and business rules can be defined in a KMP common module.

Why KMP?

  • Unified Data Modeling: Define a single source of truth for your conversion events. A shared KMP module contains the data classes for events, user data, and consent information.
  • Code Reusability: The same validation and enrichment logic can be used on the server, and potentially in a future native mobile app for field sales to input offline events, without rewriting a single line of business logic.
  • Type Safety: Eliminate guesswork and runtime errors. KMP's strong typing ensures that the data structure is consistent from the moment it's consumed from Kafka to the moment it's sent to the ad platforms.

Implementing the Unified Data Model

In your KMP commonMain, you would define a universal event structure.

// In shared/src/commonMain/kotlin/com/induji/events/ConversionModel.kt

enum class FunnelStage {
    LEAD_SUBMITTED,
    MQL,
    SQL,
    CLOSED_WON
}

data class UserIdentifiers(
    val gclid: String? = null,
    val fbp: String? = null,
    val fbc: String? = null,
    // Hashed PII
    val email_sha256: String? = null,
    val phone_sha256: String? = null
)

data class B2BConversionEvent(
    val eventId: String, // For deduplication
    val eventTimestamp: Long,
    val stage: FunnelStage,
    val value: Double,
    val currency: String,
    val identifiers: UserIdentifiers,
    val consentId: String // Links to the consent ledger
)

This common model acts as a canonical format. The KMP service's job is to consume raw data, transform it into this B2BConversionEvent, and then map this object to the specific requirements of Meta CAPI and Google Ads OCI.

Engineering DPDP Compliance from First Principles

The DPDP Act 2023 places clear responsibilities on the Data Fiduciary. This architecture is designed to fulfill those obligations programmatically, moving compliance from a legal checklist to an engineered reality.

The Data Fiduciary's Control

By using a server-side pipeline, you control exactly what data is sent, to whom, and for what purpose. You are not relying on a third-party script in the user's browser. This pipeline is your data processing engine, and the ad platforms are merely Data Processors acting on your explicit instructions.

Machine-Checkable Consent

When a user submits a form, they are shown a clear "Notice" explaining the purpose of data processing (in this case, for advertising optimization).

  1. Consent Capture: Upon consent, a unique consentId is generated and stored in your Consent Ledger alongside a timestamp and the specifics of the consent given.
  2. Consent Propagation: This consentId is passed along with the event data through the entire pipeline.
  3. Consent Validation: Before the KMP Core processes an event to be sent to an ad platform, it must query the Consent Ledger using the consentId. If consent is not valid, not present, or has been revoked, the processing for that purpose is halted. This is a machine-checkable control.

Diagram illustrating the DPDP consent flow: a user consents on a UI, a consent ID is generated and stored in a ledger, the ID travels with the event data, and the Kotlin middleware validates the ID against the ledger before sending any data to ad platforms.

Purpose Limitation & Data Minimization

The KMP data model enforces these principles. The sole purpose is ad performance measurement and optimization. The data sent is minimized to only what is required by the ad platforms' APIs. Sensitive PII like email addresses are never sent in cleartext; only SHA-256 hashed versions are used, which is the industry standard for matching.

Connecting the ERP: The Source of Truth for Value-Based Bidding

The most critical part of solving the low-volume problem is enriching the data with real business value. A lead is not just a lead. A lead from a Fortune 500 company that becomes a Sales Qualified Lead (SQL) in 48 hours is exponentially more valuable than a lead from a student that never gets past the initial email. Your ERP knows the difference.

Triggering Offline Conversions

Your ERP (e.g., ERPNext) should be configured to fire webhooks when the status of a lead or opportunity changes.

  • Lead Status -> MQL: Fire webhook with lead data.
  • Opportunity Stage -> SQL: Fire webhook with opportunity data.
  • Opportunity Stage -> Closed-Won: Fire webhook with final deal value.

These webhooks send data to an endpoint on your API gateway, which places a new, enriched event onto your Kafka bus for the KMP Core to process.

Mapping ERP Stages to Conversion Values

This is where you train the ad platforms' algorithms. You must define a clear value hierarchy.

ERP Stage Funnel Stage Conversion Value Description
New Lead Created LEAD $1 A baseline value to register the initial conversion.
Marketing Qualified MQL $50 Lead meets demographic/firmographic criteria.
Sales Qualified SQL $250 Sales has accepted the lead and deemed it a real opportunity.
Deal Won CLOSED_WON Actual Deal Value (e.g., $50,000) The ultimate success signal.

By sending these events with their associated values, you provide a rich, nuanced dataset to Google and Meta. Their VBB algorithms will start to understand the upstream characteristics of users who eventually become high-value SQLs and customers, and they will adjust bidding to find more of those people.

A flowchart illustrating the closed-loop feedback system. An ad click leads to a form fill. The lead enters an ERP. As the lead is nurtured to SQL, the ERP sends a webhook. This triggers the Kotlin pipeline, which sends the high conversion value to Google/Meta. The ad algorithms then optimize for more users like this one.

Technical Walkthrough: Meta CAPI vs. Google Ads OCI

The final step in the KMP service is to map the unified B2BConversionEvent to the platform-specific API requests. Using a library like Ktor's HTTP client makes this straightforward.

Meta Conversions API (CAPI)

Meta's CAPI relies on a combination of hashed user data and browser identifiers (_fbp, _fbc) for matching.

// In your KMP service (JVM backend)
suspend fun sendToMetaCapi(event: B2BConversionEvent) {
    val client = HttpClient(CIO)
    val response = client.post("https://graph.facebook.com/v19.0/YOUR_PIXEL_ID/events") {
        bearerAuth("YOUR_ACCESS_TOKEN")
        contentType(ContentType.Application.Json)
        setBody(
            // Use a kotlinx.serialization data class for the body
            MetaPayload(
                data = listOf(
                    MetaEvent(
                        event_name = event.stage.name, // e.g., "SQL"
                        event_time = event.eventTimestamp,
                        action_source = "system",
                        user_data = MetaUserData(
                            em = listOf(event.identifiers.email_sha256),
                            ph = listOf(event.identifiers.phone_sha256),
                            fbp = event.identifiers.fbp,
                            fbc = event.identifiers.fbc
                        ),
                        custom_data = MetaCustomData(
                            value = event.value,
                            currency = event.currency
                        )
                    )
                )
            )
        )
    }
    // Handle response
    client.close()
}

Google Ads Offline Conversion Import (OCI)

Google's primary matching key for on-site interactions is the gclid (Google Click ID). It's simpler but absolutely vital to capture it correctly.

// In your KMP service (JVM backend) using Google Ads API client library
fun sendToGoogleOci(event: B2BConversionEvent) {
    if (event.identifiers.gclid == null) return // Cannot upload without gclid

    val googleAdsClient: GoogleAdsClient = GoogleAdsClient.newBuilder().fromProperties(properties).build()

    val clickConversion = ClickConversion.newBuilder()
        .setGclid(event.identifiers.gclid)
        .setConversionAction("customers/YOUR_CUSTOMER_ID/conversionActions/YOUR_ACTION_ID")
        .setConversionValue(event.value)
        .setConversionDateTime(
            Instant.ofEpochSecond(event.eventTimestamp)
                .atZone(ZoneId.of("UTC"))
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss+00:00"))
        )
        .setCurrencyCode(event.currency)
        .build()

    ConversionUploadServiceClient.create(googleAdsClient.getConversionUploadServiceSettings()).use { serviceClient ->
        val response = serviceClient.uploadClickConversions(
            UploadClickConversionsRequest.newBuilder()
                .setCustomerId("YOUR_CUSTOMER_ID")
                .addConversions(clickConversion)
                .setPartialFailure(true)
                .build()
        )
        // Handle response and partial failures
    }
}

Frequently Asked Questions (FAQ)

Q: How do I reliably capture gclid, _fbp, and _fbc? A: These must be captured on the client side. When a user lands on your site, use JavaScript to parse them from the URL (gclid) or cookies (_fbp, _fbc). Store them in hidden fields in your lead forms. When the form is submitted, these identifiers are sent along with the rest of the lead data to your backend.

Q: What is the ideal latency for sending offline conversions? A: For auction-time bidding models, faster is better. Aim to send conversions within a few hours of the event occurring in your ERP. While you have a window of up to 90 days, sending them within 24 hours is a practical minimum to ensure the algorithms have fresh data to work with.

Q: Can this architecture handle high-volume B2C scenarios as well? A: Absolutely. The architecture is built on scalable components like Kafka. The event-driven, decoupled nature means it can scale horizontally to handle millions of events per day. The core principles of feeding quality server-side data back to ad platforms apply equally to B2C.

Q: How does this differ from using an off-the-shelf Customer Data Platform (CDP)? A: While CDPs offer convenience, a bespoke pipeline like this provides ultimate control, deeper customization, and long-term cost savings. You can implement custom lead scoring or enrichment logic directly within your KMP core that a generic CDP cannot handle. Crucially, for DPDP, owning the pipeline makes demonstrating your role as a diligent Data Fiduciary far more straightforward.

Q: What happens if a user revokes their consent under DPDP? A: Your privacy portal should allow users to manage their consent. When consent is revoked, an event is sent to your Consent Ledger, updating the record for that user. The next time the KMP core attempts to process an event for that user, the validation check will fail, and no data will be sent to the ad platforms for advertising purposes, automatically honoring the user's request.


Unlock the True Potential of Your B2B Ad Spend

Stop fighting the algorithms and start feeding them the data they need to succeed. Building a DPDP-native, ERP-driven conversion pipeline is a complex undertaking that requires expertise across system architecture, data engineering, AdTech, and regulatory compliance.

The team at Induji Technologies specializes in architecting and implementing these sophisticated B2B marketing engines. We can help you design a bespoke solution that transforms your low-volume, high-value leads into a powerful asset for driving predictable growth.

Request a Quote Today to discuss how we can build your unified conversion pipeline.

Related Articles

Ready to Transform Your Business?

Partner with Induji Technologies to leverage cutting-edge solutions tailored to your unique challenges. Let's build something extraordinary together.

Architecting a DPDP-Native Pipeline for Low-Volume B2B Ads with Kotlin Multiplatform, Meta CAPI & Google OCI | Induji Technologies Blog