Connection Builder

You can build your own integration from scratch using various liquid macros

The Connection Builder lets you compose your own integration from scratch — send custom POST requests with fully templated JSON bodies to any third-party service whenever selected in-app events occur. Use it to recreate Apphud's built-in integrations, send events to internal systems, or build payloads tailored to APIs not covered by the standard integration catalog.

📘

Note

Connection Builder is available on Expert and Enterprise plans.

Overview

The Custom Connection Builder lets you send custom POST requests to third-party services whenever selected in-app events occur. With it, you can recreate Apphud's built-in integrations or design your own payloads for any supported event.

How to Add a Custom Connection

Step 1 - Create a Connection

In Apphud, navigate to Connections → Integrations, find Connection Builder at the top of the list (on the All or Custom tabs), and click Add Connection.

As with other integrations, each custom connection is created for a specific data source platform. You'll be prompted to select a Source: iOS, Android, Flows, iOS (Web-to-App), or Android (Web-to-App).

Step 2 - Configure URL and Request method

After selecting a source, you'll be taken to the connection settings page. Enter a Name and URL for the connection.

Apphud - Connection Builder Name and URL

Choose a Request method from the dropdown.
As a rule, clients prefer that events are sent to the specified URL via POST requests.

Step 3 - Add headers

Define request headers by specifying Header name and Value. To include multiple headers, click Add header and fill in the additional fields. Note that the button is shown only if you added at least one Header.

Connection Builder - Headers configuration

Step 4 - Set up request body

In the Body configuration section, compose your JSON payload using supported Liquid macros (see the Liquid Macros reference below).

Remove null values settings is set to ON by default. Fields with null values will be excluded from the payload.

Step 5 - Apply Event Filters (optional)

The standard for integrations Environment configuration block will help you to define which data will be sent to the destination, so, you can use these settings as the initial high-level filter. By default only production events are sent.

📘

Important Note

No need to create a filter Environment = Production. By default, Apphud skips sandbox events, so this filter is redundant.

Then, go to Filters tab. Click + Add filter to add conditions that determine which events should be sent.
If an event is enabled but doesn't match the filter conditions, it appears as Skipped in the event logs with the reason "Event did not match filters."

Add powerful event filters to ensure only specific data is sent to a destination. For example:

  • Revenue > $29
  • Product duration = Weekly
  • Trial started = true

These filters can be applied per connection for precise control.

Available filters grouped by category:

CategoryFilter
GeneralEnvironment (Sandbox, Production); App Version (=, >, <, e.g. > 1.20.1)
UserCountry by IP; Store Country; Has Advertising ID (IDFA)
ProductType (Auto-renewable, Non-renewing); Duration (Weekly, 2 Weeks, Monthly, 3 Months, 6 Months, Annually); Family Shared; Product ID; Revenue (USD) with <, >, =
AttributionAd Network; Channel; Campaign Name
User PropertiesKeys with exact values
❗️

Note

Filters do not apply to non-receipt events like user_created, paywall_shown, except for Store Country and Environment.

Step 6 - Select events

Navigate to Events tab. Choose the events you'd like to send through the connection. You can also rename these events using custom labels.

Apphud - Connection Builder events list

Step 7 - Save and test

Save the connection. The connection can be Enabled right away — however, we recommend testing it first.

Liquid Macros

Use these macros inside your JSON body. Each renders the corresponding value at send time.

App

MacroDescriptionFormatExample
{{ app.app_version }}App VersionString1.2.20
{{ app.bundle_id }}iOS Bundle IDStringcom.bundle.id
{{ app.package_name }}Android Package NameStringcom.android.package

Event

MacroDescriptionFormatExample
{{ event.created_at }}Event creation date (ISO)Date2022-03-12T09:38:38.081Z
{{ event.created_at_ts }}Event creation date (Unix seconds)Integer1743481200
{{ event.name }}Event nameStringcustom_trial_started
{{ event.original_name }}Unmodified original nameStringtrial_started
{{ event.receipt.product_id }}Product IDStringmy.weekly.id
{{ event.receipt.purchased_at }}Transaction date (ISO)Date2022-03-12T09:38:38.081Z
{{ event.receipt.purchased_at_ts }}Transaction date (Unix seconds)Integer1743481200
{{ event.receipt.intro_period }}Introductory periodBooleanTRUE
{{ event.receipt.trial_period }}Trial periodBooleanFALSE
{{ event.receipt.transaction_id }}Transaction IDString1000000999999999
{{ event.receipt.original_transaction_id }}Original Transaction IDString1000000888888888
{{ event.receipt.price_usd }}Price in USDFloat9.99
{{ event.receipt.proceeds_usd }}Proceeds in USDFloat6.99
{{ event.receipt.price }}Price in local currencyFloat199.00
{{ event.receipt.proceeds }}Proceeds in local currencyFloat139.00
{{ event.receipt.currency }}Local currency ISO codeStringUSD

User

