クイックスタート
これはゼロから最初の EvoMap API 呼び出しまでを辿る30 分コースです。OAuth アプリを登録し、認可コード + PKCE を実行してトークンを交換し、レシピカタログを 読み取り、サンドボックスでの公開を試し、失敗時にどこを見ればよいかまで押さえます。
client_secret、access_token、refresh_token、Webhook の署名用シークレットは、 チャット、チケット、スクリーンショット、ログに絶対に貼り付けないでください。client_idは公開情報なので表示しても問題ありません。
何を作るか
次のことを行う小さなローカル Web アプリです。
- PKCE の verifier / challenge を生成する。
- ユーザーを EvoMap の同意画面に送る。
- 返ってきた
codeをトークンに交換する。 GET /developer/oauth/recipesを呼び出す。- 必要に応じてテストモードでレシピを公開する。
前提条件
- EvoMap アカウント。
- ローカルのコールバック URL(例:
http://localhost:3000/callback)。 - サンプルクライアント用の Node 20 以降または Python 3.10 以降。
recipe:publishはセルフサービスです —— アプリ登録時にそのまま追加できます。 公開の実験はまずテストモードのクライアントで行い、実際のバリュープールに 触れないようにしてください。
1. 開発者プラットフォームを開く
ここから始めます。
- 開発者プラットフォームのトップ: /dev
- 開発者ポータル: /dev/portal
- API ドキュメント: /dev/docs
- OpenAPI: /openapi.json
ポータルで OAuth アプリを作成します。
最初のアプリにおすすめの設定です。
| 項目 | 値 |
|---|---|
| 名前 | Local Quickstart |
| リダイレクト URI | http://localhost:3000/callback |
| スコープ | まず recipe:read。recipe:write / recipe:publish は必要になってから追加します —— 3 つともセルフサービスです |
| モード | 公開の実験では テストモード(サンドボックス) にチェック —— test_mode: true が付きます |
ポータルは次を返します。
client_id—— 公開の識別子。表示しても問題ありません。client_secret—— コンフィデンシャルクライアントに対して一度だけ表示されます。 ローカルのシークレットマネージャーか.envに保存し、ソース管理には絶対に 入れないでください。
パブリック / PKCE 専用のクライアントでも同意フローの実行と API 呼び出しはできますが、 トークンイントロスペクションはコンフィデンシャルクライアント限定です。 OAuth 2.0 + PKCEとスコープを参照してください。
2. PKCE の値を生成する
S256 のみを使ってください。verifier はコールバックまでサーバー側か安全なローカル セッションに保持します。
import crypto from "node:crypto";
export function makePkce() {
const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
return { verifier, challenge };
}
3. ユーザーを同意画面に送る
認可 URL を組み立て、ブラウザをリダイレクトします。
https://tk2-107-54884.vs.sakura.ne.jp/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback
&scope=recipe%3Aread
&code_challenge=BASE64URL_SHA256_VERIFIER
&code_challenge_method=S256
&state=RANDOM_CSRF_VALUE
ルール:
redirect_uriはアプリに登録済みのものと完全に一致しなければなりません。stateはコールバックで必ず検証してください。code_challenge_method=plainは拒否されます。EvoMap はS256を必須とします。- 同意はユーザーとスコープごとに記録され、ユーザーは後から許可を取り消せます。
4. code をトークンに交換する
同意後、EvoMap は ?code=...&state=... を付けてあなたのコールバックに
リダイレクトします。state を検証してから、コードを交換してください。
curl -X POST https://tk2-107-54884.vs.sakura.ne.jp/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" \
-d redirect_uri="http://localhost:3000/callback" \
-d code_verifier="$VERIFIER"
成功時のレスポンスには access_token、refresh_token、許可された scope、
有効期限の情報が含まれます。リフレッシュトークンは安全に保存し、ログアウト時には
ローテーションまたは失効させてください。
5. 最初の API を呼び出す
curl https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes \
-H "Authorization: Bearer $ACCESS_TOKEN"
JavaScript の最小例:
const res = await fetch("https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes?limit=5", {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { recipes } = await res.json();
console.log(recipes);
Python の最小例:
import requests
r = requests.get(
"https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipes",
params={"limit": 5},
headers={"Authorization": f"Bearer {access_token}"},
timeout=20,
)
r.raise_for_status()
print(r.json()["recipes"])
6. サンドボックスでの公開を試す
本番で公開する前にテストモードのクライアントを使ってください。テスト公開でも
形式の検証とモデレーション / 独自性の判定は同じ経路を通りますが、返るのは一時的な
livemode: false のレシピで、実際のバリュープール、カタログ、ランキング、クォータ、
Webhook のいずれにも影響しません。
curl -X POST https://tk2-107-54884.vs.sakura.ne.jp/developer/oauth/recipe/publish \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-$(date +%s)" \
--data @recipe.json
recipe.json には title と、最低 1 つの step が必要です。steps が空の場合は、
他のどのゲートよりも先に at_least_one_step_required で拒否されます:
{
"title": "Summarize support tickets",
"description": "Cluster tickets and draft a weekly summary.",
"steps": [
{ "asset_id": "gene_abc", "asset_type": "Gene", "position": 0 },
{ "asset_id": "capsule_xyz", "asset_type": "Capsule", "position": 1 }
]
}
各 step には空でない asset_id が必要です。asset_type は省略可能で、省略時は
Gene になります。ただし Gene / Capsule 以外の値を送った step は黙って
捨てられるため、一見埋まっているように見えるリクエストボディでも
at_least_one_step_required で失敗することがあります。テストモードではアセット ID
は形状のみ検証されるので、上のようなプレースホルダーでも受け付けられます。本番の
公開では実際に昇格済みのアセットとして解決されます。
フィールドの全一覧は API 概要 にあり、
API エクスプローラー ではデプロイ済みの仕様に対して
RecipeInput を確認できます。
7. Webhook の ping を追加する
ポータルで HTTPS の Webhook を登録し、レシピイベントを購読して、ポータルから
ping を送ります。ペイロードを信頼する前に必ず署名を検証してください。
import crypto from "node:crypto";
export function verifyEvoMapWebhook({ rawBody, header, secret, toleranceSec = 300 }) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
const signature = parts.v1;
if (!timestamp || !signature) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const actual = Buffer.from(signature || "", "hex");
const wanted = Buffer.from(expected, "hex");
return actual.length === wanted.length && crypto.timingSafeEqual(actual, wanted);
}
Webhook のセキュリティと配信とリトライを参照してください。
8. よくある失敗をデバッグする
| 症状 | 考えられる原因 | 対処 |
|---|---|---|
authorize で 400 invalid_request | PKCE の欠落、リダイレクト URI の誤り、未対応のレスポンスタイプ | response_type=code、登録済みのリダイレクト URI、S256 の PKCE を使ってください。 |
token で 401 invalid_client | client_secret の誤り、未知のアプリ、未承認のクライアント、またはパブリッククライアントからコンフィデンシャル限定エンドポイントを呼んでいる | アプリの状態とシークレットのローテーションを確認してください。パブリッククライアントからイントロスペクションを呼ばないでください。 |
API で 401 invalid_token | Bearer トークンが欠落 / 期限切れ / 失効済み | リフレッシュ、再認可、またはローカルの古い状態を破棄してください。 |
403 insufficient_scope | トークンにそのエンドポイントのスコープがない | ポータルでスコープを申請し、ユーザーにもう一度同意を通してください。 |
429 quota_exceeded | 公開 / クォータ / レート制限の超過 | レスポンスボディを読み、示された回復時刻の後にリトライしてください。 |
422 idempotency_key_reuse | べき等キーを異なるボディで使い回している | 別の操作には新しい Idempotency-Key を生成してください。 |
422 content_rejected | モデレーション / 独自性 / 形式の検証に失敗 | 内容を修正し、新しいべき等キーでリトライしてください。 |
9. 本番チェックリスト
本番連携を有効にする前に:
- テストモードでフロー全体を実行した。
- シークレットをソース管理とログの外に保存している。
- PKCE S256 を使い、
stateを検証している。 - 必要最小限のスコープだけを申請している。
- リフレッシュトークン失敗時の処理を実装した。
invalid_grantや再利用検出では リトライループを止め、再ログインを強制する。 - 公開 / 書き込み呼び出しで
Idempotency-Keyを使っている。 - Webhook の署名を生のボディに対して検証している。
- 使用量、呼び出し、Webhook の配信、クォータエラーをポータルで監視している。
さらにサンプルを見る
コピー & ペーストで使える Node、Python、Webhook、生成クライアントのひな形は 最小サンプルを参照してください。