Zotウェイティングリスト - ベストプラクティス

公式の `@zot-core/sdk` と CLI `@zot-core/cli` を使用して、Zot のウェイティングリストを TypeScript、React、Next.js アプリケーションに統合します。このスキルは、「ウェイティングリストを追加」「早期アクセス」「メールキャプチャ付きカミングスーンページ」などのリクエストに対応します。

Zotウェイティングリスト - ベストプラクティス とは?

このスキルは、Zot の公式 SDK と CLI を使用して、開発者が TypeScript、React、または Next.js アプリケーションにウェイティングリストを簡単に追加できるように設計されています。@zot-core/sdk はサーバーサイドとクライアントサイドの両方で動作し、サーバーでは ZotSDK クラス、ブラウザでは useAddUser React フックを提供します。CLI は API キーの認証、ウェイティングリストの作成、環境変数への ID の自動保存をワンコマンドで行います。このスキルは、TanStack Query のラッピングや生の fetch 呼び出しなどのアンチパターンを防止し、一貫性のあるエラーハンドリング、適切な API キーのスコープ設定、そして useAddUser フックによるローカルストレージを利用した「登録済み」状態の永続化を推奨します。これにより、Zot ウェイティングリストの統合が迅速かつ堅牢になります。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/launch-waitlist-zot/zot-skills/tree/HEAD/skills/waitlist

Installed? Explore more 生産性&コラボレーション skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Zotウェイティングリスト - ベストプラクティス は何をしますか?

このスキルでは、Zot ウェイティングリストを正しく統合する方法を説明します。以下のルールに従ってください。代替案を発明しないでください。

TL;DR - 最短の道のり

# 1. SDKをインストール
npm install @zot-core/sdk

# 2. ウェイティングリストを作成し、そのIDを .env.local に保存
npx @zot-core/cli waitlist create --name "Early Access" --write-env .env.local --public

# 3. 「@zot-core/sdk/react」から `useAddUser` を使用してUIを構築 (以下のパターンB参照)

Next.js プロジェクトの統合はこれで全てです。TanStack Query を追加したり、カスタムのPOSTハンドラーを書いたり、生の fetch でAPIを呼び出したりしないでください。必要なものはすべて以下にあります。

絶対的なルール

  1. 公式SDKを使用する。 パッケージ名: @zot-core/sdk。https://api.zot.so を生の fetch、axios などで呼び出さないでください。
  2. コードが実行される場所に基づいて正しいエントリポイントを選択する:
    • サーバー、APIルート、cron、スクリプト、エッジ関数 -> import { ZotSDK } from "@zot-core/sdk"
    • ブラウザで実行されるReactコンポーネント -> import { useAddUser } from "@zot-core/sdk/react"
  3. useAddUser を決してラップしない TanStack Query、SWR、useState、Zustand、またはカスタムリデューサーで。フックはすでに isPending、isUserRegistered、data、error、isError、reset を公開しています。その状態を再利用することがポイントです。
  4. サーバーサイドのAPIキーをクライアントに送信しない。 ブラウザコードには、サインアップのみの権限を持つキーを使用し、NEXT_PUBLIC_ZOT_API_KEY(またはフレームワークのパブリック環境変数相当)として公開します。
  5. 常に ZotAPIError を処理する。 409(既に登録済み)と429(レート制限)に対してユーザーフレンドリーなメッセージを表示します。

インストール

npm install @zot-core/sdk
# または
pnpm add @zot-core/sdk
# または
yarn add @zot-core/sdk

サーバーでの使用に追加のピア依存関係は必要ありません。フックを使用するには、react >= 18 が既にインストールされている必要があります(SDKはそれをオプションのピアとしてマークしています)。

認証情報を取得する

