> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversion event tracking for Meta Pixel, GTM and postbacks

> Send Fanvue conversion events to Meta Pixel, Google Tag Manager or a postback URL, and learn which of the six events to optimise each campaign on.

Fanvue sends six conversion events from a creator's page to Meta Pixel, Google Tag Manager or a server postback URL, so you can measure ad campaigns against revenue rather than clicks. Setup happens in Fanvue settings with nothing to build. You need a creator or agency account and the details for your provider: a Meta Pixel ID and Conversions API token, a Google Tag Manager server container, or a tracker that accepts postbacks.

Once events flow, the main decision is which one to optimise on. For paid subscriptions that's `Subscribe`; for free-trial offers it's `StartTrial`.

## Configuration

<Tabs>
  <Tab title="Creator">
    **Settings → Pixel tracking**. A creator configures their own providers and
    chooses which events are sent.
  </Tab>

  <Tab title="Agency">
    **Tracking → Tracking Providers**. An agency configures one set of providers
    that receives conversions for every creator it manages, using **Tracked
    Events** to choose which events are sent.
  </Tab>
</Tabs>

A creator pixel and an agency pixel can both be active. In that case both receive the same conversion, with the same `event_id`.

### Providers

| Provider | Fields you supply | Transport |
| - | - | - |
| **Meta** | Pixel ID, Conversions API token | Browser pixel + Conversions API |
| **Google Tag Manager** | GTM ID, GTM Server URL, GTM Server secret (optional) | Server-side POST to your server container |
| **Server postback** | Postback URL with macros | Server-to-server `GET` on conversion |

