Owlmetry

Attribution

See which ad campaigns drive your installs. Apple Search Ads attribution captured at install time and enriched with campaign names.

Attribution in Owlmetry answers one question: which marketing campaign did this user come from? The Swift SDK captures attribution data at install time, the server resolves it against the ad network's API, and the result lives on the user as a handful of user properties. You can then filter, segment, and compare any analytics view by campaign — conversion rates, revenue per acquisition channel, retention by ad group — without adding a separate marketing SDK.

For v1, the only supported network is Apple Search Ads. The URL namespace (/v1/identity/attribution/:source) is future-proofed for Google Ads, Meta, and TikTok once demand arrives.

Capture Flow

Swift SDK                         Server                       Apple AdServices
─────────                         ──────                       ────────────────
Owl.configure()
  └─ asks iOS AdServices
     for an attribution token
     ───────────────────────────►
                                   POST /v1/identity/attribution/apple-search-ads
                                   ──────────────────────────────────────────────►
                                                                resolves token
                                                                ◄───────────────
                                   writes asa_*_id properties
                                   to the current user

The SDK calls AAAttribution.attributionToken() on a background task shortly after Owl.configure(), POSTs the token to the server, and the server resolves it against Apple's public Attribution API (api-adservices.apple.com). On success, the server writes the numeric IDs to the user's properties; the SDK records a "captured" flag in UserDefaults so subsequent launches don't repeat the work.

Apple's API sometimes 404s for up to ~24 hours after install (the attribution record isn't ready yet). When that happens, the server returns { pending: true, retry_after_seconds } and the SDK retries on the next launch, up to 5 attempts (ASA_MAX_PENDING_ATTEMPTS). After 5 failures, the SDK writes attribution_source = "none" and gives up — you can force a retry with Owl.resetAppleSearchAdsAttributionCapture().

To opt out entirely, pass attributionEnabled: false to Owl.configure(). See the Swift SDK configuration for more.

Name Enrichment

Apple's AdServices Attribution API returns only numeric IDs — never human-readable names. "Campaign 1234567890" isn't a useful row in a dashboard, so Owlmetry resolves IDs → names through one of two complementary backends. Both cover every attributed user (subscriber or not), and both are additive — they fill empty slots but never overwrite values set by the SDK's live flow.

Configure the Apple Search Ads integration on a project and Owlmetry calls Apple's Campaign Management API directly to resolve names. Works for every attributed user, doesn't depend on any third-party SDK being in your app, and runs fire-and-forget right after the ID capture plus a daily apple_ads_sync backfill job.

This is the default path to choose if you don't already have RevenueCat in your stack.

Option B — RevenueCat backfill

If your project is already connected to RevenueCat and RC has its "Advanced Apple AdServices" integration configured upstream, RC surfaces resolved names on its subscriber attributes ($mediaSource, $campaign, $adGroup, $keyword, $ad). Owlmetry's RC sync and webhooks pull those attributes and fold them into the user's properties.

RC's name resolution happens server-side on AdServices token receipt (not gated on a subscription event), so a bulk owlmetry integrations sync revenuecat run enriches your whole attributed population, subscribers and free users alike. Only the webhook delivery path is subscription-gated by RC — it only fires for purchase/renewal events, so free users exclusively get enrichment through the sync path.

Can I use both?

Yes. Per-field merge logic in selectUnsetProps ensures neither source overwrites the other — whichever writes a given key first wins, and subsequent writes for the same key are ignored. In practice most teams use one or the other.

What You Get

Attribution writes up to 11 reserved user properties. The authoritative list lives in User properties → Reserved namespaces; the table below is a quick reference:

PropertySourceExample
attribution_sourceServer (on token resolve)apple_search_ads | none
asa_campaign_id, asa_ad_group_id, asa_keyword_id, asa_ad_id, asa_creative_set_id, asa_claim_typeServer (on token resolve)Numeric IDs from Apple's Attribution API
asa_campaign_name, asa_ad_group_name, asa_keyword, asa_ad_nameASA integration or RevenueCat backfillHuman-readable names from Campaign Management API

Attribution properties carry through the identity claim — when an anonymous user is promoted via Owl.setUser(), asa_* and attribution_source merge onto the real user. Claim race protection on the ingest path rewrites any late-arriving attribution writes to the claimed user ID.

Dashboard Surfaces

  • User rows on the Users page show a small attribution badge (🍎 Apple Search Ads) next to the anon/real user badge when attribution_source is set.
  • User detail sheet groups the asa_* properties into a dedicated "Attribution" section above Subscription.
  • Feedback cards on the Feedback kanban show the attribution badge alongside billing badges so you can spot which campaign a complaint came from.
  • Column picker on the Users page exposes the attribution fields (campaign name, ad group, keyword, ad name) as opt-in columns. Drag to reorder; your configuration is persisted to your user profile so it follows you across devices.

Debugging

Every capture attempt emits an sdk:attribution_capture event with a _outcome attribute distinguishing the states:

_outcomeLevelMeaning
successinfoApple returned a decisive answer (apple_search_ads or none).
pending_retryinfoApple 404'd; SDK will retry on next launch.
gave_upwarnHit the 5-attempt cap; SDK wrote attribution_source = "none".
token_fetch_failedwarnAAAttribution.attributionToken() threw on-device.
submit_failedwarnServer POST failed (network, 5xx, auth).

Filter the dashboard events list by sdk:attribution_capture to see the outcome for each install. For deeper debugging see Swift SDK configuration → Debugging attribution.

Limitations and Roadmap

  • Apple-only for v1. Google Ads, Meta, and TikTok are not yet supported. The /v1/identity/attribution/:source URL reserves the namespace for future networks.
  • Apple's v5 Campaign Management API sunsets 2027-01-26. Apple's replacement — the Apple Ads Platform API — is expected Summer 2026. Owlmetry will migrate behind the existing resolver interface; no action needed on your end.
  • Swift SDK only. Android and web platforms don't have a first-party attribution framework equivalent to AdServices, so there's nothing analogous to capture. If your acquisition channels are primarily non-Apple, tell us — it shapes the roadmap.

Ready to get started?

Connect your agent via MCP or CLI and start tracking.