# Deciding whether to buy an MMP

Buy a mobile measurement partner (MMP) when it removes work you would otherwise have to build and maintain, not because the app is "serious" now. Vendor comparison and pricing belong to the mmp-selection skill if installed; otherwise use the shortlist questions below.

## Contents

- Build-cost inventory
- What stays your job
- Decision rule
- Shortlist questions
- The cost test
- Measuring without an MMP
- Running two MMPs during a migration
- MCP servers and hand-offs

## Build-cost inventory

The in-house alternative is this work. The team fills in hours per year at its own rate, and the total is the build cost in the decision rule:

- Per-network install and in-app event postback integrations, and their upkeep when a network changes its API: <hours per network>.
- Per-network cost and spend ingestion, reconciled to invoices: <hours per network>.
- Conversion value schema upkeep and the on-device update code, re-tested at each release: <hours>.
- Postback copy endpoint: hosting, signature verification for each SKAdNetwork version and AdAttributionKit, deduplication, decoding and retries: <hours>.
- Android attribution across Install Referrer, network claims and the advertising ID: <hours>.
- Link domain hosting, deferred deep link routing and the fallback pages: <hours>.
- Warehouse models joining attribution to users and revenue: <hours>.

## What stays your job

- What each event means, and which state earns revenue.
- Purchase verification and the revenue ledger.
- Consent and ATT configuration, and what data goes to which partner.
- Cost reconciliation against invoices.
- The decision map, and the decision made from each report.

An MMP cannot choose a good activation event, verify a payment, or turn an unobserved journey into a known one.

## Decision rule

Count each platform separately: Apple Ads on iOS plus one different network on Android is one network per platform, not two on one platform. Apply the steps in order; the first that matches decides.

1. Any trigger fires: buy, unless the in-house build costed with the inventory above is cheaper than the MMP's first full year. That build cost is the only comparison at this step. Triggers:
   - Two or more paid networks on the same platform need install and in-app event postbacks for optimisation, or the team needs one deduplicated view across them.
   - A network the team relies on does not report its own attributed installs and needs an attribution partner to attribute and optimise (common with programmatic and many gaming networks). Check the network's current requirements.
   - iOS paid acquisition runs on networks other than Apple Ads, and nobody on the team can own conversion value updates, postback copy ingestion, signature verification and decoding.
   - Deferred deep links or web-to-app routing carry acquisition, and there is no linking vendor.
   - The warehouse needs install-level attribution joined to users and revenue, refreshed automatically.
2. No trigger fires, a trigger is expected within 6 months (for example, a second network is planned and budgeted), and the cost test below passes: buy now, so attribution history exists before the change.
3. Otherwise: wait. Run each channel's own measurement path plus Install Referrer and AdServices storage, and record the reopen triggers.

Own measurement paths while waiting:

- Apple Ads only: AdServices on the backend plus Apple Ads reporting.
- Google App campaigns only: conversions imported from Google Analytics for Firebase or, on Android, Google Play.
- One self-attributing social network only: that network's SDK or server events, with consent handled.

Reopen when any trigger fires: a second paid network on one platform, a network that needs an attribution partner, iOS spend beyond Apple Ads, a retargeting programme, a warehouse attribution requirement, or the end of a free MMP offer. Propose the entry for `decisions.md` with the step that decided it and the reopen triggers, and write it only after the user approves.

## Shortlist questions

When buying, ask every shortlisted vendor these questions and keep the written answers:

- Billing unit: what is billed (attributed conversions, re-engagements, re-attributions, data points, MAU) once any free or introductory offer ends, and what the first full year costs at your volumes.
- iOS billing: how SKAdNetwork and AdAttributionKit installs are billed against real-time attribution.
- Network coverage: are all the paid networks you need covered on the plan you would buy, not only on a higher tier.
- Export: how raw or aggregated data reaches your warehouse, on which plan, how long the export keeps data, and what history you keep if you leave.
- Apple Ads on iOS 26.2 and later: a demonstration or written confirmation that the plan receives and reports Apple Ads through AdAttributionKit. Some vendors tie AdAttributionKit to a specific product or package.
- Conversion values: whether server-to-server events can drive conversion values, or only SDK events.