For Meta, keep both the browser pixel and the Conversions API enabled. [Deduplication](#deduplication) explains why running both never double counts.

## The six events

Fanvue sends six conversion events and no others.

| Event | Fires when |
| - | - |
| `PageView` | A fan opens a creator's profile page. Requires the visitor to have accepted marketing cookies. |
| `CompleteRegistration` | A fan finishes signing up, on both the email and the social / OAuth signup paths. |
| `InitiateCheckout` | A fan reaches the payment step of a paid, creator-bound checkout. Free trials, adding a payment method and wallet top-ups are excluded. |
| `Subscribe` | A **paid profile subscription** is charged successfully. |
| `StartTrial` | A **zero-price subscription** is created: a free trial, or a claimed free-trial link. Sent with `value: 0`. |
| `Purchase` | Any other successful payment: renewals, tips, PPV posts and messages, media links, checkout links, product subscriptions, app purchases and fan experiences. |

Every event is sent only on success. A declined card fires `InitiateCheckout` but never `Purchase` or `Subscribe`.

### The new-subscriber event

Optimise for `Subscribe`. It's the only event that fires when someone becomes a paying subscriber to a creator's profile, and this is how it sits beside the other outcomes:

| Outcome | Event | `content_category` |
| - | - | - |
| Lead / new account, not yet paying | `CompleteRegistration` | none |
| New paying subscriber | `Subscribe` | `subscription` |
| Free trial started | `StartTrial` | `subscription` |
| Existing subscriber renews next month | `Purchase` | `subscription_renewal` |
| Tip, PPV unlock, or any other spend | `Purchase` | see [Reading the Purchase event](#reading-the-purchase-event) |

Two consequences follow.

1. **Renewals are `Purchase`, not `Subscribe`.** Recurring revenue from an existing subscriber doesn't
   re-fire the acquisition event, so a `Subscribe` campaign metric isn't inflated by month two.
2. **A fan acquired on a free trial never fires `Subscribe` at all.** See [Trials](#trials).

`Subscribe` fires on every new paid subscription start, including a returning fan who previously
cancelled and subscribes again. It counts subscription **starts**, not first-ever subscribers.

### Trials

If you run free-trial offers, `Subscribe` alone undercounts your acquisition. A trial fan's entire lifetime produces `StartTrial` and then `Purchase`, never `Subscribe`. This is the full trial funnel.

| Moment | Event | Notes |
| - | - | - |
| Fan lands on the profile | `PageView` | Marketing-cookie consent required |
| Fan signs up | `CompleteRegistration` | New accounts only |
| Fan reaches the payment step | **nothing** | `InitiateCheckout` is skipped for zero-value flows |
| Trial starts | `StartTrial` | `value: 0`, `content_category: subscription` |
| Trial converts and bills for the first time | `Purchase` | `content_category: subscription_renewal` |
| Every month after that | `Purchase` | `content_category: subscription_renewal` |
| Trial cancelled before it bills | **nothing** | There is no cancellation event |

For a trial campaign:

* **Optimise on `StartTrial`.** It's the acquisition moment, and the only event that fires at trial
  signup.
* **Treat the first `Purchase` as the value event.** Its payload is indistinguishable from an ordinary
  monthly renewal, so measure it by time from `StartTrial` rather than by looking for a different
  `content_category`.
* **Don't expect `InitiateCheckout`.** A trial never reaches a paid checkout, so a funnel built on
  `InitiateCheckout` → `Subscribe` shows zero trial traffic even when trials are converting.

A trial can start in two ways: a free-trial link claimed on the profile or hosted subscribe page, or a
zero-price subscription settling through the payment flow. Both send `StartTrial`, and both share one
deduplication key, so a trial is never counted twice.

`StartTrial` deliberately carries `value: 0` rather than omitting the amount, because Meta flags
`StartTrial` events sent without a currency. It doesn't inflate ROAS.

### Reading the `Purchase` event

`Purchase` covers several commercially distinct things, so every one carries a
`custom_data.content_category` you can filter on.

| `content_category` | What it was |
| - | - |
| `subscription_renewal` | Monthly renewal of a profile subscription |
| `tip` | Tip |
| `post_unlock` | Paid post unlocked |
| `message_unlock` | Paid message unlocked |
| `media_link` | Paid media link |
| `app_store` | App Store purchase |
| `fan_experience` | One-off fan experience |
| `fan_experience_subscription` | Fan experience subscription started |
| `fan_experience_subscription_renewal` | Fan experience subscription renewed |
| `product_subscription_renewal` | Renewal of a checkout-link product subscription |

## Event structure

The payload sent to the Meta Conversions API has this shape. The Google Tag Manager container receives the
same fields, wrapped with your container ID.

```json theme={null}
{
  "event_name": "Subscribe",
  "event_id": "b4c1…",
  "event_time": 1755691200,
  "action_source": "website",
  "event_source_url": "https://www.fanvue.com/creator-handle",
  "user_data": {
    "external_id": "…",
    "fbp": "fb.1.1755600000000.1234567890",
    "fbc": "fb.1.1755600000000.IwAR…",
    "em": "…",
    "ph": "…",
    "fn": "…",
    "ln": "…",
    "db": "…",
    "ct": "…",
    "st": "…",
    "zp": "…",
    "country": "…",
    "client_ip_address": "…",
    "client_user_agent": "…"
  },
  "custom_data": {
    "value": 9.99,
    "currency": "USD",
    "content_category": "subscription",
    "tracking_link": "fv-3",
    "tracking_link_name": "TikTok bio",
    "tracking_link_source": "TIKTOK",
    "utm_source": "TIKTOK",
    "utm_medium": "tracking_link",
    "utm_campaign": "TikTok bio"
  }
}
```

### Top-level fields

| Field | Notes |
| - | - |
| `event_name` | One of [the six events](#the-six-events). |
| `event_id` | Deduplication key, shared by the browser and server legs of the same conversion. Derived from the invoice, so a retry never creates a second conversion. |
| `event_time` | Unix seconds, at the moment the conversion completed. |
| `action_source` | Always `website`. |
| `event_source_url` | The page the conversion is attributed to. Always populated, including for events raised by a payment webhook with no browser in the loop. Meta requires this, and blocks events without it for advertisers in restricted categories. |

### `user_data`: customer matching

Every personal field is SHA-256 hashed before it leaves Fanvue, per Meta's specification. Raw email
addresses, phone numbers and names are never transmitted.

| Field | What it carries |
| - | - |
| `external_id` | A stable hashed Fanvue user identifier, present on every event |
| `fbp`, `fbc` | Meta's browser and click cookies, when the fan carried them. A click identifier older than Meta's 90-day attribution window is dropped rather than sent stale. |
| `em`, `ph`, `fn`, `ln`, `db` | Hashed email, phone, first name, last name and date of birth |
| `ct`, `st`, `zp`, `country` | Hashed city, state, postcode and country, taken from the payment method on the transaction where one exists |
| `client_ip_address`, `client_user_agent` | Captured from the fan's session |

The more of these are present, the higher Meta's Event Match Quality score and the better the
attribution. Non-payment events such as `PageView` naturally carry fewer of them than a `Purchase`.

### `custom_data`

| Field | Notes |
| - | - |
| `value` | Decimal amount, for example `9.99`. Absent on events with no money attached. |
| `currency` | **Always `USD`**. |
| `content_category` | Which commercial event it was. See [Reading the Purchase event](#reading-the-purchase-event). |
| `tracking_link`, `tracking_link_name`, `tracking_link_source` | The Fanvue tracking link the fan first arrived through, when there was one. |
| `utm_source`, `utm_medium`, `utm_campaign` | The same values under conventional analytics names, so GA4 and Meta Custom Conversions can segment on them with no mapping. `utm_medium` is always `tracking_link`. |

<Warning>
  `value` is always reported in USD, whatever currency the fan was charged in. Don't apply
  your own FX conversion on top of it. A €9.99 charge arrives as its USD equivalent, labelled `USD`.
</Warning>

Tracking-link fields are best-effort and use **first-click** attribution, scoped to the creator on
the conversion. If the fan arrived without a tracking link, the fields are omitted and the
conversion still fires.

## Deduplication

Meta receives each conversion from two places at once:

* **Browser pixel.** Fires in the fan's browser and carries the browser cookies (`_fbp` / `_fbc`).
* **Conversions API (server-side).** Fires from Fanvue's backend, carries hashed customer data and
  survives ad blockers and iOS restrictions.

Both legs send the same `event_id`. Meta deduplicates on the pair (`event_name`, `event_id`), so it
keeps one and discards the other, and running both doesn't double count.

Keep both legs enabled. Turning off the browser pixel doesn't improve accuracy, because it removes the
cookie signal and lowers match quality.

## Server postback (S2S)

Use a server postback for ad trackers that expect a server-to-server callback rather than a pixel.

<Steps>
  <Step title="Append the click ID to your links">
    Add `fvc_id` to the creator links you advertise, set to your tracker's
    click-ID macro, for example `?fvc_id={clickid}`. Fanvue stores it when the
    fan lands.
  </Step>

  <Step title="Set your postback URL">
    Enter an HTTPS URL containing the macros you need. Fanvue substitutes and
    URL-encodes each one, then fires a `GET` on conversion. The available macros
    are `{click_id}`, `{payout}`, `{goal}`, `{currency}`, `{event_id}` and `{event_name}`.
  </Step>

  <Step title="Send a test">
    Use **Send test** to confirm your endpoint accepts the call before going
    live.
  </Step>
</Steps>

<Warning>
  No click ID means no postback at all. If the fan didn't arrive on a link carrying `fvc_id`,
  there's nothing to attribute and no callback is sent. A tracking link with a missing or
  misspelled `fvc_id` parameter looks exactly like zero conversions, so check the parameter is
  on the live link before you conclude a campaign didn't convert.
</Warning>

Postbacks fire once per conversion and are never re-fired after a successful delivery, because
trackers generally don't deduplicate them.

## Test events

Meta's **Events Manager → Test Events** tab shows events in real time without touching your reported
metrics.

<Steps>
  <Step title="Copy the test event code">
    In Events Manager, open **Test Events** and copy the code, for example
    `TEST12345`.
  </Step>

  <Step title="Paste it into Fanvue">
    Enter it in the **Test event code** field and choose **Send test event**.
  </Step>

  <Step title="Watch it arrive">
    The event appears in Test Events within a few seconds. **Recent activity**
    in Fanvue shows the most recent dispatch for each event type, refreshed
    every 30 seconds.
  </Step>
</Steps>

Test events are recorded under the name `TestEvent` and are deliberately excluded from real
conversion reporting, so sending a test doesn't make a misconfigured setup look live.

## Reporting recipes

The tracking-link and UTM fields sit on every event, but no standard report breaks down by them on
its own. Each destination needs a one-time setup.

<AccordionGroup>
  <Accordion title="Count new subscribers in Meta">
    Meta reports `Subscribe` as a standard event, so it's available in Ads Manager with no setup.
    To separate it further (for example, new subscribers from a specific campaign), create a Custom
    Conversion on the `Subscribe` event with a rule on `utm_source` or `tracking_link`.

    If the creator runs free trials, `Subscribe` isn't your whole acquisition number. Report
    `Subscribe` and `StartTrial` side by side, and never add them together as "subscribers", because
    one is paying and one is not.
  </Accordion>

  <Accordion title="Measure a trial campaign">
    Optimise the campaign on `StartTrial`, which fires the moment the trial begins. To measure
    whether those trials monetise, compare `StartTrial` volume against `Purchase` with
    `content_category` equal to `subscription_renewal`, offset by the trial length. The first charge
    after a trial is delivered as an ordinary renewal, so there's no separate "trial converted"
    event to filter on.
  </Accordion>

  <Accordion title="Report revenue without renewals">
    Create a Custom Conversion on `Purchase` and exclude `content_category` equal to
    `subscription_renewal` and `product_subscription_renewal`. What remains is new spend rather than
    recurring billing.
  </Accordion>

  <Accordion title="Segment by tracking link">
    In **Events Manager → Custom Conversions → Create**, pick the source event, then add a rule on
    the custom property, such as `utm_source` equals `TIKTOK` or `tracking_link` equals `fv-3`. The
    resulting Custom Conversion can be reported on and optimised against.

    Meta's standard campaign reports attribute to *Meta ad clicks*, not to Fanvue links, so these
    fields don't appear there without the Custom Conversion. The raw values are always visible
    per event under Test Events and in the event detail view.
  </Accordion>

  <Accordion title="Map the fields in GTM → GA4">
    The fields arrive at the server container inside the forwarded payload, but the container ignores
    keys it doesn't recognise. Read the params from the incoming event data and map them onto GA4
    event parameters, either as custom params or onto the GA4 traffic-source dimensions
    (`source` / `medium` / `campaign`) using `utm_source` / `utm_medium` / `utm_campaign`.
    Conversions then break down by source and campaign natively.
  </Accordion>
</AccordionGroup>

The Fanvue [tracking-links table](/docs/tutorials/tracking-links) (clicks, follows, subscriptions and revenue per link) needs none
of this. It reads Fanvue's own data, covers every acquisition source rather than only Meta-ad
traffic, and is the more reliable per-link view.

## Common issues

<AccordionGroup>
  <Accordion title="Events show as Failed in Recent activity">
    The status column shows the HTTP code Meta returned. The usual causes:

    * **Invalid or expired Conversions API token.** Tokens generated against a different pixel, or
      revoked when the generating user lost access to the Business Manager, fail every event. Generate
      a new one in Events Manager and save it again.
    * **Pixel ID and token mismatch.** The token must have been generated for the pixel ID entered
      alongside it.
    * **Pixel deleted or moved to a different Business Manager.** Re-enter both fields.
  </Accordion>

  <Accordion title="Conversions appear twice in Meta">
    Deduplication needs both legs to agree on `event_name` *and* `event_id`. If
    you also run your own Meta pixel on a landing page and fire your own
    `Subscribe` or `Purchase` from it, Meta has no way to match it to Fanvue's
    event and counts both. Remove the duplicate fire, or give it your own
    distinct event name.
  </Accordion>

  <Accordion title="Low Event Match Quality">
    Match quality scales with how much of `user_data` a given event can carry. `PageView` and
    `CompleteRegistration` carry less than a `Purchase`, which also has the billing address behind it,
    so a page-view-heavy pixel shows a lower average score. That difference is expected and not a
    misconfiguration.

    The score does drop for real reasons too. Visitors who declined marketing cookies carry no `_fbp`
    or `_fbc`, and a click identifier older than 90 days is dropped rather than sent expired.
  </Accordion>

  <Accordion title="No PageView events at all">
    `PageView` requires the visitor to have accepted marketing cookies. In regions
    where a consent banner is shown, page views from visitors who declined are not
    sent. Conversion events later in the funnel are sent server-side and are not
    affected in the same way, so a pixel with few page views but healthy purchases
    is behaving correctly.
  </Accordion>

  <Accordion title="Revenue does not match the creator's earnings">
    Two differences are expected.

    * `value` is the **gross amount charged**, before Fanvue's fee, and is always in USD.
    * `Purchase` includes renewals, tips and unlocks, not only subscriptions. Filter on
      `content_category` to compare like with like.
  </Accordion>

  <Accordion title="A conversion is missing entirely">
    Check these in order.

    1. The event is enabled. Both the creator and the agency configuration have a per-event list, and
       an event that is switched off is never sent.
    2. The configuration is enabled. A saved but disabled provider sends nothing.
    3. The payment actually succeeded. Declined and pending payments fire `InitiateCheckout` only.
    4. For server postbacks, the fan arrived carrying `fvc_id`.
  </Accordion>

  <Accordion title="Events stopped after a token or pixel change">
    Saving a new token clears the stored one immediately, and events dispatched between the change and
    a correct save have failed. Send a test event after every credential change to confirm the
    new value works.
  </Accordion>
</AccordionGroup>

## See also

* [Checkout attribution](/docs/checkout/attribution)
* [Creator events: subscriptions](/docs/creator/subscriptions)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.