開発者ポータル
試験運用中 ボット&アプリ基盤は現在も活発に開発中です。セルフホストサーバーのサポートは 2026-08-13 に提供を開始しましたが、機能範囲はクラウド版より狭くなっています。対応状況を見る。
← ドキュメント

GameVox でログイン

ユーザーが GameVox アカウントで、あなたのウェブサイトやアプリにサインインできるようにします。Discord と同じ形の標準的な OAuth2 認可コードフローで、ボットは不要です。アプリがユーザーのアカウント情報の一部を求め、ユーザーが承認すると、あなたのサーバーがトークンを受け取ります。

用途

  • サインインとアカウント連携。ユーザーが誰か(変わることのない GameVox ユーザー ID、ユーザー名、アバター)が分かり、本人の許可があれば確認済みのメールアドレスも取得できます。
  • サーバー選択画面とダッシュボード。guilds を使うと、ユーザーが参加しているサーバーと、設定を管理できるサーバーが分かります。
  • ユーザーとして操作するためのものではありません。ユーザートークンでは、メッセージの送信、ボイスへの参加、GameVox 上での変更は一切できません。GameVox 内で何かを行うにはボットが必要です。

始める前に

  1. developers.gamevox.com でアプリケーションを作成します。OAuth2 タブの クライアント ID が client_id になります。
  2. OAuth2 タブで、アプリが使うリダイレクト URI をすべて追加します(最大 10 個)。https://(開発用なら http://localhost / http://127.0.0.1 も可)で、#fragment を含まないものに限ります。送信する redirect_uri は、クエリ文字列も含めていずれかと完全に一致している必要があります。
  3. クライアントシークレットをコピーします。表示されるのは、アプリの作成時かシークレットのリセット時の一度だけです。すべてのトークンリクエストで必要になるので、サーバー側で保管してください。

エンドポイント

GET  https://gamevox.com/oauth2/authorize/
POST https://app-api.gamevox.com/api/oauth2/token
POST https://app-api.gamevox.com/api/oauth2/token/revoke
GET  https://app-api.gamevox.com/api/users/@me
GET  https://app-api.gamevox.com/api/users/@me/guilds
GET  https://app-api.gamevox.com/api/oauth2/@me
PUT  https://api.gamevox.com/api/users/@me/applications/{application.id}/role-connection

/oauth2/authorize/ の末尾のスラッシュは省略しないでください。トークンとユーザーのエンドポイントは /api/v10/ 配下でも応答するため、パスにバージョンを含めるライブラリもそのまま動きます。ロール接続は app-api ではなく api.gamevox.com にあります。

1. ユーザーを GameVox へ送る

ブラウザを次の認可 URL へリダイレクトします(ポップアップや iframe ではなく、通常のページ遷移で)。

https://gamevox.com/oauth2/authorize/
  ?response_type=code
  &client_id=123456789012345678
  &scope=identify%20email
  &redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fauth%2Fgamevox
  &state=Zx81kq0vT2
パラメーター必須説明
response_type はい 常に code。
client_id はい アプリの OAuth2 タブにある クライアント ID。
scope はい スペース区切り(URL エンコードでは %20 または +)。スコープを参照してください。bot は含めないでください。bot があると、この URL はサーバーインストールになります。
redirect_uri はい 登録済みのリダイレクト URI のいずれかと完全に一致する値。
state いいえ ユーザーのセッションに紐づく、推測できない値(最大 1024 文字)。そのまま返ってくるので、照合してクロスサイトリクエストフォージェリを防いでください。
code_challenge いいえ 任意の PKCE チャレンジ。43 から 128 文字。
code_challenge_method いいえ チャレンジを送る場合は S256。plain は拒否されます。
prompt いいえ none を指定すると、ユーザーがこれらのスコープすべてについてすでにアプリを承認している場合に同意画面を省略します。新しいスコープがあれば確認を求めます。

ユーザーに表示される内容

  • gamevox.com 上の同意画面に、アプリの名前、アイコン、説明、サインイン中のアカウント、アプリが参照できる情報を分かりやすく示した一覧、そして移動先のサイトが表示されます。
  • サインインしていない場合は、Log in から GameVox ウェブアプリを経由して、同じ画面にそのまま戻ってきます。
  • 基本情報タブで利用規約とプライバシーポリシーのリンクを設定すると、ボタンの下に表示されます。

