API HTTP

Endpoints, autenticación y códigos de error.

La API del panel es REST sobre JSON. Base: https://api.growthlab.ecomlabs.dev.

Autenticación

El panel usa una cookie de sesión (gl_session, HttpOnly, Secure, SameSite=Lax) más un token CSRF en la cabecera X-GrowthLab-CSRF para toda petición que muta estado.

El token CSRF está firmado y ligado al id de sesión, así que un atacante que pudiera escribir cookies en un subdominio del dominio padre no podría producir uno válido para la sesión de la víctima.

/v1/auth/me devuelve un token fresco y refresca la cookie en la misma respuesta. Devolver un token sin actualizar la cookie rompería la siguiente mutación.

Códigos de error

Toda respuesta de error tiene la misma forma:

{
  "error": {
    "code": "not_found",
    "message": "Resource not found",
    "requestId": "0d0a2b1e-..."
  }
}
Código HTTP Significado
unauthenticated 401 Sin sesión válida
csrf_failed 403 Token CSRF ausente o inválido
forbidden 403 El rol no tiene el permiso
not_found 404 No existe o pertenece a otra organización
validation_failed 400 Payload inválido; details lleva rutas, nunca valores
weak_password 400 Contraseña rechazada por la política
email_taken 409 Ya existe una cuenta
key_taken 409 Clave de experimento duplicada
invalid_transition 409 Cambio de estado no permitido
plan_limit_reached 402 Cuota del plan agotada
account_locked 423 Bloqueo temporal por intentos fallidos
too_many_attempts 429 Rate limit
internal_error 500 Error no controlado; usa requestId para soporte

404 y no 403 entre organizaciones

Un recurso de otra organización devuelve 404, no 403. Un 403 confirmaría que el recurso existe, lo que filtra la existencia de datos de otro cliente a cualquiera que adivine ids.

Los errores nunca llevan stack trace

Un 500 devuelve un requestId y nada más. El detalle queda solo en el log del servidor. Un stack trace expone rutas de archivos, versiones de dependencias y a veces fragmentos de consulta.

validation_failed no devuelve los valores

details lleva la ruta del campo y el mensaje, nunca el valor enviado. Ese payload puede contener la contraseña que el usuario acaba de intentar registrar.

Endpoints

Autenticación

Método Ruta Descripción
POST /v1/auth/register Crea usuario, organización y proyecto
POST /v1/auth/login Inicia sesión
POST /v1/auth/logout Revoca la sesión en el servidor
GET /v1/auth/me Usuario, organizaciones, features y token CSRF
POST /v1/auth/forgot-password Solicita restablecimiento
POST /v1/auth/reset-password Aplica restablecimiento

register no crea un sitio: un sitio sin dominio registrado sería rechazado por el colector en cada evento y consumiría la única plaza del plan gratuito. La respuesta incluye nextStep: "create_site".

login responde igual ante contraseña incorrecta y ante correo inexistente, y ejecuta una verificación Argon2 completa en ambos casos para que el tiempo de respuesta no revele qué correos están registrados.

Sitios

Método Ruta
GET /v1/organizations/:organizationId/sites
POST /v1/sites
GET /v1/sites/:siteId/snippet
POST /v1/sites/:siteId/verify-installation
POST /v1/sites/:siteId/kill-switch

primaryDomain debe ser un hostname desnudo (tienda.com), no una URL. Se aceptan hosts de una sola etiqueta como localhost para integración local.

Experimentos

Método Ruta
GET /v1/sites/:siteId/experiments
POST /v1/experiments
POST /v1/experiments/:experimentId/status

Analítica

Método Ruta
GET /v1/sites/:siteId/traffic?from=&to=
GET /v1/sites/:siteId/friction?from=&to=
GET /v1/experiments/:experimentId/results?from=&to=

results devuelve el análisis completo: SRM, posteriores de conversión, bootstrap de revenue, decisión y bloqueadores. Nunca devuelve un ganador sin los bloqueadores que lo justifican.

Configuración del SDK (pública)

Método Ruta
GET /v1/sdk/config/:siteId

Sin autenticación, cacheable, Access-Control-Allow-Origin: *. Contiene lo que el navegador necesita y nada más: ni datos de organización, ni secretos. Va firmada con HMAC.

Rate limiting

Superficie Límite por defecto
API autenticada 300 req/min por IP
Login por correo 10 intentos / 5 min
Restablecimiento por correo 5 / 15 min
Colector de eventos 6.000 req/min por IP
Colector de sesiones 300 req/min por IP

El limitador usa Redis compartido, así que el límite es global y no se multiplica al escalar réplicas. Si Redis falla, permite el paso: el rate limiting es un control de abuso, no de autorización, y hacerlo fallar cerrado convertiría una incidencia de Redis en una caída total.

CORS

La API mantiene una lista blanca estricta de orígenes porque el panel envía credenciales. Los colectores aceptan cualquier origen —los necesitan— pero no aceptan cookies, y verifican que el origen sea un dominio registrado del sitio.