adattributionkit
Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKAdNetwork with AdAttributionKit for ad measurement.
What this skill does
# AdAttributionKit
Privacy-preserving ad attribution for iOS 17.4+ / Swift 6.3. AdAttributionKit
lets ad networks measure conversions (installs and re-engagements) without
exposing user-level data. It supports the App Store and alternative
marketplaces, and interoperates with SKAdNetwork.
Three roles exist in the attribution flow: the **ad network** (signs
impressions, receives postbacks), the **publisher app** (displays ads), and the
**advertised app** (the app being promoted).
## Contents
- [Overview and Privacy Model](#overview-and-privacy-model)
- [Publisher App Setup](#publisher-app-setup)
- [Advertiser App Setup](#advertiser-app-setup)
- [Impressions](#impressions)
- [Postbacks](#postbacks)
- [Conversion Values](#conversion-values)
- [Re-engagement](#re-engagement)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## Overview and Privacy Model
AdAttributionKit preserves user privacy through several mechanisms:
- **Crowd anonymity tiers** -- the device limits postback data granularity based
on the crowd size associated with the ad, ranging from Tier 0 (minimal data)
to Tier 3 (most data including publisher ID and country code).
- **Time-delayed postbacks** -- postbacks are sent 24-48 hours after conversion
window close (first window) or 24-144 hours (second/third windows).
- **No user-level identifiers** -- postbacks contain aggregate source
identifiers and conversion values, not device or user IDs.
- **Hierarchical source identifiers** -- 2, 3, or 4-digit source IDs where the
number of digits returned depends on the crowd anonymity tier.
In migration and interoperability reviews, explicitly state that the system
evaluates AdAttributionKit and SKAdNetwork impressions together, only one
impression wins per conversion, click-through beats view-through, and recency
breaks ties within click-through impressions before falling back to the most
recent view-through impression.
## Publisher App Setup
A publisher app displays ads from registered ad networks. Add each ad network's
ID to the app's Info.plist so its impressions qualify for install validation.
### Add ad network identifiers
```xml
<key>AdNetworkIdentifiers</key>
<array>
<string>example123.adattributionkit</string>
<string>another456.adattributionkit</string>
</array>
```
Ad network IDs must be lowercase. SKAdNetwork IDs (ending in `.skadnetwork`)
are also accepted -- the frameworks share IDs.
### Display a UIEventAttributionView
For click-through custom-rendered ads, place one `UIEventAttributionView` over
each tappable ad/control. It must cover the tappable area and stay above views
that would intercept touches before `handleTap()` succeeds.
```swift
import UIKit
let attributionView = UIEventAttributionView()
attributionView.frame = adContentView.bounds
attributionView.isUserInteractionEnabled = true
adContentView.addSubview(attributionView)
```
## Advertiser App Setup
The advertised app is the app someone installs or re-engages with after seeing
an ad. It must call a conversion value update at least once to begin the
postback conversion window.
### Opt in to receive winning postback copies
Add `AttributionCopyEndpoint` under the top-level `AdAttributionKit` Info.plist
dictionary so the device sends a copy of the winning postback to your server:
```xml
<key>AdAttributionKit</key>
<dict>
<key>AttributionCopyEndpoint</key>
<string>https://example.com</string>
</dict>
```
The system derives the well-known endpoint from the registrable domain in the
URL, ignoring subdomains:
```
https://example.com/.well-known/appattribution/report-attribution/
```
Configure your server to accept HTTPS POST requests at that path. The domain
must have a valid SSL certificate.
### Opt in for re-engagement postback copies
Add a second key in the same `AdAttributionKit` dictionary to also receive
copies of winning re-engagement postbacks:
```xml
<key>AdAttributionKit</key>
<dict>
<key>AttributionCopyEndpoint</key>
<string>https://example.com</string>
<key>OptInForReengagementPostbackCopies</key>
<true/>
</dict>
```
### Update conversion value on first launch
Call a conversion value update as early as possible after first launch to begin
the conversion window:
```swift
import AdAttributionKit
func applicationDidFinishLaunching() async {
do {
try await Postback.updateConversionValue(0, lockPostback: false)
} catch {
print("Failed to set initial conversion value: \(error)")
}
}
```
## Impressions
Ad networks create signed impressions using JWS (JSON Web Signature). The
publisher app uses `AppImpression` to register and handle those impressions.
### Create an impression from a JWS
```swift
import AdAttributionKit
let impression = try await AppImpression(compactJWS: signedJWSString)
```
The JWS contains the ad network ID, advertised item ID, publisher item ID,
source identifier, timestamp, and optional re-engagement eligibility flag. See
[references/adattributionkit-patterns.md](references/adattributionkit-patterns.md)
for JWS generation details.
### Check device support
```swift
guard AppImpression.isSupported else {
// Fall back to alternative ad display
return
}
```
### View-through impressions
Record a view impression when the ad content has been displayed and dismissed:
```swift
func handleAdViewed(impression: AppImpression) async {
do {
try await impression.handleView()
} catch {
print("Failed to record view-through impression: \(error)")
}
}
```
For long-lived ad views, use `beginView()` and `endView()` to track view
duration:
```swift
try await impression.beginView()
// ... ad remains visible ...
try await impression.endView()
```
### Click-through impressions
Respond to ad taps by calling `handleTap()` within 15 minutes of creating the
`AppImpression`; otherwise request a fresh impression. If the advertised app is
not installed, the system opens its App Store or marketplace page. If installed,
the system launches it directly.
```swift
func handleAdTapped(impression: AppImpression) async {
do {
try await impression.handleTap()
} catch {
print("Failed to record click-through impression: \(error)")
}
}
```
A `UIEventAttributionView` must overlay the ad for `handleTap()` to succeed.
### StoreKit-rendered ads
Pass the impression to StoreKit overlay or product view controller APIs. StoreKit
automatically records view-through impressions after 2 seconds of display and
click-through impressions on tap.
```swift
import StoreKit
let config = SKOverlay.AppConfiguration(appIdentifier: "1234567890",
position: .bottom)
config.appImpression = impression
```
## Postbacks
Postbacks are attribution reports the device sends to ad networks (and
optionally to the advertised app developer) after a conversion event.
### Conversion windows
Three windows produce up to three postbacks for winning attributions:
| Window | Duration | Postback delay |
|--------|---------------------|-------------------|
| 1st | Days 0-2 | 24-48 hours |
| 2nd | Days 3-7 | 24-144 hours |
| 3rd | Days 8-35 | 24-144 hours |
Tier 0 postbacks only produce the first postback. Nonwinning attributions
produce only one postback.
### Time windows for events
| Event | Time limit |
|--------------------------------|-----------------------------------------|
| View-through to install | 24 hours (configurable up to 7 days) |
| Click-through to install | 30 days (configurable down to 1 day) |
| Install to first update | 60 days |
| Re-engagement to first update | 2 days |
### Lock conversion values early
Lock the postback to finalize a conversion value before the windoRelated in Ads & Marketing
ads
IncludedMulti-platform paid advertising audit and optimization skill. Analyzes Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Ads. 250+ checks with scoring, parallel agents, industry templates, and AI creative generation.
banana
IncludedAI image generation Creative Director powered by Google Gemini Nano Banana models. Use this skill for ANY request involving image creation, editing, visual asset production, or creative direction. Triggers on: generate an image, create a photo, edit this picture, design a logo, make a banner, visual for my anything, and all /banana commands. Handles text-to-image, image editing, multi-turn creative sessions, batch workflows, and brand presets.
rpg-migration-analyzer
IncludedAnalyzes legacy RPG (Report Program Generator) programs from AS/400 and IBM i systems for migration to modern Java applications. Extracts business logic from RPG III/IV/ILE source code, identifies data structures (D-specs), file operations (F-specs), program dependencies (CALLB/CALLP), and converts RPG constructs to Java equivalents. Generates migration reports, complexity estimates, and Java implementation strategies with POJO classes, JPA entities, and service methods. Use when modernizing AS/400 or IBM i legacy systems, analyzing RPG source files (.rpg, .rpgle, .RPGLE), converting RPG to Java, mapping data specifications to Java classes, planning legacy system migration, or when user mentions RPG analysis, Report Program Generator, RPG III/IV/ILE, AS/400 modernization, IBM i migration, packed decimal conversion, or mainframe application rewrite.
brand-library-architect
IncludedBuild a complete brand library for a product — visual asset render pipeline, brand documentation set (BRAND, COPY, MANIFESTO, BIOS, FAQ, GLOSSARY, TONE, PRICING), open-source convention files (README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT), and a self-contained press kit. This skill should be used when the user asks to "build a brand library / brand kit / press kit / brand assets" for a product, "set up a brand library workflow," "create a positioning manifesto plus visual identity," or any combination of brand documentation + visual asset pipeline. Apply phase-by-phase or run end-to-end. Templates are product-agnostic and use {{TOKEN}} placeholders the skill prompts the user to fill.
writing-tech-post
IncludedAuthors engineering blog posts end-to-end: launch deep-dives, incident postmortems, architecture migrations, performance case studies, tutorials, AI/agent system writeups, security disclosures, and research-to-product translations. Picks the correct archetype, plans the abstraction ladder, enforces an evidence cadence (diagrams, benchmarks, profiles, traces, code, ablations), tunes voice against publisher house styles (Datadog, Vercel, GitHub, AWS, Meta, Cloudflare, Jane Street), and runs a pre-publish gate for narrative momentum and disclosure ethics. Use when drafting a new engineering post, restructuring a draft that feels flat, deciding which evidence form belongs where, validating that depth and product context are balanced, or preparing a postmortem, migration, or performance narrative for external publication. Do not use for API reference documentation, README authoring, marketing copy, release notes, generic SEO content, ghost-written executive thought leadership, or non-engineering long-form essays.
blog-google
IncludedGoogle API integration for blog performance: PageSpeed Insights, CrUX Core Web Vitals with 25-week history, Search Console performance, URL Inspection, Indexing API, GA4 organic traffic, NLP entity analysis for E-E-A-T, YouTube video search for embedding, and Google Ads Keyword Planner. Progressive feature availability based on credential tier (API key, OAuth/service account, GA4, Ads). Shares config with claude-seo at ~/.config/claude-seo/google-api.json. Use when user says "google data", "page speed", "core web vitals", "search console", "indexation", "GA4", "keyword research", "nlp entities", "blog performance", "youtube search", "google api setup".