Warteliste für ein neues SaaS

Warteliste für ein neues SaaS8 Min. Lesezeit

So baust du vor dem App-Store-Launch eine Wartelisten-Seite für eine mobile App

Eine Pre-Launch-Warteliste für eine iOS- oder Android-App, die fragt, welches Handy jemand nutzt, iPhone-Tester zu TestFlight schickt und einen Play-Closed-Test füllt.

Preet SutharGründer, Ikiform

Setze ein Wartelisten-Formular auf die Landingpage deiner App, das fragt, welches Handy die Person nutzt und ob sie früh testen will. Schick iPhone-Tester mit einem Webhook und der App Store Connect API in eine TestFlight-Gruppe und lade Android-Tester aus einem Export in einen geschlossenen Test bei Google Play. Schreib am Launch-Tag jeder Gruppe den Link zu ihrem eigenen Store.

Bevor eine App in einem Store ist, gibt es keinen Installieren-Button, zu dem du Leute schicken kannst. Eine Warteliste auf deiner eigenen Landingpage hält ihr Interesse wach, bis es einen gibt, und erledigt eine zweite Aufgabe, die die Stores nicht übernehmen: Sie findet die Tester, die du vor dem Launch brauchst.

Was bei einer mobilen App anders ist

  • Zwei Stores, zwei Listen. Die Launch-E-Mail verlinkt auf den App Store oder Google Play, und Testeinladungen laufen über TestFlight oder einen Play-Closed-Test. Du musst die Plattform jeder Person kennen, um die richtige zu senden.
  • Tester müssen zum Konto des Stores passen. TestFlight schickt eine Einladung an jede Adresse. Ein Play-Closed-Test lässt nur die Google-Konten auf seiner Liste hinein, ein Android-Tester, der sich mit einer Arbeitsadresse anmeldet, kann den Build also nicht installieren.
  • Google Play kann Tester vor dem Launch verlangen. Private Entwicklerkonten, die nach dem 13. November 2023 erstellt wurden, müssen einen geschlossenen Test mit mindestens 12 Testern durchführen, die 14 Tage am Stück dabeibleiben, bevor sie in der Produktion veröffentlichen können. Eine Warteliste ist der Ort, von dem diese 12 kommen.
  • Die Stores haben eigene Pre-Launch-Werkzeuge. Es lohnt sich, sie neben einer Warteliste zu nutzen, nicht statt einer. Siehe den Vergleich unten.
ItemApp-Store-Vorbestellungen oder Play-VorregistrierungDeine eigene Warteliste
Braucht einen Store-EintragJa, freigegeben und bereitNein. Sie funktioniert ab dem ersten Tag der Landingpage
Was du bekommstEine Zahl und eine Store-Benachrichtigung oder einen automatischen Download zum ReleaseE-Mail, Plattform und Antworten jeder Person
Kann Tester einladenNeinJa, per E-Mail zu TestFlight oder einem Play-Closed-Test
GrenzenDie Play-Vorregistrierung bleibt für neue private Konten aus, bis die Testanforderung erfüllt istDu sendest die Launch-E-Mail selbst

Felder für eine App-Warteliste

Frag zuerst nach der Plattform, weil sie ändert, was das E-Mail-Feld bedeutet. Das Handymodell ist optional und sagt dir, auf welchen Geräten du testen solltest. Die Beta-Checkbox trennt Leute, die testen wollen, von Leuten, die die Launch-E-Mail wollen.

Vorschau

Führt den Validator von Ikiform in deinem Browser aus. Es wird nichts gesendet.

Nutze auf Android das Google-Konto, mit dem du dich bei Google Play anmeldest.

Welches Handy nutzt du?
Das Radio platform ist Pflicht, jede Anmeldung landet also in einer Liste, mit der du arbeiten kannst. Füge die Definition im Tab Fields des Formulars ein.

Das Formular

Das Formular funktioniert als einfaches HTML auf jeder Landingpage. Das Skript tut zwei kleine Dinge: Es wählt die Plattform anhand des Browsers vor und zeigt den Hinweis zum Google-Konto nur Android-Nutzern.

index.html
<form id="app-waitlist" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <fieldset>
    <legend>Which phone do you use?</legend>
    <label><input type="radio" name="platform" value="iphone" required> iPhone</label>
    <label><input type="radio" name="platform" value="android"> Android</label>
  </fieldset>

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" inputmode="email" required>
  <p id="email-hint" hidden>Use the Google account you sign in to Google Play with.</p>

  <label for="first_name">First name</label>
  <input id="first_name" name="first_name" autocomplete="given-name" maxlength="60">

  <label for="device">Phone model</label>
  <input id="device" name="device" maxlength="80" placeholder="iPhone 15, Pixel 8">

  <label><input type="checkbox" name="beta" value="true"> Invite me to test the app before launch</label>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
  <button>Join the waitlist</button>
</form>

