Server-API
Als NächstesLinks anlegen, Kampagnen und Pushes an einzelne Nutzer senden, Events melden und Nutzer löschen, aus jeder Sprache.
Flutter · Serverpod · MCP
Lass deinen Coding-Agenten Lotse einbauen, mit den Skills aus dem SDK, oder mach es von Hand. Ein Paket, ein Init-Aufruf und ein Backend, das man lesen kann: Lotse läuft auf Serverpod.
Claude Code, Cursor oder Codex können die Integration übernehmen. Das Paket lotse bringt Skills mit, die deinem Agenten das SDK und, ab lotse 0.2.0, die Server-API beibringen, und über den MCP-Server liest er danach dein Projekt.
Im Ordner deiner App ausführen. Die skills-CLI kopiert die Skills des Pakets lotse in dein Projekt, wo dein Agent sie findet. Nach jedem Update von lotse skills get erneut ausführen.
flutter pub add lotse
dart pub global activate skills
skills get --package lotse --all
Vorher in der Console ein Projekt und einen API-Key anlegen. Der Agent fragt nach dem Public Key und deinem Handle und sagt dir, was du in der Console eintragen musst. Ein Secret braucht er nie.
Binde Lotse mit dem Skill lotse-setup in diese App ein. Richte danach Deep Links mit lotse-deep-links und Push mit lotse-push ein.
Mit einem Token aus den Projekteinstellungen liest dein Agent Funnels, Feedback und Links, prüft die Einrichtung und legt, wenn das Token es erlaubt, Links und Segmente an. Hier die Zeile für Claude Code. Cursor, VS Code und Codex stehen auf der Seite KI-Agenten.
claude mcp add --transport http --scope local lotse https://api.lotse.dev/mcp --header 'Authorization: Bearer ${LOTSE_MCP_TOKEN}'
Dieselben Schritte ohne Agenten, von der Registrierung bis zum ersten Deep Link auf einem Gerät, unter Android und iOS.
In der Console registrieren, ein Projekt anlegen und sein Handle wählen. Aus dem Handle wird die Link-Domain dein-handle.lotse.dev. Die Android-App mit Paketnamen und dem SHA-256-Fingerprint des Signaturschlüssels hinzufügen, die iOS-App mit Bundle ID und Team ID. Dann einen API-Key erzeugen; sein Public Key kommt in die App.
Das SDK ist das Paket lotse auf pub.dev.
flutter pub add lotse
Ein Aufruf registriert das Gerät, meldet die Session und löst einen Deferred Link auf. Er wirft nie in die App: Fehler werden geloggt, und der Link ist null.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Lotse.init(
publicKey: 'lt_pk_…',
serverUrl: 'https://api.lotse.dev/',
);
// Set on the first start after an install that came from a link.
final link = await Lotse.instance.deferredLink;
runApp(MyApp(initialLocation: link?.targetPath ?? '/'));
}
Die Link-Domain im Android-Manifest eintragen, unter iOS die Capability Associated Domains mit applinks:dein-handle.lotse.dev. Lotse liefert assetlinks.json und apple-app-site-association für das Projekt aus. Im Router /l/:slug an Lotse.instance.resolveLink(slug) geben und zu dessen targetPath navigieren.
<!-- android/app/src/main/AndroidManifest.xml, inside <activity> -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"
android:host="your-handle.lotse.dev"
android:pathPrefix="/l/"/>
</intent-filter>
Firebase wie gewohnt in die App einbinden und den Service-Account in der Console hochladen. Für iPhones zusätzlich einen APNs-Schlüssel (.p8) zur iOS-App hochladen; ohne ihn gehen Pushes an iOS über FCM. Das SDK fragt nach der Berechtigung, meldet das Token und sagt Bescheid, wenn ein Push geöffnet wurde.
// A push tapped while the app was closed, and pushes tapped while it runs.
final push = await Lotse.instance.initialPush;
Lotse.instance.pushOpened.listen((push) => router.go(push.targetPath ?? '/'));
Die Aufrufe, die die meisten Apps brauchen. Der generierte Serverpod-Client bleibt dahinter verborgen.
static Future<void> Lotse.init({required String publicKey, required String serverUrl})Future<LotseLink?> get deferredLinkFuture<LotseLink?> resolveLink(String slug)Future<void> track(String name, {Map<String, String>? props})Future<void> identify(String userId, {Map<String, String>? traits, String? userHash})Future<void> reset()LotseScreenObserver({String? Function(Route<dynamic> route)? screenName})Future<LotsePush?> get initialPushStream<LotsePush> get pushOpenedFuture<void> showFeedback(BuildContext context, {Map<String, String>? metadata, bool askForEmail = false, LotseFeedbackTexts? texts})Auch dein Server arbeitet mit Lotse: eine REST-API mit Server-Keys, und signierte Webhooks in die andere Richtung.
Links anlegen, Kampagnen und Pushes an einzelne Nutzer senden, Events melden und Nutzer löschen, aus jeder Sprache.
Feedback, Umfrageantworten, zugeordnete Installationen, abgeschlossene Kampagnen und mehr, an dein Backend geschickt und signiert nach Standard Webhooks.
Lotse ist keine dünne Schicht über einem fremden Backend. Jeder Teil nutzt ein Stück Serverpod.
Projekte, Links, Klicks, Geräte, Events, Kampagnen und Zustellungen sind .spy.yaml-Modelle. Das Schema wandert mit generierten Migrationen.
Console und SDK sprechen über den generierten Client mit dem Server. Das SDK kapselt ihn, die App sieht nie ein generiertes Symbol.
Console-Nutzer melden sich über das Auth-Modul an. Das SDK nutzt einen Public Key, der nur schreiben und sich selbst lesen darf. Backends nutzen einen Server-Key mit Scopes, gespeichert als Hash.
Der Link-Router /l/:slug und die .well-known-Dateien sind Relic-Routen. Sie finden das Projekt über den Host-Header und halten die Weiterleitung nie für die Klickerfassung auf.
Aus einer Kampagne werden Zustellzeilen. Ein Worker beansprucht sie in Batches, wiederholt mit Backoff und markiert abgelehnte Tokens als ungültig.
Derselbe Serverpod liefert die API, die Link-Domains und die Flutter-Web-Console aus, darunter Postgres.
Jeder Endpoint löst das Projekt aus dem Schlüssel auf und filtert jede Abfrage danach. Integrationstests mit zwei Projekten sichern Endpoints und Link-Router ab.
App Links scheitern an Kleinigkeiten. Diese haben uns Zeit gekostet.
Kostenlos während der Beta. Registrieren, Projekt anlegen, SDK einbinden.