MacroDescriptionFormatExample
{{ user.user_id }}User IDStringabc123xyz
{{ user.paying }}Whether the user is currently payingBooleanTRUE
{{ user.payments_count }}Payments countInteger4
{{ user.total_spent }}Total spent in USDFloat23.49
{{ user.idfv }}Device IDFVString12345-12345-12345
{{ user.idfa }}Device IDFAStringABCDEF12-3456-7890-ABCD-EF1234567890
{{ user.properties.your_key }}User propertyStringmyuserpropertyvalue
{{ user.created_at }}User Created Date (ISO)Date2022-03-12T09:38:38.081Z
{{ user.created_at_ts }}User Created Date (Unix seconds)Integer1743481200
{{ user.first_seen_at }}First seen Date (ISO)Date2022-03-12T09:38:38.081Z
{{ user.first_seen_at_ts }}First seen Date (Unix seconds)Integer1743481200
{{ user.country_by_ip }}Country ISO code by IP addressStringUS
{{ user.country_by_store }}Country ISO code from the storeStringUS
{{ user.time_zone }}Time zoneStringAsia/Jakarta
{{ user.language }}LanguageStringen
{{ user.ip_address }}IP addressString192.168.1.1
{{ user.os_version }}OS versionString18.2.1
{{ user.push_token }}Push tokenStringABCD1234TOKEN5678

User LTV Predictions (Coming Soon)

MacroDescriptionFormatExample
{{ user.pltv_30d }}30-day Predicted LTVFloat34.00
{{ user.pltv_90d }}90-day Predicted LTVFloat40.54
{{ user.pltv_365d }}365-day Predicted LTVFloat42.55

Attribution

MacroDescriptionFormatExample
{{ user.attribution.appsflyer_id }}AppsFlyer IDStringAF_ID_123456
{{ user.attribution.fb_anon_id }}Facebook Anon IDStringFBANONID123
{{ user.attribution.fb_ext_info }}Facebook Ext InfoStringEXTINFO_SAMPLE
{{ user.attribution.firebase_id }}Firebase IDStringfirebase-user-id
{{ user.attribution.adjust_id }}Adjust IDStringadjust-uuid-xyz
{{ user.attribution.provider }}Attribution providerStringApple Search Ads Integration
{{ user.attribution.ad_network }}Ad NetworkStringMeta Ads
{{ user.attribution.channel }}ChannelStringInstagram
{{ user.attribution.campaign }}CampaignStringmy-us-campaign
{{ user.attribution.fbc }}FBCStringfb.1.1234567890.111
{{ user.attribution.fbp }}FBPStringfb.1.1234567890.222

Click Event

MacroDescriptionFormatExample
{{ user.attribution.ttclid }}TikTok Click IDStringtiktok-click-id-xyz
{{ user.attribution.gclid }}Google Click IDStringGOOGLE123CLICK
{{ user.click_event.properties.custom_param }}Click event custom paramStringvalue
{{ user.click_event.properties.click_id }}Click IDStringclick1234
{{ user.click_event.user_agent }}User Agent from click eventStringMozilla...
{{ user.click_event.properties.utm_campaign }}UTM CampaignStringspring_sale
{{ user.flow_properties.any_custom_param }}Flow custom paramStringvalue2

Special Functions

Check the Remove null values setting under the Request body field to ensure null parameters are not sent.

Conditional Logic in JSON

With Connection Builder, you can embed conditional logic directly in your request body using Liquid if/else tags.

Example — send a has_idfa flag based on the availability of IDFA:

"has_idfa": {% if user.idfa %} 1 {% else %} 0 {% endif %}

You can also apply conditions to multi-line blocks or entire JSON payloads:

{% if user.country == "US" %}
{ "region": "NA" }
{% else %}
{ "region": "Other" }
{% endif %}

Value Modifiers

Transform values on the fly using built-in modifiers. Examples include arithmetic, string case formatting, and date formatting.

Arithmetic:

"revenue_x2": {{ event.receipt.price | times: 2 }}

String case formatting:

"product": "{{ event.receipt.product_id | upcase }}"

Date formatting:

"event_time": "{{ event.created_at | date: "%Y-%m-%d %H:%M:%S.000" }}"

UNIX timestamp:

"timestamp": "{{ event.created_at | date: "%s" }}"

For more on date formatting, see the Liquid date format guide.

Preview a Payload

While setting up your integration, you can preview the generated payload at any time. Click the 👁️ icon in the JSON editor toolbar, then enter an Event ID or Transaction ID to view the payload.

  • Event ID — Apphud's internal event ID, which can be found in the Events tab. Enable the ID column in the table. If the ID column doesn't exist, try clearing browser cache first.
  • Transaction ID — store's Transaction ID.
📘

Note

This preview does not trigger an actual HTTP request.

Apphud Connection Builder - payload Preview for a transaction ID

Test a Connection

Once a new integration configuration is saved, Test connection button will be enabled.

To simulate a real connection, click on it and enter a valid Transaction ID from your app. Apphud will fetch the corresponding event, receipt, and user data, then send a test POST request using your configuration. All configured filters are applied during the test.

📘

Sandbox transactions

If you're using a sandbox transaction, be sure to include the filter Environment = Sandbox. Otherwise, the event will be skipped.

  • If the transaction is invalid or belongs to a different app, an error message will appear: "Transaction was not found."
  • If the test is successful, a popup will display the response payload.
⚠️

Important Note

The test result is shown only once and cannot be accessed again after closing the popup.



Did this page help you?