sentalong 0.1.0
sentalong: ^0.1.0 copied to clipboard
Thin Flutter client for the Sentalong tracking API. Deep-link click capture, identify, and qualification stages — no analytics, no fingerprinting, no device identifiers.
sentalong #
Thin Flutter client for the Sentalong tracking API. It does exactly four things — configure, handle a deep link, identify a signed-up user, and report qualification stages — and nothing else.
- No PII generated. The SDK only sends what your app passes in.
- No analytics, no fingerprinting, no device identifiers. This is a policy requirement (App Store / Play policy safety), not a style choice.
- Fail-safe. No method ever throws: bad URLs, offline devices, and
server hiccups degrade to
null(orfalseforqualify).
Install #
# pubspec.yaml
dependencies:
sentalong: ^0.1.0
flutter pub get
The package's only runtime dependencies are shared_preferences (to persist
the click id) and http (transport).
Quickstart #
1. Configure once at startup #
import 'package:sentalong/sentalong.dart';
void main() {
Sentalong.configure(
'https://sentalong.com', // your Sentalong origin
'prg_abc123', // your program id
// urlParam: 'via', // change if your links use a different param
);
runApp(const MyApp());
}
2. Wire deep links to handleUrl #
The SDK deliberately does not depend on a deep-link package — pass it
whatever URL your existing wiring receives. With
app_links (or the older
uni_links), the wiring looks like this:
// pubspec.yaml (your app, not this SDK):
// app_links: ^6.0.0
import 'package:app_links/app_links.dart';
import 'package:sentalong/sentalong.dart';
final _appLinks = AppLinks();
Future<void> initDeepLinks() async {
// Cold start: the link that launched the app, if any.
final Uri? initial = await _appLinks.getInitialLink();
if (initial != null) {
await Sentalong.handleUri(initial);
}
// Warm/foreground links while the app is running.
_appLinks.uriLinkStream.listen((Uri uri) {
Sentalong.handleUri(uri);
});
}
handleUrl / handleUri accepts universal links (https://…), Android App
Links, and custom schemes (myapp://…) alike. When the link carries your
attribution param (default via), the SDK reports the click and persists the
returned click id for the number of days your program's attribution window
allows. It also forwards sub_id (or sub1) and the six supported ad-click
ids (gclid, fbclid, ttclid, twclid, li_fat_id, msclkid) when
present.
Links with via=test are treated as smoke tests: the SDK logs
Sentalong: test click received and sends/stores nothing — the same behavior
as the t.js web snippet.
3. Identify at signup #
Call identify right after the user completes signup. If a click was
captured (and hasn't expired), the server links the new user to the partner
who referred them:
Future<void> onSignupComplete(User user) async {
final String? referralId = await Sentalong.identify(
user.email,
externalId: user.id, // optional: your own user id
);
// referralId is 'ref_…' when attribution succeeded, or null when there
// was no stored click (organic signup) or the network was unavailable.
}
Calling identify again later is safe — the server treats repeat identifies
idempotently, and the SDK keeps the click id stored after success.
4. Report qualification stages (optional) #
await Sentalong.qualify('signup'); // also: 'onboarded', 'demo'
Returns true only when the server acknowledged the stage.
Attribution limits on iOS #
Be honest with yourself about what mobile attribution can and cannot do on iOS:
- There is no install referrer on iOS. Unlike Android's Play Install
Referrer, Apple provides no API that tells an app which link led to its
App Store install. If a user taps a partner link, installs your app from
the App Store, and then opens it, that hop through the App Store breaks
the link —
handleUrlnever fires because no URL reaches the app. - What does work: universal links and custom-scheme links that open an already-installed app deliver the full URL, and this SDK attributes those clicks reliably. Web-to-app flows where the user signs up in the browser (where the t.js snippet captured the click) also attribute normally.
- What this SDK will not do: it will not fingerprint devices, join IP addresses, or use any probabilistic matching to paper over the install gap. Those techniques violate Apple's App Tracking Transparency rules and are explicitly out of scope.
- Practical advice: for iOS install campaigns, send partners' traffic to a web landing page first (where the click is captured), and let users sign up on the web or deep-link into the app afterwards. Expect some unavoidable attribution loss on cold App Store installs.
On Android, deep links into an installed app work the same way; install
referrer capture is available in the Kotlin SDK (com.sentalong.sdk), not in
this Flutter package.
Testing your integration #
The core client is pure Dart and injectable, so you can unit-test your own wiring without a network or a Flutter engine:
final client = SentalongClient(
storage: InMemorySentalongStorage(),
httpClient: myFakeHttpClient, // implements SentalongHttpClient
);
client.configure('https://sentalong.com', 'prg_test');
See test/sentalong_test.dart in this package for a complete example.
API reference #
| Method | Returns | Notes |
|---|---|---|
Sentalong.configure(baseUrl, programId, {urlParam}) |
void |
Call once before anything else. |
Sentalong.handleUrl(String url) |
Future<String?> |
Click id (clk_…) or null. Never throws. |
Sentalong.handleUri(Uri uri) |
Future<String?> |
Same, for a pre-parsed Uri. |
Sentalong.identify(email, {externalId}) |
Future<String?> |
Referral id (ref_…) or null. |
Sentalong.qualify(stage) |
Future<bool> |
'signup', 'onboarded', or 'demo'. |
Stored state lives under the shared_preferences keys sentalong.cid and
sentalong.cid_expires_at (ISO-8601). An expired click id is discarded the
next time it is read.
License #
MIT