Portal do Desenvolvedor
Experimental A plataforma de bots e apps está em desenvolvimento ativo. O suporte a servidores auto-hospedados chegou em 13/08/2026 com menos recursos que a nuvem. Veja o que é suportado.
← Documentação

Entrar com o GameVox

Deixe as pessoas entrarem no seu site ou app com a conta do GameVox. É o fluxo OAuth2 padrão de código de autorização, no mesmo formato do Discord, e não precisa de bot: seu app pede acesso a partes da conta, a pessoa aprova e o seu servidor recebe um token.

Para que serve

  • Login e vinculação de contas. Descubra quem é a pessoa (um ID de usuário estável do GameVox, nome de usuário, avatar) e, com a permissão dela, o e-mail verificado.
  • Seletores de servidor e painéis. Com guilds, veja em quais servidores a pessoa está e em quais ela pode gerenciar as configurações.
  • Não serve para agir em nome da pessoa. Um token de usuário não pode enviar mensagens, entrar em canais de voz nem alterar nada no GameVox. Qualquer coisa que aja dentro do GameVox precisa de um bot.

Antes de começar

  1. Crie uma aplicação em developers.gamevox.com. O ID do cliente na aba OAuth2 dela é o seu client_id.
  2. Na aba OAuth2, adicione cada URI de redirecionamento que o seu app vai usar (até 10). Elas precisam ser https://, ou http://localhost / http://127.0.0.1 para desenvolvimento, sem #fragment. A redirect_uri que você enviar precisa corresponder exatamente a uma delas, incluindo a query string.
  3. Copie o segredo do cliente. Ele aparece uma única vez, quando o app é criado ou o segredo é redefinido. Mantenha-o no seu servidor: toda requisição de token precisa dele.

Endpoints

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

Mantenha a barra final em /oauth2/authorize/. Os endpoints de token e de usuário também respondem em /api/v10/, então bibliotecas que colocam uma versão no caminho funcionam sem mudanças. As conexões de cargo ficam em api.gamevox.com, não em app-api.

1. Envie a pessoa para o GameVox

Redirecione o navegador (uma navegação normal, não um popup nem um iframe) para a URL de autorização:

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
ParâmetroObrigatórioDescrição
response_type Sim Sempre code.
client_id Sim O ID do cliente da aba OAuth2 do seu app.
scope Sim Separados por espaço (codificados em URL como %20 ou +). Veja Scopes. Não inclua bot: com bot, esta URL vira uma instalação em servidor.
redirect_uri Sim Uma das suas URIs de redirecionamento registradas, exatamente igual.
state Não Um valor impossível de adivinhar, ligado à sessão da pessoa (até 1024 caracteres). Ele volta sem alterações; confira-o para impedir falsificação de requisição entre sites (CSRF).
code_challenge Não Challenge PKCE opcional, de 43 a 128 caracteres.
code_challenge_method Não S256 quando você envia um challenge. plain é recusado.
prompt Não none pula a tela de consentimento quando a pessoa já aprovou o seu app para todos esses scopes. Qualquer scope novo ainda gera a pergunta.

O que a pessoa vê

  • Uma tela de consentimento em gamevox.com com o nome, o ícone e a descrição do seu app, a conta com que a pessoa está conectada, uma lista em linguagem simples do que você poderá ver e o site para onde ela será enviada.
  • Se ela não estiver conectada, o botão “Log in” a leva pelo app web do GameVox e a traz direto de volta para a mesma tela.
  • Os links para os seus Termos de Serviço e Política de Privacidade aparecem abaixo dos botões quando você os define na aba Informações gerais.

2. Trate o retorno

Depois que a pessoa clica em “Authorize”, o GameVox a envia para a sua URI de redirecionamento com um código:

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

Se ela clicar em “Cancel”:

https://yourapp.example.com/auth/gamevox?error=access_denied
  &error_description=The+user+denied+the+request&state=Zx81kq0vT2
  • Rejeite o retorno se o state não for o que você emitiu para este navegador.
  • Um código expira em 10 minutos e funciona uma única vez. Troque-o imediatamente, a partir do seu servidor.

3. Troque o código por tokens

A partir do seu servidor, faça um POST com corpo codificado como formulário (application/x-www-form-urlencoded, não JSON) para o endpoint de token:

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"
}
  • Autentique com HTTP Basic (client_id:client_secret) como acima, ou envie client_id e client_secret como campos do formulário.
  • redirect_uri precisa ser o mesmo valor enviado no passo 1. Adicione code_verifier se você usou PKCE.
  • As respostas trazem Cache-Control: no-store. Nunca coloque o segredo do cliente nem os tokens em código do navegador ou em URLs.

4. Chame a API

Envie o token de acesso como Authorization: Bearer. GET /users/@me precisa do scope 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 é o ID estável da pessoa no GameVox. Use-o como chave dos seus registros de conta; nomes de usuário podem mudar. gamevox_id é o mesmo usuário no formato UUID nativo do GameVox.
  • email e verified só aparecem com o scope email, e só se a pessoa tiver um e-mail cadastrado. O GameVox só armazena endereços verificados, então verified é sempre true quando email está presente.
  • avatar é sempre null (as bibliotecas do Discord montariam com ele um link para a CDN do Discord). Use gamevox_avatar_url, que é omitido quando a pessoa não tem foto.

