Referencia API
Documentación general de la API pública de Zelta CRM: URL base, límites de uso, formato de respuestas y códigos de error.
URL Base
Producción:
https://api-crm.zelta.dev/public/v1Desarrollo:
https://dev-api-crm.zelta.dev/public/v1Todas las solicitudes deben usar HTTPS. Las solicitudes HTTP serán rechazadas.
Autenticación
Todas las solicitudes requieren el header:
Authorization: Bearer zk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxConsulta la para obtener una API Key y conocer los permisos disponibles.
Documentación interactiva
La API expone una interfaz Swagger UI y un documento OpenAPI para explorar y probar los endpoints sin necesidad de configurar un cliente HTTP.
| Recurso | URL | Autenticación |
|---|---|---|
| Swagger UI | /public/v1/docs | No requerida |
| OpenAPI JSON | /public/v1/doc | No requerida |
Prueba los endpoints directamente
Accede a https://api-crm.zelta.dev/public/v1/docs desde tu navegador para explorar todos los endpoints, ver los esquemas de datos y enviar solicitudes de prueba sin salir de la documentación.
Límites de uso (Rate Limiting)
La API pública tiene un límite de 60 solicitudes por minuto por API Key. Este límite se aplica de forma independiente para cada clave.
| Parámetro | Valor |
|---|---|
| Solicitudes permitidas | 60 |
| Ventana de tiempo | 60 segundos |
| Scope del límite | Por API Key |
Cuando se supera el límite, la API responde con estado 429 y el código de error API_KEY_RATE_LIMITED. Espera unos segundos antes de reintentar.
Sin headers de cuota
La API no incluye headers de cuota restante (X-RateLimit-*) en las respuestas. Si recibes un error 429, implementa un mecanismo de reintentos con espera exponencial en tu cliente.
Endpoints disponibles
| Método | Ruta | Descripción |
|---|---|---|
GET | /contacts | Listar contactos (paginación por cursor) |
POST | /contacts | Crear un nuevo contacto |
GET | /contacts/:id | Obtener un contacto por ID |
PUT | /contacts/:id | Actualizar un contacto |
DELETE | /contacts/:id | Eliminar un contacto |
GET | /companies | Listar empresas (paginación por cursor) |
POST | /companies | Crear una nueva empresa |
GET | /companies/:id | Obtener una empresa por ID |
PUT | /companies/:id | Actualizar una empresa |
DELETE | /companies/:id | Eliminar una empresa |
GET | /deals | Listar negocios (paginación por cursor) |
POST | /deals | Crear un nuevo negocio |
GET | /deals/:id | Obtener un negocio por ID |
PUT | /deals/:id | Actualizar un negocio |
DELETE | /deals/:id | Eliminar un negocio |
PATCH | /deals/:id/stage | Mover un negocio de etapa |
GET | /activities | Listar actividades (paginación por cursor) |
POST | /activities | Crear una nueva actividad |
GET | /activities/:id | Obtener una actividad por ID |
PUT | /activities/:id | Actualizar una actividad |
DELETE | /activities/:id | Eliminar una actividad |
PATCH | /activities/:id/complete | Marcar una actividad como completada |
GET | /products | Listar productos (paginación por cursor) |
POST | /products | Crear un nuevo producto |
GET | /products/:id | Obtener un producto por ID |
PUT | /products/:id | Actualizar un producto |
DELETE | /products/:id | Eliminar un producto |
GET | /search | Búsqueda global entre todos los recursos |
Formato de respuestas
Lista de recursos
Los endpoints de listado devuelven un envelope con paginación por cursor:
{
"data": [
{
"id": "abc123",
"name": "María García",
"email": "[email protected]"
}
],
"nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI0LTAxLTE1VDEwOjAwOjAwWiIsImlkIjoiYWJjMTIzIn0=",
"hasMore": true
}Recurso individual
Los endpoints que retornan un solo recurso (GET por ID, POST, PUT) devuelven el objeto directamente dentro de data:
{
"data": {
"id": "abc123",
"name": "María García",
"email": "[email protected]",
"phone": "+507 6000-0000",
"createdAt": "2024-01-15T10:00:00.000Z"
}
}Eliminación exitosa
{
"success": true
}Respuesta de error
{
"error": {
"code": "CONTACT_NOT_FOUND",
"message": "Contacto no encontrado"
}
}Error de validación
Cuando los datos enviados no cumplen con las validaciones requeridas, la respuesta incluye detalles por campo:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Error de validación",
"details": [
{
"field": "email",
"message": "Formato de correo electrónico inválido"
}
]
}
}Paginación
La API utiliza paginación por cursor en todos los endpoints de listado. Este método es más eficiente que la paginación por número de página para conjuntos de datos grandes o que cambian frecuentemente.
Parámetros de consulta
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
cursor | string | — | Cursor opaco retornado por la respuesta anterior |
limit | integer | 25 | Número de resultados por página (mínimo 1, máximo 100) |
Campos de respuesta
| Campo | Tipo | Descripción |
|---|---|---|
data | array | Resultados de la página actual |
nextCursor | string | null | Cursor para obtener la siguiente página. null cuando no hay más resultados |
hasMore | boolean | true si existen más resultados después de la página actual |
Ejemplo de paginación:
# Primera página
curl "https://api-crm.zelta.dev/public/v1/contacts?limit=10" \
-H "Authorization: Bearer $ZELTA_API_KEY"
# Siguiente página usando el cursor retornado
curl "https://api-crm.zelta.dev/public/v1/contacts?limit=10&cursor=eyJjcmVhdGVkQXQiOi4uLn0=" \
-H "Authorization: Bearer $ZELTA_API_KEY"Cuándo parar de paginar
Cuando hasMore sea false o nextCursor sea null, has llegado al final del conjunto de resultados.
Campos sanitizados
Por seguridad y privacidad, los siguientes campos nunca se incluyen en las respuestas de la API pública, aunque existan internamente:
| Campo omitido | Razón |
|---|---|
tenantId | Dato interno de aislamiento multi-tenant |
deletedAt | Campo de borrado lógico interno |
deletedBy | Campo de auditoría interna |
searchVector | Índice de texto completo interno (PostgreSQL tsvector) |
searchTsv | Índice de texto completo interno (PostgreSQL tsvector) |
Códigos de estado HTTP
| Código | Estado | Descripción |
|---|---|---|
200 | OK | Solicitud exitosa |
201 | Created | Recurso creado exitosamente |
400 | Bad Request | Datos inválidos, faltantes o formato incorrecto |
401 | Unauthorized | API Key ausente, inválida, expirada o revocada |
403 | Forbidden | La API Key no tiene permisos para esta operación |
404 | Not Found | El recurso solicitado no existe |
423 | Locked | El espacio de trabajo está suspendido |
429 | Too Many Requests | Se superó el límite de solicitudes por minuto |
500 | Internal Server Error | Error interno del servidor — contacta a soporte |
Códigos de error
| Código | HTTP | Descripción |
|---|---|---|
INVALID_API_KEY | 401 | La API Key no existe o el formato es incorrecto |
API_KEY_EXPIRED | 401 | La API Key superó su fecha de expiración |
API_KEY_REVOKED | 401 | La API Key fue revocada manualmente |
API_KEY_RATE_LIMITED | 429 | Se superó el límite de 60 solicitudes por minuto |
API_KEY_FORBIDDEN | 403 | La API Key no tiene permiso para esta operación |
TENANT_NOT_FOUND | 401 | El espacio de trabajo asociado a la clave no existe |
TENANT_SUSPENDED | 423 | El espacio de trabajo está suspendido |
CONTACT_NOT_FOUND | 404 | El contacto solicitado no existe o fue eliminado |
COMPANY_NOT_FOUND | 404 | La empresa solicitada no existe o fue eliminada |
DEAL_NOT_FOUND | 404 | El negocio solicitado no existe o fue eliminado |
ACTIVITY_NOT_FOUND | 404 | La actividad solicitada no existe o fue eliminada |
PRODUCT_NOT_FOUND | 404 | El producto solicitado no existe o fue eliminado |
VALIDATION_ERROR | 400 | Los datos enviados no cumplen con las validaciones requeridas |
INTERNAL_ERROR | 500 | Error interno del servidor |
Siguientes pasos
- — Cómo obtener y usar una API Key
- — Listar, crear y gestionar contactos por API
- — Listar, crear y gestionar empresas por API