<script>
  const form = document.getElementById("app-waitlist");
  const agent = navigator.userAgent;
  const guess = /iPhone|iPad/.test(agent) ? "iphone" : /Android/.test(agent) ? "android" : null;
  if (guess) form.querySelector(`[name="platform"][value="${guess}"]`).checked = true;
  const hint = () => {
    document.getElementById("email-hint").hidden = form.platform.value !== "android";
  };
  form.addEventListener("change", hint);
  hint();
</script>
  • Die Vermutung wählt nur ein Radio vor. Die Leute können es ändern, und ohne das Skript ist nichts ausgewählt, required zwingt sie also zur Wahl.
  • inputmode="email" und autocomplete="email" zählen hier mehr als irgendwo sonst: Die meisten Besucher der Landingpage einer App sind am Handy.
  • Setze Redirect after submit auf eine Dankeseite, die sagt, was für jede Plattform als Nächstes passiert. Für eine Framework-Version des Formulars siehe die Hauptanleitung und füge diese Felder hinzu.

Tester an jeden Store leiten

Wie iPhone- und Android-Tester die Beta jedes Stores erreicheniPhoneAndroidAnmeldungplatform =iphone undbeta = trueWebhook-Relayverifiziertdie SignaturAPI vonApp StoreConnectfügt einenBeta-TesterhinzuEinladungvonTestFlightvon ApplegemailtAnmeldungplatform =android undbeta = trueCSV-ExportnachPlattformfilternPlayConsoleE-Mail-ListedesgeschlossenenTestsOpt-in-Linkdu sendestihnsigniertwöchentlichWie iPhone- und Android-Tester die Beta jedes Stores erreicheniPhoneAndroidAnmeldungplatform = iphone und beta =trueWebhook-Relayverifiziert die SignaturAPI von App StoreConnectfügt einen Beta-Tester hinzuEinladung von TestFlightvon Apple gemailtAnmeldungplatform = android und beta =trueCSV-Exportnach Plattform filternPlay ConsoleE-Mail-Liste des geschlossenenTestsOpt-in-Linkdu sendest ihnsigniertwöchentlich
  1. Ein iPhone-Nutzer, der die Beta-Checkbox anhakt, löst einen signierten Webhook aus.
  2. Dein Relay verifiziert ihn und ruft die App Store Connect API auf, um die Adresse einer TestFlight-Gruppe hinzuzufügen.
  3. Apple sendet die TestFlight-Einladung, sobald für die Gruppe ein Build verfügbar ist.
  4. Android-Tester werden genauso gesammelt, dann als CSV exportiert und nach Plattform gefiltert.
  5. Du fügst die Google-Konten in der Play Console in die E-Mail-Liste des geschlossenen Tests ein.
  6. Du schickst ihnen aus deinem E-Mail-Tool den Opt-in-Link des Tests.
Apple hat eine API für Tester; geschlossene Tests bei Google Play werden in der Play Console oder über Google Groups verwaltet, die Android-Seite ist also ein wöchentlicher Export.

iPhone: Tester zu TestFlight hinzufügen

Erstelle in App Store Connect eine externe Testgruppe und unter Users and Access › Integrations einen API-Schlüssel mit der Rolle App Manager oder Admin. Füge dann ein Relay hinzu, das auf Ikiforms Webhook submission.created hört. Dieses hier ist ein Next.js-Route-Handler und nutzt jose, um Apples Token zu signieren; es läuft auf jedem Server, der fetch hat.

app/api/testflight/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
import { importPKCS8, SignJWT } from "jose";