必要なものは2つです:

  1. APIキー - 最も簡単な方法は npx @zot-core/cli login を実行することです。これによりユーザーが認証され、新しい ZOT_API_KEY がローカルの env ファイルに書き込まれます。または、Zotダッシュボード (https://app.zot.so/app/api-keys) から手動で生成することもできます。サーバー用のキーを1つ作成し、ブラウザから呼び出す予定がある場合は、サインアップのみにスコープされた2つ目のキーを作成します。
  2. ウェイティングリストID - wl_abc123 のような形式です。
# 認証して ZOT_API_KEY を .env.local に保存する(--env .env を渡すと .env に保存)
npx @zot-core/cli login

@zot-core/cli でウェイティングリストを作成する(推奨)

ユーザーがまだウェイティングリストを持っていない場合は、公式のZot CLIを使用します。これはユーザーの ZOT_API_KEY を使用してZot APIを呼び出し、結果のIDを直接envファイルに書き込むため、コピーペーストの必要がありません。

パッケージ: @zot-core/cli。@zot-core/agents と混同しないでください - @zot-core/agents はこのエージェントガイドをリポジトリにインストールするだけです。@zot-core/cli はZot APIと通信します。

# ZOT_API_KEY が設定されていることを確認する(シェル、.env.local、または .env 内)
npx @zot-core/cli waitlist create \
  --name "Early Access" \
  --write-env .env.local \
  --public

これが行うこと:

  • 内部的に @zot-core/sdk を介して POST /v1/wait-list を呼び出します。
  • 新しいウェイティングリスト(_id、name、フラグ、タイムスタンプ)を出力します。
  • ZOT_WAITLIST_ID=wl_... を .env.local に追加します。
  • --public が渡されたため、Next.js でクライアントサイドで使用するために NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_... も追加します。

便利なバリエーション:

# 純粋なサーバースタック(Node/Nest/Express)。--public は不要。
npx @zot-core/cli waitlist create --name "Beta list" --write-env .env

# 非対話型 / スクリプトでの使用。JSONのみ出力。
npx @zot-core/cli waitlist create --name "Beta list" --api-key $ZOT_API_KEY --json

# 自動ウェルカムメールを無効にする。
npx @zot-core/cli waitlist create --name "Quiet list" --no-send-email

CLI が No API key found で終了した場合は、npx @zot-core/cli login を実行して認証し、ZOT_API_KEY を env ファイルに書き込むか、https://app.zot.so/app/api-keys からキーを取得してシェルにエクスポートするか、再試行する前に .env.local / .env に追加します。

代替: コードから作成する(明確な理由がある場合のみ)

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); // <- これを ZOT_WAITLIST_ID として保存

これは、ユーザーが自分のアプリ内で作成フローを明示的に希望する場合のみ行ってください(例: テナントごとに新しいウェイティングリストを作成する管理UI)。単発のプロジェクトセットアップでは、常にCLIを優先してください。

環境変数

ランタイムに一致するパターンを選択してください。

純粋なサーバー / Node / バックエンド

# .env
ZOT_API_KEY=zot_xxx
ZOT_WAITLIST_ID=wl_abc123

クライアントでReactフックを使用するNext.js

# .env.local
NEXT_PUBLIC_ZOT_API_KEY=zot_xxx_signup_only
NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_abc123

NEXT_PUBLIC_ キーはサインアップのみにスコープされたキーにのみ使用してください。フルアクセスキーはサーバーサイドで ZOT_API_KEY として保持してください。

パターンA - サーバー / API ルート

フォームがバックエンド(Next.js Server Action、APIルート、Expressハンドラー、Nestコントローラーなど)に送信される場合に使用します。

// 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 });
  }
}

パターンB - useAddUser を使用するNext.jsクライアントコンポーネント(ランディングページに推奨)

これは、フォームがReactコンポーネント内にある場合の標準的な方法です。フックは「既に登録済み」の状態を waitlistId をキーとして localStorage に保持するため、再訪問者はサーバーへのラウンドトリップなしで確認を表示できます。

Zotダッシュボードでは、最初の成功したサインアップが登録エンドポイントに到達した後に、ウェイティングリストは「接続済み」と見なされます。ユーザーに接続ガイドのバナーがある場合、実際の送信が成功した後に消えるはずです。

"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: () => {
      // オプション: 分析、トースト、リダイレクトなど
    },
  });

  if (isUserRegistered) {
    return (
      <div>
        <p>リストに登録されました。間もなくメールをお送りします。</p>
        <button type="button" onClick={reset}>
          別のメールアドレスを登録
        </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="名前(任意)"
        disabled={isPending}
      />
      <button type="submit" disabled={isPending}>
        {isPending ? "参加中..." : "ウェイティングリストに参加"}
      </button>
      {error && (
        <p role="alert">
          {"statusCode" in error && (error as { statusCode: number }).statusCode === 409
            ? "このメールアドレスは既にリストに登録されています。"
            : error.message}
        </p>
      )}
    </form>
  );
}

全APIサーフェス(必要なものだけ)

サーバーサイドクライアント

