Autenticación y seguridad

GUÍA

Toda petición autenticada a Winal viaja por TLS y presenta tu llave secreta como un Bearer token. Esta página consolida en un solo lugar cómo se autentica cada superficie del API, cómo se rotan y expiran las llaves, y la postura de red y de acceso al portal.

Autenticación de la API

Winal expone tres superficies con esquemas de autenticación distintos. El securityScheme del contrato OpenAPI declara exactamente el de /v1.

SuperficieCómo se autenticaQuién la llama
/v1/*Authorization: Bearer sk_test_… / sk_live_…tu backend (nunca el navegador)
/public/*el client_secret del intent (en el cuerpo o el query); sin cabecera Authorizationel navegador del pagador vía winal.js
/health, /status, /pay/*, /webhooks/in/*sin autenticaciónmonitoreo, checkout hosteado, callbacks del proveedor
http
GET /v1/payment_intents/5b6b8b3e-... HTTP/1.1
Host: api.winal.com.mx
Authorization: Bearer sk_test_9fZ3kQ...
🔑 La llave secreta es secreta
sk_ = secret key. Va solo en tu servidor. No hay una "llave publicable" que exponer al navegador: el frente se autentica con el client_secret efímero de un intent, que solo puede confirmar ese cobro y nada más. Si una sk_ se filtra, revócala de inmediato (abajo).

Una petición a /v1 sin Authorization, con una llave mal formada o revocada, responde 401 con el envelope de error estándar (error.type = "authentication_error"; ver Errores).

Ciclo de vida de las llaves

Cada llave se almacena hasheada (jamás en claro): el valor completo se muestra una única vez, al crearla o rotarla. Después solo queda un prefijo enmascarado para identificarla — ni siquiera nosotros podemos recuperarla, así que guárdala al momento. La rotación y la revocación son autoservicio: las haces tú mismo desde tu tablero en https://winal.com.mx/app/, sin pedírnoslas.

Rotación sin downtime

Rotar no corta el servicio: al rotar, Winal emite una llave nueva y mantiene la anterior válida durante un periodo de gracia configurable. Despliegas la nueva, verificas que todo tu tráfico ya la usa, y entonces cierras la vieja (o dejas que expire sola al terminar la gracia). Este solape es la forma correcta de cambiar una credencial en producción.

1

Rota

Rotas tú desde tu tablero en https://winal.com.mx/app/; Winal emite la sk_… nueva y fija old_expires_at (fin de la gracia) a la anterior.

2

Despliega

Actualizas el secreto en tu backend. Ambas llaves autentican durante la gracia.

3

Cierra

Completas la rotación (o esperas a old_expires_at): la vieja deja de servir.

Expiración

Una llave puede tener expires_at. En cuanto pasa esa fecha —o si la revocas— deja de autenticar y toda petición con ella recibe 401. En el listado de tus llaves, el campo active refleja no revocada Y (sin expiración O aún no expirada).

Revocación inmediata

🔓 La revocación es self-service
Desde tu tablero en https://winal.com.mx/app/ puedes cortar cualquiera de tus llaves tú mismo (DELETE /app/api/api-keys/{id}), sin escribirnos ni esperar a nadie. Tenlo en tu runbook de incidentes: si se te filtra una llave (un commit accidental, un log, un ticket), entra al tablero y revócala de inmediato.

Si una llave se compromete, revócala tú mismo desde el tablero: el corte es inmediato, sin gracia. Emite una llave nueva y actualiza tu backend. La revocación queda en la bitácora de auditoría del tenant (api_key.rotated / api_key.created y la revocación), con actor y fecha.

⚠️ Nunca en el cliente ni en el repo
Mantén sk_live_ fuera de HTML, apps móviles, repos y logs. Si tu stack lo permite, inyéctala como variable de entorno o desde un gestor de secretos. Un secreto en un commit se considera comprometido aunque borres el commit después — rótalo.

Scopes de la API key

Además de identificar al tenant, una API key lleva una lista de scopes: permisos granulares que acotan QUÉ puede hacer esa llave, más allá de a quién pertenece. Toda llave emitida hoy recibe el comodín * (acceso total: pasa cualquier scope), pero el modelo está pensado para llaves de alcance acotado — p. ej. una llave de integración de solo lectura, o una que jamás debería poder ordenar una dispersión de fondos.

ScopeExigido por
payouts:writePOST /v1/payouts, POST /v1/connect/transfers (y /redisperse, /release), POST /v1/payroll/runs/{id}/execute — toda operación que ORDENA una salida real de dinero por SPEI.
webhooks:managePOST/DELETE /v1/webhook_endpoints — alta/baja de a dónde se entregan tus eventos.
reports:writePOST /v1/reports/periods/{year}/{month}/close — cierre irreversible de un período contable.
*Comodín de acceso total: satisface cualquier scope que un endpoint exija. Es lo que trae toda llave emitida hoy.

Una llave sin el scope requerido recibe 403 con error.type = "authorization_error" y error.code = "insufficient_scope" (ver Errores) — el mensaje nombra exactamente el scope que falta. Es aditivo: un endpoint sin scope declarado no cambia de comportamiento, y las operaciones de solo lectura (listar/consultar) nunca lo exigen.

Idempotencia como salvaguarda

Todo POST que mueve dinero exige un header Idempotency-Key (un UUID que tú generas). Reintentar con la misma clave devuelve la respuesta original sin duplicar el cargo — tu red de seguridad ante timeouts y reintentos. Es una de las tres capas de idempotencia de Winal (API, hacia el proveedor, y dedupe por event_id en tus consumidores de webhook). Detalle en Referencia de API.

Límite de solicitudes

El API aplica rate limiting por ventana fija de 1 minuto. Las peticiones autenticadas se cuentan por llave; las de /public/* por IP de cliente. Al excederlo recibes 429 con Retry-After: 60. Los detalles y los límites por defecto están en Referencia → Límites de solicitudes.

Postura de red

mTLS y allowlist de IP por llave
Hoy la autenticación de /v1 es Bearer sobre TLS. El allowlist de IP por llave y el mTLS mutuo para clientes de plataforma están en el roadmap de endurecimiento y se habilitan por acuerdo (no son configurables self-service todavía). Si tu caso los requiere para cumplimiento, indícalo en el alta.

Tu tablero

Desde https://winal.com.mx/app/ tu equipo administra la cuenta sin escribirnos — llaves, facturación, conectores, ruteo y quién tiene acceso (detalle completo en Tu tablero). Dos piezas de seguridad viven ahí:

Acceso al portal de plataforma

Distinto de tu tablero está el portal de plataforma (/portal): la consola interna que usa el equipo de Winal para operar TODOS los comercios. Es multi-usuario, con roles por usuario y sesiones por cookie igual que tu tablero, pero no está expuesto a internet —se opera por túnel administrativo, y por eso /portal responde 404 desde fuera—. Estas son sus protecciones:

Las llaves de API, la sesión de tu tablero y el acceso al portal de plataforma son planos de seguridad separados: cerrar una sesión no invalida las sk_, y revocar una sk_ no cierra ninguna sesión de portal. Gestiona cada uno según su riesgo.