async function appStoreToken() {
  const key = await importPKCS8(process.env.ASC_PRIVATE_KEY!, "ES256");
  return new SignJWT({})
    .setProtectedHeader({ alg: "ES256", kid: process.env.ASC_KEY_ID!, typ: "JWT" })
    .setIssuer(process.env.ASC_ISSUER_ID!)
    .setAudience("appstoreconnect-v1")
    .setIssuedAt()
    .setExpirationTime("15m")
    .sign(key);
}

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({ payload, headers: request.headers, secret: process.env.IKIFORM_WEBHOOK_SECRET! });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });
    const { email, first_name, platform, beta } = event.data.fields;
    if (platform !== "iphone" || beta !== true) return new Response(null, { status: 204 });

    const response = await fetch("https://api.appstoreconnect.apple.com/v1/betaTesters", {
      method: "POST",
      headers: { authorization: `Bearer ${await appStoreToken()}`, "content-type": "application/json" },
      body: JSON.stringify({
        data: {
          type: "betaTesters",
          attributes: { email: String(email), firstName: first_name ? String(first_name) : undefined },
          relationships: { betaGroups: { data: [{ type: "betaGroups", id: process.env.ASC_BETA_GROUP_ID! }] } },
        },
      }),
    });
    if (response.ok || response.status === 409) return new Response(null, { status: 204 });
    return new Response(`App Store Connect answered ${response.status}`, { status: 502 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • ASC_PRIVATE_KEY ist der Inhalt der .p8-Datei, die Apple dir gibt, mit Zeilenumbrüchen. ASC_KEY_ID und ASC_ISSUER_ID stehen auf derselben Seite.
  • Apples Tokens dürfen höchstens 20 Minuten gelten, das Relay signiert also pro Zustellung ein frisches.
  • Ein 409 heißt, Apple hat die Änderung als Konflikt abgelehnt, oft weil die Adresse schon Tester ist. Das Relay behandelt das als erledigt; wenn ein Tester also nie eine Einladung bekommt, prüfe die Gruppe in App Store Connect. Jeder andere Fehler antwortet mit 502, damit Ikiform die Zustellung wiederholt.
  • TestFlight erlaubt bis zu 10.000 externe Tester pro App. Der erste Build, den du einer externen Gruppe gibst, durchläuft die Beta App Review, und jeder Build läuft nach 90 Tagen ab.

Füge die URL des Relays in Ikiform unter Webhooks hinzu und kopiere das whsec_-Secret in IKIFORM_WEBHOOK_SECRET. Ein Workspace kann 3 Endpunkte haben, das hier kann also neben einem Relay laufen, das dein E-Mail-Tool füttert.

Android: einen geschlossenen Test füllen

Die Publishing-API von Google Play verwaltet Tester geschlossener Tests über Google Groups, nicht über einzelne Adressen, diese Seite ist also ein wöchentlicher Export. Exportiere die Liste mit Export CSV, filtere sie in einer Tabelle auf platform = android und beta = true und füge die E-Mails in der Play Console unter Test and release › Testing › Closed testing › Manage track › Testers zur E-Mail-Liste des geschlossenen Tests hinzu. Schick diesen Leuten dann aus deinem E-Mail-Tool den Opt-in-Link des Tests.

Wenn du die Anforderung von 12 Testern erfüllen willst, lade mehr als 12 ein. Leute, die sich abmelden oder nie zustimmen, zählen nicht, und die 14 Tage brauchen die ganze Zeit 12 Tester, die zugestimmt haben.

Ikiform schreibt der Person, die das Formular ausfüllt, keine E-Mail. Benachrichtigungs-E-Mails gehen nur an dich und dein Team, es gibt also keinen Autoresponder, keine Bestätigungs-E-Mail und kein Double-Opt-in. Sende diese aus deinem E-Mail-Tool, ausgelöst durch einen Webhook. TestFlight sendet seine eigene Einladung, iPhone-Tester hören also von Apple. Android-Tester hören nur von dir.

Launch-Tag

Exportiere die ganze Liste und teile sie nach platform auf. Schick jeder Hälfte eine E-Mail mit einem Store-Link. Wer iPhone gewählt hat, braucht den Play-Link nicht, und eine einzelne E-Mail mit zwei Badges verliert auf einem kleinen Bildschirm Taps. Wenn du device gesammelt hast, gleich vor dem Versand deine Absturzberichte mit den Modellen ab, die die Leute angegeben haben.

Prüfen, ob es funktioniert

  1. Füge unter Allowed domains des Formulars die Domain deiner Site hinzu, plus localhost, während du entwickelst. Lass das Feld leer, um Posts von überall anzunehmen.
  2. Öffne die Seite auf einem Handy. Die richtige Plattform ist vorausgewählt, und auf Android erscheint der Hinweis zum Google-Konto.
  3. Sende als iPhone-Tester ab. Die Einsendung zeigt platform als iphone und beta als true.
  4. Öffne den Endpunkt des Relays unter Webhooks. Die Zustellung zeigt succeeded, und die Adresse steht in deiner TestFlight-Gruppe.
  5. Sende als Android-Tester ab, exportiere die CSV und prüfe, dass die Zeile mit platform auf android herausgefiltert wird.
  6. Sende einen Post mit ausgefülltem Honeypot, _gotcha=bot. Er bekommt trotzdem eine Erfolgsantwort, aber die Einsendung wird als Spam gespeichert und löst weder E-Mail noch Webhook aus.

Stolperfallen

ProblemWas zu tun ist
Die Zustellungen des Relays scheitern mit 502, und Apple hat 401 geantwortetDas Token ist falsch: Prüfe die Key-ID, die Issuer-ID und dass der Private Key in der Umgebungsvariable seine Zeilenumbrüche behalten hat.
Ein Tester wurde hinzugefügt, hat aber keine TestFlight-E-Mail bekommenDie Gruppe hat noch keinen Build, oder der erste Build ist noch in der Beta App Review. Einladungen gehen raus, sobald ein Build für die Gruppe verfügbar ist.
Ein Android-Tester kann den Opt-in-Link nicht öffnenDie Adresse auf der Liste ist nicht das Google-Konto, das auf dem Handy angemeldet ist. Bitte die Person, sich mit diesem Konto erneut anzumelden.
422 validation_failedDer Post passt nicht zur Felddefinition des Formulars. error.fields nennt jedes Feld und den Grund. Gleiche den name des Eingabefelds an die Feld-ID an.
Die Einsendung ist im Dashboard, aber keine E-Mail und kein Webhook kam anSie wurde als Spam gespeichert. Spam löst nichts aus. Öffne sie unter dem Filter Spam, um die Gründe zu sehen, und markiere sie als kein Spam, um ihre E-Mail und Webhooks zu senden.

Fragen

Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.