SDKs that send nothing before you say start
Consent-first SDKs for Android, iOS, Web, React Native and Flutter, with signed requests, a persisted queue and the same behaviour on every platform. Plus APIs for everything the dashboard does.
Pick your platform
Default endpoint: https://sdk.appvero.ai
Android SDK
Kotlin
- Kotlin, usable from Java
- minSdk 21
- Works in apps on Kotlin 1.9 or newer
- One runtime dependency (Play Install Referrer)
- 1 Add the dependency
- 2 Start the SDK early, with listeners set first
- 3 Track events and purchases
dependencies {
implementation("io.appvero:appvero-android:0.1.0")
// Optional: lets the SDK read the Google Advertising ID and App Set ID.
implementation("com.google.android.gms:play-services-ads-identifier:18.3.0")
implementation("com.google.android.gms:play-services-appset:16.1.0")
}class App : Application() {
override fun onCreate() {
super.onCreate()
AppVero.setAttributionListener { attribution ->
Log.i("App", "attribution ${attribution.status} ${attribution.mediaSource} ${attribution.campaign}")
}
AppVero.setDeepLinkListener { deepLink ->
if (deepLink?.value == "product/123") openProduct(deepLink.params["product_id"])
}
AppVero.start(
this,
AppVeroConfig(
appId = "app_7f3k9q2m",
sdkSecretId = "ss_4hd82kq0",
sdkSecret = BuildConfig.APPVERO_SDK_SECRET,
),
)
}
}AppVero.trackEvent("av_add_to_cart", mapOf("sku" to "A1", "qty" to 2))
AppVero.trackEvent("av_subscribe", mapOf("plan" to "monthly"), revenue = 9.99, currency = "USD")iOS SDK
Swift
- Swift, Objective-C supported
- iOS 13+
- Swift Package Manager or CocoaPods
- Privacy manifest included
- 1 Add the dependency
- 2 Start the SDK early, with listeners set first
- 3 Track events and purchases
dependencies: [
.package(url: "<repo>", from: "0.1.0"),
],
targets: [
.target(name: "MyApp", dependencies: [.product(name: "AppVero", package: "AppVero")]),
]import AppVero
let config = AppVeroConfig(appId: "app_7f3k9q2m", sdkSecretId: "ss_4hd82kq0", sdkSecret: "<43-char secret>")
AppVero.shared.onAttribution = { attribution in print(attribution.status, attribution.mediaSource ?? "-") }
AppVero.shared.onDeepLink = { link in
guard let link = link else { return } // nil: no deferred deep link for this install
router.open(link.value, params: link.params)
}
AppVero.shared.start(config: config)AppVero.shared.trackEvent("av_add_to_cart", params: ["sku": "A1", "qty": 2, "first": true])
let result = try await product.purchase()
if case .success(let verification) = result {
AppVero.shared.trackPurchase(verification) // revenue/currency from the transaction + JWS
}Web SDK
JavaScript
- About 7.6 KB gzipped
- Script tag, npm or Google Tag Manager
- Smart banner + desktop QR card
- Nothing stored or sent before init
- 1 Add the dependency
- 2 Start the SDK early, with listeners set first
- 3 Track events and purchases
<script>
!(function (w, d, n, u) {
w[n] = w[n] || function () { (w[n].q = w[n].q || []).push(arguments); };
var s = d.createElement("script"); s.async = 1; s.src = u; d.head.appendChild(s);
})(window, document, "AppVero", "https://sdk.appvero.ai/web/v1/appvero.min.js");
AppVero("init", { appId: "app_7f3k9q2m" });
</script>import { init, track, smartLink } from "@appvero/web";
init({ appId: "app_7f3k9q2m", spa: true });
track("av_add_to_cart", { sku: "A1" });AppVero("event", "av_purchase", { sku: "A1", qty: 2 }, { revenue: 49.9, currency: "USD" });
AppVero("setCustomerUserId", "u_123");
AppVero(function (av) { // runs once the SDK is loaded
location.href = av.smartLink("https://northwind.appvero.link/app");
});React Native SDK
TypeScript
- React Native 0.74+
- New Architecture (TurboModule) and old bridge
- Thin wrapper over the native SDKs
- Expo: development builds
- 1 Add the dependency
- 2 Start the SDK early, with listeners set first
- 3 Track events and purchases
npm install @appvero/react-native
cd ios && pod installimport AppVero from '@appvero/react-native';
AppVero.onAttribution((attribution) => {
console.log(attribution.status, attribution.mediaSource, attribution.campaign);
});
AppVero.onDeepLink((link) => {
if (link === null) return; // the install has no deferred deep link
router.open(link.value, link.params); // e.g. "product/123", { product_id: "123" }
});
await AppVero.start({
appId: 'app_7f3k9q2m',
sdkSecretId: 'ss_4hd82kq0',
sdkSecret: Config.APPVERO_SDK_SECRET,
});Flutter SDK
Dart
- Flutter 3.38+, Dart 3.10+
- Android and iOS
- Thin wrapper over the native SDKs
- Streams for attribution and deep links
- 1 Add the dependency
- 2 Start the SDK early, with listeners set first
- 3 Track events and purchases
dependencies:
appvero_flutter:
path: ../appvero/sdk/flutter # or: git: { url: <repo>, path: sdk/flutter }import 'package:appvero_flutter/appvero_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await AppVero.start(const AppVeroConfig(
appId: 'app_7f3k9q2m',
sdkSecretId: 'ss_4hd82kq0',
sdkSecret: String.fromEnvironment('APPVERO_SDK_SECRET'),
));
runApp(const MyApp());
}
// Anywhere, also after start():
AppVero.attributionStream.listen((a) => print('${a.status} ${a.mediaSource} ${a.campaign}'));
AppVero.deepLinkStream.listen((link) => router.go('/${link.value}', extra: link.params));One contract, five SDKs
Every SDK — and every wrapper — follows the same protocol, so your app behaves the same on Android, iOS and cross-platform frameworks.
| Call | Behaviour |
|---|---|
Before start() | Nothing is sent. Calls made earlier are buffered and replayed after start. |
stop() | Stops sending for this process; the persisted queue and install ID are kept and resume on the next start. |
setEnabled(false) | Opt-out: stops sending, wipes the persisted queue, survives restarts. The install ID is kept. |
Install ID | Available even before start (null only before the very first start). |
Attribution callback | Fires when the install response arrives, and again on every later start with the persisted attribution. |
Deferred deep link | Delivered at most once per install — or null after the timeout (default 5 s). |
resolveLink(url) | Resolves AppVero links (and unwraps email click-tracking links); non-AppVero URLs return their own parameters. |
Errors | Structured: the server’s error code (e.g. sig_invalid) and HTTP status, not only a message. |
Persisted queue
Up to 1,000 requests survive app kills. The install request is never dropped and always goes first.
Retries with backoff
1 s, 2 s, 4 s … up to 10 minutes with jitter; 429s honour Retry-After.
Signed requests
HMAC-SHA256 over method, path, timestamp and body hash, with clock-skew correction.
Batched & compressed
Events in batches of up to 100; bodies of 1 KB or more are gzip-compressed.
Everything the dashboard does, over HTTPS
JSON in and out at https://api.appvero.ai. Errors use one shape: {"error": {"code": "…", "message": "…"}}.
adminEverythingreadGET only — for BI and reportings2sOnly the S2S events API
curl https://api.appvero.ai/v1/apps \
-H "Authorization: Bearer avk_<id>_<secret>"| Resource | Paths | What it does |
|---|---|---|
| Apps & SDK secrets | /v1/apps, /v1/apps/{app_id}/sdk-secrets | Create apps, attribution windows, fraud settings; rotate SDK secrets. |
| Domains | /v1/domains | Vendor subdomains, custom domains (CNAME verification), email click domains, aliases. |
| Links | /v1/apps/{app_id}/links, /links/bulk, /links/import, /templates | Short links, up to 1,000 per bulk request, imports, templates, QR as PNG/SVG. |
| Partners | /v1/apps/{app_id}/partners | Postback templates, event mapping, revenue inclusion. |
| Webhooks | /v1/apps/{app_id}/webhooks | Real-time signed callbacks; test deliveries; delivery log. |
| Exports | /v1/apps/{app_id}/exports | S3, GCS, BigQuery, Snowflake destinations; runs and SLA. |
| SKAN & web | /skan-schema, /web-config | Conversion-value schema (versioned) and smart-banner config. |
| Reports | /v1/apps/{app_id}/reports/… | Aggregate, cohort, owned channels, SKAN, fraud, web-to-app, DDL — JSON, CSV, Parquet. |
| Raw pull | /v1/apps/{app_id}/raw/{table} | Up to 7 days of installs, events, clicks or SKAN postbacks. |
| S2S events | /v1/s2s/events | Server-side events with an s2s-scoped key. |
curl "https://api.appvero.ai/v1/apps/app_7f3k9q2m/reports/aggregate?\
from=2026-10-01&to=2026-10-07&group_by=media_source,campaign&\
metrics=installs,non_organic_installs,revenue,validated_revenue&format=parquet" \
-H "Authorization: Bearer avk_<id>_<secret>" -o installs.parquetNeed an SDK walkthrough?
We’ll pair with your engineers on the first integration — Android, iOS, web or cross-platform — and verify signed traffic end to end.