Cycle 6 でプライバシーポリシー・利用規約・特商法表記・オンボーディングウィザードが完成し、ミセバンAIは「合法的に公開できる」状態になった。法務と導線は整った。しかし、ベータユーザーが「実際の業務で使える」かどうかは別の話だ。
Sprint Cycle 7 では、ベータユーザーが日常業務に組み込める機能を揃える。3つの柱: csv_export.rs によるデータ出力、全18エンドポイントの API ドキュメント、そしてカスタム404エラーページ。Data Export × API Reference × Building in Public Day 2 のサイクルだ。
1. CSV Export API -- データを手元に
CSV データ出力モジュール
来客データを CSV ファイルとしてダウンロードするモジュール。plan_guard の csv_export フィーチャーフラグと連携し、Pro / Enterprise プランのみ利用可能。
GET /api/v1/stores/me/export/csv-- CSV出力エンドポイント- Query params:
from,to(NaiveDate, YYYY-MM-DD形式) Content-Disposition: attachmentでファイルダウンロード- CSV columns: 日付, 時間, カメラ名, 来客数
店舗オーナーが最も求める機能のひとつは「データを手元に持ち出す」こと。ダッシュボードで見るだけでなく、Excel で加工したり、他のツールに取り込みたい。CSV出力はそのニーズに直接応える。
エンドポイント設計
CSV Export フロー
実装コード
/// GET /api/v1/stores/me/export/csv
pub async fn export_csv(
State(state): State<AppState>,
AuthUser(user_id): AuthUser,
Query(params): Query<CsvExportParams>,
) -> Result<impl IntoResponse, ApiError> {
let store = get_store_by_owner(&state.pool, &user_id).await?;
let tier = store.tier.as_deref().unwrap_or("free");
// Plan guard: csv_export は Pro/Enterprise のみ
if !tier_has_feature(tier, "csv_export") {
return Err(ApiError::Forbidden(
"CSV出力はProプラン以上で利用できます".into()
));
}
let rows = fetch_visitor_data(
&state.pool,
&store.id,
params.from,
params.to,
).await?;
// CSV生成 (標準ライブラリのみ)
let mut csv = String::from("日付,時間,カメラ名,来客数\n");
for row in &rows {
csv.push_str(&format!(
"{},{},{},{}\n",
row.date, row.time, row.camera_name, row.count
));
}
let filename = format!(
"miseban_{}_{}_{}.csv",
store.name, params.from, params.to
);
Ok((
[
(header::CONTENT_TYPE, "text/csv; charset=utf-8"),
(
header::CONTENT_DISPOSITION,
&format!("attachment; filename=\"{}\"", filename),
),
],
csv,
))
}
plan_guard の tier_has_feature(tier, "csv_export") が最初の関門。Free/Starter ユーザーがこのエンドポイントを叩くと、403でアップグレードを促す。Pro/Enterprise ならCSVデータを生成し、Content-Disposition: attachment ヘッダーでブラウザにファイルダウンロードさせる。
Query パラメータ
#[derive(Deserialize)]
pub struct CsvExportParams {
/// 開始日 (YYYY-MM-DD)
pub from: NaiveDate,
/// 終了日 (YYYY-MM-DD)
pub to: NaiveDate,
}
from と to を必須パラメータにすることで、無制限のデータ取得を防ぐ。日付バリデーションは NaiveDate の Deserialize に任せている。不正な形式なら 400 Bad Request が自動で返る。
2. API Documentation Page -- docs.html
全18エンドポイントの API リファレンス
ベータユーザーや将来の開発パートナーに向けた、静的HTMLの API ドキュメント。認証方式、エンドポイント一覧、curl サンプル、レスポンス例を網羅。
- 認証方式: JWT Bearer token (Supabase Auth)
- カテゴリ: Public / JWT Required / Billing
- Method badges: GET POST PATCH
- curl examples + JSON response examples
APIドキュメントがないプロダクトは信用されない。ベータユーザーに「APIがある」と言っても、仕様書がなければ使いようがない。Cycle 7 では全18エンドポイントを体系的にドキュメント化した。
認証方式
# Supabase Auth でJWTトークンを取得
curl -X POST https://xxx.supabase.co/auth/v1/token?grant_type=password \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"***"}'
# APIリクエスト (Bearer token)
curl https://misebanai.com/api/v1/stores/me \
-H "Authorization: Bearer eyJhbG..."
エンドポイント一覧 (抜粋)
| Method | Path | 認証 | 説明 |
|---|---|---|---|
| GET | /api/v1/health |
Public | ヘルスチェック |
| POST | /api/v1/stores |
JWT | 店舗登録 |
| GET | /api/v1/stores/me |
JWT | 自店舗情報取得 |
| PATCH | /api/v1/stores/me |
JWT | 店舗情報更新 |
| POST | /api/v1/cameras |
JWT | カメラ登録 |
| GET | /api/v1/cameras |
JWT | カメラ一覧取得 |
| POST | /api/v1/frames |
JWT | フレーム送信 (画像解析) |
| GET | /api/v1/analytics/daily |
JWT | 日次分析データ |
| GET | /api/v1/analytics/hourly |
JWT | 時間帯別分析データ |
| GET | /api/v1/stores/me/usage |
JWT | 使用量・プラン情報 |
| GET | /api/v1/stores/me/export/csv |
JWT | CSV出力 (Pro以上) |
| POST | /api/v1/alerts |
JWT | アラート設定 |
| GET | /api/v1/alerts |
JWT | アラート一覧取得 |
| POST | /api/v1/line/webhook |
Public | LINE Webhook受信 |
| POST | /api/v1/billing/checkout |
JWT | Stripe Checkout Session作成 |
| POST | /api/v1/billing/portal |
JWT | Stripe Customer Portal |
| POST | /api/v1/webhooks/stripe |
Stripe | Stripe Webhook受信 |
| GET | /api/v1/onboarding/status |
JWT | オンボーディング進捗 |
レスポンス例
// GET /api/v1/stores/me/usage
{
"tier": "pro",
"cameras_used": 3,
"cameras_max": 16,
"retention_days": 90,
"features": [
["basic_count", true],
["demographics", true],
["heatmaps", true],
["alerts", true],
["line_alerts", true],
["csv_export", true],
["api_access", true],
["custom_models", false]
]
}
// GET /api/v1/stores/me/export/csv
// Content-Type: text/csv; charset=utf-8
// Content-Disposition: attachment; filename="miseban_cafe_2026-02-01_2026-02-23.csv"
日付,時間,カメラ名,来客数
2026-02-01,09:00,入口カメラ,12
2026-02-01,10:00,入口カメラ,28
2026-02-01,11:00,入口カメラ,45
...
全エンドポイントに対して curl コマンド + 期待されるレスポンスJSON を記載。ベータユーザーがドキュメントだけで API 連携を実装できる状態を目指した。
3. カスタム 404 エラーページ
ユーザーフレンドリーな 404 ページ
存在しないURLにアクセスしたユーザーを迷子にさせない。ブラウザデフォルトの404ではなく、ブランドに沿ったカスタムページで適切な導線を提供。
- ミセバンAI のブランドデザインを維持
- トップページ / ダッシュボード / ブログ への導線ボタン
- 「お探しのページは見つかりませんでした」の日本語メッセージ
- レスポンシブ対応
些細に見えるが、404ページの品質はプロダクトの信頼感に直結する。ブラウザのデフォルトエラー画面が表示されると「このサービスは雑に作られている」という印象を与える。カスタム404ページは、迷子になったユーザーを正しい場所へ誘導するナビゲーションの役割を果たす。
4. Before / After 比較
| 項目 | Before (Cycle 6) | After (Cycle 7) |
|---|---|---|
| データ出力 | なし | CSV出力 (Pro以上) |
| API仕様書 | なし | 18エンドポイント文書化 |
| エラーページ | ブラウザデフォルト | カスタム404ページ |
| ベータ準備 | 6/8項目 | 8/8項目完了 |
Cycle 6 まで残っていた「データ出力」と「API仕様書」の2項目が埋まり、クローズドベータに必要な全8項目が完了した。ダッシュボード、API、決済、法務、オンボーディング、データ出力、ドキュメント、エラーハンドリング。プロダクトとして公開するために必要な要素が揃った。
ベータチェックリスト 8/8: 認証、API、決済、プランガード、法務、オンボーディング、CSV出力、APIドキュメント -- 全項目 DONE。
5. 技術的判断
なぜこの設計にしたか
- 外部CSVクレートを使わない理由 -- 出力するCSVは4列のシンプルな構造。
csvクレートは強力だが、今のユースケースにはformat!()+Stringで十分。依存を増やすメリットがない。複雑なエスケープが必要になった時点でクレートに移行する - 静的HTMLドキュメントにした理由 -- Swagger/OpenAPI は将来的に導入するが、現段階ではエンドポイント数が18個。JSON スキーマの定義に時間をかけるより、即座に読める HTML ドキュメントを先に出すほうがベータユーザーにとって価値が高い
- Plan guard による機能ゲーティング -- CSV出力を Pro 以上に限定することで、Free/Starter から Pro へのアップグレード動機を作る。「データを見る」は無料、「データを持ち出す」は有料。この線引きがフリーミアムの要
- Content-Disposition ヘッダー -- ブラウザでCSVを直接表示させず、ファイルとしてダウンロードさせる。ユーザーが「保存先を選ぶ」UIが出ることで、データが手元に来た実感を与える
- NaiveDate による日付バリデーション -- Deserialize トレイトに任せることで、バリデーションコードをゼロに。不正な日付は serde が自動で 400 を返す
6. 成果と数字
作成・更新したファイル
crates/api/src/csv_export.rs-- CSV出力モジュール (新規、約80行)crates/api/src/main.rs--mod csv_export宣言、GET /api/v1/stores/me/export/csvルート追加 (+15行)web/landing/docs.html-- APIドキュメントページ (新規、約350行)web/landing/404.html-- カスタム404ページ (新規、約60行)
「動くプロダクト」と「使えるプロダクト」は違う。データが出力でき、仕様書があり、迷子にならない。この3つが揃って初めて、ベータユーザーに「使ってください」と言えるプロダクトになる。
7. ロードマップ
7サイクルを経て、ミセバンAIはパブリックベータに耐えうる完成度に到達した。Cycle 8 ではいよいよ実店舗にデプロイし、実データでの動作検証とフィードバック収集に入る。ここからが本番だ。
7サイクル、7週間。ダッシュボードから始まって、API、決済、法務、そしてデータ出力とドキュメントまで。Building in Public で走り続けた結果、ベータに出せるプロダクトが出来上がった。次はユーザーの声を聞く番だ。
クローズドベータへの参加をお待ちしています。
ミセバンAIは、7スプリントサイクルで「使えるプロダクト」に到達。
既存の防犯カメラがAI店長に変わる、小規模店舗のための次世代AI分析。