From 4b9954b23d7ee51f57f34507131517ff9d1cb2c9 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 17 Mar 2026 18:43:10 +0100 Subject: [PATCH 1/3] fix-meta --- content/docs/integrations/facebook-pixel.mdx | 556 +++++------------- .../docs/integrations/meta-conversion-api.mdx | 554 +++++++++++++++++ content/docs/integrations/meta.json | 1 + 3 files changed, 705 insertions(+), 406 deletions(-) create mode 100644 content/docs/integrations/meta-conversion-api.mdx diff --git a/content/docs/integrations/facebook-pixel.mdx b/content/docs/integrations/facebook-pixel.mdx index 5ed37df0..11fc759a 100644 --- a/content/docs/integrations/facebook-pixel.mdx +++ b/content/docs/integrations/facebook-pixel.mdx @@ -1,516 +1,260 @@ --- title: "Facebook Pixel" -description: "The Meta Conversion API integration sends subscription lifecycle events from Superwall directly to Facebook's server-side Conversion API. This enables accurate attribution for Facebook and Instagram ad campaigns, optimizes ad delivery for subscription events, and provides reliable tracking that isn't affected by browser privacy restrictions or ad blockers." +description: "Track browser-side paywall and checkout events from Superwall web paywalls with Facebook Pixel. This integration injects Meta's client-side Pixel script and maps Web2App events to Pixel events." --- -In the **Marketing** section within **Integrations**, you can connect your Facebook Pixel account to Superwall: +Use the Facebook Pixel integration to send browser-side events from Superwall +web paywalls to Meta. This integration injects the standard Pixel script into +the page and maps Web2App events to Meta Pixel events such as `ViewContent`, +`InitiateCheckout`, and `Purchase`. -![](../images/integrations-facebook-pixel.jpeg) +> **Note:** If you need server-side subscription lifecycle events such as +> renewals, cancellations, expirations, or refunds, use +> [Meta Conversion API](/docs/integrations/meta-conversion-api) instead. That +> integration is separate from Facebook Pixel. -## Features +## How this integration works -- **Server-Side Event Tracking**: Events are sent directly to Meta's servers, bypassing browser limitations -- **Standard Event Mapping**: Automatically maps subscription events to Meta's standard events (Subscribe, Purchase, StartTrial) -- **Sandbox Environment Support**: Separate Pixel ID and access token for testing without affecting production data -- **Test Event Mode**: Use test event codes to validate integration in Meta Events Manager -- **Flexible Revenue Reporting**: Report either gross revenue or net proceeds after store fees -- **Anonymous User Handling**: Configurable behavior for users without an identified app user ID -- **Custom Event Names**: Override default event mappings to match your existing Meta Pixel conventions -- **Deduplication**: Event IDs prevent duplicate events from being counted multiple times +Superwall exposes Facebook Pixel as a Web2App browser integration. When the +integration is enabled: -## Configuration +- Superwall injects Meta's `fbq` bootstrap script into the page +- The Pixel is initialized with your `pixelId` +- A `PageView` event is sent when the script loads +- Supported paywall and checkout events are forwarded to `fbq` -### Required Settings +This is browser-side tracking for web paywalls. It does not forward +subscription lifecycle events from webhooks, and it does not use Meta's +Conversion API. -| Field | Description | Example | -|-------|-------------|---------| -| `integration_id` | Must be set to `"meta"` | `"meta"` | -| `access_token` | Meta access token with `ads_management` permission | `"EAAG..."` | -| `pixel_id` | Your Facebook Pixel ID | `"123456789012345"` | -| `sales_reporting` | Whether to report gross Revenue or net Proceeds | `"Revenue"` or `"Proceeds"` | +## Configuration -### Optional Settings +The browser integration uses `integrationId: "facebook-pixel"` with one +required field: `config.pixelId`. -| Field | Description | Default | +### Required setting + +| Field | Description | Example | |-------|-------------|---------| -| `sandbox_access_token` | Separate access token for sandbox/test events | None (sandbox events skipped) | -| `sandbox_pixel_id` | Separate Pixel ID for sandbox/test events | None (sandbox events skipped) | -| `test_event_code` | Test event code for validation in Events Manager | None | -| `anonymous_user_behavior` | How to handle events from users without an app user ID | `"send"` | -| `eventNameMappings` | Custom mapping to rename default event names | None | +| `pixelId` | Your Facebook Pixel ID from Meta Events Manager | `"123456789012345"` | -### Example Configuration +### Example configuration ```json { - "integration_id": "meta", - "access_token": "EAAG1234567890abcdef...", - "pixel_id": "123456789012345", - "sales_reporting": "Revenue", - "sandbox_access_token": "EAAG0987654321fedcba...", - "sandbox_pixel_id": "543210987654321", - "test_event_code": "TEST12345", - "anonymous_user_behavior": "send", - "eventNameMappings": { - "sw_subscription_cancelled": "CancelSubscription", - "sw_refund": "Refund" + "integrationId": "facebook-pixel", + "config": { + "pixelId": "123456789012345" } } ``` -## Getting Your Credentials - -### Access Token +In Superwall's generated static web config, the integration is represented like +this: -1. Go to [Meta Events Manager](https://business.facebook.com/events_manager) -2. Select your Pixel from **Data Sources** -3. Click **Settings** tab -4. Scroll to **Conversions API** section -5. Click **Generate access token** or use an existing System User token -6. Copy the access token - -**Note**: The access token requires `ads_management` permission. For production use, Meta recommends using a System User token rather than a personal access token. - -### Pixel ID - -1. Go to [Meta Events Manager](https://business.facebook.com/events_manager) -2. Select your Pixel from **Data Sources** -3. The Pixel ID is displayed at the top of the page (e.g., "Pixel ID: 123456789012345") - -### Test Event Code (Optional) - -1. In Events Manager, select your Pixel -2. Click the **Test Events** tab -3. Your test event code is displayed (e.g., "TEST12345") -4. Events sent with this code appear in the Test Events tab for validation - -## Event Mapping +```json +{ + "id": "42", + "name": "facebook-pixel", + "enabled": true, + "type": "integration", + "integrationId": "facebook-pixel", + "config": { + "pixelId": "123456789012345" + } +} +``` -Superwall events are mapped to Meta's standard events when possible. Using standard events enables Meta's machine learning to optimize ad delivery for specific conversion goals. +## Getting your Pixel ID -### Standard Event Mappings +You only need the Pixel ID for this integration. -| Superwall Event | Meta Standard Event | Description | -|-----------------|---------------------|-------------| -| `sw_subscription_start` | `Subscribe` | New paid subscription | -| `sw_trial_start` | `StartTrial` | Free trial begins | -| `sw_renewal` | `Purchase` | Subscription renewal payment | -| `sw_trial_converted` | `Purchase` | Trial converts to paid | -| `sw_intro_offer_converted` | `Purchase` | Intro offer converts to paid | +1. Go to [Meta Events Manager](https://business.facebook.com/events_manager). +2. Select your Pixel from **Data Sources**. +3. Copy the Pixel ID shown at the top of the page. -### Custom Event Mappings +## Script bootstrap -Events without a standard Meta equivalent are sent with their Superwall event names: +When the integration loads, Superwall injects Meta's standard client-side +script into the page: -| Superwall Event | Meta Event Name | -|-----------------|-----------------| -| `sw_subscription_cancelled` | `sw_subscription_cancelled` | -| `sw_trial_cancelled` | `sw_trial_cancelled` | -| `sw_subscription_expired` | `sw_subscription_expired` | -| `sw_billing_issue` | `sw_billing_issue` | -| `sw_refund` | `sw_refund` | -| `sw_product_change` | `sw_product_change` | +```html + +``` -### Complete Event Mapping Reference +Superwall also adds Meta's `noscript` image fallback for the same Pixel ID. -| Superwall Event | Condition | Meta Event | -|-----------------|-----------|------------| -| `INITIAL_PURCHASE` | `periodType = Trial` | `StartTrial` | -| `INITIAL_PURCHASE` | `periodType = Normal` | `Subscribe` | -| `INITIAL_PURCHASE` | `periodType = Intro` | `sw_intro_offer_start` | -| `RENEWAL` | `periodType = Trial` | `Purchase` | -| `RENEWAL` | `periodType = Normal` | `Purchase` | -| `RENEWAL` | `isTrialConversion = true` | `Purchase` | -| `CANCELLATION` | `periodType = Trial` | `sw_trial_cancelled` | -| `CANCELLATION` | `periodType = Normal` | `sw_subscription_cancelled` | -| `EXPIRATION` | Any | `sw_*_expired` | -| Any event | `price < 0` | `sw_refund` | +## Event mapping -## Event Format +The browser integration maps Web2App events to Pixel events as follows: -Events are sent to Meta's Conversion API in the following format: +| Superwall browser event | Meta Pixel event | Notes | +|-------------------------|------------------|-------| +| `paywall_open` | `ViewContent` | Includes `content_name`, `content_id`, and `content_type: "paywall"` | +| `transaction_start` | `InitiateCheckout` | Includes `content_ids` and `content_type: "product"` | +| `transaction_complete` | `Purchase` | Includes `transaction_id`, `content_ids`, and optional `value` and `currency` | +| `paywall_close` | `trackCustom("PaywallClosed")` | Custom event with `paywall_id` | +| `manageLink_click` | `trackCustom("ManageLinkClick")` | Custom event with subscription fields | +| `activateDeviceLink_click` | `trackCustom("ActivateDeviceLinkClick")` | Custom event with subscription fields | -### API Endpoint +## Event payload details -``` -POST https://graph.facebook.com/v21.0/{pixel_id}/events?access_token={access_token} -``` +Each mapped event sends a small payload based on the browser event data. -### Request Payload +### `paywall_open` -> `ViewContent` ```json { - "data": [ - { - "event_name": "Subscribe", - "event_time": 1705312200, - "event_id": "evt_abc123", - "action_source": "app", - "user_data": { - "external_id": ["user_12345"] - }, - "custom_data": { - "value": 9.99, - "currency": "USD", - "content_type": "product", - "content_name": "com.app.premium.monthly", - "content_ids": ["com.app.premium.monthly"] - } - } - ], - "test_event_code": "TEST12345" + "content_name": "Main paywall", + "content_id": "paywall_123", + "content_type": "paywall" } ``` -### Event Fields - -| Field | Description | Example | -|-------|-------------|---------| -| `event_name` | Meta standard event or custom event name | `"Subscribe"` | -| `event_time` | Unix timestamp in seconds | `1705312200` | -| `event_id` | Unique event ID for deduplication | `"evt_abc123"` | -| `action_source` | Always set to `"app"` for mobile app events | `"app"` | -| `user_data` | User identification data | `{"external_id": ["user_12345"]}` | -| `custom_data` | Event-specific data including revenue | See below | - -### Custom Data Fields - -| Field | Description | Example | -|-------|-------------|---------| -| `value` | Revenue amount (based on `sales_reporting` setting) | `9.99` | -| `currency` | ISO 4217 currency code | `"USD"` | -| `content_type` | Always `"product"` for subscription events | `"product"` | -| `content_name` | Product identifier | `"com.app.premium.monthly"` | -| `content_ids` | Array containing the product ID | `["com.app.premium.monthly"]` | - -## User Identification - -Meta's Conversion API requires user identification for event matching. The integration uses `external_id` to identify users. - -### Known Users - -For users with an `originalAppUserId` set in Superwall: +### `transaction_start` -> `InitiateCheckout` ```json { - "user_data": { - "external_id": ["user_12345"] - } + "content_ids": ["com.app.premium.monthly"], + "content_type": "product" } ``` -### Anonymous Users - -For users without an `originalAppUserId`, the behavior depends on `anonymous_user_behavior`: - -**When set to `"send"` (default)**: -- Events are sent with a synthetic ID: `$STORE_NAME:originalTransactionId` +### `transaction_complete` -> `Purchase` ```json { - "user_data": { - "external_id": ["$APP_STORE:1000000123456789"] - } + "transaction_id": "txn_123", + "content_ids": ["com.app.premium.monthly"], + "content_type": "product", + "value": 9.99, + "currency": "USD" } ``` -**When set to `"dontSend"`**: -- Events from anonymous users are skipped entirely - -## Revenue Tracking - -### Revenue vs Proceeds - -The `sales_reporting` setting controls which amount is sent in the `value` field: - -- **Revenue**: The full price charged to the customer (e.g., $9.99) -- **Proceeds**: The amount after store fees are deducted (e.g., $8.49 after Apple's 15-30% commission) - -### Zero-Value Events +The `value` and `currency` fields are only included when they are present in +the browser event payload. -For events without revenue (cancellations, expirations), the `value` and `currency` fields are omitted: +### `paywall_close` -> `PaywallClosed` ```json { - "custom_data": { - "content_type": "product", - "content_name": "com.app.premium.monthly", - "content_ids": ["com.app.premium.monthly"] - } + "paywall_id": "paywall_123" } ``` -### Refund Events - -Refunds are sent with negative values: +### `manageLink_click` -> `ManageLinkClick` ```json { - "event_name": "sw_refund", - "custom_data": { - "value": -9.99, - "currency": "USD", - "content_type": "product", - "content_name": "com.app.premium.monthly", - "content_ids": ["com.app.premium.monthly"] - } + "subscription_name": "Premium Monthly", + "subscription_status": "active", + "redemption_code": "ABC123", + "provider": "stripe" } ``` -## Sandbox Handling - -The integration supports separate handling for sandbox (test) events: - -### With Sandbox Credentials Configured - -When both `sandbox_pixel_id` and `sandbox_access_token` are provided: -- Production events use the main credentials -- Sandbox events use the sandbox credentials -- Events are tracked separately in Meta Events Manager - -### Without Sandbox Credentials - -When sandbox credentials are not provided: -- Production events are sent normally -- Sandbox events are **skipped entirely** -- This prevents test data from affecting your production Pixel - -## Test Event Mode - -Use the `test_event_code` setting to validate your integration without affecting production data: - -1. Get your test event code from Meta Events Manager → Test Events -2. Add `test_event_code` to your configuration -3. Send test events from your app -4. View events in the Test Events tab of Events Manager - -Events sent with a test event code: -- Appear in the Test Events tab -- Are **not** counted in your main event metrics -- Are **not** used for ad optimization -- Help validate your integration before going live - -**Important**: Remove the `test_event_code` before deploying to production. - -## Custom Event Names - -Use `eventNameMappings` to override default event names: +### `activateDeviceLink_click` -> `ActivateDeviceLinkClick` ```json { - "eventNameMappings": { - "sw_trial_start": "CustomTrialStart", - "sw_subscription_start": "CustomSubscribe", - "sw_renewal": "CustomRenewal" - } + "subscription_name": "Premium Monthly", + "redemption_code": "ABC123" } ``` -**Note**: Mappings override both standard and custom event names. For example, mapping `sw_subscription_start` will send your custom name instead of the Meta standard `Subscribe` event. - -## Testing the Integration - -### 1. Configure Test Event Code - -Add your `test_event_code` from Meta Events Manager to validate events without affecting production metrics. - -### 2. Send Test Events - -Trigger subscription events from your app in sandbox mode. - -### 3. Verify in Meta Events Manager - -1. Go to Meta Events Manager → your Pixel -2. Click the **Test Events** tab -3. Look for events with your test event code -4. Verify event names, parameters, and user data are correct - -### 4. Check Event Quality - -1. Go to Meta Events Manager → your Pixel → **Overview** -2. Check the **Event Match Quality** score -3. Higher scores indicate better event matching - -### 5. Test Scenarios - -Verify these scenarios work correctly: +## Facebook Pixel vs. Meta Conversion API -- [ ] Production event sends to main Pixel -- [ ] Sandbox event sends to sandbox Pixel (if configured) -- [ ] Sandbox event is skipped when no sandbox credentials -- [ ] Trial start maps to `StartTrial` -- [ ] Subscription start maps to `Subscribe` -- [ ] Renewal maps to `Purchase` -- [ ] Cancellation sends as custom event -- [ ] Anonymous users handled per configuration -- [ ] Revenue is included for paid events -- [ ] Test event code appears in Test Events tab +These integrations are related, but they solve different problems. -## Best Practices +| Integration | Tracking mode | Best for | Does it send renewals, cancellations, and refunds? | +|-------------|---------------|----------|----------------------------------------------------| +| Facebook Pixel | Browser-side | Web paywall interactions and checkout flow events | No | +| Meta Conversion API | Server-side | Subscription lifecycle and revenue webhook events | Yes | -1. **Use System User tokens**: For production, create a System User in Meta Business Manager and use its access token instead of a personal token for better security and stability. +Use Facebook Pixel when you want client-side behavioral signals from web +paywalls. Use [Meta Conversion API](/docs/integrations/meta-conversion-api) +when you need server-side revenue and subscription lifecycle events. -2. **Configure sandbox credentials**: Use a separate test Pixel for development to keep your production data clean. +## Testing the integration -3. **Remove test event code for production**: Test event codes prevent events from being used for optimization. +Validate the browser-side integration before relying on it in campaigns. -4. **Match user IDs across platforms**: Use consistent `external_id` values between your Pixel browser events and server events for better cross-device attribution. +1. Enable the Facebook Pixel integration with your Pixel ID. +2. Open a web paywall that uses the integration. +3. Confirm `PageView` appears in Meta Events Manager. +4. Trigger paywall and checkout events. +5. Verify `ViewContent`, `InitiateCheckout`, `Purchase`, and any custom events + appear as expected. -5. **Monitor Event Match Quality**: Check your Event Match Quality score in Events Manager regularly. Scores below 6.0 indicate potential matching issues. +You can also confirm that `fbq` is loaded in the browser and inspect network +requests to Meta while exercising the paywall flow. -6. **Use standard events when possible**: Standard events like `Subscribe`, `Purchase`, and `StartTrial` enable Meta's machine learning to optimize for those specific conversions. +## Common use cases -## Common Use Cases +### Track paywall impressions -### Optimizing Campaigns for Subscriptions +Use `ViewContent` from `paywall_open` to measure paywall views and build +remarketing audiences around paywall engagement. -1. Send `Subscribe` events for new paid subscriptions -2. Create a Custom Conversion in Meta Ads Manager based on `Subscribe` -3. Optimize your campaigns for subscription conversions -4. Meta will show your ads to users most likely to subscribe +### Track checkout starts -### Measuring Trial-to-Paid Conversion +Use `InitiateCheckout` from `transaction_start` to see where users begin the +purchase flow but do not complete it. -1. Track `StartTrial` events for trial starts -2. Track `Purchase` events for trial conversions -3. Create a funnel in Meta Analytics -4. Analyze conversion rate and time-to-convert +### Track completed purchases in the browser -### Retargeting Churned Users +Use `Purchase` from `transaction_complete` to capture client-side checkout +completion on web paywalls. -1. Track `sw_subscription_cancelled` events -2. Create a Custom Audience of users who cancelled -3. Run re-engagement campaigns with special offers -4. Exclude recent subscribers to avoid wasted ad spend +### Track manage-subscription interactions -### Value-Based Optimization - -1. Include revenue in `custom_data.value` -2. Create Value-Based Custom Conversions -3. Optimize campaigns for highest value subscribers -4. Meta prioritizes showing ads to users likely to generate more revenue +Use `ManageLinkClick` and `ActivateDeviceLinkClick` to understand how users +interact with account and device-link flows. ## Troubleshooting -### Events Not Appearing in Events Manager +### Events are not appearing in Meta **Possible causes:** -- Invalid access token (expired or insufficient permissions) -- Incorrect Pixel ID -- Sandbox events without sandbox credentials (events are skipped) -- Test event code routing events to Test Events tab only - -**Solutions:** -1. Verify your access token has `ads_management` permission -2. Confirm your Pixel ID matches Events Manager -3. Check for sandbox credentials if testing -4. Remove `test_event_code` to see events in main Overview -### Authentication Errors (Error 190) - -**Possible causes:** -- Access token has expired -- Token doesn't have required permissions -- Token was revoked +- The Pixel ID is incorrect +- The browser integration is not enabled +- The page did not load the injected `fbq` script +- The paywall flow did not emit the expected browser event **Solutions:** -1. Generate a new access token in Events Manager -2. Ensure the token has `ads_management` permission -3. Consider using a System User token for stability -### Low Event Match Quality +1. Verify the Pixel ID in Meta Events Manager. +2. Confirm the integration is enabled for the web paywall flow. +3. Check that `fbq` is available in the page. +4. Inspect the browser console and network requests during the flow. -**Possible causes:** -- Only `external_id` is being sent -- No additional user data available - -**Solutions:** -- Event Match Quality can be improved by including additional user data fields (email, phone, IP address) if available in your webhook data -- Ensure `external_id` values are consistent with other data sources - -### Events Show as "Duplicate" +### `Purchase` is missing value or currency **Possible causes:** -- Same event being sent multiple times -- Event ID collision - -**Solutions:** -- The integration uses the Superwall event ID as `event_id` for deduplication -- Verify your webhook isn't triggering multiple times for the same event - -### Wrong Event Names -**Possible causes:** -- Custom event name mappings overriding standard events -- Unexpected event type mapping +- The `transaction_complete` event did not include those fields **Solutions:** -- Review your `eventNameMappings` configuration -- Check the event mapping reference table above -- Test with `test_event_code` to verify event names -## Rate Limits +- Verify the browser event payload includes `value` and `currency`. +- If you need more complete revenue lifecycle reporting, use + [Meta Conversion API](/docs/integrations/meta-conversion-api). -Meta's Conversion API has the following limits: +### You need renewals, cancellations, or refunds -| Limit | Value | -|-------|-------| -| Requests per hour | 10,000 per Pixel | -| Events per request | 1,000 maximum | -| Request body size | 1MB maximum | +Facebook Pixel does not send those lifecycle events in this integration. -The integration sends one event per webhook, which is well within these limits. For high-volume applications, Meta automatically handles queuing. - -## API Reference - -### Endpoint - -``` -POST https://graph.facebook.com/v21.0/{pixel_id}/events -``` - -### Authentication - -Access token passed as URL parameter: - -``` -?access_token={access_token} -``` - -### Request Headers - -``` -Content-Type: application/json -Accept: */* -``` - -### Response - -**Success (200 OK)**: -```json -{ - "events_received": 1, - "messages": [], - "fbtrace_id": "ABC123..." -} -``` - -**Error (400/401/403)**: -```json -{ - "error": { - "message": "Invalid OAuth access token.", - "type": "OAuthException", - "code": 190, - "fbtrace_id": "ABC123..." - } -} -``` +Use [Meta Conversion API](/docs/integrations/meta-conversion-api) for that +server-side workflow. -## Additional Resources +## Additional resources -- [Meta Conversion API Documentation](https://developers.facebook.com/docs/marketing-api/conversions-api) -- [Server Events Parameters Reference](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters) -- [Event Quality Scoring Guide](https://www.facebook.com/business/help/765081237991954) -- [App Events Best Practices](https://developers.facebook.com/docs/app-events/best-practices) - [Meta Events Manager](https://business.facebook.com/events_manager) +- [Meta Pixel documentation](https://developers.facebook.com/docs/meta-pixel/) +- [Meta Conversion API](/docs/integrations/meta-conversion-api) for + server-side subscription lifecycle tracking diff --git a/content/docs/integrations/meta-conversion-api.mdx b/content/docs/integrations/meta-conversion-api.mdx new file mode 100644 index 00000000..8af2c1a9 --- /dev/null +++ b/content/docs/integrations/meta-conversion-api.mdx @@ -0,0 +1,554 @@ +--- +title: "Meta Conversion API" +description: "Send subscription lifecycle events from Superwall to Meta's server-side Conversion API. Use this integration to report subscriptions, renewals, cancellations, refunds, and other revenue events to your Meta Pixel." +--- + +Use the Meta Conversion API integration to forward Superwall subscription +lifecycle events to Meta over a server-side API. This is the integration to use +when you want Meta to receive renewals, cancellations, expirations, refunds, +and other webhook-driven revenue events. + +> **Note:** If you need browser-side tracking for web paywalls, see +> [Facebook Pixel](/docs/integrations/facebook-pixel). That integration tracks +> client-side paywall events and does not send subscription lifecycle events. + +In the **Marketing** section within **Integrations**, you can connect your Meta +Conversion API integration in Superwall: + +![](../images/integrations-facebook-pixel.jpeg) + +## Features + +- **Server-side event tracking**: Events are sent directly to Meta's servers. +- **Standard event mapping**: Superwall maps subscription events to Meta + standard events where possible. +- **Sandbox environment support**: Use separate credentials for sandbox events. +- **Test event mode**: Validate events in Meta Events Manager before going + live. +- **Flexible revenue reporting**: Send gross revenue or net proceeds. +- **Anonymous user handling**: Control how events behave when a user has no + `originalAppUserId`. +- **Custom event names**: Override the default event mappings. +- **Deduplication**: Event IDs prevent duplicate event counting. + +## Configuration + +The Meta Conversion API integration uses the server-side `meta` integration in +`openrev-integrations`. + +### Required settings + +| Field | Description | Example | +|-------|-------------|---------| +| `integration_id` | Must be set to `"meta"` | `"meta"` | +| `access_token` | Meta access token with `ads_management` permission | `"EAAG..."` | +| `pixel_id` | Your Meta Pixel ID | `"123456789012345"` | +| `sales_reporting` | Whether to report gross revenue or net proceeds | `"Revenue"` or `"Proceeds"` | + +### Optional settings + +| Field | Description | Default | +|-------|-------------|---------| +| `sandbox_access_token` | Separate access token for sandbox events | None (sandbox events skipped) | +| `sandbox_pixel_id` | Separate Pixel ID for sandbox events | None (sandbox events skipped) | +| `test_event_code` | Test event code for validation in Events Manager | None | +| `anonymous_user_behavior` | How to handle events from users without an app user ID | `"send"` | +| `eventNameMappings` | Custom mapping to rename default event names | None | + +### Example configuration + +```json +{ + "integration_id": "meta", + "access_token": "EAAG1234567890abcdef...", + "pixel_id": "123456789012345", + "sales_reporting": "Revenue", + "sandbox_access_token": "EAAG0987654321fedcba...", + "sandbox_pixel_id": "543210987654321", + "test_event_code": "TEST12345", + "anonymous_user_behavior": "send", + "eventNameMappings": { + "sw_subscription_cancelled": "CancelSubscription", + "sw_refund": "Refund" + } +} +``` + +## Getting your credentials + +Meta Events Manager is the source of truth for the credentials this integration +needs. + +### Access token + +1. Go to [Meta Events Manager](https://business.facebook.com/events_manager). +2. Select your Pixel from **Data Sources**. +3. Click the **Settings** tab. +4. Scroll to **Conversions API**. +5. Generate an access token, or use an existing System User token. +6. Copy the access token. + +**Note**: The access token requires `ads_management` permission. For +production use, Meta recommends using a System User token instead of a +personal token. + +### Pixel ID + +1. Go to [Meta Events Manager](https://business.facebook.com/events_manager). +2. Select your Pixel from **Data Sources**. +3. Copy the Pixel ID shown at the top of the page. + +### Test event code + +1. In Events Manager, select your Pixel. +2. Click the **Test Events** tab. +3. Copy the displayed test event code. +4. Add it to `test_event_code` while validating your integration. + +## Event mapping + +Superwall maps revenue lifecycle events to Meta standard events when possible. +Using standard events helps Meta optimize ad delivery for those outcomes. + +### Standard event mappings + +| Superwall event | Meta standard event | Description | +|-----------------|---------------------|-------------| +| `sw_subscription_start` | `Subscribe` | New paid subscription | +| `sw_trial_start` | `StartTrial` | Free trial begins | +| `sw_renewal` | `Purchase` | Subscription renewal payment | +| `sw_trial_converted` | `Purchase` | Trial converts to paid | +| `sw_intro_offer_converted` | `Purchase` | Intro offer converts to paid | + +### Custom event mappings + +Events without a standard Meta equivalent are sent with their Superwall event +names by default: + +| Superwall event | Meta event name | +|-----------------|-----------------| +| `sw_subscription_cancelled` | `sw_subscription_cancelled` | +| `sw_trial_cancelled` | `sw_trial_cancelled` | +| `sw_subscription_expired` | `sw_subscription_expired` | +| `sw_billing_issue` | `sw_billing_issue` | +| `sw_refund` | `sw_refund` | +| `sw_product_change` | `sw_product_change` | + +### Complete event mapping reference + +| Superwall event | Condition | Meta event | +|-----------------|-----------|------------| +| `INITIAL_PURCHASE` | `periodType = Trial` | `StartTrial` | +| `INITIAL_PURCHASE` | `periodType = Normal` | `Subscribe` | +| `INITIAL_PURCHASE` | `periodType = Intro` | `sw_intro_offer_start` | +| `RENEWAL` | `periodType = Trial` | `Purchase` | +| `RENEWAL` | `periodType = Normal` | `Purchase` | +| `RENEWAL` | `isTrialConversion = true` | `Purchase` | +| `CANCELLATION` | `periodType = Trial` | `sw_trial_cancelled` | +| `CANCELLATION` | `periodType = Normal` | `sw_subscription_cancelled` | +| `EXPIRATION` | Any | `sw_*_expired` | +| Any event | `price < 0` | `sw_refund` | + +## Event format + +Superwall sends events to Meta's Conversion API in the following format: + +### API endpoint + +``` +POST https://graph.facebook.com/v21.0/{pixel_id}/events?access_token={access_token} +``` + +### Request payload + +```json +{ + "data": [ + { + "event_name": "Subscribe", + "event_time": 1705312200, + "event_id": "evt_abc123", + "action_source": "app", + "user_data": { + "external_id": ["user_12345"] + }, + "custom_data": { + "value": 9.99, + "currency": "USD", + "content_type": "product", + "content_name": "com.app.premium.monthly", + "content_ids": ["com.app.premium.monthly"] + } + } + ], + "test_event_code": "TEST12345" +} +``` + +### Event fields + +| Field | Description | Example | +|-------|-------------|---------| +| `event_name` | Meta standard event or custom event name | `"Subscribe"` | +| `event_time` | Unix timestamp in seconds | `1705312200` | +| `event_id` | Unique event ID for deduplication | `"evt_abc123"` | +| `action_source` | Always set to `"app"` for mobile app events | `"app"` | +| `user_data` | User identification data | `{"external_id": ["user_12345"]}` | +| `custom_data` | Event-specific data including revenue | See below | + +### Custom data fields + +| Field | Description | Example | +|-------|-------------|---------| +| `value` | Revenue amount based on `sales_reporting` | `9.99` | +| `currency` | ISO 4217 currency code | `"USD"` | +| `content_type` | Always `"product"` for subscription events | `"product"` | +| `content_name` | Product identifier | `"com.app.premium.monthly"` | +| `content_ids` | Array containing the product ID | `["com.app.premium.monthly"]` | + +## User identification + +Meta's server-side event matching depends on the `external_id` value that +Superwall sends. + +### Known users + +For users with an `originalAppUserId`, Superwall sends: + +```json +{ + "user_data": { + "external_id": ["user_12345"] + } +} +``` + +### Anonymous users + +For users without an `originalAppUserId`, behavior depends on +`anonymous_user_behavior`. + +When set to `"send"`: + +- Events are sent with a synthetic ID using the store name and original + transaction ID. + +```json +{ + "user_data": { + "external_id": ["$APP_STORE:1000000123456789"] + } +} +``` + +When set to `"dontSend"`: + +- Events from anonymous users are skipped. + +## Revenue tracking + +The `sales_reporting` setting controls which amount is sent in `custom_data`. + +### Revenue vs. proceeds + +- **Revenue**: The full amount charged to the customer. +- **Proceeds**: The amount after store fees and taxes. + +### Zero-value events + +Events without revenue, such as cancellations and expirations, omit `value` +and `currency`: + +```json +{ + "custom_data": { + "content_type": "product", + "content_name": "com.app.premium.monthly", + "content_ids": ["com.app.premium.monthly"] + } +} +``` + +### Refund events + +Refunds are sent with a negative value: + +```json +{ + "event_name": "sw_refund", + "custom_data": { + "value": -9.99, + "currency": "USD", + "content_type": "product", + "content_name": "com.app.premium.monthly", + "content_ids": ["com.app.premium.monthly"] + } +} +``` + +## Sandbox handling + +The integration supports separate credentials for sandbox events. + +### With sandbox credentials configured + +When both `sandbox_pixel_id` and `sandbox_access_token` are provided: + +- Production events use the main credentials. +- Sandbox events use the sandbox credentials. +- Events stay separated in Meta Events Manager. + +### Without sandbox credentials + +When sandbox credentials are not provided: + +- Production events are sent normally. +- Sandbox events are skipped entirely. +- Test data does not pollute your production Pixel. + +## Test event mode + +Use `test_event_code` to validate the integration without affecting production +metrics. + +1. Copy a test event code from Meta Events Manager. +2. Add it to `test_event_code`. +3. Send test events from your app. +4. Verify the events in the **Test Events** tab. + +Events sent with a test event code: + +- Appear in **Test Events** +- Do not count toward main metrics +- Are not used for optimization + +Remove the test event code before sending live traffic. + +## Custom event names + +Use `eventNameMappings` to override the default event names: + +```json +{ + "eventNameMappings": { + "sw_trial_start": "CustomTrialStart", + "sw_subscription_start": "CustomSubscribe", + "sw_renewal": "CustomRenewal" + } +} +``` + +Mappings override both standard and custom event names. For example, mapping +`sw_subscription_start` sends your custom value instead of `Subscribe`. + +## Testing the integration + +Validate the integration end-to-end before relying on it for attribution. + +### 1. Configure a test event code + +Add `test_event_code` from Meta Events Manager. + +### 2. Send test events + +Trigger subscription events from your app in sandbox mode. + +### 3. Verify in Meta Events Manager + +1. Go to Meta Events Manager. +2. Select your Pixel. +3. Open **Test Events**. +4. Verify the event names, parameters, and user data. + +### 4. Check event quality + +1. Open your Pixel in Meta Events Manager. +2. Check the **Event Match Quality** score. +3. Review whether matching quality is acceptable for your use case. + +### 5. Test scenarios + +- [ ] Production event sends to the main Pixel +- [ ] Sandbox event sends to the sandbox Pixel when configured +- [ ] Sandbox event is skipped when sandbox credentials are not configured +- [ ] Trial start maps to `StartTrial` +- [ ] Subscription start maps to `Subscribe` +- [ ] Renewal maps to `Purchase` +- [ ] Cancellation sends as a custom event +- [ ] Anonymous users follow your configured behavior +- [ ] Revenue is included for paid events +- [ ] Test event code appears in **Test Events** + +## Best practices + +1. Use System User tokens for production access. +2. Configure separate sandbox credentials for testing. +3. Remove `test_event_code` before going live. +4. Keep `external_id` values consistent across data sources. +5. Monitor Event Match Quality in Events Manager. +6. Prefer Meta standard events when they fit your use case. + +## Common use cases + +### Optimizing campaigns for subscriptions + +1. Send `Subscribe` events for new paid subscriptions. +2. Create a custom conversion in Meta Ads Manager based on `Subscribe`. +3. Optimize campaigns for subscription conversions. + +### Measuring trial-to-paid conversion + +1. Track `StartTrial` for trial starts. +2. Track `Purchase` for trial conversions. +3. Build reporting in Meta around the conversion funnel. + +### Retargeting churned users + +1. Track `sw_subscription_cancelled`. +2. Build a custom audience for cancelled users. +3. Run re-engagement campaigns with targeted offers. + +### Value-based optimization + +1. Include revenue in `custom_data.value`. +2. Create value-based conversions in Meta. +3. Optimize for higher-value subscribers. + +## Troubleshooting + +### Events not appearing in Events Manager + +**Possible causes:** + +- Invalid access token +- Incorrect Pixel ID +- Sandbox events without sandbox credentials +- Test event code routing events into **Test Events** only + +**Solutions:** + +1. Verify your token has `ads_management`. +2. Confirm the Pixel ID matches Events Manager. +3. Check whether sandbox credentials are configured. +4. Remove `test_event_code` if you expect the events in the main overview. + +### Authentication errors (Error 190) + +**Possible causes:** + +- Expired token +- Missing permissions +- Revoked token + +**Solutions:** + +1. Generate a new access token. +2. Verify the token has `ads_management`. +3. Prefer a System User token for long-term stability. + +### Low event match quality + +**Possible causes:** + +- Only `external_id` is being sent +- Limited user data is available + +**Solutions:** + +- Keep `external_id` consistent with your other Meta data sources. +- Include additional user data if your webhook pipeline supports it. + +### Events show as duplicate + +**Possible causes:** + +- The same event is sent more than once +- Event ID collisions + +**Solutions:** + +- Superwall uses the event ID for deduplication. +- Verify that the same webhook is not being processed multiple times. + +### Wrong event names + +**Possible causes:** + +- `eventNameMappings` overrides the defaults +- The event type maps differently than expected + +**Solutions:** + +1. Review your `eventNameMappings`. +2. Compare the payload against the mapping table above. +3. Validate with `test_event_code`. + +## Rate limits + +Meta's Conversion API has the following published limits: + +| Limit | Value | +|-------|-------| +| Requests per hour | 10,000 per Pixel | +| Events per request | 1,000 maximum | +| Request body size | 1MB maximum | + +The integration sends one event per webhook, which is well within those limits. + +## API reference + +### Endpoint + +``` +POST https://graph.facebook.com/v21.0/{pixel_id}/events +``` + +### Authentication + +The access token is passed as a URL parameter: + +``` +?access_token={access_token} +``` + +### Request headers + +``` +Content-Type: application/json +Accept: */* +``` + +### Response + +**Success (200 OK)**: + +```json +{ + "events_received": 1, + "messages": [], + "fbtrace_id": "ABC123..." +} +``` + +**Error (400/401/403)**: + +```json +{ + "error": { + "message": "Invalid OAuth access token.", + "type": "OAuthException", + "code": 190, + "fbtrace_id": "ABC123..." + } +} +``` + +## Additional resources + +- [Meta Conversion API Documentation](https://developers.facebook.com/docs/marketing-api/conversions-api) +- [Server Events Parameters Reference](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters) +- [Event Quality Scoring Guide](https://www.facebook.com/business/help/765081237991954) +- [App Events Best Practices](https://developers.facebook.com/docs/app-events/best-practices) +- [Meta Events Manager](https://business.facebook.com/events_manager) +- [Facebook Pixel](/docs/integrations/facebook-pixel) for browser-side web + paywall tracking diff --git a/content/docs/integrations/meta.json b/content/docs/integrations/meta.json index b149eda0..da6f53bd 100644 --- a/content/docs/integrations/meta.json +++ b/content/docs/integrations/meta.json @@ -8,6 +8,7 @@ "webhooks", "apple-search-ads", "apple-retention-messaging", + "meta-conversion-api", "facebook-pixel", "mixpanel", "adjust", From f355e91fc48d517d3c98cc7b13a2378589eea01d Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 17 Mar 2026 18:45:23 +0100 Subject: [PATCH 2/3] fix --- content/docs/integrations/meta-conversion-api.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/content/docs/integrations/meta-conversion-api.mdx b/content/docs/integrations/meta-conversion-api.mdx index 8af2c1a9..0a2eef38 100644 --- a/content/docs/integrations/meta-conversion-api.mdx +++ b/content/docs/integrations/meta-conversion-api.mdx @@ -40,7 +40,6 @@ The Meta Conversion API integration uses the server-side `meta` integration in | Field | Description | Example | |-------|-------------|---------| -| `integration_id` | Must be set to `"meta"` | `"meta"` | | `access_token` | Meta access token with `ads_management` permission | `"EAAG..."` | | `pixel_id` | Your Meta Pixel ID | `"123456789012345"` | | `sales_reporting` | Whether to report gross revenue or net proceeds | `"Revenue"` or `"Proceeds"` | From 540dcfe638cde7bce761585e172230f772a51566 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Tue, 17 Mar 2026 18:58:18 +0100 Subject: [PATCH 3/3] fix docs --- .../docs/integrations/meta-conversion-api.mdx | 117 +++++++----------- 1 file changed, 48 insertions(+), 69 deletions(-) diff --git a/content/docs/integrations/meta-conversion-api.mdx b/content/docs/integrations/meta-conversion-api.mdx index 0a2eef38..2107544e 100644 --- a/content/docs/integrations/meta-conversion-api.mdx +++ b/content/docs/integrations/meta-conversion-api.mdx @@ -28,50 +28,45 @@ Conversion API integration in Superwall: - **Flexible revenue reporting**: Send gross revenue or net proceeds. - **Anonymous user handling**: Control how events behave when a user has no `originalAppUserId`. -- **Custom event names**: Override the default event mappings. - **Deduplication**: Event IDs prevent duplicate event counting. -## Configuration +## Set up in Superwall -The Meta Conversion API integration uses the server-side `meta` integration in -`openrev-integrations`. +Set this up from the dashboard UI, not by manually building a JSON payload. -### Required settings +1. Open your app in Superwall. +2. Go to **Integrations**. +3. In **Marketing**, open **Meta Conversion API**. +4. Fill in the fields shown in the form. +5. Click **Enable Meta Conversion API**. -| Field | Description | Example | -|-------|-------------|---------| -| `access_token` | Meta access token with `ads_management` permission | `"EAAG..."` | -| `pixel_id` | Your Meta Pixel ID | `"123456789012345"` | -| `sales_reporting` | Whether to report gross revenue or net proceeds | `"Revenue"` or `"Proceeds"` | +If the integration is already connected, Superwall shows an overview with +status, configured field count, recent delivery performance, and an **Edit +Configuration** button. -### Optional settings +### Revenue tracking requirement -| Field | Description | Default | -|-------|-------------|---------| -| `sandbox_access_token` | Separate access token for sandbox events | None (sandbox events skipped) | -| `sandbox_pixel_id` | Separate Pixel ID for sandbox events | None (sandbox events skipped) | -| `test_event_code` | Test event code for validation in Events Manager | None | -| `anonymous_user_behavior` | How to handle events from users without an app user ID | `"send"` | -| `eventNameMappings` | Custom mapping to rename default event names | None | +This integration depends on Superwall revenue tracking. If revenue tracking is +not configured, the dashboard shows a **Revenue tracking required** banner and +blocks the form until you set that up first. -### Example configuration +### Fields shown in the dashboard -```json -{ - "integration_id": "meta", - "access_token": "EAAG1234567890abcdef...", - "pixel_id": "123456789012345", - "sales_reporting": "Revenue", - "sandbox_access_token": "EAAG0987654321fedcba...", - "sandbox_pixel_id": "543210987654321", - "test_event_code": "TEST12345", - "anonymous_user_behavior": "send", - "eventNameMappings": { - "sw_subscription_cancelled": "CancelSubscription", - "sw_refund": "Refund" - } -} -``` +| UI field | Required | What to enter | +|----------|----------|---------------| +| `Access Token` | Yes | A Meta access token with `ads_management` permission | +| `Pixel Id` | Yes | The Meta Pixel ID that should receive events | +| `Sales Reporting` | Yes | Choose `Revenue` or `Proceeds` | +| `Sandbox Access Token` | No | Optional token for sandbox events | +| `Sandbox Pixel Id` | No | Optional sandbox Pixel ID | +| `Test Event Code` | No | Optional Meta test code for validation in Events Manager | +| `Anonymous User Behavior` | No | Choose `send` or `dontSend` for users without an app user ID | + +Superwall handles the internal integration identifier automatically. You do not +enter `integration_id` in the dashboard UI. + +The current dashboard UI also does not expose custom event-name mappings for +this integration, so setup is limited to the fields above. ## Getting your credentials @@ -102,7 +97,7 @@ personal token. 1. In Events Manager, select your Pixel. 2. Click the **Test Events** tab. 3. Copy the displayed test event code. -4. Add it to `test_event_code` while validating your integration. +4. Add it to the **Test Event Code** field while validating your integration. ## Event mapping @@ -199,7 +194,7 @@ POST https://graph.facebook.com/v21.0/{pixel_id}/events?access_token={access_tok | Field | Description | Example | |-------|-------------|---------| -| `value` | Revenue amount based on `sales_reporting` | `9.99` | +| `value` | Revenue amount based on your **Sales Reporting** setting | `9.99` | | `currency` | ISO 4217 currency code | `"USD"` | | `content_type` | Always `"product"` for subscription events | `"product"` | | `content_name` | Product identifier | `"com.app.premium.monthly"` | @@ -224,8 +219,8 @@ For users with an `originalAppUserId`, Superwall sends: ### Anonymous users -For users without an `originalAppUserId`, behavior depends on -`anonymous_user_behavior`. +For users without an `originalAppUserId`, behavior depends on the +**Anonymous User Behavior** setting. When set to `"send"`: @@ -246,7 +241,7 @@ When set to `"dontSend"`: ## Revenue tracking -The `sales_reporting` setting controls which amount is sent in `custom_data`. +The **Sales Reporting** setting controls which amount is sent in `custom_data`. ### Revenue vs. proceeds @@ -291,7 +286,7 @@ The integration supports separate credentials for sandbox events. ### With sandbox credentials configured -When both `sandbox_pixel_id` and `sandbox_access_token` are provided: +When both **Sandbox Pixel Id** and **Sandbox Access Token** are provided: - Production events use the main credentials. - Sandbox events use the sandbox credentials. @@ -307,11 +302,11 @@ When sandbox credentials are not provided: ## Test event mode -Use `test_event_code` to validate the integration without affecting production -metrics. +Use the **Test Event Code** field to validate the integration without affecting +production metrics. 1. Copy a test event code from Meta Events Manager. -2. Add it to `test_event_code`. +2. Add it to the **Test Event Code** field. 3. Send test events from your app. 4. Verify the events in the **Test Events** tab. @@ -321,24 +316,7 @@ Events sent with a test event code: - Do not count toward main metrics - Are not used for optimization -Remove the test event code before sending live traffic. - -## Custom event names - -Use `eventNameMappings` to override the default event names: - -```json -{ - "eventNameMappings": { - "sw_trial_start": "CustomTrialStart", - "sw_subscription_start": "CustomSubscribe", - "sw_renewal": "CustomRenewal" - } -} -``` - -Mappings override both standard and custom event names. For example, mapping -`sw_subscription_start` sends your custom value instead of `Subscribe`. +Remove the **Test Event Code** value before sending live traffic. ## Testing the integration @@ -346,7 +324,7 @@ Validate the integration end-to-end before relying on it for attribution. ### 1. Configure a test event code -Add `test_event_code` from Meta Events Manager. +Add a value to the **Test Event Code** field from Meta Events Manager. ### 2. Send test events @@ -382,7 +360,7 @@ Trigger subscription events from your app in sandbox mode. 1. Use System User tokens for production access. 2. Configure separate sandbox credentials for testing. -3. Remove `test_event_code` before going live. +3. Remove the **Test Event Code** value before going live. 4. Keep `external_id` values consistent across data sources. 5. Monitor Event Match Quality in Events Manager. 6. Prefer Meta standard events when they fit your use case. @@ -429,7 +407,8 @@ Trigger subscription events from your app in sandbox mode. 1. Verify your token has `ads_management`. 2. Confirm the Pixel ID matches Events Manager. 3. Check whether sandbox credentials are configured. -4. Remove `test_event_code` if you expect the events in the main overview. +4. Clear the **Test Event Code** field if you expect the events in the main + overview instead of **Test Events**. ### Authentication errors (Error 190) @@ -473,14 +452,14 @@ Trigger subscription events from your app in sandbox mode. **Possible causes:** -- `eventNameMappings` overrides the defaults - The event type maps differently than expected **Solutions:** -1. Review your `eventNameMappings`. -2. Compare the payload against the mapping table above. -3. Validate with `test_event_code`. +1. Compare the payload against the mapping table above. +2. Validate with the **Test Event Code** field. +3. If you need a different naming scheme, note that the current dashboard UI + does not expose custom event-name mappings for this integration. ## Rate limits