Clientes (API)
Un cliente representa a la persona a la que cobrarás dentro del proyecto. La forma habitual de crear un cliente es capturando su tarjeta con un : Zelta Pay tokeniza la tarjeta y crea el cliente automáticamente, emitiendo el webhook customer.created. También puedes crear clientes directamente por API y adjuntarles una tarjeta vaulteada.
Un cliente vaulteado es aquel que tiene una tarjeta almacenada de forma segura por un procesador de tarjetas (NMI o Emetec). El cliente registra qué procesador la guardó (paymentProvider), y sus cobros posteriores se hacen con ese mismo procesador. Solo los clientes vaulteados pueden ser cobrados con o suscritos con .
Clientes guardados con NMI
Para cobrar a un cliente cuya tarjeta está guardada en NMI, NMI debe estar activo en para el entorno del cobro. Si activas Emetec (lo que desactiva NMI), los cobros a esos clientes fallan hasta que vuelvas a activar NMI.
Todos los endpoints requieren tu API key de proyecto en el header X-API-Key.

El objeto cliente
Todos los endpoints que devuelven un cliente usan el mismo objeto: los de clientes (crear, obtener y adjuntar vault bajo data.customer; listar bajo data.customers como arreglo, junto con total), las respuestas de que incluyen al cliente y expand=customer en y suscripciones. Los campos ligados a la tarjeta (paymentProvider, cardLast4, cardBrand) son null hasta que el cliente queda vaulteado. Este es un cliente vaulteado, como lo devuelve GET /v1/customers/:id:
{
"success": true,
"data": {
"customer": {
"id": "7315555666677778888",
"projectId": "7301234567890123456",
"name": "John Doe",
"email": "[email protected]",
"phone": "+50760000000",
"paymentProvider": "nmi",
"cardLast4": "1111",
"cardBrand": "visa",
"status": "active",
"metadata": null,
"createdAt": "2026-06-04T00:00:00.000Z",
"updatedAt": "2026-06-04T00:00:00.000Z",
"deletedAt": null
}
},
"message": "Operation completed successfully",
"timestamp": "2026-06-04T00:00:00.000Z"
}| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único del cliente |
projectId | string | Proyecto al que pertenece el cliente |
name | string | Nombre del cliente |
email | string | Correo del cliente. Puede ser una cadena vacía si el cliente se creó con un link de colección sin capturar su correo |
phone | string | null | Teléfono del cliente (null si no se envió) |
status | string | active o inactive |
paymentProvider | string | null | Procesador que almacenó la tarjeta (nmi o emetec); null si aún no está vaulteado |
cardLast4 | string | null | Últimos 4 dígitos de la tarjeta (solo para mostrar) |
cardBrand | string | null | Marca de la tarjeta (solo para mostrar) |
createdAt | string | Fecha de creación (ISO 8601) |
updatedAt | string | Fecha de última actualización (ISO 8601) |
El objeto también incluye metadata y deletedAt, que puedes ignorar.
El BIN y el vencimiento llegan por webhook
El objeto cliente no incluye la referencia de la bóveda (customerVaultId), el BIN (cardBin) ni el vencimiento (cardExp) de la tarjeta. El BIN y el vencimiento se entregan una sola vez, en el webhook (customer.card.bin y customer.card.exp): guárdalos en tu sistema si los necesitas. Si adjuntas la tarjeta tú mismo con , esos valores son los que envías en la solicitud.
Todos los datos de tarjeta del cliente son solo para mostrar. Los cobros siempre usan la tarjeta guardada en la bóveda del procesador, nunca estos valores.
Listar clientes
GET /v1/customersParámetros de consulta: paginación estándar (lim de 1 a 100, default 10; off) más un filtro opcional status (active, inactive). Los resultados se ordenan del más reciente al más antiguo.
Con fields (lista separada por comas) recibes solo esos campos del ; un campo que no pertenece al objeto devuelve 400 con ERR_INVALID_FIELDS. Este endpoint no admite expand: cualquier valor devuelve 400 con ERR_INVALID_RELATIONS.
curl "https://api-pay.zelta.dev/v1/customers?lim=10" \
-H "X-API-Key: tu-api-key-de-proyecto"Crear un cliente
POST /v1/customersCuerpo de la solicitud:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del cliente (1-120 caracteres) |
email | string | Sí | Correo del cliente (formato de email válido) |
phone | string | No | Teléfono del cliente (máx. 40 caracteres) |
curl -X POST https://api-pay.zelta.dev/v1/customers \
-H "X-API-Key: tu-api-key-de-proyecto" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "[email protected]",
"phone": "+50760000000"
}'Responde 201 con el mensaje "Customer created" y el cliente en data.customer. Un cliente creado así aún no tiene tarjeta almacenada. Para poder cobrarle debes adjuntarle una tarjeta vaulteada (ver abajo) o capturarla con un , que crea un cliente nuevo. Crear un cliente por API no envía el webhook customer.created, y la API no evita clientes duplicados con el mismo email.
Obtener un cliente
GET /v1/customers/:idcurl "https://api-pay.zelta.dev/v1/customers/7315555666677778888" \
-H "X-API-Key: tu-api-key-de-proyecto"Si el cliente no existe en el proyecto, responde 404 con ERR_CUSTOMER_NOT_FOUND.
Adjuntar una tarjeta vaulteada
Asocia al cliente una tarjeta ya tokenizada por el procesador indicado, pasando el id de la bóveda (customerVaultId) que ese procesador devolvió al tokenizarla. Esto convierte al cliente en vaulteado y habilita cobros y suscripciones.
POST /v1/customers/:id/vaultCuerpo de la solicitud:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
customerVaultId | string | Sí | Id de la bóveda que devolvió el procesador al tokenizar la tarjeta |
paymentProvider | string | No | Procesador que almacenó la tarjeta: nmi o emetec. Por defecto nmi, incluso si el cliente ya tenía una tarjeta de Emetec |
cardLast4 | string | No | Últimos 4 caracteres de la tarjeta, exactamente 4 (solo para mostrar) |
cardBrand | string | No | Marca de la tarjeta (máx. 40 caracteres, solo para mostrar) |
cardBin | string | No | BIN de la tarjeta (6-8 dígitos, solo para mostrar) |
cardExp | string | No | Vencimiento en formato MMYY (solo para mostrar) |
Los campos card* son metadata de facturación solo para mostrar: los cobros siempre usan la referencia de la bóveda, nunca estos valores. Responde 200 con el mensaje "Vault attached" y el actualizado en data.customer. Zelta Pay guarda customerVaultId, cardBin y cardExp, pero la respuesta no los repite: guárdalos en tu sistema al enviarlos si los necesitas.
Reemplazar la tarjeta
Si el cliente ya tenía una tarjeta, esta llamada reemplaza customerVaultId y paymentProvider. Los campos card* que no envíes conservan su valor anterior, así que envíalos todos al reemplazar la tarjeta. Zelta Pay no verifica el customerVaultId con el procesador en este paso ni envía un webhook.
curl -X POST https://api-pay.zelta.dev/v1/customers/7315555666677778888/vault \
-H "X-API-Key: tu-api-key-de-proyecto" \
-H "Content-Type: application/json" \
-d '{
"paymentProvider": "nmi",
"customerVaultId": "1234567890",
"cardLast4": "1111",
"cardBrand": "visa",
"cardBin": "411111",
"cardExp": "1028"
}'Las colecciones son solo tarjeta
El vaulteo solo aplica a tarjetas (NMI o Emetec). Yappy y PayPal no pueden almacenar una tarjeta para cobros posteriores, por lo que no pueden usarse para crear clientes vaulteados.
Errores
| Código | HTTP | Cuándo |
|---|---|---|
ERR_CUSTOMER_NOT_FOUND | 404 | El cliente no existe en el proyecto |
ERR_API_KEY_NOT_PROJECT_SCOPED | 403 | Usaste una API key de la cuenta en lugar de la del proyecto |
ERR_INVALID_FIELDS | 400 | fields en GET /v1/customers incluye un campo que no pertenece al objeto cliente |
ERR_INVALID_RELATIONS | 400 | Enviaste expand en GET /v1/customers |
Los cuerpos o parámetros inválidos devuelven 400 con el formato de .
Siguientes pasos
- — captura la tarjeta y crea el cliente en un solo paso
- — cobra a un cliente vaulteado
- — suscribe a un cliente vaulteado a un plan
- — el evento
customer.created