¿Qué hace Zot Waitlist - Mejores Prácticas?
Este skill te indica cómo integrar correctamente una lista de espera de Zot. Sigue estas reglas. No inventes alternativas.
TL;DR - el camino más corto
# 1. Instala el SDK
npm install @zot-core/sdk
# 2. Crea la lista de espera y persiste su ID en .env.local
npx @zot-core/cli waitlist create --name "Acceso Anticipado" --write-env .env.local --public
# 3. Construye la UI (ver Patrón B más abajo) usando `useAddUser` de "@zot-core/sdk/react"
Esa es la integración completa para un proyecto Next.js. No agregues TanStack Query, no escribas un manejador POST personalizado, no llames a la API con fetch en bruto. Todo lo que necesitas está a continuación.
Reglas no negociables
- Usa el SDK oficial. Nombre del paquete:
@zot-core/sdk. Nunca llames ahttps://api.zot.soconfetch,axioso similar en bruto. - Elige el punto de entrada correcto según dónde se ejecute el código:
- Servidor, ruta de API, cron, script, función edge ->
import { ZotSDK } from "@zot-core/sdk" - Componente React que se ejecuta en el navegador ->
import { useAddUser } from "@zot-core/sdk/react"
- Servidor, ruta de API, cron, script, función edge ->
- Nunca envuelvas
useAddUseren TanStack Query, SWR,useState, Zustand o un reducer personalizado. El hook ya exponeisPending,isUserRegistered,data,error,isErroryreset. Reutilizar su estado es todo el propósito. - Nunca envíes una clave API del lado del servidor al cliente. Para código del navegador, usa una clave con permisos solo de registro y exponla como
NEXT_PUBLIC_ZOT_API_KEY(o el equivalente de entorno público de tu framework). - Maneja siempre
ZotAPIError. Muestra un texto amigable para el usuario en caso de 409 (ya registrado) y 429 (límite de velocidad).
Instalación
npm install @zot-core/sdk
# o
pnpm add @zot-core/sdk
# o
yarn add @zot-core/sdk
No se requieren dependencias adicionales para uso del servidor. Para el hook, react >= 18 debe estar ya instalado (el SDK lo marca como dependencia opcional).
Obtén tus credenciales
Necesitas dos cosas:
- Clave API - el camino más rápido es
npx @zot-core/cli login, que autentica al usuario y escribe una nuevaZOT_API_KEYen el archivo de entorno local. Alternativamente, genera una manualmente desde el panel de control de Zot en https://app.zot.so/app/api-keys. Crea una clave para uso del servidor y, si planeas llamar desde el navegador, una segunda clave con permisos solo de registro. - ID de lista de espera - tiene el formato
wl_abc123.
# Autenticar y persistir ZOT_API_KEY en .env.local (o .env si pasas --env .env)
npx @zot-core/cli login
Crear la lista de espera con @zot-core/cli (recomendado)
Si el usuario no tiene ya una lista de espera, usa el CLI oficial de Zot. Llama a la API de Zot con la ZOT_API_KEY del usuario y escribe el ID resultante directamente en el archivo de entorno para que nunca tengas que copiarlo y pegarlo.
Paquete:
@zot-core/cli. No lo confundas con@zot-core/agents-@zot-core/agentssolo instala esta guía de agente en un repositorio;@zot-core/clise comunica con la API de Zot.
# Asegúrate de que ZOT_API_KEY esté definida (en el shell, en .env.local o en .env)
npx @zot-core/cli waitlist create \
--name "Acceso Anticipado" \
--write-env .env.local \
--public
Lo que esto hace:
- Llama a
POST /v1/wait-listmediante@zot-core/sdkinternamente. - Imprime la nueva lista de espera (
_id,name, flags, marcas de tiempo). - Agrega
ZOT_WAITLIST_ID=wl_...a.env.local. - Debido a que se pasó
--public, también agregaNEXT_PUBLIC_ZOT_WAITLIST_ID=wl_...para uso del lado del cliente en Next.js.
Variaciones útiles:
# Pila de servidor puro (Node/Nest/Express). Omite --public.
npx @zot-core/cli waitlist create --name "Lista Beta" --write-env .env
# Uso no interactivo / mediante script. Imprime solo JSON.
npx @zot-core/cli waitlist create --name "Lista Beta" --api-key $ZOT_API_KEY --json
# Deshabilitar el correo de bienvenida automático.
npx @zot-core/cli waitlist create --name "Lista Silenciosa" --no-send-email
Si el CLI sale con No API key found, ejecuta npx @zot-core/cli login para autenticar y escribir ZOT_API_KEY en el archivo de entorno, o obtén una de https://app.zot.so/app/api-keys y expórtala en el shell / agrégala a .env.local / .env antes de reintentar.
Alternativa: crearla desde código (solo si tienes una razón clara)
import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: process.env.ZOT_API_KEY! });
const waitlist = await zot.waitlists.create({
name: "Acceso Anticipado",
sendEmailToNewSignup: true,
});
console.log(waitlist._id); // <- guarda esto como ZOT_WAITLIST_ID
Solo haz esto si el usuario quiere explícitamente un flujo de creación dentro de su propia aplicación (por ejemplo, interfaz de administración que crea nuevas listas de espera por inquilino). Para una configuración única de proyecto, siempre prefiere el CLI.
Variables de entorno
Elige el patrón que coincida con el entorno de ejecución.
Servidor puro / Node / backend
# .env
ZOT_API_KEY=zot_xxx
ZOT_WAITLIST_ID=wl_abc123
Next.js con el hook React en el cliente
# .env.local
NEXT_PUBLIC_ZOT_API_KEY=zot_xxx_signup_only
NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_abc123
Usa la clave NEXT_PUBLIC_ solo para la clave con ámbito de registro. Mantén la clave de acceso completo del lado del servidor bajo ZOT_API_KEY.
Patrón A - Servidor / ruta de API
Úsalo cuando el formulario se envíe a tu backend (Server Action de Next.js, ruta de API, manejador de Express, controlador de Nest, etc.).
// app/api/waitlist/route.ts (App Router de Next.js)
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 });
}
}
Patrón B - Componente cliente de Next.js con useAddUser (recomendado para páginas de aterrizaje)
Esta es la forma canónica cuando el formulario vive en un componente React. El hook persiste el estado "ya registrado" en localStorage claveado por waitlistId, por lo que los visitantes recurrentes ven la confirmación sin un viaje de ida y vuelta al servidor.
En el panel de control de Zot, una lista de espera se considera "conectada" después de que el primer registro exitoso llega al endpoint de registro. Si el usuario tiene un banner de guía de conexión, debería desaparecer después de un envío exitoso real.
"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: () => {
// Opcional: analytics, toast, redirección, etc.
},
});
if (isUserRegistered) {
return (
<div>
<p>Estás en la lista. Te enviaremos un correo electrónico pronto.</p>
<button type="button" onClick={reset}>
Registrar otro correo electrónico
</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="Tu nombre (opcional)"
disabled={isPending}
/>
<button type="submit" disabled={isPending}>
{isPending ? "Uniéndose..." : "Únete a la lista de espera"}
</button>
{error && (
<p role="alert">
{"statusCode" in error && (error as { statusCode: number }).statusCode === 409
? "Este correo electrónico ya está en la lista."
: error.message}
</p>
)}
</form>
);
}
Superficie completa de la API (solo lo que necesitas)
Cliente del lado del servidor
import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: "zot_xxx" });
| Llamada | Propósito |
|---|---|
zot.waitlists.create(params) | Crear una lista de espera. |
zot.waitlists.list() | Listar listas de espera para la clave API. |
zot.waitlist(id).get() | Obtener una lista de espera. |
zot.waitlist(id).update(params) | Actualizar configuraciones. |
zot.waitlist(id).delete() | Eliminar permanentemente. |
zot.waitlist(id).stats() | Estadísticas agregadas. |
zot.waitlist(id).addUser({ email, name?, referredBy?, source?, metadata? }) | Registrar un usuario. |
zot.waitlist(id).listUsers() | Listar usuarios. |
zot.waitlist(id).userCount() | { total, referred }. |
zot.waitlist(id).searchUser(email) | Encontrar un usuario. |
zot.waitlist(id).updateUserStatus({ email, status }) | El estado es `"waiting" |
zot.waitlist(id).bulkDeleteUsers(emails) | Acepta un solo correo electrónico o un array. |
zot.waitlist(id).blockedUsers() / blockedUserCount() | Usuarios bloqueados. |
zot.waitlist(id).webhookEvents() | Historial de entrega de webhooks. |
Hook React
import { useAddUser } from "@zot-core/sdk/react";
const { addUser, data, error, isPending, isUserRegistered, isError, reset } = useAddUser({
apiKey: "...",
waitlistId: "wl_...",
onSuccess: (user) => {},
onError: (err) => {},
});
Todo lo que devuelve el hook está listado arriba. No crees estado paralelo para estos valores.
Códigos de error
| Código | Significado | Guía de UX |
|---|---|---|
| 400 | Error de validación | Muestra error a nivel de campo. |
| 401 | Clave API incorrecta/faltante | Nunca se muestra a los usuarios finales, regístralo. |
| 404 | Lista de espera no encontrada | Problema de configuración, regístralo. |
| 409 | Correo electrónico ya registrado | Trátalo como éxito en la mayoría de las páginas de aterrizaje. |
| 429 | Límite de velocidad superado | Pide al usuario que intente de nuevo en un minuto. |
| 5xx | Problema del servidor | Reintenta una vez, luego pide al usuario que intente más tarde. |
Detéctalo con err instanceof ZotAPIError y lee err.statusCode y err.body.
Anti-patrones - NO hagas esto
fetchen bruto ("https://api.zot.so/v1/wait-list/..."). Usa el SDK.- Envolver
useAddUserenuseMutationde TanStack Query. Redundante. - Agregar un
useState<boolean>manual para carga. UsaisPending. - Almacenar "ya registrado" en tu propia clave de localStorage.
useAddUserya lo hace bajozot:waitlist:registered:<waitlistId>. - Usar la clave de acceso completo
ZOT_API_KEYdentro de un componente"use client". - Llamar a
zot.waitlists.createen cada renderizado, compilación o inicio de la aplicación. Usanpx @zot-core/cli waitlist createuna vez en la configuración y almacena el ID. - Codificar el ID de la lista de espera dentro del componente. Léelelo de env (
ZOT_WAITLIST_IDoNEXT_PUBLIC_ZOT_WAITLIST_ID). - Tragar errores silenciosamente. El 409 es una decisión de producto; los demás deben mostrarse o registrarse.
Lista de verificación
Antes de decirle al usuario que la integración está completa, confirma TODO lo siguiente:
-
@zot-core/sdkestá enpackage.jsonen la última versión publicada. - Existe una lista de espera y su ID está almacenado en env (creado mediante
npx @zot-core/cli waitlist createo, solo si está justificado, mediante un script de una sola vez). - La clave API está en las variables de entorno, no codificada.
- Si la clave se usa en código del cliente, está prefijada con
NEXT_PUBLIC_(o equivalente) Y es una clave con ámbito de registro. - El ID de la lista de espera se lee de env, no está codificado, a menos que el usuario haya solicitado explícitamente una constante codificada.
- El manejador de envío captura errores y la interfaz de usuario muestra algo para el caso de error.
- El estado de éxito se maneja (ya sea el
isUserRegistereddel hook o tu propia redirección/toast después deonSuccess). - Al menos un registro real se probó con éxito (esto es lo que marca la lista de espera como conectada en el panel de control).
- No hay
fetchen bruto aapi.zot.soen ninguna parte. - No hay estado duplicado de carga/error/éxito alrededor de
useAddUser.
Si alguna casilla está sin marcar, arréglala antes de devolver la tarea al usuario.