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.
| Superficie | Cómo se autentica | Quié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 Authorization | el navegador del pagador vía winal.js |
/health, /status, /pay/*, /webhooks/in/* | sin autenticación | monitoreo, checkout hosteado, callbacks del proveedor |
GET /v1/payment_intents/5b6b8b3e-... HTTP/1.1
Host: api.winal.com.mx
Authorization: Bearer sk_test_9fZ3kQ...
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.
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.
Despliega
Actualizas el secreto en tu backend. Ambas llaves autentican durante la gracia.
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
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.
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.
| Scope | Exigido por |
|---|---|
payouts:write | POST /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:manage | POST/DELETE /v1/webhook_endpoints — alta/baja de a dónde se entregan tus eventos. |
reports:write | POST /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
- TLS obligatorio. Todo el tráfico entra por HTTPS en el borde (Caddy termina TLS con certificados gestionados). Las peticiones en claro se redirigen/rechazan.
- IP de cliente confiable. Winal toma la IP real del último salto que anexa el proxy de confianza, no un
X-Forwarded-Forarbitrario — así el particionado de rate limit y la telemetría no son falsificables desde el cliente. - Sin custodia de fondos. Winal orquesta el cobro pero nunca custodia tu dinero: las CLABEs y credenciales de proveedor son tuyas (ADR-0001). Reduce drásticamente la superficie de un incidente.
- Nunca tocamos el PAN. Ningún endpoint acepta el número de tarjeta; la tokenización es del lado del cliente con los campos seguros del proveedor (PCI SAQ A). Ver Métodos de pago.
/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í:
- 2FA (TOTP). Cada persona de tu equipo activa su propio segundo factor desde la sección Seguridad del tablero, con cualquier app de códigos (Google Authenticator, 1Password, Authy). Con 2FA activo, el login exige el código de 6 dígitos además de la contraseña — y es uno de los requisitos del checklist de producción.
- Recuperación de contraseña. Si la olvidas, "¿Olvidaste tu contraseña?" en el login te manda un enlace de un solo uso; al canjearlo se cierran todas tus sesiones activas, por si el olvido fue en realidad un acceso indebido.
- Equipo con roles. El dueño de la cuenta agrega y deshabilita miembros con rol
owner,operadorolecturadesde el tablero; el rol de lectura no puede escribir nada, ni con la cabecera anti-CSRF correcta.
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:
- 2FA (TOTP). Cada usuario puede activar un segundo factor con cualquier app de códigos (Google Authenticator, 1Password, Authy). Con 2FA activo, el login exige el código de 6 dígitos además de la contraseña.
- SSO / OIDC. El portal acepta inicio de sesión con el
id_tokende tu proveedor de identidad (OpenID Connect), para que tu equipo entre con las credenciales corporativas. - Roles. Distingue quién puede ver contra quién puede emitir/revocar llaves y mover configuración sensible.
- Cierre de sesiones. Puedes cerrar todas las sesiones activas de un usuario (revocación global) si sospechas de un acceso indebido.
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.