Cross-app promotions
Cross-promote your own portfolio without an ad network. A source app promotes one of your otherapps at a named moment — the backend picks an eligible destination (targeting, caps, cooldown, rotation) and the iOS SDK presents it using Apple's native install UI. One SDK call; everything else is configured remotely and changes without an app update.
A promotion is a normal AppMate flow (type app_promotion) that belongs to a source app and references destination appsalready registered in the same account — so you never re-enter a destination's App Store details. Unlike the other flows it has no hosted web page: the SDK asks for a placement and gets back a decision (or nothing).
How it works
- Fill in each app's promotion metadata once (App Store ID, App Store URL, universal-link base, and any named deep-link destinations) under App → Preferences.
- On the app you want to promote from, add a Promote another app flow, pick a placement name (e.g.
cleaning_completed), choose single or rotating destinations, presentation, and frequency, then publish. - In the app, call
RetentionFlow.promotions.present("cleaning_completed")at that moment. The SDK resolves + presents; when nothing is eligible it does nothing.
Destination modes & rotation
- Single— always promote one destination app while it's eligible.
- Rotating — promote several. Choose a strategy:
smart(avoid immediate repeats, show all before repeating),even(roughly equal impressions), orweighted(a distribution you set per destination).
Presentation
- Apple native —
SKOverlay, the fast native GET banner. Recommended default. - Custom card → Apple native — render your own contextual card, then present the overlay on the CTA (use
resolve(_:), below). - Store product page — the full
SKStoreProductViewControllersheet, with an App Store URL fallback.
iOS SDK
The one-liner resolves the placement and presents the decision end-to-end — opening the destination's deep link if it's installed, otherwise the native install UI — and reports the funnel for you. It's silent and non-blocking when nothing is eligible, so it's safe to call inline.
// At a natural product moment:
await RetentionFlow.promotions.present("cleaning_completed")
// Pass a userId if you have one (used for per-user frequency caps):
await RetentionFlow.promotions.present("cleaning_completed", userId: user.id)Render your own card
Resolve the decision, present whatever UI you like, then hand back to the SDK on your CTA — keeping AppMate's decisioning and analytics.
if let promo = await RetentionFlow.promotions.resolve("settings_more_apps") {
// …show a card using promo.destination (name, iconURL, …)…
RetentionFlow.promotions.report(.tapped, trackingToken: promo.trackingToken)
_ = await RetentionFlow.promotions.present(promo)
}Deferred first launch
When a destination configures a deferred route, the freshly-installed app can pick it up on first launch and route the user there. This is a best-effort attributed first launch (matched by destination app + recency, one-time, default 72h window) — not guaranteed install attribution.
// In the DESTINATION app, after configuring AppMate:
if let dest = await RetentionFlow.promotions.pendingDestination() {
router.open(dest.route) // e.g. "premium"
}resolve/report/pendingDestination work and present falls back to opening the universal link or App Store page. Requires the SDK at 0.19.0 or newer.Public API (SDK-facing)
Unauthenticated, keyed on appSlug— the same trust model as the other public endpoints. You normally don't call these directly; the SDK does.
POST /api/public/promotions/resolve— body{ appSlug, placement, userId?, anonymousId?, platform?, country?, language?, appVersion? }→ a decision, or{ "decision": "none" }.POST /api/public/promotions/event— body{ trackingToken, event }→ records a funnel event.POST /api/public/promotions/pending— body{ appSlug, userId?, anonymousId? }→ a pending deferred destination, or{ "pending": null }.
Owner (v1) API
GET /api/v1/apps/{idOrSlug}/flows/app-promotion→ flow, draft, published (pass?flowSlug=for a specific placement)PUT /api/v1/apps/{idOrSlug}/flows/app-promotion→ fullapp_promotionconfigPOST /api/v1/apps/{idOrSlug}/flows/app-promotion/publish→ promotes the draft
For AI agents (MCP)
The MCP server exposes get_app_promotion_flow, update_app_promotion_draft, and publish_app_promotion_flow (pass flowSlug to target a placement). Destinations are referenced by their AppMate app id; their reusable metadata lives on the destination app.