2. コールバックを処理する

ユーザーが Authorize をクリックすると、GameVox はコードを付けてリダイレクト URI へ送ります。

https://yourapp.example.com/auth/gamevox?code=3vR0b...&state=Zx81kq0vT2

代わりに Cancel をクリックした場合:

https://yourapp.example.com/auth/gamevox?error=access_denied
  &error_description=The+user+denied+the+request&state=Zx81kq0vT2
  • state がこのブラウザに発行したものと一致しなければ、コールバックを拒否してください。
  • コードの有効期限は 10 分で、使えるのは一度だけです。サーバーからすぐに交換してください。

3. コードをトークンと交換する

サーバーから、フォームエンコードした本文(JSON ではなく application/x-www-form-urlencoded)をトークンエンドポイントへ POST します。

curl -X POST https://app-api.gamevox.com/api/oauth2/token \
  -u "123456789012345678:CS.your-client-secret" \
  -d grant_type=authorization_code \
  -d code=3vR0b... \
  -d redirect_uri=https://yourapp.example.com/auth/gamevox
{
  "access_token": "GVO.mJ4c...",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": "GVR.9tQe...",
  "scope": "identify email"
}
  • 認証は上記のとおり HTTP Basic(client_id:client_secret)で行うか、client_id と client_secret をフォームのフィールドとして送ります。
  • redirect_uri はステップ 1 で送ったものと同じ値にしてください。PKCE を使った場合は code_verifier も追加します。
  • レスポンスには Cache-Control: no-store が付きます。クライアントシークレットやトークンを、ブラウザ側のコードや URL に決して含めないでください。

4. API を呼び出す

アクセストークンを Authorization: Bearer として送ります。GET /users/@me には identify スコープが必要です。

curl https://app-api.gamevox.com/api/users/@me \
  -H "Authorization: Bearer GVO.mJ4c..."
{
  "id": "1557709980780474368",
  "gamevox_id": "edf412bc-a7b8-4f2a-a810-8b9eb8c02462",
  "username": "alice",
  "discriminator": "0",
  "global_name": "alice",
  "avatar": null,
  "gamevox_avatar_url": "https://...",
  "email": "alice@example.com",
  "verified": true
}
  • id はユーザーの変わることのない GameVox ID です。アカウントのレコードはこれをキーにしてください。ユーザー名は変わることがあります。gamevox_id は、同じユーザーを GameVox ネイティブの UUID 形式で表したものです。
  • email と verified が含まれるのは、email スコープがあり、かつユーザーがメールアドレスを登録している場合だけです。GameVox は確認済みのアドレスしか保存しないため、email がある場合 verified は常に true です。
  • avatar は常に null です(Discord のライブラリはここから Discord CDN のリンクを組み立ててしまうため)。代わりに gamevox_avatar_url を使ってください。ユーザーに画像がない場合は省略されます。

ユーザーのサーバー

GET /users/@me/guilds には guilds スコープが必要です。

[
  { "id": "1557445672742031360", "name": "Raid Night", "icon": null,
    "owner": true,  "permissions": "2251799813685247", "features": [] },
  { "id": "1557445672742031361", "name": "Guild Hall", "icon": null,
    "owner": false, "permissions": "1056", "features": [] }
]
  • permissions はサーバーを選ぶための要約で、完全な計算結果ではありません。オーナーと管理者にはすべてのビット(ADMINISTRATOR を含む)、サーバー設定を編集できるユーザーには MANAGE_GUILD(32)と VIEW_CHANNEL(1024)、それ以外のユーザーには VIEW_CHANNEL が入ります。
  • 1 回のレスポンスで返るサーバーは最大 limit 件(1 から 200、既定値は 200)で、古い順に並びます。Discord と同様に、after または before に前のページのサーバーの id を指定してページングします。

現在の認可

GET /oauth2/@me はどのスコープでも使え、トークンがどのアプリのものか、何が許可されているか、いつ期限切れになるかが分かります。identify がある場合は user も含まれます。

{
  "application": { "id": "123456789012345678", "name": "RaidPlanner", ... },
  "scopes": ["identify", "email"],
  "expires": "2026-10-15T15:03:20Z",
  "user": { "id": "1557709980780474368", "username": "alice", ... }
}

