Ghosty
API

Autenticación

OAuth2 con PKCE para hablar con la API de Ghosty Studio en nombre de una persona — scopes, tokens, errores y cómo obtener un client_id.

Actualizado 2026-09-16

La API es por persona: tu aplicación actúa en nombre de alguien que le dio permiso. El mecanismo es OAuth2 (authorization_code con PKCE), el mismo que usa la app móvil de Ghosty.

Endpoints

RutaQué hace
GET /oauth2/authorizePantalla de consentimiento. Devuelve un code al redirect_uri.
POST /oauth2/tokenCanjea code o refresh_token por tokens.
POST /oauth2/revokeRevoca un token (RFC 7009). Siempre 200.
GET /oauth2/proveedoresLista los proveedores de login activos (Google, Apple). Público.

Scopes

ScopePermite
profileSaber quién es (id y correo).
agents:readListar agentes, leer conversaciones, suscribirse a eventos.
agents:writeMandar mensajes, cancelar, contestar permisos, programar, compartir, borrar cuenta.
filesSubir y leer archivos de la cuenta, transcribir audio.

Pides los que necesitas separados por espacio. El servidor los acota al techo de tu cliente sin fallar: si pides files y tu cliente no lo tiene, el token sale sin él. Los scopes quedan congelados en el token; un permiso nuevo llega hasta el siguiente login.

Flujo

  1. Genera code_verifier (43–128 caracteres) y code_challenge = BASE64URL(SHA256(code_verifier)). Sólo se acepta S256.
  2. Abre en el navegador:
https://www.ghosty.studio/oauth2/authorize  ?response_type=code  &client_id=TU_CLIENT_ID  &redirect_uri=https://tu-app.com/callback  &scope=profile%20agents:read%20agents:write%20files  &state=ALEATORIO  &code_challenge=…  &code_challenge_method=S256
  1. La persona entra (Google, Apple o correo) y acepta. Vuelve a tu redirect_uri con ?code=…&state=…. El código vale 60 segundos.
  2. Canjéalo:
bash
curl -X POST https://www.ghosty.studio/oauth2/token \  -d grant_type=authorization_code \  -d code=EL_CODE \  -d redirect_uri=https://tu-app.com/callback \  -d client_id=TU_CLIENT_ID \  -d code_verifier=EL_VERIFIER
json
{  "access_token": "…",  "refresh_token": "…",  "token_type": "Bearer",  "expires_in": 3600,  "scope": "profile agents:read agents:write files"}
  1. Usa el token:
bash
curl https://www.ghosty.studio/api/v2/me/agents \  -H "Authorization: Bearer ACCESS_TOKEN"
typescript
const res = await fetch("https://www.ghosty.studio/api/v2/me/agents", {  headers: { Authorization: `Bearer ${accessToken}` },});const { agentes } = await res.json();
python
import requestsr = requests.get("https://www.ghosty.studio/api/v2/me/agents",                 headers={"Authorization": f"Bearer {access_token}"})agentes = r.json()["agentes"]

Refrescar

El access token dura 1 hora; el refresh token, 90 días, y rota en cada uso: guarda el nuevo y descarta el viejo. Reusar un refresh token ya rotado revoca la familia entera (es la señal de un robo).

bash
curl -X POST https://www.ghosty.studio/oauth2/token \  -d grant_type=refresh_token \  -d refresh_token=EL_REFRESH \  -d client_id=TU_CLIENT_ID

Regla práctica: 401 → refresca y reintenta una vez; 403 → falta un scope, no reintentes.

Clientes confidenciales y públicos

  • Público (móvil, SPA): sólo client_id; PKCE obligatorio.
  • Confidencial (backend): además client_secret, por client_secret_post o Authorization: Basic.

redirect_uri se compara exacto: sin comodines ni subrutas.

Errores

CódigoCuándo
400 invalid_grantCódigo caducado, verifier incorrecto, refresh rotado, redirect_uri distinto. No distingue la causa a propósito.
401 invalid_clientclient_id desconocido o secreto incorrecto.
400 unsupported_grant_typeSólo authorization_code y refresh_token.

Obtener un client_id

El alta de clientes es manual mientras la API está en beta: escribe a con el nombre de tu app, los redirect_uri exactos y los scopes que necesitas. Los de la casa (Teams, la app móvil) saltan la pantalla de consentimiento; un tercero siempre la ve.