View as Markdownllms.txt

Integrations

Meta Ads

AppRefer sends your installs, trials and purchases to Meta's Conversions API from the server, matched to the ad click, and imports your Meta spend for cost and ROAS.

What you need

  • A Meta dataset (pixel) in Events Manager.
  • An access token that can send events to that dataset: generate one in Events Manager (dataset → Settings → Conversions API), or use a system user token with access to the dataset.
  • For spend sync: the ad account IDs, and a token with ads_read on them.

Setup

  1. In Settings → Integrations → Meta, paste the Dataset ID and access token and save. The integration is connected once both are stored.
  2. Optionally add a Test Event Code from Events Manager and click Test Connection. With a code the test event shows in Test Events; without one it is a real Lead event.
  3. Add your ad account IDs to turn on spend sync (see below).
  4. Point your Meta ads at an AppRefer tracking link. See Tracking links for the URL parameters.

Configuration fields

FieldMeaning
datasetIdDataset (pixel) that receives events. Required.
accessTokenConversions API token. Required, secret.
webFunnelDatasetIdOptional second dataset for website conversions: verified website receipts and purchases made in a web funnel checkout. Without it they use datasetId, as do all app events.
testEventCodeUsed only by Test Connection.
adAccountIdsAd accounts to import spend from, for example act_123456789.
costAccessTokenOptional separate token for spend sync. Use it when the Conversions API token has no ads_read on the ad account (Meta error #200). Secret.
adAccountOwnershipWhich campaigns in each ad account belong to this app (see below).

The dashboard covers the common fields. Set webFunnelDatasetId, costAccessToken and adAccountOwnership with PATCH /api/v1/apps/{appId}/integrations/meta or the MCP tool configure_integration. Send only the fields you change; stored secrets you leave out are kept.

What AppRefer sends

Each event goes to the dataset with action_source: "website", because the ad click happened on a web tracking link. This lets you run web campaigns while AppRefer closes the loop to the in-app conversion.

Meta Conversions API
POST https://graph.facebook.com/v24.0/{datasetId}/events
FieldSource
fbcThe click's fbc from the landing page, or fb.1.{clickTime}.{fbclid}. Never sent when the fbclid looks like an unfilled template such as {{fbclid}}.
fbpBrowser ID from the landing page, or from your website receipt.
client_ip_address, client_user_agentThe browser at click time when known, otherwise the device.
external_idYour hashed user ID from a website receipt, then the hashed AppRefer device ID.
em, ph, fn, ln, db, ct, st, zp, countryAdvanced Matching, already SHA-256 hashed.
madidIDFA or GAID, only for installs that allowed tracking.
custom_datavalue and currency. Always present on Purchase (value 0 if unknown).
event_source_urlThe website page from a receipt, otherwise the tracking link URL.

Event mapping

These are the defaults. Change any of them in Settings → Event Mapping. Events with no mapping that are not one of Meta's standard event names are not sent, so in-app events such as app_opened or paywall_viewed never reach Meta unless you map them.

AppRefer eventMeta event
install, sign_upLead
complete_registration (website sign_up receipts)CompleteRegistration
startTrial, qualified_trialStartTrial
Subscription first payment or trial conversion (subscription_purchase)Subscribe
Subscription renewal (subscription_renewal)Subscribe
One-time purchase, SDK purchase event, website purchase receiptPurchase
subscribe (plan change)Subscribe
add_to_cartAddToCart
initiate_checkout (including website checkout_started)InitiateCheckout
searchSearch
view_contentViewContent

Subscription payments follow the mapping RevenueCat recommends for Meta: a subscription's first payment (including intro and promotional offers), its trial conversion and every renewal are Subscribe; only one-time purchases are Purchase. If your live ad sets optimise for Purchase, map subscription_purchase (and subscription_renewal if you want) to Purchase until you rebuild them.

Organic purchases

The subscription split applies to Meta-attributed installs. Organic and unattributed purchases sent to Meta keep the plain mapping, so their payments arrive as Purchase.

Trial starts can be held back and only sent once a trial looks likely to convert. See Qualified trials.

Advanced Matching

Give AppRefer the user's email or phone after sign-up or login. The SDK normalises and SHA-256 hashes each value on the device; nothing readable leaves the phone.

await AppRefer.setAdvancedMatching(email: "user@example.com", phone: "+447700900123")

All four SDKs also accept firstName, lastName and dateOfBirth.

Website events

Verified receipts from your payment backend (web events) are sent to Meta for Meta-attributed clicks: purchase as Purchase, sign_up as CompleteRegistration, checkout_started as InitiateCheckout and start_trial as StartTrial. To improve matching, a receipt can carry:

  • fbp: the _fbp cookie from your site. Used when the click itself has no browser ID. A value not in _fbp format is rejected with 400 invalid_fbp.
  • emailSha256 and the other hashed fields (firstNameSha256, lastNameSha256, citySha256, stateSha256, zipSha256, countrySha256): lowercase hex SHA-256, sent as Advanced Matching.
  • externalIdSha256: the same hashed user ID your Meta pixel sends, so browser and server events line up.

Test Events never carry fbc

When a website receipt is routed to Meta Test Events, AppRefer leaves fbc out. Test events still feed your dataset's diagnostics, and a made-up click ID would raise Meta's "modified fbclid" warning against your real traffic. Production events are not affected. Never invent an fbclid to test a live setup.

Spend sync and shared ad accounts

Add each Meta ad account to adAccountIds. AppRefer imports campaign spend, impressions and clicks on a schedule (each run refreshes today and yesterday), using costAccessToken if set and accessToken otherwise. It reads each account's currency from Meta and stores spend in that currency. Totals are only added up when the rows share one currency; mixed currencies are flagged instead, and ROAS is hidden when spend and revenue aren't comparable.

An ad account is treated as belonging to this app alone unless you say otherwise. If several apps share an account, give each app an adAccountOwnership rule with mode shared and the exact campaign IDs or campaign-name prefixes it owns:

PATCH /api/v1/apps/{appId}/integrations/metaJSON
{
  "adAccountIds": ["act_123456789", "act_987654321"],
  "adAccountOwnership": {
    "act_123456789": { "mode": "shared", "campaignNamePrefixes": ["Sparkly |"] },
    "act_987654321": { "mode": "dedicated" }
  }
}

Ownership rules fail closed

A shared account with no campaign IDs and no name prefixes imports no spend and records a sync error. Once adAccountOwnership exists, every account in adAccountIds needs a rule (dedicated or shared); an account without one is skipped with an error. This stops another app's campaigns being counted against this one.

Deduplication

Every event has a stable event_id, reused on retries, so Meta deduplicates repeats within its 48-hour window. Each renewal or repeat purchase gets its own ID.