リンクされたロール

role_connections.write を使うと、ユーザーがあなたのサービス側でどのアカウントを使っているかを記録できます。ホストに注意してください: api.gamevox.com です。

curl -X PUT \
  https://api.gamevox.com/api/users/@me/applications/123456789012345678/role-connection \
  -H "Authorization: Bearer GVO.mJ4c..." \
  -H "Content-Type: application/json" \
  -d '{"platform_name": "RaidPlanner", "platform_username": "ShadowMage", "metadata": {}}'

ユーザーには Settings > Authorized Apps に “Linked as ShadowMage on RaidPlanner” と表示されます。metadata のキーは、アプリのロール接続メタデータで定義したものでなければなりません。同じ URL に GET すると読み戻せます。

トークンの更新

アクセストークンの有効期間は 7 日です(expires_in は秒単位)。リフレッシュトークンで新しいペアを取得します。

curl -X POST https://app-api.gamevox.com/api/oauth2/token \
  -u "123456789012345678:CS.your-client-secret" \
  -d grant_type=refresh_token \
  -d refresh_token=GVR.9tQe...
  • リフレッシュトークンはローテーションされます。レスポンスには新しいリフレッシュトークンが含まれ、古いリフレッシュトークンとそのアクセストークンは直ちに使えなくなります。使う前に新しいペアを保存してください。
  • scope を送ると、新しいトークンのスコープを絞り込めます。ユーザーが許可していないスコープを求めると invalid_scope で失敗します。
  • 使われていないリフレッシュトークンは 90 日で期限切れになります。1 人のユーザーが 1 つのアプリについて持てる有効なトークンペアは最大 25 組で、26 組目を作ると最も古いものが削除されます。

取り消し(ログアウト)

ユーザーがアプリからサインアウトしたら取り消してください。ペアのどちらのトークンを使っても、ペア全体が取り消されます。有効なクライアントであれば、トークンが存在したかどうかにかかわらず、エンドポイントは常に 200 を返します。

curl -X POST https://app-api.gamevox.com/api/oauth2/token/revoke \
  -u "123456789012345678:CS.your-client-secret" \
  -d token=GVO.mJ4c...

PKCE

任意ですが、認可 URL を他人に見られうる場所で組み立てる場合は推奨します。GameVox が対応しているのは S256 だけで、トークンリクエストには引き続きクライアントシークレットが必要です。シークレットなしのパブリッククライアントはありません。

// Before redirecting: keep the verifier with the user's pending login.
const verifier = base64url(crypto.randomBytes(32));
const challenge = base64url(crypto.createHash('sha256').update(verifier).digest());
// authorize URL: ...&code_challenge=CHALLENGE&code_challenge_method=S256
// token request: ...&code_verifier=VERIFIER (client secret still required)

スコープ

スコープアプリができること
identify ユーザーの ID、ユーザー名、アバターを読み取ります: GET /users/@me、および GET /oauth2/@me の user。
email /users/@me にユーザーの確認済みメールアドレスを追加します。
guilds ユーザーのサーバー一覧を読み取ります: GET /users/@me/guilds。
role_connections.write このアプリについて、ユーザーのリンクされたロールの情報を読み書きします。

それ以外のスコープ(connections、guilds.join、bot なしの applications.commands を含む)は、同意画面でその旨のメッセージとともに拒否されます。

エラー

トークンと取り消しのエンドポイントは、標準の OAuth2 形式 {"error": "...", "error_description": "..."} でエラーを返すため、OAuth ライブラリはそのまま読み取れます。

errorHTTP意味
invalid_client 401 クライアント ID またはシークレットが誤っている、アプリが削除されている、またはアプリのオーナーのアカウントが停止されています。
invalid_grant 400 コードまたはリフレッシュトークンが不明、期限切れ、使用済み、別のアプリに発行されたもの、または redirect_uri か code_verifier が一致しません。ユーザーがアプリの認可を解除した後にも返されます。
invalid_request 400 必須フィールドが欠けているか、本文がフォームエンコードされていません。
invalid_scope 400 リフレッシュで、元の許可に含まれないスコープが求められました。
unsupported_grant_type 400 対応しているのは authorization_code と refresh_token だけです。
slow_down 429 あなたの IP からのクライアント認証の失敗が多すぎます(1 分あたり 30 回)。1 分待ってください。

