Skip to content

Docs, Event Data + Custom Event Parameter Page - #45

Open
ChetanBhosale wants to merge 12 commits into
mainfrom
chetan/lin-236-accept-process-forward-custom-event-parameters-product_id
Open

Docs, Event Data + Custom Event Parameter Page#45
ChetanBhosale wants to merge 12 commits into
mainfrom
chetan/lin-236-accept-process-forward-custom-event-parameters-product_id

Conversation

@ChetanBhosale

@ChetanBhosale ChetanBhosale commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Documentation

    • Added in-depth custom event parameters guide with ad-network mappings, examples, best practices, and nav entry
    • Expanded Event Capture and Revenue Tracking docs with standardized event-data fields, currency/dedup guidance, and new examples
    • Added link redirection and Flutter usage guides, plus a global writing/branding guide; minor SDK doc branding fixes
  • New Features

    • Documented optional eventId across SDK guides to support server-side deduplication and cross-source consistency

@linear

linear Bot commented Feb 18, 2026

Copy link
Copy Markdown
LIN-236 Accept, process & forward custom event parameters (product_id, order_id, etc.) to ad networks

Background

A customer reported that Meta flagged product_id not being populated in purchase events. Investigation reveals a systemic gap: custom event parameters sent by SDKs are accepted and stored but never forwarded to ad networks (Meta, Google, TikTok, Snapchat).

We want to adopt the AppsFlyer model — a single flat event_data map where standard params (like product_id, order_id) and arbitrary custom params coexist. Standard params get mapped to each ad network's expected field names; everything else is passed through as-is.


