subrouter について
SubRouter のアカウント認証と、API ゲートウェイを使うクライアント設定をエージェントに案内するスキルです。認証情報の保護と、設定を永続的に変更する前の確認を重視します。
主な機能
- 01デバイス認証による API キー取得
- 02OpenAI・Anthropic 互換クライアントの設定
- 03SubRouter 経由のモデルリクエスト転送
- 04画像・動画タスクの非同期送信とポーリング
- 05認証情報管理と課金時の安全対策
活用シーン
- 01エージェント用 SubRouter アカウントの認証
- 02指定した SDK やアプリの SubRouter 接続
- 03非同期タスクによる画像・動画生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ abingyyds/ subrouter-skills --skill subrouterインストール後、Claude Code で/subrouterを入力して読み込まれたか確認します。
同梱ファイルreferences/provider-models.md
詳細
SubRouter エージェントのオンボーディング
SubRouter は AI API ゲートウェイです。1 つの API キーで、OpenAI および Anthropic 互換のエンドポイント、プロバイダー間のリクエストごとのスマートルーティング、透明性の高いトークン単位の課金を利用できます。このスキルでは、エージェントがユーザーを SubRouter に接続する方法を最初から最後まで説明します。
ベース URL:ユーザーが指定したサイトを使用するか、デフォルトの https://subrouter.ai を使用します。以下の例ではすべて $BASE を使用します。
セキュリティルール(必須)
- API キーを送信してよいのは、ユーザーが選択した SubRouter ホストのみです。他のドメインには絶対に送信しないでください。
- リダイレクトによってキーが別の場所に渡ることがないよう、必ず
--max-redirs 0を指定して呼び出してください。 - ユーザーが求めたクライアント設定以外のログやファイルに、キー全体を絶対に出力しないでください。確認時に表示できるのは、
sk-xxxx…のような先頭部分のみです。 - 認証情報を stdout に出力してはいけません。 認証レスポンスには
device_code、key、access_tokenが完全な形で含まれています。通常のcurlを実行すると、それらがターミナルとエージェントのトランスクリプトに出力されます。レスポンスは必ず0600のファイルに書き込み、そこから個々のフィールドを読み取ってください(手順 1 で方法を説明します)。報告するのはステータス、user_code、確認 URL、sk-の先頭部分のみです。 - 支払いは必ずユーザーがブラウザーで行います。カード番号、暗号資産のシードフレーズ、決済認証情報を扱ってはいけません。
手順 0 — 変更内容について合意する(最初に実施)
キーの取得と、ユーザーのコーディングエージェントの設定変更は別の作業です。キーの取得を求められただけで、後者も求められたと判断してはいけません。手順 1 の前に、理解した内容を伝え、同意を得てください。
- 目的 — ユーザーが求めているのは次のどれですか?
- アカウントのみ:認証してキーを表示し、ディスク上の何も変更しません。
- 今回限りの呼び出し:このセッションで環境変数を使用してキーを使います。設定には何も書き込みませんが、ユーザーが削除を求めない限り、手順 1 の認証情報ファイルは残ります(手順 1 を参照)。
- SDK/アプリを設定:ユーザーが指定したプロジェクトに書き込みます。
- コーディングエージェントのモデルプロバイダーを切り替え:Claude Code/Codex/Cursor の接続先を SubRouter に変更します。
- 対象 — 変更するファイルを絶対パスで正確に示し、変更がセッション中のみか永続的かも明記します。
- 既存の設定 — まず対象を読み、現在の設定内容を伝えます。認証情報やプロバイダーの設定がすでにある場合はその旨を伝え、変更前に確認してください。
要望を満たす範囲で、最も影響の小さい方法を選んでください。判断に迷ったら、アカウントのみ で止めてユーザーに確認してください。
特に Codex の場合: Codex ユーザーの多くは API キーではなく ChatGPT サブスクリプションでサインインしています。ユーザーが Codex を SubRouter に切り替えるよう明示的に求めていない限り、~/.codex/config.toml に書き込んだり、~/.codex/auth.json を上書きしたり、既存の model_provider を置き換えたりしてはいけません。追加する設定をユーザーに提示し、承認を得てください。
既存の設定ファイルを上書きしてはいけません。追記するか、新しいプロファイルを作成し、元に戻す方法をユーザーに伝えてください。
ステップ 1 — デバイス認証(API キーの取得)
新しいフローを開始する前に、保存済みの認証情報がないか確認してください(~/.subrouter/credentials.json、または前回の実行時に保存したファイルや環境変数)。その sk- キーで GET $BASE/v1/models が 200 を返す場合は、それを再利用してステップ 2 に進みます。保存済みのキーがない、または機能しなくなった場合にのみ、再認証してください。
デバイス認証を開始します。channel はエージェントの種類(claude、cursor、codex、…)を示します。同じ channel で再認証すると、新しいトークンを作成せず、アカウントに既に有効化されている Agent 授权令牌 (<channel>) トークンが返されるため、ログインを繰り返してもキーが増え続けることはありません。ただし、キーが渡されるのはこのフローを通じてのみです。取得したら、必ず保存してください。
/api 配下のすべてのエンドポイントは、結果をエンベロープでラップします。 ペイロードは常に success とともに data の下にあります。形式は {"success":true,"message":"","data":{…}} です。一部のエンドポイントは、ほかのフィールドも返します(/api/pricing は vendors、usable_group、group_ratio も返し、/api/marketplace/models は total を返します)。そのため、レスポンス全体をペイロードだと想定せず、明示的に .data を読み取ってください。失敗時は success が false になり、message に理由が格納されます。/v1 配下のエンドポイントは OpenAI/Anthropic の生の形式で、エンベロープではラップされません。
レスポンスが画面に出力されないように、非公開ファイルに書き込みます。
umask 077
AUTH_TMP="$(mktemp -t subrouter-auth)" # 0600 via umask
curl -s --max-redirs 0 -X POST "$BASE/api/device_auth" \
-H 'Content-Type: application/json' \
-d '{"channel":"claude"}' -o "$AUTH_TMP"
.data には次の内容が格納されます。
{
"user_code": "ABCD-2345",
"device_code": "<64-char secret — never print this>",
"verification_url": "https://subrouter.ai/device?code=ABCD-2345",
"expires_in": 600,
"interval": 3
}
ファイルから .data.user_code と .data.verification_url を読み取り、その 2 つだけを表示してください。device_code はマシンが保持する秘密情報です。これを見た人はあなたより先にキーを取得できるため、決して出力してはいけません。
ブラウザーで URL を開き、ログイン(または登録)して、ページに表示されている user_code が同じであることを確認してから、承認するようユーザーに伝えてください。コードの有効期限は 10 分です。
続いて、.data.interval 秒ごとにポーリングします(それより短い間隔でポーリングしてはいけません)。ここでも、非公開ファイルに書き込みます。
curl -s --max-redirs 0 -X POST "$BASE/api/device_auth/poll" \
-H 'Content-Type: application/json' \
-d "{\"device_code\":\"$DEVICE_CODE\"}" -o "$AUTH_TMP"
.data.status に応じて処理を分岐します。
authorization_pending— 待機を続けます。slow_down— ポーリングが速すぎます。間隔を長くして待ちます。approved—.data.key(sk-…)、.data.access_token、.data.user_id、.data.base_urlが設定されます。この情報が渡されるのは 1 回限りです。失った場合は、フロー全体をやり直す必要があります。ファイルから保存し、エコーで出力してはいけません。success:falseと "invalid or expired" — フローの有効期限が切れたか、既に消費されています。ステップ 1 からやり直してください。
認証情報を保存してください。承認済みのレスポンスを一時パスから、次のセッションで見つけられる安定した非公開ファイルに移動します。たとえば、install -m 600 "$AUTH_TMP" ~/.subrouter/credentials.json を使います(mkdir -p -m 700 ~/.subrouter でディレクトリを作成してください)。タスク終了時に削除してはいけません。トークンに有効期限はなく、削除すると次回、ユーザーがブラウザーで再度承認する必要が生じます。ファイルを削除するのは、ユーザーから求められた場合に限り、削除したファイル名を伝えてください。
受け取る値は 3 つあり、それぞれ権限が異なります。
key(sk-…)— リレートークンです。モデル呼び出し、/v1/models、請求情報の照会に使用します。クライアント設定に入力してください。access_token— アカウント管理用の認証情報です。ユーザーのアカウントに対する管理 API へのフルアクセス権があります。クライアント設定には絶対に入力せず、以下の管理呼び出しにのみ使用し、skキー以上に慎重に保護してください。user_id— 数値のアカウント ID です。秘密情報ではありませんが、これがないとすべての管理呼び出しが失敗します(以下を参照)。アクセストークンと一緒に保存してください。
ステップ 2 — クライアントを設定する
同じキーを両方のプロトコルサーフェスで使用できます。
Claude Code(Anthropic プロトコル):
export ANTHROPIC_BASE_URL="$BASE"
export ANTHROPIC_API_KEY="sk-…"
OpenAI SDK / Cursor / Codex(OpenAI プロトコル):
export OPENAI_BASE_URL="$BASE/v1"
export OPENAI_API_KEY="sk-…"
主要なエンドポイント:POST /v1/chat/completions(OpenAI)、POST /v1/messages(Anthropic)、GET /v1/models。
sk キーは、マルチモーダル機能全般でも使用できます(利用可否はモデルによって異なります。/api/pricing を確認してください)。
- 画像と動画:「ステップ 2b」を参照してください。同期エンドポイントではなく、非同期タスク API を使用してください
- 音声:
POST /v1/audio/transcriptions、/v1/audio/speech、/v1/audio/translations - その他:
POST /v1/embeddings、/v1/rerank、/v1/moderations、/v1/responses、GET /v1/realtime(WebSocket)
ステップ 2b — 画像と動画:非同期タスク API を使用する
画像と動画の生成には、数十秒から数分かかります。同期呼び出しでは、その間ずっと HTTP 接続が維持されるため、問題が発生します。ゲートウェイの前段にあるプロキシや CDN がタイムアウトして 504 エラーになり、複数の処理を並行して実行すると接続を使い果たして 502 エラーが発生します。基本的には非同期を使用してください。
| ケース | 使用方法 |
|---|---|
| 動画、すべてのモデル | 非同期 — 常に使用します。動画を同期で呼び出すことは絶対に避けてください。 |
| 画像、一度に複数 | 非同期 |
| 画像、バッチ処理または無人実行 | 非同期 |
| 画像 1 枚、ユーザーが対話しながら待っている | 同期でも問題ありません |
課金ルール(生成を始める前にお読みください)
成功した生成はすべて課金対象です。再試行も含まれます。 以下のルールは必ず守ってください。
- ユーザーのリクエスト 1 件につき、送信は 1 回です。 独自の判断で複数のバリエーション、サイズ、または「改善」版を生成しないでください。ユーザーが画像を 1 枚求めた場合は、1 回だけ送信してください。
- 再試行は新たな課金です。 失敗、タイムアウト、またはユーザーが気に入らなかった出力の後に再送信する前に、「もう 1 回生成すると課金されます」と明確に伝え、許可を得てください。例外は、課金前に送信が失敗した場合(タスク ID のない 4xx 拒否)のみです。
- 課金済みの生成物を破棄しないでください。 完了したすべてのタスクの出力は、次に進む前にディスクへ書き込んでください。これには、最終的にユーザーが気に入らなかった中間の試行も含まれます。後の試行で上書きして有料の画像を失うことは、ユーザーの金銭に実害を与えます。タスク ID ごとに異なるファイル名を使い、試行間で同じパスを再利用しないでください。
- 完了した結果は速やかにダウンロードしてください。 上流の結果 URL は期限切れになります。
statusがcompletedになったら、すぐに取得して保存してください。完了したタスクを、別の作業をする間に未取得のまま放置しないでください。 - 送信前に検証し、送信後にはしないでください。 まず
/api/pricingでsize/アスペクト比、動画の長さ、モデルの機能を確認してください。課金後にパラメーターが拒否されると、費用が無駄になります。 - 最後に報告してください: 送信した生成の回数、モデル、サイズ/動画の長さ、合計費用、保存した各ファイルの絶対パス。
処理の途中で接続が切れても、再送信しないでください。タスクはすでにサーバー側に存在します。GET /api/task/self(以下の「過去のタスクの一覧表示」)で復旧し、結果をダウンロードしてください。これが非同期を優先すべき主な理由です。同期呼び出しでは、すでに料金を支払った画像が接続切断によって失われます。
送信する
画像 — 通常のパスの末尾に /async を追加します。
curl -s --max-redirs 0 -X POST "$BASE/v1/images/generations/async" \
-H "Authorization: Bearer sk-…" -H 'Content-Type: application/json' \
-d '{"model":"<image-model>","prompt":"…","n":1,"size":"1024x1024"}'
POST /v1/images/edits/async も同様に動作します。
Location: /v1/tasks/{task_id} ヘッダーと media.task 本文を含む 202 Accepted を返します。
{"id":"…","object":"media.task","type":"image","action":"generate",
"status":"pending","progress":0,"model":"…","created_at":1756400000,
"result":{"images":[],"videos":[]},"usage":{"quota":1234},"error":null}
/async ファサードは、上流のプロバイダーが同期呼び出ししかサポートしていない場合でも、常にタスクを生成します。ゲートウェイがバックグラウンドで実行します。
動画 — POST /v1/video/generations はすでに非同期です。リクエスト本文はモデル固有のスキーマに従います。レスポンスはプラットフォーム固有のネイティブな送信形式ですが、その中のタスク ID は SubRouter のものなので、同じ方法でポーリングしてください。POST /v1/videos は OpenAI 互換のエイリアスです。
ポーリング
画像と動画のタスクは、どちらも1つのエンドポイントで確認できます。
curl -s --max-redirs 0 "$BASE/v1/tasks/<task_id>" -H "Authorization: Bearer sk-…"
status は pending、processing、completed、failed のいずれかです。ポーリングは2~3秒より短い間隔で行わず、時間のかかる動画ジョブでは間隔を徐々に延ばしてください。completed の場合、画像では result.images[].url / result.images[].b64_json、動画では result.videos[].url を読み取ります。failed の場合は error.message を読み取ります。result には常に両方の配列が含まれます。タスクの種類に一致しない配列は null ではなく空のままです。
プラットフォーム固有のポーリングパス(GET /v1/images/generations/{task_id}、GET /v1/video/generations/{task_id}、GET /v1/videos/{task_id})もありますが、すべてのモデルで形状が同一の /v1/tasks/{task_id} を優先してください。
動画のダウンロード
curl -fL "$BASE/v1/videos/<task_id>/content" -H "Authorization: Bearer sk-…" -o out.mp4
上流の動画 URL は有効期間が短いことや、地域制限があることがよくあります。このエンドポイントは sk キーを使ってゲートウェイ経由でファイルをストリーミングします。result.videos[].url を直接取得する代わりに、このエンドポイントを使用してください。
過去のタスクを一覧表示する
GET /api/task/self(アクセストークンを使用)では、ユーザーのメディアタスクを一覧表示できます。クライアントが紛失したタスク ID を取り戻すのに便利です。
ステップ3 — モデル(および必要に応じてプロバイダー)を選ぶ
料金とプロバイダーマーケットプレイスは公開されており、認証は不要です。
curl -s --max-redirs 0 "$BASE/api/pricing" # models + prices
curl -s --max-redirs 0 "$BASE/api/marketplace/models" # marketplace models with provider offers
curl -s --max-redirs 0 "$BASE/api/marketplace/providers" # provider (商家) list
curl -s --max-redirs 0 "$BASE/api/marketplace/providers/<slug>" # one provider's detail
curl -s --max-redirs 0 "$BASE/api/marketplace/reviews" # provider reviews
ユーザーが示した希望(最安値、特定のベンダー/プロバイダー、機能)に基づいて選んでください。上位の候補を料金とともに提示し、ユーザーに選んでもらってください。推測で勝手に決めないでください。
ステップ4 — チャージする(ユーザーが行います。あなたではありません)
何かを判断する前に、残高を確認してください。 新規登録したアカウントには残高がありませんが、既存のアカウントに再認証するユーザーは、すでにチャージ済みの場合があります。残高がゼロまたは不足していることを確認した場合、あるいは 402 を受け取った場合に限り、$BASE/console/topup へ案内してください。チャージは必ずユーザーのブラウザーで行われます。
curl -s --max-redirs 0 "$BASE/v1/dashboard/billing/subscription" -H "Authorization: Bearer sk-…"
curl -s --max-redirs 0 "$BASE/v1/dashboard/billing/usage" -H "Authorization: Bearer sk-…"
アカウント管理(access_token を使用)
管理用エンドポイントには、ヘッダーが1つではなく2つ必要です。
Authorization: Bearer <access_token>(Bearerプレフィックスは省略可能です)New-Api-User: <user_id>— ステップ1で取得した数値のアカウント ID
両方とも必須です。1つ目だけを送信すると 401 无权进行此操作,未提供 New-Api-User が返され、アクセストークンの所有者と一致しない user_id を指定すると 401 New-Api-User 与登录用户不匹配 が返されます。そのため、推測することはできません。これは、以下に記載するプロバイダー(商家)関連のエンドポイントも含め、すべての /api 管理用エンドポイントに適用されます。
AUTH='Authorization: Bearer <access_token>'
AUTH_USER='New-Api-User: <user_id>'
# Account info & balance
curl -s --max-redirs 0 "$BASE/api/user/self" -H "$AUTH" -H "$AUTH_USER"
# Usage reconciliation ("where did my money go")
curl -s --max-redirs 0 "$BASE/api/log/self?cursor=first&page_size=20" -H "$AUTH" -H "$AUTH_USER" # per-call logs, newest first; if has_more, pass next_cursor as cursor
curl -s --max-redirs 0 "$BASE/api/log/self/stat" -H "$AUTH" -H "$AUTH_USER" # totals (today by default)
# Log list/stat/export default to today 00:00 → now and refuse ranges over 31 days ("时间跨度不能超过 31 天"): split longer questions into ≤31-day windows.
curl -s --max-redirs 0 "$BASE/api/data/self" -H "$AUTH" -H "$AUTH_USER" # per-day quota usage
curl -s --max-redirs 0 "$BASE/api/task/self" -H "$AUTH" -H "$AUTH_USER" # async media tasks
# Redeem a voucher code the user already has (this is NOT payment)
curl -s --max-redirs 0 -X POST "$BASE/api/user/self/topup" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"key":"<redemption-code>"}'
# Referral: the user's invite code and earnings
curl -s --max-redirs 0 "$BASE/api/user/self/aff" -H "$AUTH" -H "$AUTH_USER" # invite code → share $BASE/register?aff=<code>
curl -s --max-redirs 0 "$BASE/api/user/self/aff_dashboard" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/user/self/aff_earnings" -H "$AUTH" -H "$AUTH_USER"
# Move referral earnings into the usable balance (confirm amount with the user first)
# NOTE: quota is in internal units, 500000 = $1 — never pass a dollar number directly
curl -s --max-redirs 0 -X POST "$BASE/api/user/self/aff_transfer" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"quota":<amount-in-quota-units>}'
# List / create API tokens
curl -s --max-redirs 0 "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 -X POST "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"name":"my-project","expired_time":-1,"unlimited_quota":true}'
モデルまたはプロバイダーを限定したキー
トークンでは、特定のモデル、モデルごとの特定のプロバイダー(商家)、および価格上限を設定できます。ユーザーが「只用某商家」または「只允许这个模型」と指定した場合に使用してください。
curl -s --max-redirs 0 -X POST "$BASE/api/token/" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{
"name": "sonnet-via-acme",
"expired_time": -1,
"unlimited_quota": true,
"model_limits_enabled": true,
"model_limits": "claude-sonnet-4-6,claude-haiku-4-5",
"subrouter_model_providers": "{\"claude-sonnet-4-6\":[\"acme\"]}"
}'
model_limits— カンマ区切りのモデル許可リスト(model_limits_enabled: trueとともに設定します)。subrouter_model_providers— モデル名から許可するプロバイダーのスラッグへの対応を示す JSON 文字列です。スラッグはGET /api/marketplace/providersから取得します。一覧にないモデルは通常のルーティングを維持します。subrouter_model_price_limits— モデルごとの入出力価格上限(任意。こちらも JSON 文字列です)。
キーを作成する前に、設定する制限を正確にユーザーへ伝えてください。
プロバイダー(商家)になる、モデルを掲載する
ユーザーのアカウントはプロバイダーへの登録を申請でき、登録後は同じ access_token と New-Api-User ヘッダーを使って、自身のチャネルやモデルを管理できます。
モデルの追加や更新、価格の変更、一括インポート、プライマリ/バックアップチャネルについては、まずプロバイダーモデル管理をお読みください。価格設定戦略、キャッシュ価格の null/ゼロの扱い、変更を適用しない価格プレビュー、遅延価格変更、ページネーション、ルートリストのバージョン管理された置き換えについて説明しています。これらは販売者向けの掲載 API であり、以下の販売代理店 API や公式キー市場の API ではありません。
# Apply to become a provider (platform review may apply)
curl -s --max-redirs 0 -X POST "$BASE/api/provider/register" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{...}'
# After approval, provider management lives under /api/provider/*
curl -s --max-redirs 0 "$BASE/api/provider/self" -H "$AUTH" -H "$AUTH_USER" # provider profile
curl -s --max-redirs 0 "$BASE/api/provider/models?page=1&page_size=50" -H "$AUTH" -H "$AUTH_USER" # data is one page; total is at the envelope root
# POST /api/provider/models creates; PUT /api/provider/models/<id> updates.
# Omit all pricing fields for metadata-only edits. Partial price edits merge
# into the saved/pending quote, but the pricing strategy can derive other prices.
# POST /api/provider/models/price-preview calculates the proposal without saving.
# GET/PUT /api/provider/models/<id>/routes manages ordered primary/backup targets.
# Two optional markdown doc fields ride along on the same model body, and they
# publish very differently: params_doc (the request-parameter table users read
# in the docs page) goes live on save, while integration_guide (the whole
# calling guide) is queued for AI review and stays invisible for about an hour.
# Put a parameter table in params_doc, not in integration_guide. Omitting either
# field leaves it untouched. params_doc clears with ""; integration_guide does
# NOT — "" is refused on a custom endpoint and queues an empty draft for review
# on a standard one, so clear it with use_standard_integration_guide: true.
# Channels: /api/provider/channels (GET/POST/PUT/DELETE)
# Bulk model maintenance (e.g. syncing upstream price changes)
# POST /api/provider/models/batch — create many models at once
# POST /api/provider/models/batch-status — enable/disable in bulk
# POST /api/provider/models/batch-delete — remove in bulk
# Channel debugging — <id> is a channel id from GET /api/provider/channels
curl -s --max-redirs 0 "$BASE/api/provider/channels/<id>/test" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/channels/<id>/fetch_models" -H "$AUTH" -H "$AUTH_USER"
# Earnings (read-only)
curl -s --max-redirs 0 "$BASE/api/provider/earnings/summary" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/earnings" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/provider/payouts" -H "$AUTH" -H "$AUTH_USER" # payout history
出金(POST /api/provider/payout)は資金に関わる操作です。自分で呼び出してはいけません。ユーザーには、ブラウザーでプロバイダーコンソールを開くよう案内してください。
プロバイダー登録の申請や、掲載内容、価格、資金に影響する変更を行う前に、送信する内容を正確に説明し、ユーザーから明示的な確認を得てください。一括操作の場合は、変更内容(モデル名、価格、ステータス)の全一覧を先に提示してください。
販売代理店ステーション(分站站长)の運営
販売代理店(分销商)とは、SubRouter を利用した独自のステーションを運営するユーザーです。ステーション管理は /api/distributor/* 配下にあり、上記のアカウント呼び出しと同じ access_token と New-Api-User の組み合わせを使います。アカウントには、すでに販売代理店のロールが必要です。販売代理店になる手続きと、ステーションの更新や割引の購入は、ブラウザーで $BASE/console/distributor にアクセスして有料で行います。これらのエンドポイント(/api/distributor/register/pay、/subscription/renew、/discount/buy)は決して呼び出さないでください。
必ずメインサイトを呼び出してください。 ここでの $BASE は、メインの SubRouter ホスト(ユーザーが別のメインサイトを指定しない限り https://subrouter.ai)を指し、ステーション独自のドメインではありません。ステーションのドメインでは、403 该接口不能通过分站域名访问 が返され、/api/dist/* 以外の /api パスはすべて拒否されます。ステップ 1 もメインサイトに対して実行してください。
変更を加える前にステーションを読み取り、書き込みを行う前には毎回読み直してください。複数の PUT エンドポイントはオブジェクト全体を置き換えます。
curl -s --max-redirs 0 "$BASE/api/distributor/self" -H "$AUTH" -H "$AUTH_USER" # name, domain, display_mode, enable_all_models, global_markup, registration_mode, …
curl -s --max-redirs 0 "$BASE/api/distributor/dashboard" -H "$AUTH" -H "$AUTH_USER" # traffic, revenue, customer counts
ステーションの掲載モードを確認する
/self の enable_all_models によってモデルの掲載方法が決まり、各モードでは異なるエンドポイントを使います。まず値を確認し、対応するエンドポイント群を使ってください。
enable_all_models | モデルの掲載方法 | エンドポイント |
|---|---|---|
true(全站上架) | オーナーがサブスクライブしているすべてのプロバイダーの全モデルが自動的に掲載されます。オーナーは除外または価格変更のみを行います | /models/global* |
false(手動) | オーナーがモデルを 1 つずつ掲載します | /models、/providers/:id/models |
どちらのモードでも、共通の事前条件があります。モデルを掲載するには、まずオーナー自身のアカウントがメインのマーケットプレイス上のプロバイダーにサブスクライブする必要があります。そうでない場合、API は 该商家不在当前分站订阅范围内 を返します。GET /api/distributor/providers?keyword=&page=1&page_size=50 は、サブスクライブ済みのプロバイダーのみを一覧表示します。追加するには、POST /api/marketplace/subscribe に {"provider_id":<id>} を指定します(ID は GET /api/marketplace/providers で取得します)。解除するには DELETE /api/marketplace/subscribe/<id> を使います。サブスクライブ自体に料金はかかりません。
これらのエンドポイントから返される provider_id は、プロバイダーではなく共有プランを表す仮想 ID の場合があります。そのまま渡し、決して自分で生成しないでください。
全站上架が有効な場合の上架/下架
curl -s --max-redirs 0 "$BASE/api/distributor/models/global" -H "$AUTH" -H "$AUTH_USER"
# items: provider_id, provider_slug, provider_name, model_name, display_name, enabled,
# input_price, output_price, fixed_price, price_currency, has_custom_price, custom_*
# 下架 one model (adds an exclusion); "enabled": true restores it
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"provider_id":12,"model_name":"gpt-5.4","enabled":false}'
# a whole provider at once
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/provider-status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"provider_id":12,"enabled":false}'
# override the price of one model; omit a field to leave it alone
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/global/price" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"provider_id":12,"model_name":"gpt-5.4","custom_input_price":2.5,"custom_output_price":10}'
# alternatives: "custom_fixed_price" (per-call models), "custom_price_multiplier";
# {"provider_id":12,"model_name":"gpt-5.4","clear_custom_pricing":true} returns to provider price + markup
手動モードでの上架/下架
curl -s --max-redirs 0 "$BASE/api/distributor/models" -H "$AUTH" -H "$AUTH_USER" # what is listed now (id, provider_id, model_name, display_name, markup_percent, custom_*_price, enabled, sort_order)
curl -s --max-redirs 0 "$BASE/api/distributor/providers/12/models" -H "$AUTH" -H "$AUTH_USER" # a provider's catalogue, marked with what the station already lists
# 上架 several models from one provider
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/providers/12/models" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"models":[{"model_name":"gpt-5.4","display_name":""},{"model_name":"gpt-5.4-mini","display_name":""}]}'
# 上架 one model with its own markup
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/models" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"provider_id":12,"model_name":"gpt-5.4","display_name":"","markup_percent":20,"enabled":true,"sort_order":0}'
# edit: send the full record from GET /models with your changes. display_name, markup_percent,
# custom_input_price, custom_output_price and sort_order are overwritten by whatever you send;
# "enabled" only changes when present.
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/models/<id>" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{...full record..., "enabled": false}'
# 下架 for good: remove the record. PUT enabled:false hides it but keeps the configuration.
curl -s --max-redirs 0 -X DELETE "$BASE/api/distributor/models/<id>" -H "$AUTH" -H "$AUTH_USER"
価格設定のルール
global_markup(PUT /selfで設定)はパーセンテージです。30は +30% を意味し、個別の価格が設定されていないすべての掲載モデルに適用されます。- モデルごとに、
markup_percent(0は全体の値を使う)または明示的なcustom_input_price/custom_output_priceを設定できます。カスタム価格は、掲載に表示される通貨price_currencyでの 100 万トークンあたりの価格です。数値を設定する前に、必ずこの値を確認してください。 - ユーザーに提示する変更一覧には、パーセンテージだけでなく、マークアップ適用後の実効価格を記載してください。
パッケージ
curl -s --max-redirs 0 "$BASE/api/distributor/packages" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 -X POST "$BASE/api/distributor/packages" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' \
-d '{"name":"入门包","description":"","type":"quota","price":99,"original_price":129,
"quota_amount":50000000,"duration":30,"quota_reset_period":"never","enabled":true,"sort_order":0}'
サーバー側のルール:name は必須で、最大128文字です。type は quota、daily、weekly、monthly のいずれかです。price は CNY で、0 より大きい必要があります。original_price は0以上です(取り消し線で表示する価格)。quota_amount は クォータ単位で 0より大きい値です。500000 = $1 なので、「$100のパッケージ」は 50000000 です。duration の単位は日数で、デフォルトは30です。quota_reset_period は never、daily、weekly、monthly のいずれかです。
PUT /api/distributor/packages/<id> はすべてのフィールドを置き換えます。GET で取得したオブジェクト全体に変更を反映して送信してください。DELETE /api/distributor/packages/<id> で削除します。
販売済みパッケージ:GET /api/distributor/package-subscriptions で一覧を取得します。POST /package-subscriptions/<id>/reset-usage と POST /package-subscriptions/<id>/invalidate は、料金を支払った顧客への提供内容を変更します。顧客名と変更の影響を伝え、同意を得てから実行してください。
ステーション設定
PUT /api/distributor/self は部分更新です。送信したフィールドのみが変更されます。オーナーからよく依頼されるフィールドは、name、domain、logo、favicon、announcement、display_mode(simple または full)、registration_mode(open、invite、closed)、global_markup、enable_all_models、enable_topup、hide_provider_info、currency_display です。GET /self で現在の値を読み取り、各キーを「変更前 → 変更後」の形式で表示してから更新してください。
支払い、メール、OAuth の認証情報フィールド(epay_*、stripe_*、creem_*、smtp_*、*_oauth_client_id、*_oauth_client_secret、theme_password)は絶対に設定しないでください。オーナーがコンソールで入力します。
ルーティングソースとマークアップは同じエンドポイントにあり、ステーションの収益を左右します。変更前後に値を読み取って確認してください。
| フィールド | デフォルト | 影響 |
|---|---|---|
include_official_channels | true | ステーションのキーでプラットフォームの公式チャネルにルーティングできるかどうか |
include_shared_subscriptions | true | ステーションのキーでサブスクリプション共有用のルートにルーティングできるかどうか |
include_provider_self | true | ステーションのキーでプロバイダー自身のリスティングにルーティングできるかどうか |
include_cache_hit_rate | true | 候補の順序付けでキャッシュヒット率を考慮するかどうか |
subrouter_route_preference | プラットフォームのデフォルト | 候補の順序付け。例:price_first、stability_first |
shared_subscription_markup | 0 | サブスクリプション共有ルートに適用されるマークアップ。0~10000(パーセント) |
上記のうち shared_subscription_markup を除くすべての設定は、display_mode が simple の場合にのみ有効です。full ではステーションの値は無視され、各キーにその値が個別に保持されます。そのため、ここでステーションの値を書き込んでも成功を返すだけで、ルーティングは変わりません。
ソースを無効にすると、そのソースだけが提供していたモデルはすべて利用できなくなります。収益性が上がるのではなく、顧客の呼び出しが失敗するようになります。書き込む前に、利用できなくなるモデルを伝えてください。
顧客、キーグループ、サブスクリプション共有、公式チャネル
# Customers (the station's end users)
curl -s --max-redirs 0 "$BASE/api/distributor/customers?page=1&page_size=20&keyword=" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/distributor/customers/<userId>/dashboard" -H "$AUTH" -H "$AUTH_USER"
# status 1 = enabled, 2 = banned — confirm with the owner first
curl -s --max-redirs 0 -X PUT "$BASE/api/distributor/customers/<userId>/status" -H "$AUTH" -H "$AUTH_USER" \
-H 'Content-Type: application/json' -d '{"status":2}'
# Key groups (bundles of listed models sold under one label)
curl -s --max-redirs 0 "$BASE/api/distributor/key-groups" -H "$AUTH" -H "$AUTH_USER"
curl -s --max-redirs 0 "$BASE/api/distributor/key-groups/models" -H "$AUTH" -H "$AUTH_USER" # models a group may include
# POST /key-groups and PUT /key-groups/<id> take the group record: name, vendor_category, description,
# model_limits (comma-separated, only models from /key-groups/models), subrouter_providers (comma-separated slugs),
# price_discount (0.8 = 20% off), is_private, is_recommended, enabled, sort_order.
# Members of a private group: GET/POST /key-groups/<id>/users, DELETE /key-groups/<id>/users/<userId>
# Shared subscriptions (进货 a shared plan, then list it)
curl -s --max-redirs 0 "$BASE/api/distributor/shared-subscriptions" -H "$AUTH" -H "$AUTH_USER"
# PUT /shared-subscriptions/<id> {"purchased":true,"enabled":true,"markup_percent":0,"sort_order":0}
# "purchased": true records the procurement and activates the plan on the owner's account — confirm first;
# "enabled" cannot be true unless "purchased" is.
# PUT /shared-subscriptions/<id>/models {"model_name":"…","enabled":true,"markup_percent":0,"sort_order":0}
# (also custom_input_price / custom_output_price / custom_fixed_price / custom_price_multiplier)
# PUT /shared-subscriptions/<id>/models/bulk-status — enable or disable many at once
# Official channels offered by the platform
curl -s --max-redirs 0 "$BASE/api/distributor/official-channels" -H "$AUTH" -H "$AUTH_USER"
# PUT /official-channels/<id> — GET first to learn the record shape
「ステーションの状況はどうですか」といった質問に使える読み取り専用エンドポイント:GET /logs(cursor=first を指定し、以降は前回の next_cursor を使用。page_size、type、start_timestamp、end_timestamp — 期間は最大31日、デフォルトは今日 —、model_name、username、token_name)、/logs/stat(同じ期間ルール)、/earnings、/earnings/summary、/earnings/payouts、/topups、/redemptions、/invoices、/sub-distributors、/package-fund。
呼び出してはいけないステーションのエンドポイント
これらの操作は資金または認証情報に影響します。その旨を伝え、所有者を $BASE/console/distributor に案内してください。
POST /earnings/withdraw、POST /package-fund/topup、POST /package-fund/withdraw、PUT /package-fund/settingsPOST /redemptions— 償還コードを作成すると残高が発行されますPUT /customers/<userId>/password、PUT /customers/<userId>/commission、PUT /customers/<userId>/commission-application/saas-activation-token(GET、rotate、DELETE)、POST /smtp/test、PUT /invoices/<id>
/api/distributor 配下でこれ以外の書き込みを行う前に、変更内容を正確にすべて列挙してください。モデル名と適用価格、パッケージのフィールド、設定キーの変更前 → 変更後、顧客 ID を含め、明示的な承認を得てください。一括で上架または下架する場合は、件数ではなく、対象となるモデルの一覧をすべて事前に提示してください。
その他の機能(ここでは詳述しません)
このプラットフォームでは、エンドユーザー向けの共有サブスクリプション、公式キーのマーケット、請求書、2FA/パスキー管理、決済の開始も提供しています。これらは認証情報、購入、またはアカウントのセキュリティに関わるため、API 経由で操作しないでください。ユーザーから依頼された場合は、ウェブコンソール($BASE/console)を案内してください。
トラブルシューティング
- 401 Unauthorized — キーがないか誤っている、または
sk-プレフィックスが欠落しています。設定した内容を再確認してください。 - 402 / quota messages — アカウント残高が空です。ユーザーはチャージが必要です(ステップ 4)。
- 429 — レート制限に達しました。待機してから、指数バックオフで再試行してください。
- Model not found — モデル名は
GET /api/pricingと完全に一致する必要があります。名前を推測してはいけません。
破壊的な操作
トークンの削除、アカウント設定の変更、/console 管理 API 配下の操作、/api/distributor 配下のすべての書き込みは、必ず事前にユーザーに確認してください。