Autenticación y firma
Cada vez que le pides a Alibanca que mueva dinero, la API necesita estar segura de dos cosas: quién eres y que el mensaje llegó tal cual lo enviaste, sin que nadie lo tocara en el camino. Para eso, cada request lleva tu llave de API (te identifica) y una firma criptográfica RSA-SHA256 (prueba que el mensaje es auténtico e íntegro). Esta guía te explica, paso a paso y sin dar nada por sabido, cómo funcionan tus llaves, cómo se arma y se firma el envelope canónico, cómo el nonce evita que un request se reenvíe dos veces, y cómo depurar el temido error 401 cuando la firma no cuadra.
Cómo funciona la autenticación en Alibanca
La autenticación de Alibanca tiene dos capas independientes que se refuerzan entre sí. Entenderlas por separado te ahorra horas de depuración, porque cuando algo falla casi siempre es una de las dos, no ambas.
Capa 1 — Identidad (tu llave de API). En cada request mandas el header x-api-key con tu llave secreta. Es como el nombre de usuario del request: le dice a Alibanca de qué cuenta viene la operación y qué permisos y límites aplican. Pero una llave, por sí sola, puede ser copiada; si alguien la ve, podría hacerse pasar por ti. Por eso existe la segunda capa.
Capa 2 — Autenticidad e integridad (la firma). Además de la llave, cada request lleva una firma digital en el header x-signature. Esa firma se calcula con una llave privada que solo tú tienes y que nunca viaja por la red. Alibanca la verifica con tu llave pública (que sí conoce). Si aunque sea un byte del mensaje cambió, o si quien lo firmó no tenía tu llave privada, la verificación falla y el request se rechaza. Esta capa es la que hace que mover dinero sea seguro: aun si alguien roba tu x-api-key, no puede firmar por ti sin tu privada.
GET /v2/version (es público, no requiere llave ni firma). Te confirma que estás apuntando al ambiente correcto y te devuelve los enlaces al openapi y al jwks. Es la forma barata de descartar problemas de red antes de meterte con la criptografía.Tus llaves: sk_test_ y sk_live_
Una llave de API (API key) es un texto secreto que te identifica ante Alibanca. Tienes dos tipos, y la diferencia importa muchísimo porque una mueve dinero de verdad y la otra no.
| Prefijo | Ambiente | Qué hace |
|---|---|---|
| sk_test_ | Sandbox | Vive en https://alibanca-api-production.up.railway.app. Corre contra un proveedor simulado (MockProvider): no mueve un solo céntimo real. Es tu campo de pruebas para desarrollar e integrar sin riesgo. |
| sk_live_ | Producción | Vive en https://api.alibanca.com. Cada request que aceptes aquí mueve dinero real. Trátala con el mismo cuidado que la clave de una bóveda. |
El prefijo (sk_ = secret key) te recuerda que son secretas por diseño: no son un identificador público, son una credencial. En sandbox, además, tienes a tu disposición los montos mágicos → para disparar escenarios como unknown o el trap del doble-pago, y así probar que tu integración se comporta bien sin arriesgar dinero.
sk_test_. Integra completo contra el sandbox, ejercita los montos mágicos, verifica tu manejo de unknown, y solo cuando todo el flujo esté sólido cambias la base URL y la llave a sk_live_. El código no cambia; solo el ambiente.Seguridad de llaves: siempre del lado del servidor
Tu x-api-key y, sobre todo, tu llave privada RSA son secretos server-side. Eso significa que viven y se usan únicamente en tu backend — nunca en el navegador, nunca en una app móvil, nunca en código que el usuario final pueda descargar o inspeccionar.
La razón es simple: cualquier cosa que llegue al cliente, el cliente la puede leer. Si pones tu llave en el JavaScript de tu web o dentro de tu app, cualquiera con las herramientas de desarrollador puede extraerla y empezar a mover dinero como si fuera tú. La API de Alibanca siempre se llama desde tu servidor.
sk_live_ ni tu llave privada en el repositorio, ni en un archivo .env que subas por accidente, ni pegada en un ticket, un chat o un PR. Guárdalas en un gestor de secretos o en variables de entorno del servidor. Si una llave se filtra, rótala de inmediato: asume que ya fue comprometida.Buenas costumbres mínimas para dormir tranquilo:
Guárdalas fuera del código
Variables de entorno o un secret manager. Nunca hardcodeadas.
Sepáralas por ambiente
Que sk_test_ y sk_live_ vivan en configuraciones distintas, para que sea imposible mandar a producción algo que pensabas probar.
Protege la llave privada más que nada
Tu privada RSA es la joya de la corona. Con ella se firma el movimiento de dinero. Restringe quién y qué proceso puede leerla.
Rota ante la duda
Si sospechas exposición, rota. Es más barato rotar una llave sana que limpiar un fraude.
Firma asimétrica RSA-SHA256: qué es y por qué
Aquí está el corazón de la seguridad de Alibanca, así que vamos con calma y una analogía.
Firmar asimétricamente es como poner un sello de cera personal e infalsificable sobre un sobre cerrado. En criptografía asimétrica tienes dos llaves que forman una pareja: una privada (que solo tú tienes) y una pública (que puedes repartir sin miedo). Lo que se firma con la privada, solo se verifica con la pública correspondiente — y viceversa. Son matemáticamente inseparables, pero de la pública no se puede deducir la privada.
Aplicado a Alibanca: tú firmas cada request con tu llave privada, y Alibanca verifica esa firma con tu llave pública (que le registraste al dar de alta tu integración). El resultado tiene dos garantías:
Autenticidad — solo quien posee tu llave privada pudo producir esa firma, así que Alibanca sabe que el request salió realmente de ti, no de un impostor que copió tu x-api-key.
Integridad — la firma se calcula sobre el contenido exacto del mensaje. Si un intermediario cambia un solo carácter del monto o del destino, la firma deja de cuadrar y el request se rechaza. Nadie puede alterar un pago en tránsito sin que se note.
El SHA-256 de "RSA-SHA256" es la función de hash: antes de firmar, el mensaje se resume en una huella digital de tamaño fijo. Firmar esa huella con RSA es lo que produce la firma. No necesitas implementar esto a mano — cualquier librería de criptografía estándar (la nativa de tu lenguaje) lo hace por ti; solo tienes que darle el texto correcto a firmar. Y ese "texto correcto" es lo más importante de toda esta página: el envelope canónico.
El envelope canónico: qué se firma exactamente
No firmas "el request" en abstracto. Firmas un texto muy concreto y construido con reglas fijas, llamado envelope canónico. "Canónico" significa que se arma siempre igual, en un orden determinístico, para que tú y Alibanca lleguen exactamente al mismo texto y por tanto a la misma verificación. Si tú lo armas distinto a como Alibanca lo reconstruye, la firma no cuadra — aunque tu criptografía sea perfecta.
El envelope es la concatenación en este orden exacto de seis piezas:
| Orden | Pieza | Qué es |
|---|---|---|
| 1req | Método HTTP | El verbo del request, tal cual: POST, GET, PATCH, DELETE. En mayúsculas. |
| 2req | Ruta | El path del endpoint, por ejemplo /v2/payments. Es la ruta que estás llamando, sin el dominio. |
| 3req | API key | Tu x-api-key (sk_test_… o sk_live_…). Atarla al envelope amarra la firma a tu identidad. |
| 4req | Idempotency-key | El valor de tu header idempotency-key: el identificador único y determinístico de esta operación lógica. |
| 5req | Raw body | El cuerpo crudo del request: el string JSON exacto, byte a byte, que vas a enviar. Ni re-serializado, ni con espacios distintos. |
| 6req | Nonce | Un número creciente por llave que evita reenvíos (ver la sección del nonce). Viaja dentro del envelope firmado. |
JSON.stringify(objeto) y luego tu cliente HTTP re-serializa el objeto con otro orden de claves u otros espacios, el body que llega a Alibanca ya no es el que firmaste, y obtienes 401. Regla de oro: serializa una sola vez, guarda ese string, fírmalo y envía ese mismo string.Cómo armar y firmar el envelope
El proceso completo, del lado de tu servidor, en cuatro pasos:
Serializa el body una sola vez
Convierte tu objeto a JSON y guarda ese string. Ese es tu raw body — el que firmas y el que envías, sin volver a tocarlo.
Concatena las seis piezas en orden
Método, ruta, api-key, idempotency-key, raw body, nonce. En ese orden, con un separador consistente entre cada una.
Firma el envelope con tu llave privada
Aplica RSA-SHA256 sobre el envelope y codifica el resultado en base64. Esa es tu firma.
Manda los headers y el body
x-api-key, x-signature e idempotency-key en los headers; el raw body como cuerpo del request.
En pseudo-código (Node.js con la librería nativa crypto), se ve así:
// 0. Prepara los datos de la operación const method = "POST"; const path = "/v2/payments"; const apiKey = process.env.ALIBANCA_KEY; // sk_live_... (server-side) const idemKey = "pay-2026-07-22-inv-8842"; // único y determinístico por operación const nonce = nextNonce(apiKey); // creciente por llave (high-water) // 1. Serializa el body UNA sola vez — este string es el que firmas Y envías const rawBody = JSON.stringify({ amountMinor: "150000", currency: "VES", rail: "PM", beneficiary: "0414-1234567" }); // 2. Concatena las 6 piezas EN ORDEN (método, ruta, api-key, idem-key, body, nonce) const envelope = [method, path, apiKey, idemKey, rawBody, nonce].join("\n"); // 3. Firma el envelope con TU llave privada → base64 const signature = crypto .createSign("RSA-SHA256") .update(envelope) .sign(privateKeyPem, "base64"); // 4. Envía. OJO: manda EXACTAMENTE rawBody como cuerpo, no re-serialices el objeto. await fetch("https://api.alibanca.com/v2/payments", { method, headers: { "content-type": "application/json", "x-api-key": apiKey, "x-signature": signature, "idempotency-key": idemKey }, body: rawBody });
El nonce high-water: anti-replay para principiantes
Imagina que un atacante no logra romper tu firma, pero sí copia un request válido tuyo y lo reenvía tal cual, muchas veces. Como la firma es auténtica, ¿pasaría? A ese ataque se le llama replay (reenvío), y el nonce existe justo para bloquearlo.
Un nonce es un número que acompaña a cada request. Alibanca lleva, por cada llave, una marca de agua creciente (high-water mark): recuerda el nonce más alto que ya vio. La regla es simple: cada request nuevo debe usar un nonce mayor que el anterior. Cuando llega uno con un nonce igual o menor al que ya se registró, Alibanca sabe que es un reenvío o algo fuera de orden, y lo rechaza.
La analogía: es como el número de cheque de una chequera. Los cheques van 1001, 1002, 1003… Si al banco le llega otra vez el cheque 1002 después de haber procesado el 1003, sabe que es un duplicado y no lo paga. El nonce es ese número correlativo, pero por llave.
Implementación práctica del lado tuyo: basta con que el nonce sea estrictamente creciente por llave. Un timestamp en milisegundos o microsegundos suele bastar; un contador persistente por llave también. Lo único inviolable es que nunca retroceda.
Los headers requeridos
Un request firmado a Alibanca lleva estos headers. Los tres primeros son la columna vertebral de la seguridad; el content-type es el estándar de cualquier request con cuerpo JSON.
| Header | Tipo | Descripción |
|---|---|---|
| x-api-keyreq | string | Tu llave de API (sk_test_… o sk_live_…). Te identifica y determina permisos y límites. |
| x-signaturereq | string (base64) | La firma RSA-SHA256 del envelope canónico, hecha con tu llave privada. Prueba autenticidad e integridad. |
| idempotency-keyreq | string | Identificador único y determinístico de la operación lógica. Reintentar con el mismo valor devuelve el mismo resultado. Además forma parte del envelope firmado. |
| content-type | string | application/json para requests con cuerpo. Estándar REST. |
GET /v2/version, GET /v2/openapi.json, GET /v2/webhook/keys (el JWKS) y GET /health son públicos: puedes llamarlos sin llave ni firma. Todo lo demás — mover dinero, listar pagos, gestionar beneficiarios, tu /v2/me — exige llave y firma.Ejemplo de un request firmado completo
Así se ve, extremo a extremo, una emisión de pago firmada: primero el request con sus headers y su body, luego la respuesta.
POST /v2/payments HTTP/1.1 Host: api.alibanca.com content-type: application/json x-api-key: sk_live_9f2c…a71b x-signature: Qm9nT2xh… (RSA-SHA256 del envelope, en base64) idempotency-key: pay-2026-07-22-inv-8842 {"amountMinor":"150000","currency":"VES","rail":"PM","beneficiary":"0414-1234567"}
{ "status": "success", "traceId": "trc_7f3a91c0e2", "payload": { "id": "pay_01J8Z…", "state": "completed", "amountMinor": "150000", "currency": "VES" } }
Fíjate en que el body de la respuesta sigue el sobre estándar de Alibanca: status, traceId (guárdalo: es tu número de referencia para soporte) y payload con el resultado. El campo state te dice cómo quedó el pago — profundiza en Estados de pago → para entender completed, unknown y failed.
Cómo Alibanca verifica tu firma
Entender qué hace Alibanca del otro lado te vuelve mucho más rápido depurando, porque sabes exactamente qué tiene que coincidir. Al recibir tu request, Alibanca:
Identifica tu llave
Lee x-api-key y busca la llave pública que registraste para esa cuenta.
Reconstruye el envelope
Vuelve a armar las seis piezas en el mismo orden — método, ruta, tu api-key, tu idempotency-key, el raw body que recibió y el nonce.
Verifica la firma
Comprueba, con tu llave pública, que la x-signature corresponde a ese envelope. Si un byte no cuadra, la verificación falla.
Chequea el nonce
Confirma que el nonce es mayor que la marca de agua de tu llave. Si es igual o menor, es replay y se rechaza.
Solo si las cuatro cosas pasan, Alibanca procesa la operación. Por eso la firma es una prueba tan fuerte: cualquier alteración del método, la ruta, la llave, la idempotency-key, el cuerpo o el nonce rompe la coincidencia.
Errores de firma (401) y cómo depurarlos
Cuando la autenticación falla, Alibanca responde con código 401 y el sobre de error { "error": "mensaje" }. Casi siempre la causa es que tu envelope no es idéntico al que Alibanca reconstruyó. Esta es tu lista de sospechosos, ordenada por frecuencia:
| Síntoma | Causa probable | Cómo arreglarlo |
|---|---|---|
| 401 al azar en algunos requests | raw body | Estás firmando un string pero enviando otro (tu cliente HTTP re-serializa el objeto). Serializa una vez, firma ese string y envía ese string. |
| 401 constante | orden del envelope | Las piezas no están en el orden exacto método, ruta, api-key, idem-key, body, nonce. Revisa la concatenación. |
| 401 constante | ruta equivocada | Firmaste /payments en vez de /v2/payments, o incluiste el dominio, o un query string que no corresponde. Usa el path exacto. |
| 401 tras rotar llaves | par de llaves | Estás firmando con una privada cuya pública no es la que Alibanca tiene registrada. Verifica que el par coincida (ver el fingerprint más abajo). |
| 401 en reintentos | nonce | Reusaste o bajaste el nonce. Cada request debe llevar un nonce estrictamente mayor. |
| 401 con la llave correcta | ambiente | Mandaste una sk_test_ a producción o al revés. Verifica base URL y prefijo de llave. |
GET /v2/me con tu llave firmada: te devuelve tu keyType, el environment y el publicKeyFingerprint (un SHA256:… de tu llave pública registrada). Si ese fingerprint no coincide con el de la pública que corresponde a tu privada, ahí está tu problema. /v2/me nunca devuelve secretos, así que es seguro para depurar.Técnica de depuración recomendada. Cuando pelees con un 401, imprime en tu log el envelope exacto que estás firmando (con separadores visibles) y el body exacto que sale por la red. El 90% de los casos se resuelve al ver que esos dos strings no son los que creías. Compara carácter por carácter contra lo que esperas.
Buenas prácticas
Para cerrar, el resumen de hábitos que hacen tu integración segura y fácil de operar en el tiempo:
Serializa el body una sola vez. Es el error número uno. Guarda el string, fírmalo, envíalo. No dejes que ninguna capa lo re-serialice.
La privada nunca sale del servidor. Ni en logs, ni en el cliente, ni en el repo. Si dudas si se filtró, rota.
Nonce siempre creciente por llave. Un timestamp monotónico o un contador persistente. Que nunca retroceda, ni siquiera al reiniciar tu servicio.
Idempotency-key determinística por operación. Derívala de algo estable de tu lado (por ejemplo, el id de tu factura), no de un aleatorio nuevo en cada intento. Así un reintento de red reusa la misma clave y no genera un pago duplicado.
Saluda antes de firmar. GET /v2/version te confirma ambiente y conectividad sin criptografía de por medio; descarta problemas triviales antes de los complejos.
Separa ambientes con rigor. Config distinta para sk_test_ y sk_live_, base URLs distintas, y ojalá una salvaguarda que impida mandar test a producción.
sk_live_, monta todo el circuito de firma contra el sandbox: arma el envelope, fírmalo, emite un pago con un monto normal (queda completed) y luego con uno terminado en .02 para ver un unknown. Si tu firma cuadra en sandbox, cuadra en producción — la criptografía es idéntica; solo cambia que ahí el dinero es real.