
Revenuecat Customer Center
- 269 installs
- 55 repo stars
- Updated August 3, 2026
- revenuecat/ai-toolkit
Implement RevenueCat Customer Center so users manage subscriptions, upgrades, cancellations, billing issues, and restore purchases in-app without custom support flows.
About
Guides agents to integrate RevenueCat Customer Center in mobile apps: embed the managed UI, wire navigation and entitlements, support plan changes and cancellations, and align styling with app branding for self-serve billing.
- Native Customer Center embed
- Plan change and cancel UX
- Restore purchases entry points
- Billing issue deep links
- Branding and localization hooks
Revenuecat Customer Center by the numbers
- 269 all-time installs (skills.sh)
- +29 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #383 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/revenuecat/ai-toolkit --skill revenuecat-customer-centerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 269 |
|---|---|
| repo stars | ★ 55 |
| Last updated | August 3, 2026 |
| Repository | revenuecat/ai-toolkit ↗ |
What it does
Implement RevenueCat Customer Center so users manage subscriptions, upgrades, cancellations, billing issues, and restore purchases in-app without custom support flows.
Files
revenuecat-customer-center: add the RevenueCat Customer Center
Use this skill when the user wants an out of the box UI that lets their customers manage active subscriptions, request refunds, cancel, restore, or contact support, without shipping custom UI. The UI is configured in the RevenueCat dashboard and rendered by the RevenueCatUI SDKs.
Prerequisite: integrate-revenuecat has already run. Purchases.configure(…) must succeed before the Customer Center can load customer data.
1. Detect the platform
Inspect the working directory and pick the first match, from top to bottom:
1. React Native: package.json has a react-native-purchases entry, or react-native as a dependency. The Customer Center ships in react-native-purchases-ui. Read platforms/react-native.md. If expo is also a dependency, note it as an Expo project. 2. Flutter: pubspec.yaml exists at the project root. The Customer Center ships in purchases_ui_flutter. Read platforms/flutter.md. 3. Kotlin Multiplatform: build.gradle.kts has a kotlin { … } multiplatform block, or depends on com.revenuecat.purchases:purchases-kmp*. The Customer Center composable is in purchases-kmp-ui. Read platforms/kmp.md. 4. Android (native): build.gradle(.kts) applies com.android.application (and is not KMP). The Customer Center composable is in com.revenuecat.purchases:purchases-ui. Read platforms/android.md. 5. iOS (native): Package.swift, *.xcodeproj, *.xcworkspace, or Podfile at the project root. CustomerCenterView is in RevenueCatUI. Read platforms/ios.md.
If several match (e.g. an ios/ folder inside a Flutter project), pick the outermost project, the one that owns the build. If still ambiguous, ask the user which platform they want to configure.
2. Shared concepts (all platforms)
- Customer Center is a dashboard configured UI. Actions, copy, promotional offers, refund flows, cancel survey options, and support contact details are defined in the RevenueCat dashboard under Customer Center. Without configuration, the view renders a minimal default layout; users will see almost nothing useful.
- It needs an identified user with purchases to surface anything meaningful. If the user is anonymous and has never bought anything, the Customer Center renders an empty / restore only state. If the app supports login, call
Purchases.logIn(userId)before opening the Customer Center. - Customer Center is separate from paywalls. The standard pattern: expose a "Manage subscription" row in the settings screen that opens the Customer Center. Paywalls are for new purchases; the Customer Center is for existing subscribers.
- The UI owns the flow. Restore, cancel, refund, and promotional offer flows run inside the component. Listen for the lifecycle callbacks (
onRestoreCompleted,onRefundRequestStarted,onManagementOptionSelected,onPromotionalOfferSucceeded, etc.) to react in app code, not to drive the flow. - Platform availability varies. iOS has had Customer Center longest; Android, KMP, Flutter, and React Native follow. Platform files flag any gaps. Refund requests are an iOS only action because only Apple exposes in app refund requests; on Android, the "Manage subscription" option links out to the Google Play subscriptions screen.
- If the installed SDK version is older than Customer Center support, the fallback is a manual subscription management screen: show the user's active entitlements from
Purchases.customerInfo(), expose aPurchases.restorePurchases()button, and link to the store's subscription management URL. Point the user to upgrade the SDK if they want the full Customer Center.
3. Implementation
Read the platform file that matches detection:
platforms/ios.mdplatforms/android.mdplatforms/kmp.mdplatforms/flutter.mdplatforms/react-native.md
Each platform file is self contained: install command, exact snippet to present the Customer Center, and the callback shape.
4. Verify
Do not claim the integration is complete until:
1. The project builds on the target platform. 2. Sign into the app with a test user that has at least one active sandbox subscription. Trigger the code path that opens the Customer Center. The UI loads and the subscription appears in the list. 3. At least one configured action runs end to end. The simplest check: tap Restore purchases and confirm the restore completed callback fires with a non-empty customerInfo.entitlements.active map. If the dashboard has Cancel / Refund / Support actions configured, verify the corresponding callback (onManagementOptionSelected, onRefundRequestStarted, etc.) fires when the user taps through. 4. Dismissing the Customer Center fires the onDismiss callback.
If the Customer Center opens but is empty, the signed in user has no purchases, or the dashboard Customer Center section is not configured. Fix in the dashboard, reload, and retry.
revenuecat-customer-center: Android (native Kotlin)
Install
Find the latest stable release at <https://github.com/RevenueCat/purchases-android/releases> and substitute that tag for <latest> in the snippet below. If GitHub is unreachable, ask the user for a version to pin or check their existing project files for one.
Add the purchases-ui artifact alongside purchases:
// app/build.gradle.kts
dependencies {
implementation("com.revenuecat.purchases:purchases:<latest>")
implementation("com.revenuecat.purchases:purchases-ui:<latest>")
}purchases-ui requires Jetpack Compose. If the app is not already on Compose, enable it in the module:
android {
buildFeatures { compose = true }
composeOptions { kotlinCompilerExtensionVersion = "…" }
}Implement
Two APIs: the CustomerCenter composable and the ShowCustomerCenter activity result contract. Pick one based on how your app is built.
Compose: embed CustomerCenter directly
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.revenuecat.purchases.ui.revenuecatui.customercenter.CustomerCenter
@Composable
fun SubscriptionSettingsScreen(onDismiss: () -> Unit) {
CustomerCenter(
modifier = Modifier.fillMaxSize(),
onDismiss = onDismiss,
)
}CustomerCenter(modifier, options, onDismiss) also accepts a CustomerCenterOptions builder for configuring listener callbacks. The minimal form above is sufficient for most apps.
Activity result contract: launch from any ComponentActivity or Fragment
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import com.revenuecat.purchases.ui.revenuecatui.customercenter.ShowCustomerCenter
class SettingsActivity : ComponentActivity() {
private val customerCenter: ActivityResultLauncher<Unit> =
registerForActivityResult(ShowCustomerCenter()) {
// The Customer Center was dismissed. Refresh subscription state.
}
private fun openCustomerCenter() {
customerCenter.launch(Unit)
}
}ShowCustomerCenter starts the bundled CustomerCenterActivity, which hosts the composable full screen with Material 3 theming and a close button.
Notes
CustomerCenterandShowCustomerCenterrequirepurchases-ui8.x or newer. If the installed version is older, fall back to rendering subscription state fromPurchases.sharedInstance.getCustomerInfo(…)plus a restore button and a link tohttps://play.google.com/store/account/subscriptions.- Refund requests are not supported on Android (refunds go through Google Play). Any refund related dashboard action degrades gracefully to either hiding the option or deep linking to the Play subscriptions page.
- When the user taps Manage subscription on Android, the Customer Center opens the system subscriptions screen via an
Intent. Your app returns to the foreground afterwards. - The composable requires a Material 3 theme in the Compose tree.
CustomerCenterActivitysets one up for you; if you embedCustomerCenterinside your own Compose host, wrap it inMaterialTheme { … }. - Identify the user before opening.
Purchases.sharedInstance.logIn(appUserId, …)ensures the Customer Center loads the right customer's subscriptions.
Verify
Sign into the app with a Google account that has at least one active sandbox subscription:
1. Open the Customer Center via either the composable or ShowCustomerCenter. The subscription appears in the list, with the actions configured in the dashboard. 2. Tap Restore purchases. The active entitlements reload. If you wired CustomerCenterOptions.listener, its onRestoreCompleted callback fires. 3. Tap Manage subscription. The Google Play subscriptions screen opens in a new task. 4. Close the Customer Center. With the composable, onDismiss fires. With ShowCustomerCenter, the ActivityResultCallback runs. 5. adb logcat -s Purchases shows the customer info fetch and any transaction events.
If the view is empty for a user who has purchases, confirm Purchases.sharedInstance.appUserID matches the dashboard user who owns those transactions and that the dashboard's Customer Center section is configured.
revenuecat-customer-center: Flutter
The Customer Center ships in purchases_ui_flutter, the same package that provides paywalls.
Install
Find the latest stable release at <https://github.com/RevenueCat/purchases-flutter/releases> and substitute that tag for <latest> in the snippet below. If GitHub is unreachable, ask the user for a version to pin or check their existing project files for one.
In pubspec.yaml:
dependencies:
purchases_flutter: ^<latest>
purchases_ui_flutter: ^<latest>Then:
flutter pub get
cd ios && pod install && cd ..Minimum iOS deployment target is 15.0 (required by RevenueCatUI's CustomerCenterView). Update ios/Podfile:
platform :ios, '15.0'Android minimum SDK is 21. The Customer Center uses Jetpack Compose on Android under the hood.
Implement
Two APIs: the imperative RevenueCatUI.presentCustomerCenter(…) method and the declarative CustomerCenterView widget. Prefer the imperative one for a "Manage subscription" button. Use the widget when you want to embed the Customer Center inside a Flutter route.
Imperative: presentCustomerCenter
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';
Future<void> openCustomerCenter() async {
await RevenueCatUI.presentCustomerCenter(
onRestoreCompleted: (customerInfo) {
// refresh app state
},
onShowingManageSubscriptions: () {
// user navigated into the manage-subscription flow
},
onManagementOptionSelected: (optionId, url) {
// optionId is one of: 'cancel', 'custom_url', 'missing_purchase', 'refund_request', 'change_plans', …
},
onPromotionalOfferSucceeded: (customerInfo, transaction, offerId) {
// promo offer accepted
},
);
}All callbacks are optional. Pass only the ones the app cares about.
Declarative: CustomerCenterView widget
import 'package:flutter/material.dart';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';
class SubscriptionSettingsPage extends StatelessWidget {
const SubscriptionSettingsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomerCenterView(
shouldShowCloseButton: false, // rely on the app bar back button
onDismiss: () => Navigator.of(context).pop(),
onRestoreCompleted: (customerInfo) {
// refresh app state
},
onManagementOptionSelected: (optionId, url) {
// react to cancel / change plan / custom url
},
),
);
}
}Notes
purchases_ui_fluttersupports iOS and Android only. The Customer Center is unavailable on other Flutter targets.- Refund requests are iOS only. On Android,
onRefundRequestStarted/onRefundRequestCompletednever fire; the UI deep links to Google Play's subscriptions screen instead. shouldShowCloseButtononly affects iOS. On Android, the Customer Center always shows a close button regardless of this flag.- Log the user in before presenting. Call
Purchases.logIn(userId)if your app has identified users; the Customer Center then loads that user's subscriptions. - Do not call
Purchases.restorePurchases()while the Customer Center is on screen. The Restore action inside the UI drives that flow. CustomerCenterViewembeds a native platform view. Place it inside a constrained container (Scaffold,SizedBox, orExpanded). Do not give it zero-height constraints.
Verify
Run on a device or simulator signed into a sandbox account that owns at least one active subscription:
1. Trigger the Customer Center. The active subscription appears with the dashboard configured actions. 2. Tap Restore purchases. onRestoreCompleted fires with a CustomerInfo whose entitlements.active is non-empty. 3. Tap the manage action. On iOS, the system manage subscriptions sheet opens. On Android, Google Play's subscriptions screen opens in a new task. onManagementOptionSelected fires with the selected option id. 4. Close the Customer Center. onDismiss fires.
If the view is empty for a user who has purchases, confirm Purchases.appUserID matches the user who owns them, and that the Customer Center is configured in the RevenueCat dashboard.
revenuecat-customer-center: iOS (native)
Install
Find the latest stable release at <https://github.com/RevenueCat/purchases-ios/releases> and substitute that tag for <latest> in the snippet below. If GitHub is unreachable, ask the user for a version to pin or check their existing project files for one.
CustomerCenterView ships in the RevenueCatUI product of the purchases-ios package. If the app already has RevenueCat, add the RevenueCatUI library to the app target.
Swift Package Manager
In Xcode: target → General → Frameworks, Libraries, and Embedded Content → +, pick the purchases-ios package and add the RevenueCatUI library.
For a Package.swift-based project:
.target(
name: "MyApp",
dependencies: [
.product(name: "RevenueCat", package: "purchases-ios"),
.product(name: "RevenueCatUI", package: "purchases-ios"),
]
)CocoaPods
pod 'RevenueCat'
pod 'RevenueCatUI'Then pod install.
Minimum deployment target for CustomerCenterView is iOS 15.0. The Customer Center is iOS only: it is unavailable on macOS, tvOS, and watchOS.
Implement
SwiftUI: present as a .sheet
import SwiftUI
import RevenueCat
import RevenueCatUI
struct SettingsView: View {
@State private var isShowingCustomerCenter = false
var body: some View {
List {
Button("Manage subscription") {
isShowingCustomerCenter = true
}
}
.sheet(isPresented: $isShowingCustomerCenter) {
CustomerCenterView()
.onCustomerCenterRestoreCompleted { customerInfo in
// refresh app state
}
.onCustomerCenterManagementOptionSelected { action in
// log which option the user picked
}
}
}
}CustomerCenterView() with no arguments is the intended usage. It loads the current Purchases.shared.customerInfo() and renders the dashboard configured actions.
SwiftUI: full screen modal
.fullScreenCover(isPresented: $isShowingCustomerCenter) {
NavigationStack {
CustomerCenterView()
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close") { isShowingCustomerCenter = false }
}
}
}
}View modifiers for lifecycle events
Attach modifiers to the CustomerCenterView to react to specific flows:
.onCustomerCenterRestoreStarted { … }.onCustomerCenterRestoreCompleted { customerInfo in … }.onCustomerCenterRestoreFailed { error in … }.onCustomerCenterShowingManageSubscriptions { … }.onCustomerCenterRefundRequestStarted { productId in … }.onCustomerCenterRefundRequestCompleted { productId, status in … }.onCustomerCenterFeedbackSurveyCompleted { optionId in … }.onCustomerCenterManagementOptionSelected { action in … }.onCustomerCenterCustomActionSelected { actionId, purchaseId in … }.onCustomerCenterPromotionalOfferSucceeded { … }
All are optional.
UIKit
A SwiftUI view hosted inside a UIHostingController presents the Customer Center from UIKit:
import UIKit
import SwiftUI
import RevenueCatUI
final class SettingsViewController: UIViewController {
@IBAction func openCustomerCenter() {
let host = UIHostingController(rootView: CustomerCenterView())
present(host, animated: true)
}
}Notes
- Customer Center is available on
iOS 15+only. Guard the call site withif #available(iOS 15, *)if your deployment target is lower. - The view is self contained. Do not call
Purchases.shared.restorePurchases()from outside when it is on screen; the Restore flow inside the view owns that path. - Refund request flows (Apple's
SKPaymentQueue.showRequestRefundController) are triggered by the Customer Center only when the dashboard has the refund action enabled and the underlying transaction is eligible. - Dashboard configuration lives under Customer Center in the RevenueCat app. Without it, the view shows an empty state.
Verify
Sign into the app with an Apple ID that has at least one active sandbox subscription:
1. Present the Customer Center. The active subscription appears in the list, with the actions configured in the dashboard. 2. Tap Restore purchases. .onCustomerCenterRestoreCompleted fires with a CustomerInfo whose entitlements.active is non-empty. 3. Tap the manage / cancel action. .onCustomerCenterManagementOptionSelected fires with the chosen action. For Apple hosted cancel, the system subscription management sheet opens. 4. Dismiss the sheet. The isPresented binding flips back to false.
If the view is empty for a test user who has purchases, verify they are signed into the correct RevenueCat appUserID via Purchases.shared.logIn(…) and that the dashboard's Customer Center section is configured.
revenuecat-customer-center: Kotlin Multiplatform
purchases-kmp-ui exposes a Compose Multiplatform CustomerCenter composable that wraps the native Customer Center on each target (SwiftUI CustomerCenterView on iOS, Compose CustomerCenter on Android).
Install
Find the latest stable release at <https://github.com/RevenueCat/purchases-kmp/releases> and substitute that tag for <latest> in the snippet below. The KMP tag uses a <wrapper>+<bundled-deps> format (e.g. 2.10.2+17.55.1), where the part before + is the KMP wrapper version and the part after is the bundled purchases-hybrid-common version. Use the full tag string as the artifact version first; if Gradle interprets the + as a wildcard, fall back to the wrapper portion only (e.g. 2.10.2). If GitHub is unreachable, ask the user for a version to pin.
In the shared module's build.gradle.kts:
kotlin {
// your targets: androidTarget(), iosX64(), iosArm64(), iosSimulatorArm64(), etc.
sourceSets {
commonMain.dependencies {
implementation("com.revenuecat.purchases:purchases-kmp-core:<latest>")
implementation("com.revenuecat.purchases:purchases-kmp-ui:<latest>")
}
}
}Compose Multiplatform must already be set up on the shared module. On iOS, the UI artifact bridges to RevenueCatUI; for CocoaPods-based KMP projects, the Kotlin CocoaPods plugin wires the pod automatically. For pure SPM setups, follow the iOS linking section of the purchases-kmp README.
Implement
Shared composable:
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.revenuecat.purchases.kmp.ui.revenuecatui.CustomerCenter
@Composable
fun SubscriptionSettingsScreen(onDismiss: () -> Unit) {
CustomerCenter(
modifier = Modifier.fillMaxSize(),
onDismiss = onDismiss,
)
}The common API is minimal: modifier and a required onDismiss callback. Platform specific Customer Center callbacks (restore, refund, management option, etc.) surface through the native SDKs; if you need them on common, check the installed purchases-kmp-ui version for an options overload. If the overload is not present in your version, host the composable behind a platform specific wrapper and listen to events using the native SDK's view modifiers (iOS) or CustomerCenterOptions.listener (Android).
Presenting from platform UI
- Android: place
CustomerCenter(…)inside any Compose screen. It fills the provided modifier constraints. - iOS: wrap
CustomerCenter(…)in a Compose MultiplatformUIViewControllerviaComposeUIViewController { CustomerCenter(…) }, then present that from SwiftUI with.sheetor.fullScreenCover, or from UIKit viapresent(_:animated:).
Notes
- Customer Center requires iOS 15+ on the iOS target. On Android, refund flows degrade to deep linking into Google Play (Apple only feature).
- The KMP common API exposes fewer hooks than either native SDK. If your product requires
onRestoreCompleted,onManagementOptionSelected, or other lifecycle callbacks, wire them through platform code directly, or upgrade to apurchases-kmp-uirelease that exposes them in common. - If the IDE cannot resolve
com.revenuecat.purchases:purchases-kmp-ui, confirm artifact coordinates against the purchases-kmp README for your installed version; the module has evolved across 1.x. - Ensure
Purchases.logIn(appUserId)has run for signed in users before opening the Customer Center, or subscriptions will show up under an anonymous ID.
Verify
Run each platform target with a sandbox account that owns at least one active subscription:
1. Present the shared CustomerCenter composable on iOS (via a Compose UIViewController host) and on Android (directly in Compose). The subscription and dashboard configured actions render on both platforms. 2. Tap Restore purchases on each platform. The purchase restores and the native SDK logs the restore event. 3. Tap the manage / cancel action. On iOS, the system manage subscriptions sheet opens. On Android, the Google Play subscriptions page opens in a new task. 4. Close the view. onDismiss fires on both platforms. 5. After restore, call Purchases.sharedInstance.customerInfo() from common code and confirm entitlements.active["premium"] exists.
revenuecat-customer-center: React Native
The Customer Center ships in react-native-purchases-ui, the same package that provides paywalls.
Install
The npm install commands below resolve the current latest at install time, so no version pin is needed in this skill. To verify the installed version after install, check package.json. The full release history lives at <https://github.com/RevenueCat/react-native-purchases/releases>.
Bare React Native
npm install react-native-purchases-ui
cd ios && pod install && cd ..Expo
npx expo install react-native-purchases-uireact-native-purchases-ui links native code. It will not work in Expo Go. Use a development build: npx expo prebuild (bare) or eas build --profile development.
Deployment targets: iOS 15+, Android minSdk 24 (Compose requirement on the native side).
Implement
Two APIs: the imperative RevenueCatUI.presentCustomerCenter(…) method and the declarative <RevenueCatUI.CustomerCenterView /> component. Prefer the imperative one for a "Manage subscription" button. Use the component when you want to embed the Customer Center inside a React Native screen.
Imperative: presentCustomerCenter
import RevenueCatUI from 'react-native-purchases-ui';
async function openCustomerCenter() {
await RevenueCatUI.presentCustomerCenter({
callbacks: {
onRestoreCompleted: ({ customerInfo }) => {
// refresh app state
},
onShowingManageSubscriptions: () => {
// user navigated into the manage-subscription flow
},
onManagementOptionSelected: (event) => {
// event.option is 'cancel' | 'custom_url' | 'missing_purchase' | 'refund_request' | 'change_plans' | ...
// event.url is the custom URL for 'custom_url', null otherwise
},
onPromotionalOfferSucceeded: ({ customerInfo, transaction, offerId }) => {
// promo offer accepted
},
},
});
}All callbacks are optional. The promise resolves when the Customer Center is dismissed.
Declarative: <RevenueCatUI.CustomerCenterView /> component
import { View } from 'react-native';
import RevenueCatUI from 'react-native-purchases-ui';
export function SubscriptionSettingsScreen({ navigation }) {
return (
<View style={{ flex: 1 }}>
<RevenueCatUI.CustomerCenterView
shouldShowCloseButton={false}
onDismiss={() => navigation.goBack()}
onRestoreCompleted={({ customerInfo }) => {
// refresh app state
}}
onManagementOptionSelected={(event) => {
// react to cancel / change_plans / custom_url
}}
/>
</View>
);
}Notes
react-native-purchases-uiis iOS + Android only. There is no web or desktop target.- Refund requests are iOS only.
onRefundRequestStarted/onRefundRequestCompletednever fire on Android; the UI deep links into Google Play's subscriptions screen instead. shouldShowCloseButtononly affects iOS. Android always shows a close button regardless of this prop.<RevenueCatUI.CustomerCenterView />is a native view host. Wrap it in<View style={{ flex: 1 }} />or give it an explicit height. Zero-size constraints render a blank view.- Log the user in before opening. If your app has identified users, call
Purchases.logIn(userId)first; the Customer Center loads that user's subscriptions. - Do not call
Purchases.restorePurchases()while the Customer Center is on screen. The UI runs restore itself.
Verify
Run the app on a device or simulator signed into a sandbox account with at least one active subscription:
1. Open the Customer Center. The subscription appears in the list with the dashboard configured actions. 2. Tap Restore purchases. onRestoreCompleted fires with a customerInfo whose entitlements.active is non-empty. 3. Tap the manage action. On iOS, the system manage subscriptions sheet opens. On Android, Google Play's subscriptions screen opens in a new task. onManagementOptionSelected fires with the selected option. 4. Close the Customer Center. The imperative promise resolves, or the component's onDismiss fires. 5. After restore, call Purchases.getCustomerInfo() from JS and confirm customerInfo.entitlements.active['premium'] is defined.
If the view is empty, confirm the signed in user matches the dashboard appUserID that owns the transactions and that the Customer Center is configured in the RevenueCat dashboard.