AppsFlyer Model (What We're Adopting)

AppsFlyer uses a single flat key-value map for all event parameters:

{
  "event_name": "purchase",
  "event_data": {
    "product_id": "sku_12345",
    "order_id": "order_98765",
    "content_type": "shoes",
    "num_items": 2,
    "currency": "USD",
    "amount": 49.99,
    "payment_plan": "annual",
    "coupon_code": "SAVE20"
  }
}
  • Standard params (product_id, order_id, etc.) are recognized and mapped to each ad network's equivalent fields
  • Arbitrary custom params (payment_plan, coupon_code, etc.) are passed through as-is
  • No separate containers — everything lives in one flat map

Current Gap Analysis

Custom Event Flow (POST /api/client/capture-event)

Step File Status
SDK sends event_data JSON src/controllers/data.ts:987 OK — accepted
Kafka message includes event_data src/controllers/data.ts:1039-1053 OK — forwarded
Stored in Event.object src/services/event_service.ts:169-181 OK — stored
Postback dispatch src/services/event_service.ts:186-194 BROKEN — only event_data.amount extracted, everything else dropped
Meta/Google/TikTok/Snapchat postback handlers No custom params arrive

Payment Event Flow (POST /api/client/capture-payment)

Step File Status
SDK sends payment data src/controllers/data.ts:30-32 BROKEN — no metadata/event_data accepted from SDK at all
Kafka message src/controllers/data.ts:99-117 No custom data included
Revenue record stored src/services/payment_service.ts:200-217 metadata field exists in schema but never populated from API
Postback dispatch src/services/payment_service.ts:264-274 BROKEN — only amountValue forwarded, no custom params
Meta/Google/TikTok/Snapchat postback handlers Only amount + currency arrive

Ad Network Forwarding

Network File Status
Meta CAPI src/utils/meta/conversion_api.ts:781-784 Only sends {currency, value} in custom_data — no content_ids, order_id, etc.
Google Ads src/utils/google_ads_api.ts:927-929 Sends empty body {}app_event_data never populated
TikTok src/services/postback_service.ts:824 No custom params forwarded
Snapchat src/services/postback_service.ts:961 No custom params forwarded

Standard Parameter Mapping

These standard params in event_data get mapped to each network's expected field names:

Customer sends in event_data Meta CAPI (custom_data) Google Ads (app_event_data)
product_id content_ids: [product_id] item_id: [product_id]
order_id order_id order_id
content_type content_type ("product" or "product_group") content_type
num_items num_items num_items
contents (array of {id, quantity, item_price}) contents items
search_string search_string search_string
amount / value value (already works) value query param (already works)
currency currency (already works) currency_code query param (already works)
Any other key Spread into custom_data as custom property Passed through in app_event_data body

Meta supports arbitrary custom properties in custom_data. Google accepts freeform JSON in the app_event_data request body.


Implementation Tasks

Task 1: Accept event_data in capturePayment API

File: src/controllers/data.ts:30-32

  • Add event_data (or metadata) to the destructured request body
  • Include it in the Kafka message at lines 99-117
  • The PaymentData interface in payment_service.ts:9-23 already has metadata?: Record<string, any> — just needs to be populated

Task 2: Thread event_data through payment processing

File: src/services/payment_service.ts:264-274

  • Pass payment.metadata to processEventForPostbacks as a new eventParams argument

Task 3: Thread event_data through custom event processing

File: src/services/event_service.ts:186-194

  • Currently only event_data.amount is extracted: event.event_data?.amount ? Number(event.event_data.amount) : undefined
  • Pass the full event.event_data object to processEventForPostbacks

Task 4: Update processEventForPostbacks and InAppEventPostback type

File: src/services/postback_service.ts:146-154

  • Add eventParams?: Record<string, any> to the method signature
  • Set event_params on the InAppEventPostback objects built at lines 285-323 and 335-374

File: src/utils/types/postback.ts

  • Add to InAppEventPostback:
// Custom event parameters (product_id, order_id, etc.) — AppsFlyer-style flat map
event_params?: Record<string, any>;

Task 5: Forward to Meta CAPI

File: src/services/postback_service.tshandleMetaPostback (line 492)

  • When calling trackPurchaseFromPayment (line 764) and trackCustomEventFromTrigger (line 795), build custom_data from data.event_params:
    • product_id -> content_ids: [product_id]
    • order_id -> order_id
    • content_type -> content_type
    • num_items -> num_items
    • contents -> contents
    • All other keys -> spread into custom_data

File: src/utils/types/meta-integration.ts:76-84

  • Add explicit standard fields to CustomData interface:
export interface CustomData {
    value?: number;
    currency?: string;
    content_name?: string;
    content_category?: string;
    content_ids?: string[];
    content_type?: string;
    contents?: Array<{ id: string; quantity: number; item_price?: number }>;
    order_id?: string;
    num_items?: number;
    search_string?: string;
    status?: string;
    // Existing fields
    description?: string;
    level?: string;
    max_rating_value?: number;
    success?: boolean;
    [key: string]: any;
}

File: src/utils/meta/conversion_api.ts:781-784

  • trackPurchase currently builds customData as only {currency, value} — merge incoming custom_data fields into it

Task 6: Forward to Google Ads

File: src/utils/google_ads_api.ts:927-929

  • sendInAppEvent currently sends empty body {} — populate app_event_data from data.event_params:
    • product_id -> item_id: [product_id]
    • All other custom params -> pass through in app_event_data

Task 7: Forward to TikTok and Snapchat

File: src/services/postback_service.ts

  • handleTikTokPostback (line 824) — pass event_params through to TikTok integration
  • handleSnapchatPostback (line 961) — pass event_params through to Snapchat integration
  • Update respective integration files:
    • src/utils/tiktok/linkrunner-tiktok-integration.ts
    • src/utils/snapchat/linkrunner-snapchat-integration.ts

Task 8: Update docs.linkrunner.io

After all backend changes are deployed, update the public documentation at docs.linkrunner.io with the following:

8a. Update capture-event documentation

  • Document that event_data supports a flat key-value map of custom parameters
  • List the standard recognized parameters and explain they get mapped to ad networks automatically:
    • product_id — Product SKU/identifier (forwarded to Meta as content_ids, Google as item_id)
    • order_id — Transaction/order identifier
    • content_type"product" or "product_group"
    • num_items — Number of items in the transaction
    • contents — Array of { id, quantity, item_price } for multi-product events
    • search_string — Search query text (for search events)
  • Explain that any additional custom keys (e.g. payment_plan, coupon_code, subscription_tier) are passed through as-is to ad networks
  • Add code examples for each SDK (Flutter, iOS, Android) showing how to send events with custom params

8b. Update capture-payment documentation

  • Document the new event_data/metadata field accepted by the payment API
  • Show how to include product_id, order_id, and other params alongside amount and currency
  • Add code examples showing a purchase event with product-level data:
{
  "payment_id": "pay_123",
  "amount": 49.99,
  "currency": "USD",
  "event_data": {
    "product_id": "sku_12345",
    "order_id": "order_98765",
    "content_type": "product",
    "num_items": 1
  }
}

8c. Add a "Custom Event Parameters" reference page

  • Dedicated page explaining the AppsFlyer-style flat map approach
  • Full table of standard parameters with descriptions and which ad networks they map to
  • Best practices:
    • Always include product_id for purchase events (required by Meta for product catalog matching)
    • Include order_id for deduplication across ad networks
    • Use content_type: "product" when product_id refers to a specific SKU
    • Revenue params (amount, currency) are handled automatically — don't duplicate them in event_data
  • Examples for common event types: purchase, add_to_cart, subscribe, custom events

Acceptance Criteria

  • capture-payment API accepts event_data/metadata from SDK (flat key-value map, AppsFlyer-style)
  • capture-event forwards full event_data through the postback pipeline (not just amount)
  • Standard params (product_id, order_id, etc.) are mapped to each ad network's expected field names
  • Arbitrary custom params beyond the standard set are passed through as-is
  • Custom params reach Meta CAPI in custom_data (with content_ids, order_id, etc.)
  • Custom params reach Google Ads in app_event_data request body
  • Custom params reach TikTok and Snapchat via their respective CAPI
  • Existing amount/currency revenue tracking continues working unchanged
  • No DB migrations needed — Event.object and Revenue.metadata already store JSON
  • docs.linkrunner.io updated: capture-event docs, capture-payment docs, and new "Custom Event Parameters" reference page

Notes

  • No breaking changesevent_data is already accepted by capture-event; adding it to capture-payment is additive
  • No schema migrationEvent.object (JSON) and Revenue.metadata (JSON) already exist
  • Meta requires events within 7 days — existing validation handles this
  • Google app_event_data is freeform JSON — no strict schema
  • Adjust uses a different model (callback_params vs partner_params) — we're going with AppsFlyer's simpler single-map approach

@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds a new Custom Event Parameters API doc, standardizes event_data keys across Event Capture and Revenue Tracking docs, propagates an optional eventId/event_id parameter across Android/Flutter/iOS/React Native SDK docs, adds a comprehensive Flutter usage guide, and updates site navigation.

Changes

Cohort / File(s) Summary
API Reference — New & Updated docs
api-reference/custom-event-parameters.mdx, api-reference/event-capture.mdx, api-reference/revenue-tracking.mdx, docs.json
Adds a new Custom Event Parameters page; standardizes the event_data schema and mapping to ad-network parameters; documents passthrough custom params, revenue handling, and dedup via event_id/eventId; updates examples and navigation.
SDK Docs — eventId deduplication & branding fixes
sdk/android.mdx, sdk/flutter.mdx, sdk/ios.mdx, sdk/react-native.mdx
Adds optional eventId parameter to trackEvent examples across SDK docs and public-facing examples; notes on server-side deduplication and aligning IDs with Event Capture API; fixes branding typo (LinkRunner → Linkrunner).
Flutter — New usage guide
sdk/flutter/usage.mdx
Introduces an extensive Flutter usage guide covering initialization, attribution retrieval, identification, custom event and payment tracking (with eventId), SDK signing, privacy/PII handling, function placement, and a full sample app.
Contributor Guide
CLAUDE.md
Adds a comprehensive documentation authoring and style guide: branding, tone, front-matter requirements, Mintlify component usage, code/table/link conventions, and page templates.
Feature docs — Link redirection
features/link-redirection.mdx
Adds a detailed link redirection workflow doc: URL parsing, device detection, click recording, routing/fallbacks, redirect priorities, troubleshooting, and examples.

Sequence Diagram(s)

sequenceDiagram
  participant App as User App (SDK)
  participant API as Event Capture API
  participant DB as Database
  participant Meta as Meta CAPI
  participant Google as Google Ads
  participant TikTok as TikTok
  participant Snap as Snapchat

  rect rgba(200,230,255,0.5)
    App->>API: Send event (event_name, event_data, eventId?)
  end

  rect rgba(220,255,200,0.5)
    API->>DB: Store event, event_data, eventId
    API-->>API: Auto-map standard keys to network params
  end

  rect rgba(255,240,200,0.5)
    API->>Meta: Forward mapped + passthrough custom params
    API->>Google: Forward mapped + passthrough custom params
    API->>TikTok: Forward mapped + passthrough custom params
    API->>Snap: Forward mapped + passthrough custom params
  end

  API->>App: Acknowledge receipt (dedup info)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped from docs to code with glee,
eventId tucked safe beneath my tree,
params mapped, revenues counted bright,
networks hum through day and night,
I thump my paw — the docs are right.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
api-reference/event-capture.mdx (1)

35-45: Consider adding event_id to the request example.

It’s newly documented and showing it in the sample payload makes dedup usage more discoverable.

Proposed update
 {
     "event_name": "purchase_completed",
     "event_data": {
         "product_id": "ABC123",
         "order_id": "ORD-12345",
         "content_type": "product",
         "num_items": 2,
         "amount": 149.99,
         "currency": "USD"
     },
-    "user_id": "user_12345"
+    "user_id": "user_12345",
+    "event_id": "550e8400-e29b-41d4-a716-446655440000"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api-reference/event-capture.mdx` around lines 35 - 45, The sample JSON
payload is missing an event_id field which helps demonstrate deduplication;
update the example payload to include an "event_id" key at the top level
(alongside "event_name" and "user_id") with a representative value (e.g., a UUID
or unique string) so readers see how to pass dedup keys—locate the JSON block
containing "event_name", "event_data", and "user_id" and insert "event_id":
"<unique-id>" at the same level.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api-reference/revenue-tracking.mdx`:
- Around line 48-56: The parameter table omits whether the amount field is
required; update the row for the amount parameter (the "amount" column entry) to
include its requirement status to match others (e.g., prefix with "Required." or
"Optional."), ensuring the description reads like the other rows (for example:
"Required. Payment amount. Send in one currency only"); leave other parameter
rows unchanged.

---

Nitpick comments:
In `@api-reference/event-capture.mdx`:
- Around line 35-45: The sample JSON payload is missing an event_id field which
helps demonstrate deduplication; update the example payload to include an
"event_id" key at the top level (alongside "event_name" and "user_id") with a
representative value (e.g., a UUID or unique string) so readers see how to pass
dedup keys—locate the JSON block containing "event_name", "event_data", and
"user_id" and insert "event_id": "<unique-id>" at the same level.

Comment thread api-reference/revenue-tracking.mdx
ChetanBhosale and others added 2 commits February 19, 2026 00:59
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Rewrite custom-event-parameters.mdx for clarity: add "why" section,
  standard parameters table, Steps component, Notes/Warnings/Tips,
  contextual example descriptions, and related page links
- Add CLAUDE.md with docs writing conventions for consistency
- Fix LinkRunner → Linkrunner in prose text across SDK pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
sdk/flutter.mdx (2)

746-749: Consider adding eventData to the complete example for consistency.

The complete example passes eventId but omits eventData. While valid, including an eventData map (even empty) would better demonstrate the full signature and maintain consistency with the detailed examples earlier in the document.

📝 Suggested improvement
                 await LinkRunner().trackEvent(
                   eventName: 'button_clicked',
+                  eventData: {'screen': 'home'},
                   eventId: '550e8400-e29b-41d4-a716-446655440000', // Optional: Unique event identifier (String) for deduplication
                 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/flutter.mdx` around lines 746 - 749, Update the complete example call to
LinkRunner().trackEvent to include the eventData parameter so it matches the
full signature shown elsewhere; specifically add an eventData map (can be empty
or with an example key/value) alongside eventName and eventId in the call to
LinkRunner().trackEvent to demonstrate the full usage.

767-768: Consider using 'Setup' instead of 'Set Up' in the heading.

Based on learnings, the repository prefers using the single-word form 'Setup' over the phrasal verb 'Set Up' in headings for consistency.

📝 Suggested heading change
-    <Card title="Set Up Deep Linking" icon="link" href="/features/deep-linking">
+    <Card title="Setup Deep Linking" icon="link" href="/features/deep-linking">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/flutter.mdx` around lines 767 - 768, Change the Card title prop from the
phrasal verb to the single-word form by updating the Card with title="Set Up
Deep Linking" to title="Setup Deep Linking" (the Card component and its title
prop are the unique identifiers to edit).
sdk/react-native.mdx (1)

339-340: Consider using 'Setup' instead of 'Set Up' in the heading.

Based on learnings, the repository prefers using the single-word form 'Setup' over the phrasal verb 'Set Up' in headings for consistency.

📝 Suggested heading change
-    <Card title="Set Up Deep Linking" icon="link" href="/features/deep-linking">
+    <Card title="Setup Deep Linking" icon="link" href="/features/deep-linking">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/react-native.mdx` around lines 339 - 340, Update the Card component title
prop to use the single-word form "Setup" for consistency: change the title value
on the Card (currently "Set Up Deep Linking") to "Setup Deep Linking" so the
heading matches the repository style.
sdk/android.mdx (1)

631-632: Consider using 'Setup' instead of 'Set Up' in the heading.

Based on learnings, the repository prefers using the single-word form 'Setup' over the phrasal verb 'Set Up' in headings for consistency.

📝 Suggested heading change
-    <Card title="Set Up Deep Linking" icon="link" href="/features/deep-linking">
+    <Card title="Setup Deep Linking" icon="link" href="/features/deep-linking">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/android.mdx` around lines 631 - 632, Change the Card component title from
the phrasal form to the single-word form by updating the title prop value in the
Card with title="Set Up Deep Linking" to title="Setup Deep Linking"; locate the
Card element (the JSX/Markdown fragment with Card title="Set Up Deep Linking"
icon="link" href="/features/deep-linking") and replace the title string
accordingly to maintain repository heading consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@sdk/android.mdx`:
- Around line 631-632: Change the Card component title from the phrasal form to
the single-word form by updating the title prop value in the Card with
title="Set Up Deep Linking" to title="Setup Deep Linking"; locate the Card
element (the JSX/Markdown fragment with Card title="Set Up Deep Linking"
icon="link" href="/features/deep-linking") and replace the title string
accordingly to maintain repository heading consistency.

In `@sdk/flutter.mdx`:
- Around line 746-749: Update the complete example call to
LinkRunner().trackEvent to include the eventData parameter so it matches the
full signature shown elsewhere; specifically add an eventData map (can be empty
or with an example key/value) alongside eventName and eventId in the call to
LinkRunner().trackEvent to demonstrate the full usage.
- Around line 767-768: Change the Card title prop from the phrasal verb to the
single-word form by updating the Card with title="Set Up Deep Linking" to
title="Setup Deep Linking" (the Card component and its title prop are the unique
identifiers to edit).

In `@sdk/react-native.mdx`:
- Around line 339-340: Update the Card component title prop to use the
single-word form "Setup" for consistency: change the title value on the Card
(currently "Set Up Deep Linking") to "Setup Deep Linking" so the heading matches
the repository style.

RathodDarshil and others added 2 commits February 20, 2026 11:53
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants