SETTO API

Autenticación

Cómo funcionan los tokens de API de SETTO — la cabecera bearer, los prefijos live y test, el alcance por organización, expiración, rotación y revocación, y la diferencia entre 401 y 403.

Cada petición a la API externa lleva una sola credencial: un token de API de organización, enviado en la cabecera Authorization como bearer token. Los tokens se emiten desde el panel de organizador de SETTO, pertenecen a una organización y no a una persona, son de solo lectura y se muestran completos exactamente una vez. Esta página cubre el formato de la cabecera, los dos prefijos de token, qué puede y qué no puede ver un token, y cómo rotarlo o revocarlo.

La cabecera

GET /v1/external/tournaments HTTP/1.1
Host: api.setto.io
Authorization: Bearer setto_live_YOUR_TOKEN_HERE

No hay alternativa. La API no acepta un token en la query string, en una cookie ni en una cabecera X-Api-Key — solo Authorization: Bearer. Una cabecera ausente o mal formada es un 401, nunca una lectura anónima silenciosa.

Formato del token

Un token es un prefijo más un secreto. Los primeros 16 caracteres son el prefijo, que SETTO guarda en claro para que el panel pueda mostrarte cuál token es cuál; el resto es el secreto, que SETTO guarda solo como hash.

PrefijoSignificado
setto_live_…Token de producción. Funciona contra la API de producción.
setto_test_…Token no productivo. La API de producción lo rechaza.

Usa tokens setto_test_ cuando apuntes una integración a una API de SETTO de staging o local; usa setto_live_ para cualquier cosa real. Ambos son de solo lectura.

El prefix es lo que ves en todos los lugares donde el token se muestra — en la lista del panel y en el campo token.prefix de GET /v1/external/me. Registra el prefijo cuando necesites distinguir dos tokens. Nunca registres el token completo.

Crear un token

En app.setto.io ve a Organización, selecciona la organización, abre la pestaña API y elige Crear token.

  • Solo el creador de la organización o un miembro con el rol ADMIN puede crear, listar o revocar tokens. Los demás miembros reciben un 403 de los endpoints de gestión; la pestaña se los indica.
  • El nombre es obligatorio. Nombra los tokens según quién los consume (Club website, Airtable sync), no según una persona.
  • La expiración es opcional: nunca, 30, 90 o 365 días. Un token expirado devuelve 401 con EXPIRED_API_TOKEN.
  • Una organización puede tener como máximo 10 tokens activos a la vez. Revoca uno antes de crear el número once.

Copia el secreto de inmediato

El token completo aparece una sola vez, en el diálogo posterior a la creación. SETTO conserva solo un hash, así que nadie — ni tú, ni el soporte de SETTO — puede recuperarlo después. Si se pierde, revoca y vuelve a crear.

Qué puede ver un token

Un token está limitado a una organización. Lee:

  • todos los torneos cuya organization sea esa organización — incluidos los que están en DRAFT y ARCHIVED, y los torneos cuya página pública está apagada (isPublic: false);
  • las categorías, equipos, rondas, grupos, cuadros, partidos, marcadores y clasificaciones que cuelgan de esos torneos — incluidos los marcados con isVisible: false.

No lee:

  • torneos personales — los torneos creados por un usuario fuera de una organización son invisibles para cualquier token, incluso el de su dueño;
  • datos de otras organizaciones, aunque seas miembro de ambas. Un token, una organización; usa un token por organización;
  • datos personales de contacto — correos, teléfonos, redes sociales y similares se eliminan de todas las respuestas;
  • datos de pago o facturación — cuotas de inscripción pagadas, métodos de pago, identificadores de Stripe y notas del organizador no forman parte del contrato.

Como el material oculto y en borrador se devuelve, trata el payload como interno hasta que lo filtres. Si estás renderizando una página pública, filtra tú mismo por status y por isVisible.

Mantén los tokens del lado del servidor

Un token setto_live_ da acceso de lectura a toda tu organización. Su lugar es una variable de entorno del servidor, un gestor de secretos o una función serverless — nunca un bundle del navegador, una app móvil, un repositorio público o una URL.

Si necesitas datos de torneo en el navegador, pon una pequeña ruta de servidor delante de la API:

app/api/standings/route.ts
export async function GET() {
  const res = await fetch(
    'https://api.setto.io/v1/external/rounds/9d3c6a84-7e15-4b02-8f6d-1c4a9e7b53f2',
    {
      // SETTO_API_TOKEN es solo de servidor. Nunca le pongas el prefijo NEXT_PUBLIC_.
      headers: { Authorization: `Bearer ${process.env.SETTO_API_TOKEN}` },
      next: { revalidate: 60 },
    },
  );

  // Revisa el STATUS antes que el cuerpo. Un payload de error no trae
  // `isVisible` ni `pools`, así que proyectarlo respondería 200 con un
  // resultado vacío y escondería un token expirado detrás de "esta ronda no
  // tiene clasificaciones".
  if (res.status === 404) {
    return new Response('Not found', { status: 404 });
  }

  if (!res.ok) {
    // El problema de tu credencial no es el de tu visitante. Registra el
    // detalle y devuelve un fallo genérico — nunca reenvíes el cuerpo ni el
    // status de arriba.
    console.error(`SETTO API ${res.status} al cargar la clasificación`);
    return Response.json(
      { error: 'upstream_unavailable' },
      { status: 502 },
    );
  }

  const round = await res.json();

  // Tu token ve material oculto y en borrador; esta ruta no pide ninguno, así
  // que todo lo que devuelvas aquí es público. Una ronda oculta responde 404
  // igual que una inexistente — nunca confirmes que existe.
  if (round.isVisible === false) {
    return new Response('Not found', { status: 404 });
  }

  // ...y luego publica una proyección explícita en lugar del payload de arriba,
  // para que un campo que la API agregue después no se filtre por defecto.
  return Response.json({
    name: round.name,
    pools: (round.pools ?? []).map((pool) => ({
      name: pool.name,
      standings: (pool.standings ?? []).map((row) => ({
        rank: row.rank,
        team: row.team?.name,
        points: row.awardedPoints,
      })),
    })),
  });
}

Cachea la proyección, nunca la respuesta de arriba — un payload completo cacheado es una filtración esperando a la siguiente persona que lo reutilice.

La misma regla aplica al panel Try it de la referencia: dispara la petición desde tu propio navegador, así que pega un token desechable o que vayas a rotar pronto en lugar del de producción.

Rotación y revocación

Los tokens no se rotan solos. Para rotar:

  1. Crea un segundo token con la misma intención (Website widget (new)).
  2. Despliega el nuevo valor en el consumidor.
  3. Confirma que el tráfico se movió — token.lastUsedAt del token nuevo empieza a avanzar.
  4. Revoca el token viejo desde la pestaña API.

La revocación surte efecto de inmediato: la siguiente petición con ese token es un 401 con INVALID_API_TOKEN. Si un token se filtra, revoca primero y pregunta después — nada de la API tiene estado, así que un token revocado y reemplazado no te cuesta más que un redeploy.

Rota con calendario si puedes, y siempre que alguien con acceso se vaya.

401 contra 403

Significan cosas distintas y piden respuestas distintas.

EstadoCódigosQué pasóQué hacer
401MISSING_API_TOKENNo hay cabecera Authorization, o no era Bearer setto_…Corrige la petición
401INVALID_API_TOKENToken desconocido, revocado o de otro entorno (un setto_test_ en producción)Vuelve a emitir el token
401EXPIRED_API_TOKENEl expiresAt del token ya pasóCrea un token nuevo
403INSUFFICIENT_SCOPEEl token es válido pero no lleva el alcance que la ruta necesitaNo reintentes

En corto: 401 significa no sabemos quién eres — detente y consigue una credencial que funcione. 403 significa sabemos exactamente quién eres y la respuesta sigue siendo no — reintentar con el mismo token nunca va a funcionar.

Un recurso que pertenece a otra organización no es un 403. Es un 404 (TOURNAMENT_NOT_FOUND / ROUND_NOT_FOUND), idéntico a la respuesta para un id que no existe en ningún lado — la API nunca confirma la existencia de datos que no tienes permitido leer. Consulta Errores.

En esta página