Os servidores da pessoa

GET /users/@me/guilds precisa do scope 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 é um resumo para escolher servidores, não um cálculo completo: todos os bits (incluindo ADMINISTRATOR) para o dono e os administradores, MANAGE_GUILD (32) mais VIEW_CHANNEL (1024) onde a pessoa pode editar as configurações do servidor, e VIEW_CHANNEL para todos os demais.
  • Até limit servidores por resposta (de 1 a 200, padrão 200), dos mais antigos para os mais novos. Pagine com after ou before definido como o id de um servidor da página anterior, como no Discord.

A autorização atual

GET /oauth2/@me funciona com qualquer scope e informa a qual app o token pertence, o que foi concedido e quando ele expira. user vem incluído com identify.

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

Cargos vinculados

Com role_connections.write você pode registrar qual conta a pessoa tem do seu lado. Repare no host: 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": {}}'

A pessoa vê isso em Settings > Authorized Apps como “Linked as ShadowMage on RaidPlanner”. As chaves de metadata precisam ser as que você definiu nos metadados de conexão de cargo do seu app. Um GET na mesma URL lê os dados de volta.

Renovação

Tokens de acesso duram 7 dias (expires_in é em segundos). Obtenha um novo par com o refresh token:

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...
  • Refresh tokens são rotacionados. A resposta traz um novo refresh token, e o refresh token antigo e o token de acesso dele param de funcionar imediatamente, então salve o novo par antes de usá-lo.
  • Você pode enviar scope para restringir o novo token a menos scopes. Pedir um scope que a pessoa nunca concedeu falha com invalid_scope.
  • Um refresh token sem uso expira depois de 90 dias. Cada pessoa mantém no máximo 25 pares de tokens ativos por app; criar o 26º descarta o mais antigo.

Revogação (logout)

Revogue quando a pessoa sair do seu app. Qualquer um dos tokens de um par revoga o par inteiro. O endpoint sempre responde 200 para um cliente válido, quer o token exista ou não.

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

PKCE

Opcional, e recomendado se a URL de autorização puder ser montada em um lugar onde outra pessoa possa vê-la. O GameVox só aceita S256, e uma requisição de token continua exigindo o seu segredo do cliente: não existem clientes públicos sem segredo.

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

Scopes

ScopePermite ao seu app
identify Ler o ID, o nome de usuário e o avatar da pessoa: GET /users/@me, e user em GET /oauth2/@me.
email Adicionar o e-mail verificado da pessoa a /users/@me.
guilds Ler a lista de servidores da pessoa: GET /users/@me/guilds.
role_connections.write Ler e gravar os dados de cargo vinculado da pessoa para o seu app.

Qualquer outro scope, incluindo connections, guilds.join e applications.commands sem bot, é recusado na tela de consentimento com uma mensagem explicando o motivo.

Erros

Os endpoints de token e de revogação respondem erros no formato OAuth2 padrão, {"error": "...", "error_description": "..."}, então bibliotecas OAuth os leem sem adaptação:

errorHTTPSignificado
invalid_client 401 ID do cliente ou segredo incorreto, app excluído ou conta do dono do app suspensa.
invalid_grant 400 O código ou o refresh token é desconhecido, expirou, já foi usado, foi emitido para outro app, ou o redirect_uri ou o code_verifier não corresponde. Também é retornado depois que a pessoa desautoriza o seu app.
invalid_request 400 Falta um campo obrigatório, ou o corpo não está codificado como formulário.
invalid_scope 400 Uma renovação pediu um scope que a concessão original não inclui.
unsupported_grant_type 400 Só authorization_code e refresh_token são suportados.
slow_down 429 Falhas demais de autenticação de cliente a partir do seu IP (30 por minuto). Aguarde um minuto.

Chamadas à API com um token inválido, expirado ou revogado recebem 401; um token sem o scope necessário recebe 403 (50001 Missing Access). As respostas trazem os cabeçalhos X-RateLimit-* de sempre, contados por pessoa e por app.

Quando o acesso termina

Um token pode parar de funcionar antes de expirar. Isso acontece quando:

  • a pessoa clica em Deauthorize em Settings > Authorized Apps (todos os tokens que você tem dela deixam de valer na hora, e a sua entrada de cargo vinculado para ela é excluída)
  • a conta da pessoa é desativada
  • a sua aplicação é excluída
  • a sua conta de desenvolvedor é suspensa

Trate um 401 ou invalid_grant como “desconectado” e mande a pessoa pelo passo 1 de novo.

Exemplo completo (Node.js + Express)

Login com verificação de state, a troca do código e a leitura do usuário. Node 18+ para o fetch nativo.

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

Diferenças em relação ao Discord

  • Só a concessão por código de autorização. Sem concessão implícita (response_type=token) e sem client_credentials.
  • Sem clientes públicos: toda requisição de token precisa do segredo do cliente, com ou sem PKCE.
  • Instalações em conta (applications.commands sem bot, integration_type=1) ainda não estão disponíveis.
  • Sem os scopes connections, guilds.join, guilds.members.read ou rpc.
  • Em /users/@me/guilds, permissions é um resumo para escolher servidores, não um cálculo completo (veja acima).

← Voltar para a documentação