## The cost test

This is a heuristic, not a vendor or industry rule. It decides only step 2 of the decision rule. The 10% is a default the team adjusts and records in `decisions.md`:

- Value side: 10% of annual paid acquisition spend, as a stand-in for the budget that better cross-network allocation can plausibly move.
- Cost side: the first full year after any free or introductory offer ends, plus internal setup and maintenance hours at the team's rate. Build it from:
  - Billable units, including re-engagements and re-attributions. AppsFlyer's billing article, for example, lists re-engagements and re-attributions as billing units beside non-organic installs, so a retargeting launch can raise the bill without adding new installs.
  - Month 13 and later, after free offers end. Do not budget from the launch month.
  - iOS billing basis. AppsFlyer's billing article says SKAdNetwork billing uses its configured single source of truth, or otherwise the higher of the SKAdNetwork and real-time attribution counts per media source per day. It does not state an AdAttributionKit rule; ask.
  - Warehouse loading work when the export keeps limited history. AppsFlyer documents that its Data Locker Snowflake share keeps data for a limited period (14 days on the checked date), so history needs loading into your own tables.
- Pass: cost side below value side.

Example with fictional numbers: annual paid spend 240,000 USD gives a value side of 24,000 USD. A quote of 14,000 USD for the first full year plus 60 internal hours at 90 USD (5,400 USD) gives 19,400 USD. The test passes, which matters only if no trigger fires and one is expected within 6 months.

## Measuring without an MMP

- Keep the same decision map. Each channel's own report governs that channel; nothing is summed across channels.
- iOS: configure the postback copy keys to your own endpoint only if someone will verify, deduplicate and decode them, including pre-version-4 SKAdNetwork postbacks. Otherwise leave them for a future MMP.
- Android: read Install Referrer once on first run and store it with the response code.
- Store the analytics SDK's app instance ID and the account ID at login so a later MMP or warehouse model can join history.
- Use the analytics SDK's consent and identity rules from the start; they carry over.

## Running two MMPs during a migration

- One sender of postbacks to each network at any time. Two production senders produce double optimisation signals.
- One owner of each Apple postback copy key; the key holds one URL.
- One component updates conversion values.
- One sender of revenue events per destination.
- An end date for the overlap and an export of the history you need before access ends.

## MCP servers and hand-offs

As checked on 2026-10-17. Re-check status and permissions before connecting any of them:

- AppsFlyer MCP server, beta. Its support article lists:
  - Reads: aggregated performance and SKAdNetwork data, Incrementality experiments, app settings (attribution windows, session settings), cost and ad revenue integrations, OneLink templates and links, Smart Banners, Audiences connections, users and roles, and AppsFlyer documentation.
  - One write: `create_onelink_link` creates a short link under an existing OneLink template, after the AI shows the details and the user confirms. Limit: 10 links per account per day.
  - Auth: browser OAuth, valid for 7 days, or an account-level MCP bearer token created in Security Center that inherits the permissions of the admin who created it and stays valid until revoked.
  - Rules: keep MCP use read-only while planning. Before any `create_onelink_link` call, show the user the exact template, parameters and link, and confirm only after explicit approval. Prefer browser OAuth for planning work; a token carries admin scope.
- Adjust MCP server: Early Access, enabled on the account through Adjust, OAuth for all supported clients, aggregated data only (no user-level or event-level data).
- For other MMPs, look for an official MCP server and its read and write scope before connecting one. Prefer read-only use while planning.
- Vendor choice, billing units and contract terms: the mmp-selection skill if installed; otherwise the shortlist questions above. Integration testing before launch: the mmp-implementation-qa skill if installed; otherwise the first-launch acceptance paths in [sdk-and-event-practices.md](sdk-and-event-practices.md), plus a received postback copy of each type from a released version.
