Zot-Warteliste – Best Practices
Dieses Skill erklärt, wie eine Zot-Warteliste korrekt integriert wird. Befolge diese Regeln. Erfinde keine Alternativen.
TL;DR – Der kürzeste Weg
# 1. SDK installieren
npm install @zot-core/sdk
# 2. Warteliste erstellen und ihre ID in .env.local speichern
npx @zot-core/cli waitlist create --name "Early Access" --write-env .env.local --public
# 3. UI erstellen (siehe Muster B unten) mit `useAddUser` aus "@zot-core/sdk/react"
Das ist die gesamte Integration für ein Next.js-Projekt. Füge kein TanStack Query hinzu, schreibe keinen benutzerdefinierten POST-Handler und rufe die API nicht mit rohem fetch auf. Alles, was du brauchst, findest du unten.
Unverhandelbare Regeln
- Verwende das offizielle SDK. Paketname:
@zot-core/sdk. Rufehttps://api.zot.soniemals mit rohemfetch,axiosoder ähnlichem auf. - Wähle den richtigen Einstiegspunkt basierend darauf, wo der Code ausgeführt wird:
- Server, API-Route, Cron, Skript, Edge-Funktion ->
import { ZotSDK } from "@zot-core/sdk" - React-Komponente, die im Browser läuft ->
import { useAddUser } from "@zot-core/sdk/react"
- Server, API-Route, Cron, Skript, Edge-Funktion ->
- Ummantle
useAddUserniemals mit TanStack Query, SWR,useState, Zustand oder einem benutzerdefinierten Reducer. Der Hook stellt bereitsisPending,isUserRegistered,data,error,isErrorundresetzur Verfügung. Die Wiederverwendung seines Zustands ist der Sinn der Sache. - Übermittle niemals einen serverseitigen API-Schlüssel an den Client. Verwende für Browser-Code einen Schlüssel mit reinen Anmeldeberechtigungen und mache ihn als
NEXT_PUBLIC_ZOT_API_KEYverfügbar (oder das öffentliche Umgebungsvariable-Pendant deines Frameworks). - Behandle
ZotAPIErrorimmer. Zeige benutzerfreundliche Texte für 409 (bereits registriert) und 429 (Ratenbegrenzung).
Installation
npm install @zot-core/sdk
# oder
pnpm add @zot-core/sdk
# oder
yarn add @zot-core/sdk
Für die serverseitige Nutzung sind keine zusätzlichen Peer-Abhängigkeiten erforderlich. Für den Hook muss react >= 18 bereits installiert sein (das SDK markiert es als optionales Peer).
Anmeldeinformationen abrufen
Du benötigst zwei Dinge:
- API-Schlüssel – der schnellste Weg ist
npx @zot-core/cli login, der den Benutzer authentifiziert und einen neuenZOT_API_KEYin die lokale Umgebungsvariablendatei schreibt. Alternativ kann man manuell einen über das Zot-Dashboard unter https://app.zot.so/app/api-keys generieren. Erstelle einen Schlüssel für die serverseitige Nutzung und, wenn du vom Browser aus aufrufen möchtest, einen zweiten Schlüssel, der nur für Anmeldungen berechtigt ist. - Wartelisten-ID – sieht aus wie
wl_abc123.
# Authentifizieren und ZOT_API_KEY in .env.local speichern (oder .env, wenn du --env .env übergibst)
npx @zot-core/cli login
Warteliste mit @zot-core/cli erstellen (empfohlen)
Wenn der Benutzer noch keine Warteliste hat, verwende das offizielle Zot-CLI. Es ruft die Zot-API mit dem ZOT_API_KEY des Benutzers auf und schreibt die resultierende ID direkt in die Umgebungsvariablendatei, sodass du sie nie kopieren und einfügen musst.
Paket:
@zot-core/cli. Verwechsle es nicht mit@zot-core/agents–@zot-core/agentsinstalliert nur diesen Agentenleitfaden in ein Repository;@zot-core/clikommuniziert mit der Zot-API.
# Stelle sicher, dass ZOT_API_KEY gesetzt ist (in der Shell, in .env.local oder in .env)
npx @zot-core/cli waitlist create \
--name "Early Access" \
--write-env .env.local \
--public
Was das bewirkt:
- Ruft
POST /v1/wait-listüber@zot-core/sdkim Hintergrund auf. - Gibt die neue Warteliste aus (
_id,name, Flags, Zeitstempel). - Fügt
ZOT_WAITLIST_ID=wl_...an.env.localan. - Da
--publicübergeben wurde, wird auchNEXT_PUBLIC_ZOT_WAITLIST_ID=wl_...für die clientseitige Nutzung in Next.js angehängt.
Nützliche Variationen:
# Reiner Server-Stack (Node/Nest/Express). --public weglassen.
npx @zot-core/cli waitlist create --name "Beta list" --write-env .env
# Nicht-interaktive / scriptgesteuerte Verwendung. Gibt nur JSON aus.
npx @zot-core/cli waitlist create --name "Beta list" --api-key $ZOT_API_KEY --json
# Automatische Willkommens-E-Mail deaktivieren.
npx @zot-core/cli waitlist create --name "Quiet list" --no-send-email
Wenn das CLI mit No API key found beendet wird, führe npx @zot-core/cli login aus, um dich zu authentifizieren und ZOT_API_KEY in die Umgebungsvariablendatei zu schreiben, oder hole dir einen Schlüssel von https://app.zot.so/app/api-keys und exportiere ihn in der Shell / füge ihn zu .env.local / .env hinzu, bevor du es erneut versuchst.
Alternative: programmatisch erstellen (nur wenn du einen triftigen Grund hast)
import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: process.env.ZOT_API_KEY! });
const waitlist = await zot.waitlists.create({
name: "Early Access",
sendEmailToNewSignup: true,
});
console.log(waitlist._id); // <- speichere dies als ZOT_WAITLIST_ID
Tue dies nur, wenn der Benutzer explizit den Erstellungsablauf in seiner eigenen App wünscht (z. B. Admin-UI, die neue Wartelisten pro Mandant erstellt). Für ein einmaliges Projektsetup bevorzuge immer das CLI.
Umgebungsvariablen
Wähle das Muster, das zur Laufzeitumgebung passt.
Reiner Server / Node / Backend
# .env
ZOT_API_KEY=zot_xxx
ZOT_WAITLIST_ID=wl_abc123
Next.js mit React-Hook auf dem Client
# .env.local
NEXT_PUBLIC_ZOT_API_KEY=zot_xxx_signup_only
NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_abc123
Verwende den NEXT_PUBLIC_-Schlüssel nur für den anmeldeberechtigten Schlüssel. Behalte den Vollzugriffsschlüssel serverseitig unter ZOT_API_KEY.
Muster A – Server / API-Route
Verwende dies, wenn das Formular an dein Backend sendet (Next.js Server Action, API-Route, Express-Handler, Nest-Controller usw.).
// app/api/waitlist/route.ts (Next.js App Router)
import { NextResponse } from "next/server";
import { ZotSDK, ZotAPIError } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: process.env.ZOT_API_KEY! });
export async function POST(request: Request) {
const { email, name } = await request.json();
try {
const user = await zot.waitlist(process.env.ZOT_WAITLIST_ID!).addUser({
email,
name,
});
return NextResponse.json({ ok: true, user });
} catch (err) {
if (err instanceof ZotAPIError) {
if (err.statusCode === 409) {
return NextResponse.json(
{ ok: false, reason: "already_registered" },
{ status: 409 },
);
}
return NextResponse.json(
{ ok: false, reason: "zot_error", details: err.body },
{ status: err.statusCode },
);
}
return NextResponse.json({ ok: false }, { status: 500 });
}
}
Muster B – Next.js-Clientkomponente mit useAddUser (empfohlen für Landing Pages)
Dies ist die kanonische Methode, wenn das Formular in einer React-Komponente lebt. Der Hook speichert den Status "bereits registriert" im localStorage unter dem Schlüssel waitlistId, sodass wiederkehrende Besucher die Bestätigung ohne Server-Roundtrip sehen.
Im Zot-Dashboard gilt eine Warteliste als "verbunden", nachdem die erste erfolgreiche Anmeldung den Registrierungsendpunkt erreicht hat. Wenn der Benutzer ein Banner mit einer Verbindungsanleitung hat, sollte es nach einem erfolgreichen echten Absenden verschwinden.
"use client";
import { useState } from "react";
import { useAddUser } from "@zot-core/sdk/react";
export function WaitlistForm() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const { addUser, isPending, isUserRegistered, error, reset } = useAddUser({
apiKey: process.env.NEXT_PUBLIC_ZOT_API_KEY!,
waitlistId: process.env.NEXT_PUBLIC_ZOT_WAITLIST_ID!,
onSuccess: () => {
// Optional: Analytics, Toast, Weiterleitung usw.
},
});
if (isUserRegistered) {
return (
<div>
<p>Du bist auf der Liste. Wir werden dir bald eine E-Mail schicken.</p>
<button type="button" onClick={reset}>
Eine andere E-Mail registrieren
</button>
</div>
);
}
return (
<form
onSubmit={(event) => {
event.preventDefault();
addUser({ email, name: name || undefined });
}}
>
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
placeholder="[email protected]"
required
disabled={isPending}
/>
<input
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="Dein Name (optional)"
disabled={isPending}
/>
<button type="submit" disabled={isPending}>
{isPending ? "Werde Mitglied..." : "Der Warteliste beitreten"}
</button>
{error && (
<p role="alert">
{"statusCode" in error && (error as { statusCode: number }).statusCode === 409
? "Diese E-Mail ist bereits auf der Liste."
: error.message}
</p>
)}
</form>
);
}
Vollständige API-Oberfläche (nur was du brauchst)
Serverseitiger Client
import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: "zot_xxx" });
| Aufruf | Zweck |
|---|---|
zot.waitlists.create(params) | Eine Warteliste erstellen. |
zot.waitlists.list() | Wartelisten für den API-Schlüssel auflisten. |
zot.waitlist(id).get() | Eine Warteliste abrufen. |
zot.waitlist(id).update(params) | Einstellungen aktualisieren. |
zot.waitlist(id).delete() | Dauerhaft löschen. |
zot.waitlist(id).stats() | Aggregierte Statistiken. |
zot.waitlist(id).addUser({ email, name?, referredBy?, source?, metadata? }) | Einen Benutzer registrieren. |
zot.waitlist(id).listUsers() | Benutzer auflisten. |
zot.waitlist(id).userCount() | { total, referred }. |
zot.waitlist(id).searchUser(email) | Einen Benutzer finden. |
zot.waitlist(id).updateUserStatus({ email, status }) | Status ist "waiting" | "invited" | "converted" | "churned". |
zot.waitlist(id).bulkDeleteUsers(emails) | Akzeptiert eine einzelne E-Mail oder ein Array. |
zot.waitlist(id).blockedUsers() / blockedUserCount() | Blockierte Benutzer. |
zot.waitlist(id).webhookEvents() | Webhook-Zustellungsverlauf. |
React Hook
import { useAddUser } from "@zot-core/sdk/react";
const { addUser, data, error, isPending, isUserRegistered, isError, reset } = useAddUser({
apiKey: "...",
waitlistId: "wl_...",
onSuccess: (user) => {},
onError: (err) => {},
});
Alles, was der Hook zurückgibt, ist oben aufgeführt. Erstelle keinen parallelen Zustand für diese Werte.
Fehlercodes
| Code | Bedeutung | UX-Hinweis |
|---|---|---|
| 400 | Validierungsfehler | Zeige feldbezogenen Fehler an. |
| 401 | Ungültiger/fehlender API-Schlüssel | Niemals Endbenutzern zeigen, protokollieren. |
| 404 | Warteliste nicht gefunden | Konfigurationsproblem, protokollieren. |
| 409 | E-Mail bereits registriert | Bei den meisten Landing Pages als Erfolg behandeln. |
| 429 | Ratenbegrenzung | Bitte den Benutzer, es in einer Minute erneut zu versuchen. |
| 5xx | Serverproblem | Einmal wiederholen, dann den Benutzer bitten, es später zu versuchen. |
Erkenne mit err instanceof ZotAPIError und lese err.statusCode und err.body aus.
Anti-Patterns – Führe diese NICHT aus
- Rohes
fetch("https://api.zot.so/v1/wait-list/..."). Verwende das SDK. - Ummanteln von
useAddUserinuseMutationvon TanStack Query. Überflüssig. - Hinzufügen eines manuellen
useState<boolean>für den Ladezustand. VerwendeisPending. - Speichern von "bereits registriert" in einem eigenen localStorage-Schlüssel.
useAddUsererledigt das bereits unterzot:waitlist:registered:<waitlistId>. - Verwenden des Vollzugriffs-
ZOT_API_KEYinnerhalb einer"use client"-Komponente. - Aufrufen von
zot.waitlists.createbei jedem Rendern, Build oder App-Start. Verwendenpx @zot-core/cli waitlist createeinmal beim Einrichten und speichere die ID. - Hartcodieren der Wartelisten-ID innerhalb der Komponente. Lies sie aus der Umgebungsvariablen (
ZOT_WAITLIST_IDoderNEXT_PUBLIC_ZOT_WAITLIST_ID). - Stillschweigendes Verschlucken von Fehlern. 409 ist eine Produktentscheidung; andere sollten angezeigt oder protokolliert werden.
Überprüfungs-Checkliste
Bevor du dem Benutzer mitteilst, dass die Integration abgeschlossen ist, bestätige ALLE der folgenden Punkte:
-
@zot-core/sdkbefindet sich inpackage.jsonin der neuesten veröffentlichten Version. - Eine Warteliste existiert und ihre ID ist in den Umgebungsvariablen gespeichert (erstellt über
npx @zot-core/cli waitlist createoder, nur wenn begründet, über ein einmaliges Skript). - API-Schlüssel befindet sich in den Umgebungsvariablen, nicht hartcodiert.
- Wenn der Schlüssel im Client-Code verwendet wird, ist er mit
NEXT_PUBLIC_(oder einem Äquivalent) versehen UND ist ein Schlüssel, der nur für Anmeldungen berechtigt ist. - Die Wartelisten-ID wird aus den Umgebungsvariablen gelesen, nicht hartcodiert, es sei denn, der Benutzer hat explizit nach einer hartcodierten Konstante gefragt.
- Der Submit-Handler fängt Fehler ab und die UI zeigt etwas für den Fehlerfall an.
- Der Erfolgszustand wird behandelt (entweder das
isUserRegistereddes Hooks oder eine eigene Weiterleitung/ein eigener Toast nachonSuccess). - Mindestens eine echte Anmeldung wurde erfolgreich getestet (dadurch wird die Warteliste im Dashboard als verbunden markiert).
- Es gibt kein rohes
fetchzuapi.zot.soirgendwo. - Es gibt keinen doppelten Lade-/Fehler-/Erfolgszustand um
useAddUserherum.
Wenn ein Kästchen nicht angekreuzt ist, behebe es, bevor du die Aufgabe an den Benutzer zurückgibst.