Connect third-party services to sync data into Owlmetry user properties.
Integrations connect external services to Owlmetry. They sync data from services like RevenueCat into user properties, giving you a unified view of each user's subscription status, revenue, and entitlements alongside their analytics data.
Each integration is configured per-project and consists of:
Provider — the third-party service (e.g., revenuecat)
Config — API credentials and settings specific to that provider
Webhook — an endpoint that receives real-time updates from the provider
Sync — on-demand API calls to pull data for existing users
When a webhook fires or a sync runs, the integration maps the provider's data to Owlmetry user properties. These properties then appear in the Users list in the dashboard.
Whether the user has a live, renewing subscription. False for cancelled trials and cancelled paid subs even if they're still within the paid period.
"true" or "false"
rc_will_renew
Whether the subscription will auto-renew at the end of the current period. Used alongside rc_period_type to distinguish active trials (sky badge) from cancelled trials (red badge).
Generate a RevenueCat V2 Secret API key in the RevenueCat dashboard (Project Settings → API Keys → + New secret API key). Set both of these sections to Read only at the section level (use the top-right dropdown on each section):
Customer information → Read only
Project configuration → Read only
Setting section-level Read only gives us all the sub-permissions we need today and any new ones added later. All other sections can stay No access.
Add the integration via dashboard (Project > Integrations > Connect RevenueCat) or CLI:
In the dashboard, click Show webhook setup on the RevenueCat integration card. MCP tool: get-revenuecat-webhook-setup. All three surface the existing webhook secret without rotating it, so any webhook deliveries already configured on RevenueCat's side keep working. Admin role required.
RevenueCat Purchase/Renewal/Cancellation -> Webhook POST to /v1/webhooks/revenuecat/:projectId -> Maps event to user properties (rc_subscriber, rc_status, etc.) -> Merges into the project-level user's properties -> Visible in Users list with Paid/Cancelled badges
Webhooks capture future events, but users who subscribed before the integration was set up need a backfill. The sync endpoints call RevenueCat's REST API to fetch subscriber data:
Bulk sync — syncs all non-anonymous users in the project (rate-limited to ~180 req/min)
Single-user sync — fetches one subscriber's data immediately
Sync and webhooks also backfill Apple Search Ads attribution from RevenueCat's subscriber attributes ($mediaSource, $campaign, $adGroup, $ad, $keyword). RC populates these attributes server-side on AdServices token receipt for every attributed user — subscribers and free users alike — so a bulk sync enriches your whole attributed population, not just paying users. Only the webhook delivery path is subscriber-gated: RC fires webhooks on subscription events (INITIAL_PURCHASE, RENEWAL, CANCELLATION, etc.) but never for free users, so non-subscribers only get name enrichment via a bulk sync run. If you'd rather not depend on RC having the Advanced ASA integration configured, the Apple Search Ads integration below resolves the same names directly from Apple for every attributed user. Existing attribution data written by either path is never overwritten (per-field merge). Fixture installs (attribution_source = "apple_test_install" — TestFlight, Xcode dev builds, simulator) carry no asa_* IDs, so neither this RC backfill nor the ASA integration enriches them — there's nothing to resolve.
Resolves Apple Search Ads campaign, ad group, keyword, and ad IDs — captured at install time by the Swift SDK's AdServices token flow — into human-readable names via Apple's Campaign Management API. Complementary to (not dependent on) the RevenueCat integration: both sources resolve names for every attributed user, but the ASA integration works without RC's SDK in the app and without RC's Advanced ASA integration being configured — it's the self-contained path.
Numeric IDs (asa_campaign_id, asa_ad_group_id, asa_keyword_id, asa_ad_id) are captured separately by the Swift SDK at install time — see Attribution. The integration never overwrites existing keys; names are additive.
Owlmetry generates the EC P-256 keypair on your behalf and keeps the private half server-side — you never handle an openssl command or a .pem file. You just copy the public key into Apple's UI, paste back three IDs, and pick an org.
Invite an API user at ads.apple.com → Account Settings → User Management → Invite Users. Assign role API Account Read Only (read access is all Owlmetry needs). If your Apple Ads account already has one API user configured (e.g. for RevenueCat), add a second one — Apple only supports one active public key per user, and sharing a certificate between tools isn't possible.
Create the integration (dashboard: Integrations page → Connect Apple Search Ads, or CLI):
The response includes a freshly-generated public key (PEM) in config.public_key_pem. The dashboard displays it inline in a "Finish setup" card.
Upload the public key to Apple at ads.apple.com → Account Settings → User Management → (your API user) → API → paste the key. Apple returns three values: clientId, teamId, keyId.
Dashboard: fill in the three inputs in the "Finish setup" card and click Save IDs.
Pick the org — the dashboard automatically lists accessible orgs via GET /api/v5/acls once the three IDs are saved; pick from the dropdown. From the CLI, get your orgId (shown as "Account ID" in the ads.apple.com profile menu) and run:
The integration auto-enables as soon as all four IDs (client_id, team_id, key_id, org_id) are present.
Backfill existing users with owlmetry integrations sync apple-search-ads --project-id <id> — sweeps every user with a stored asa_campaign_id and resolves names.
To rotate the keypair, remove the integration and reconnect — Owlmetry generates a new keypair, and you upload the new public key to Apple (replacing the old one on the same API user).
Swift SDK AdServices capture -> server writes asa_*_id (numeric) -> fire-and-forget: call Apple Ads API to resolve names -> writes asa_campaign_name / asa_ad_group_name / asa_keyword / asa_ad_name
For users attributed before the integration was connected, the bulk sync (apple_ads_sync job) handles the backfill. Errors are surfaced on the integrations page via the Test Connection action.
The same apple_ads_sync job also pulls campaign + ad-group spend / impressions / taps / installs from Apple's Reports API into ad_campaign_lifetime + ad_adgroup_lifetime (one row per (project, network, campaign | ad_group), refreshed daily and on every "Sync now"). The dashboard's /dashboard/ads page joins the rollup against attributed-user revenue and surfaces spend, ROAS, and campaign start dates alongside the existing user / paying / revenue columns.
Apple's Reports API is org-scoped — every campaign across every app under the same Apple Developer team comes back in one response — so Owlmetry filters by the campaign's adamId against your apps.apple_app_store_id. Only campaigns belonging to your project's apps land in the rollup; if two projects share an Apple developer account, each project sees only its own campaigns.
Lifetime totals are computed from a rolling 12-month window (4× 90-day chunks summed in JS, since Apple caps single requests at 90 days). Numbers will be within ~5–10% of what you see in ads.apple.com — the small drift is settled vs. pending billing in Apple's UI; the API is the source of truth.
USD only for v1 — when your reporting org uses a non-USD currency, the dashboard surfaces a banner, leaves the spend column blank, and stores the raw localSpend.amount in spend_local_micros for a future multi-currency upgrade. ROAS stays null in that case.
Apple has announced the v5 Campaign Management API will be sunset on January 26, 2027, replaced by a new Apple Ads Platform API shipping Summer 2026. Owlmetry will migrate customers to the Platform API ahead of the sunset.
Pulls App Store reviews via Apple's App Store Connect (ASC) API. The reviews land in the app_store_reviews table and surface on /dashboard/reviews. Replaces the older iTunes RSS feed (which Apple deprecated in early 2025 and now blocks from many datacenter IPs). See Store Reviews for the data model.
Powers app_store_connect_reviews_sync — a system-scoped job that runs daily at 05:30 UTC, fanning out across every project with an active App Store Connect integration and calling GET /v1/apps/{id}/customerReviews for every Apple app that has an apple_app_store_id. Single-project manual triggers still work via Dashboard → Integrations → Sync, the CLI, and the sync-integration MCP tool.
Returns rating, title, body, reviewer nickname, territory (mapped from ISO 3166 alpha-3 to alpha-2), developer response, and dev-response timestamp.
Paginates 200 reviews per page, newest-first, and stops as soon as a page contains a review already in the DB. Re-runs are cheap.
After pagination, runs a refresh pass over local rows still in PENDING_PUBLISH state: a direct GET on customerReviewResponses/{id} flips them to PUBLISHED once Apple has actually published the reply (the customerReviews payload doesn't reliably surface the transition), and clears the local developer_response_* fields if Apple returns 404 because the response was deleted externally.
Dispatches an app.review_new notification per app when at least one new review row was inserted and the app already had reviews on file (so the first ASC sync after setup doesn't flood inboxes). The notification body shows the count plus a snippet of the newest review.
ASC keys are scoped to one Apple Developer team — each Apple account requires its own integration in each project that holds its apps. Setup is a single dialog (no multi-step wizard like ASA):
In App Store Connect, go to Users and Access → Integrations → App Store Connect API, click "+" on the Individual Keys tab.
Pick the Customer Support role (least-privilege for read-only review access).
Download the .p8 (one chance — Apple won't re-issue).
Copy the Issuer ID (top of the page) and Key ID (10-char alphanumeric, also embedded in the .p8 filename).
Owlmetry → Dashboard → Integrations → App Store Connect → Set up, paste all three.
Test connection to confirm Apple accepts the key and lists your apps. Sync reviews to backfill.
To rotate the key: re-issue a new one in App Store Connect, then owlmetry integrations update app-store-connect --project-id <id> --key-id <new> --private-key-p8-file ./new.p8. Omit --private-key-p8-file to update only the IDs (the existing .p8 is preserved). The .p8 is never returned by GET — Owlmetry stores it server-side and redacts it from API responses.
One ASC key per project. If you have apps from multiple Apple Developer teams in one project, only the team that owns the configured key will have its reviews synced.
The Customer Support role does not allow posting developer responses. Replying to reviews from inside Owlmetry would require an Admin or App Manager role and is not yet implemented.
Open the Integrations sidebar entry in the dashboard (/dashboard/integrations). The page lists every project and its configured integrations, with per-integration controls to update config, toggle enabled/disabled, sync users, or remove. When a project has no integration for a given provider but another project in the same team does, the card shows a Copy from another project button so you can reuse the credentials without retyping them.
If your team has multiple projects that share the same provider account (e.g., one Apple Ads account running campaigns for several apps, or one RevenueCat account used by several projects), you can copy a configured integration between projects rather than re-entering credentials each time. Copies live independently of the source — credentials are duplicated, not shared, so rotating the source project's API key does not propagate to the copy. For Apple Search Ads, the full config (keypair + client/team/key/org IDs) is duplicated verbatim and Owlmetry immediately runs a live connection test against Apple's /api/v5/acls so the response confirms the clone works end-to-end — no Apple-side setup required on the target. For RevenueCat, the API key is copied but a fresh webhook secret is generated on the target because each project has its own webhook URL (you paste the returned webhook_setup values into RC if you want events delivered to the copy's project). Copying requires admin role and is only permitted within the same team.
Copy-paste this prompt to have your coding agent set up the RevenueCat integration end-to-end:
Set up RevenueCat integration for my Owlmetry project.1. Use the Owlmetry CLI to add the RevenueCat integration to my project with my RC V2 secret API key (needs Customer information → Read only AND Project configuration → Read only at the section level, everything else No access). Use `/owlmetry-cli` for CLI syntax.2. Show me the webhook setup values from the output so I can paste them into RevenueCat.3. After I confirm the webhook is configured, run a bulk sync to backfill existing subscribers.4. Use `/owlmetry-swift` or `/owlmetry-node` to add Owl.setUserProperties() calls after in-app purchase events so the dashboard updates immediately (without waiting for the webhook).
This prompt guides the agent through all four steps: CLI integration setup, webhook configuration, backfill sync, and client-side property updates for instant feedback.
The integration framework uses a central provider registry in packages/shared/src/integrations.ts. Each provider defines its config fields (required/optional, sensitive/public) and validation rules. Adding a new provider is a matter of adding an entry to the INTEGRATION_PROVIDERS array.
Ready to get started?
Connect your agent via MCP or CLI and start tracking.