不正、期限切れ、または取り消されたトークンでの API 呼び出しには 401 が返り、必要なスコープがないトークンには 403(50001 Missing Access)が返ります。レスポンスには通常の X-RateLimit-* ヘッダーが付き、ユーザーごと、アプリごとにカウントされます。

アクセスが終わるとき

トークンは期限切れの前に使えなくなることがあります。次のような場合です。

  • ユーザーが Settings > Authorized Apps で Deauthorize をクリックした(そのユーザーについて保持しているトークンはすべて即座に無効になり、そのユーザーのリンクされたロールの記録も削除されます)
  • ユーザーのアカウントが無効化された
  • アプリケーションが削除された
  • 開発者アカウントが停止された

401 や invalid_grant は “サインアウト” として扱い、ユーザーにステップ 1 からやり直してもらってください。

完全な例(Node.js + Express)

state の確認、コードの交換、ユーザー情報の読み取りを含むサインインの例です。組み込みの fetch を使うため Node 18 以降が必要です。

import express from 'express';
import crypto from 'node:crypto';

const CLIENT_ID = process.env.GAMEVOX_CLIENT_ID;
const CLIENT_SECRET = process.env.GAMEVOX_CLIENT_SECRET;
const REDIRECT_URI = 'https://yourapp.example.com/auth/gamevox';

// state must be tied to THIS browser, or someone can hand a victim a callback
// URL carrying the attacker's own code and sign them in as the attacker.
// A short-lived HttpOnly cookie does that without a session store.
const STATE_COOKIE = 'gv_oauth_state';
const readCookie = (req, name) =>
  (req.headers.cookie || '').split(';').map((c) => c.trim().split('='))
    .find(([k]) => k === name)?.[1] || '';

const app = express();

app.get('/login', (req, res) => {
  const state = crypto.randomBytes(16).toString('hex');
  res.cookie(STATE_COOKIE, state, {
    httpOnly: true, secure: true, sameSite: 'lax', maxAge: 10 * 60 * 1000, path: '/auth/gamevox',
  });
  const url = new URL('https://gamevox.com/oauth2/authorize/');
  url.search = new URLSearchParams({
    response_type: 'code',
    client_id: CLIENT_ID,
    scope: 'identify email',
    redirect_uri: REDIRECT_URI,
    state,
  }).toString();
  res.redirect(url.toString());
});

app.get('/auth/gamevox', async (req, res) => {
  const { code, state, error } = req.query;
  const expected = readCookie(req, STATE_COOKIE);
  res.clearCookie(STATE_COOKIE, { path: '/auth/gamevox' });
  if (!expected || typeof state !== 'string' || state !== expected) {
    return res.status(400).send('Login expired or started in another browser');
  }
  if (error) return res.redirect('/?login=cancelled');
  if (typeof code !== 'string') return res.status(400).send('Missing code');

  const tokenRes = await fetch('https://app-api.gamevox.com/api/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: 'Basic ' + Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64'),
    },
    body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI }),
  });
  if (!tokenRes.ok) return res.status(502).send('GameVox login failed');
  const tokens = await tokenRes.json();

  const me = await fetch('https://app-api.gamevox.com/api/users/@me', {
    headers: { Authorization: 'Bearer ' + tokens.access_token },
  }).then((r) => r.json());

  // me.id is the stable GameVox user id: key your account record on it.
  // Store tokens.refresh_token server-side if you need access later.
  res.send('Signed in as ' + me.username);
});

app.listen(3000);

Discord との違い

  • 認可コードグラントのみです。インプリシットグラント(response_type=token)と client_credentials はありません。
  • パブリッククライアントはありません。PKCE の有無にかかわらず、すべてのトークンリクエストにクライアントシークレットが必要です。
  • ユーザーインストール(bot なしの applications.commands、integration_type=1)はまだ利用できません。
  • connections、guilds.join、guilds.members.read、rpc の各スコープはありません。
  • /users/@me/guilds の permissions はサーバーを選ぶための要約で、完全な計算結果ではありません(上記参照)。

← ドキュメントに戻る