Autenticación
La API de Zelta Pay utiliza claves API (API keys) para autenticar todas las solicitudes. Cada solicitud debe incluir tu clave en el header X-API-Key.
Autenticación con API Key
Cómo funciona
Todas las solicitudes a la API deben incluir el header X-API-Key con tu clave:
X-API-Key: tu-api-key-aquiLa API valida la clave antes de procesar la solicitud. Una API key no tiene fecha de vencimiento ni estado activo/inactivo: existe o no existe. Si la regeneras o la eliminas, el valor anterior deja de funcionar de inmediato.
Tipos de API key
| Tipo | Dónde se crea | Para qué sirve |
|---|---|---|
| API key de la cuenta | Menú lateral API Key | Endpoints de (/v1/payment-links) |
| API key de proyecto | Proyectos → tu proyecto → pestaña Resumen → tarjeta API Key | Endpoints de la y también /v1/payment-links, limitado a los links del proyecto. Cada proyecto tiene una sola API key |
Los endpoints de la API de Integración (/v1/products, /v1/customers, /v1/subscriptions, /v1/collections, /v1/charges) solo aceptan API keys de proyecto: con una API key de la cuenta responden 403 con ERR_API_KEY_NOT_PROJECT_SCOPED.
Alcance de las API keys
Cada tipo de API key alcanza distintos links de pago en , y :
| Tipo | Links de pago que alcanza |
|---|---|
| API key de la cuenta | Todos los links de la cuenta, sin importar cómo se crearon |
| API key de proyecto | Solo los links del proyecto: los creados con la API key de ese proyecto (POST /v1/payment-links y los ) |
Con una API key de proyecto, los links creados desde el dashboard o con la API key de otro proyecto no aparecen en el listado ni cuentan en total, y obtenerlos o cancelarlos responde 404 con ERR_PAYMENT_LINK_NOT_FOUND, la misma respuesta que un link que no existe. Los links pertenecen al proyecto, no al valor de la API key: si regeneras la API key del proyecto, la nueva sigue alcanzando los links creados con la anterior.
Los productos, clientes, suscripciones y cargos de la API de Integración también pertenecen a un proyecto, y solo la API key de ese proyecto los alcanza.
Obtener tu API Key de la cuenta
- Inicia sesión en tu
- Abre API Key en el menú lateral (visible solo para los roles propietario y administrador)
- En la tarjeta Nueva API key, escribe un Alias descriptivo (ej.
backend-prod) y haz clic en Crear - En la fila de la nueva llave, haz clic en Ver llave y luego en Copiar. Guárdala de forma segura
Necesitas un proveedor de pago activo
El botón Crear permanece deshabilitado hasta que la cuenta tenga al menos un proveedor de pago activo en . Si intentas crear la llave sin uno, la solicitud se rechaza con ERR_NO_ACTIVE_PAYMENT_PROVIDER.
Puedes volver a ver la llave más tarde con Ver llave, reemplazarla con Regenerar (el valor anterior deja de funcionar de inmediato) o borrarla con Eliminar.
API key de proyecto
La API key de un proyecto solo se muestra completa al crearla o regenerarla; después solo verás sus últimos 4 caracteres. Si la pierdes, regenérala.
Rate Limiting
La API aplica un límite de 60 solicitudes por ventana de 60 segundos por API key.
- Ventana fija: empieza con la primera solicitud de la llave después de que terminó la ventana anterior, y el contador vuelve a cero al cerrarse
- Por API key: cada llave tiene su propio límite, compartido por todos los endpoints
/v1(links de pago, productos, clientes, suscripciones, colecciones y cargos) - Las solicitudes rechazadas por autenticación (
401,403o404) no llevan headers de rate limit
Headers de rate limit
Las respuestas a solicitudes con una API key válida incluyen estos headers:
| Header | Descripción | Ejemplo |
|---|---|---|
RateLimit-Policy | Límite y duración de la ventana en segundos | 60;w=60 |
RateLimit-Limit | Número máximo de solicitudes por ventana | 60 |
RateLimit-Remaining | Solicitudes restantes en la ventana actual | 45 |
RateLimit-Reset | Segundos que faltan para que se reinicie la ventana (se omite cuando llega a 0) | 37 |
Retry-After | Segundos que debes esperar antes de reintentar (solo en un 429) | 37 |
Manejo de rate limits
Cuando superas el límite, la API responde 429 Too Many Requests. El cuerpo de esta respuesta es texto plano, no JSON, y no incluye un código de error:
HTTP/1.1 429 Too Many Requests
Content-Type: text/plain; charset=UTF-8
Retry-After: 37
RateLimit-Policy: 60;w=60
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 37
Too many requests, please try again later.Espera lo que indica Retry-After
Revisa response.status === 429 antes de llamar a response.json() (con este cuerpo, json() falla) y espera los segundos que indica Retry-After antes de reintentar.
Manejo de errores
Errores comunes de autenticación
| Código de error | HTTP Status | Descripción |
|---|---|---|
ERR_API_KEY_MISSING | 401 | No se incluyó el header X-API-Key en la solicitud |
ERR_ACCOUNT_API_KEY_NOT_FOUND | 404 | La API key no existe: está mal copiada, se regeneró o se eliminó |
ERR_ACCOUNT_NOT_FOUND | 404 | La cuenta asociada a la API key no existe |
ERR_ACCOUNT_SUSPENDED | 403 | La cuenta asociada a la API key está suspendida |
ERR_PROJECT_NOT_FOUND | 404 | API key de proyecto cuyo proyecto ya no existe |
ERR_PROJECT_INACTIVE | 403 | API key de proyecto cuyo proyecto no está activo |
ERR_API_KEY_NOT_PROJECT_SCOPED | 403 | Se usó una API key de la cuenta en un endpoint de la API de Integración, que exige una API key de proyecto |
Una API key inválida devuelve 404, no 401: el 401 solo significa que falta el header. Un 429 por rate limit no tiene código de error (ver ).
Formato de respuesta de error
{
"success": false,
"message": "Api key not found",
"error": {
"code": "ERR_ACCOUNT_API_KEY_NOT_FOUND"
},
"timestamp": "2026-03-13T10:30:00.000Z"
}Usa error.code para decidir qué hacer; el texto de message puede cambiar. Los errores de validación (400) tienen otro formato, descrito en la .
Ejemplos de implementación
async function zeltaRequest(path, options = {}) {
const response = await fetch(`https://api-pay.zelta.dev${path}`, {
...options,
headers: {
'X-API-Key': process.env.ZELTA_API_KEY,
'Content-Type': 'application/json',
...options.headers
}
});
if (response.status === 429) {
// Cuerpo en texto plano: no llames a response.json()
const retryAfter = Number(response.headers.get('Retry-After') ?? 60);
throw Object.assign(new Error(`Rate limit: reintenta en ${retryAfter}s`), { status: 429, retryAfter });
}
const result = await response.json();
if (!response.ok) {
// Los errores de validación (400) traen error.issues y no traen message
const detail = result.message ?? JSON.stringify(result.error?.issues);
throw Object.assign(new Error(detail), { status: response.status, code: result.error?.code });
}
return result;
}
const links = await zeltaRequest('/v1/payment-links');
console.log(links.data.total);Buenas prácticas de seguridad
Variables de entorno
Nunca incluyas API keys directamente en tu código fuente. Usa variables de entorno:
# .env (nunca subir al control de versiones)
ZELTA_API_KEY=tu-api-key-aquiNo subas API keys al control de versiones
Nunca guardes API keys en el control de versiones. Agrega .env a tu archivo .gitignore.
Rotación de claves
- Rota tus API keys periódicamente: crea una llave nueva, actualiza tus aplicaciones y elimina la anterior
- Si una llave se ve comprometida, usa Regenerar o Eliminar en el : el valor anterior deja de funcionar de inmediato
- Usa alias descriptivos para identificar qué servicio usa cada llave
Control de acceso
- Genera llaves diferentes para cada servicio o aplicación
- Revisa el Último uso de cada llave en el dashboard para detectar llaves que ya no se usan
- Almacena las claves en un gestor de secretos en producción
Manejo de errores
- Nunca expongas tu API key en mensajes de error del lado del cliente
- Registra los errores de autenticación para detectar intentos de acceso no autorizado
Probar la autenticación
Verifica que tu API key funcione correctamente con esta solicitud:
curl -X GET "https://api-pay.zelta.dev/v1/payment-links?lim=1" \
-H "X-API-Key: tu-api-key-aqui" \
-H "Content-Type: application/json"Una respuesta exitosa confirma que tu clave es válida:
{
"success": true,
"data": {
"paymentLinks": [],
"total": 0
},
"message": "Operation completed successfully",
"timestamp": "2026-03-13T10:30:00.000Z"
}Solución de problemas
Error 401 — Falta el header
- Verifica que estés enviando el header
X-API-Key(noAuthorization)
Error 404 — ERR_ACCOUNT_API_KEY_NOT_FOUND
- La llave está mal copiada, se regeneró o se eliminó
- Confirma que la clave no tenga espacios en blanco adicionales
- Copia la llave vigente desde API Key → Ver llave en tu
Error 404 — ERR_PAYMENT_LINK_NOT_FOUND con una API key de proyecto
- Una API key de proyecto solo alcanza los links de su proyecto (ver )
- Para leer o cancelar links creados desde el dashboard o por otro proyecto, usa la API key de la cuenta
Error 403 — Cuenta suspendida o proyecto inactivo
- La cuenta está suspendida (
ERR_ACCOUNT_SUSPENDED) o el proyecto de la llave no está activo (ERR_PROJECT_INACTIVE) - Contacta a soporte para resolverlo
Error 429 — Rate limit excedido
- Enviaste más de 60 solicitudes en la ventana de 60 segundos de esa llave
- Espera los segundos que indica
Retry-Afterantes de reintentar - Espacia tus solicitudes o guarda en caché las respuestas que consultas con frecuencia
Siguientes pasos
- — Documentación completa de endpoints y formatos
- — Crea tu primer link de pago
- — Guía paso a paso para empezar