import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: "zot_xxx" });
呼び出し目的
zot.waitlists.create(params)ウェイティングリストを作成する。
zot.waitlists.list()APIキーのウェイティングリストを一覧表示する。
zot.waitlist(id).get()1つのウェイティングリストを取得する。
zot.waitlist(id).update(params)設定を更新する。
zot.waitlist(id).delete()完全に削除する。
zot.waitlist(id).stats()集計統計を取得する。
zot.waitlist(id).addUser({ email, name?, referredBy?, source?, metadata? })ユーザーを登録する。
zot.waitlist(id).listUsers()ユーザーを一覧表示する。
zot.waitlist(id).userCount(){ total, referred } を返す。
zot.waitlist(id).searchUser(email)ユーザーを検索する。
zot.waitlist(id).updateUserStatus({ email, status })ステータスは `"waiting"
zot.waitlist(id).bulkDeleteUsers(emails)単一のメールアドレスまたは配列を受け入れます。
zot.waitlist(id).blockedUsers() / blockedUserCount()ブロックされたユーザー。
zot.waitlist(id).webhookEvents()Webhook配信履歴。

Reactフック

import { useAddUser } from "@zot-core/sdk/react";

const { addUser, data, error, isPending, isUserRegistered, isError, reset } = useAddUser({
  apiKey: "...",
  waitlistId: "wl_...",
  onSuccess: (user) => {},
  onError: (err) => {},
});

フックが返すものはすべて上記の通りです。これらの値に対して並行状態を作成しないでください。

エラーコード

コード意味UXガイダンス
400バリデーションエラーフィールドレベルのエラーを表示する。
401APIキーが不正/不足エンドユーザーには絶対に表示せず、ログに記録する。
404ウェイティングリストが見つからない設定の問題、ログに記録する。
409メールアドレスが既に登録されているほとんどのランディングページでは成功として扱う。
429レート制限1分後に再試行するようにユーザーに依頼する。
5xxサーバー問題1回再試行し、その後ユーザーに後で再試行するように依頼する。

err instanceof ZotAPIError で検出し、err.statusCode と err.body を読み取ります。

アンチパターン - これらを行わないでください

  • 生の fetch("https://api.zot.so/v1/wait-list/...")。SDKを使用してください。
  • useAddUser をTanStack Queryの useMutation でラップする。冗長です。
  • ローディングのために手動の useState<boolean> を追加する。isPending を使用してください。
  • 「既に登録済み」を独自のlocalStorageキーに保存する。useAddUser はすでに zot:waitlist:registered:<waitlistId> の下で行っています。
  • "use client" コンポーネント内でフルアクセスの ZOT_API_KEY を使用する。
  • レンダリング、ビルド、またはアプリケーション起動のたびに zot.waitlists.create を呼び出す。セットアップ時に npx @zot-core/cli waitlist create を1回使用し、IDを保存してください。
  • ウェイティングリストIDをコンポーネント内にハードコーディングする。env(ZOT_WAITLIST_ID または NEXT_PUBLIC_ZOT_WAITLIST_ID)から読み取ってください。
  • エラーを黙って無視する。409はプロダクトの判断です。それ以外は表面化またはログに記録する必要があります。

検証チェックリスト

統合が完了したことをユーザーに伝える前に、以下のすべてが確認されていることを確認してください:

  • @zot-core/sdk が最新の公開バージョンで package.json に存在する。
  • ウェイティングリストが存在し、そのIDがenvに保存されている(npx @zot-core/cli waitlist create によって作成された、または正当な理由がある場合のみワンタイムスクリプトによって作成された)。
  • APIキーはenv変数にあり、ハードコーディングされていない。
  • キーがクライアントコードで使用される場合、NEXT_PUBLIC_(または同等のもの)がプレフィックスとして付けられており、かつサインアップのみにスコープされたキーである。
  • ウェイティングリストIDはenvから読み取られ、ハードコーディングされていない(ユーザーが明示的にハードコーディングされた定数を要求した場合を除く)。
  • 送信ハンドラーがエラーをキャッチし、UIがエラーケースに対して何かをレンダリングする。
  • 成功状態が処理されている(フックの isUserRegistered または onSuccess 後の独自のリダイレクト/トースト)。
  • 少なくとも1つの実際のサインアップが正常にテストされている(これにより、ダッシュボードでウェイティングリストが接続済みとしてマークされます)。
  • api.zot.so への生の fetch がどこにもない。
  • useAddUser の周りに重複したローディング/エラー/成功状態がない。

いずれかのチェックボックスがオフの場合、タスクをユーザーに返す前に修正してください。

関連スキル