Contactos
Gestiona los contactos de tu espacio de trabajo. Los contactos representan personas individuales — prospectos, clientes, proveedores o cualquier persona con la que tu equipo interactúa.
Autenticación
Requiere header Authorization: Bearer {token}. Ver .
Permisos requeridos
Las operaciones sobre contactos requieren los permisos contacts:read, contacts:create, contacts:update o contacts:delete según corresponda. Los usuarios con acceso limitado al ámbito propio (contacts:read:own) solo ven los contactos asignados a ellos.
Listar contactos
GET /contactsRetorna una lista paginada de contactos del espacio de trabajo. Soporta filtros, búsqueda de texto libre y ordenamiento.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
cursor | string | No | Cursor de paginación. Usa el valor nextCursor de la respuesta anterior |
limit | integer | No | Cantidad de resultados por página. Mínimo 1, máximo 100. Default: 25 |
sort | string | No | Campo por el que ordenar. Valores: created_at, updated_at, first_name, last_name, email, score. Default: created_at |
order | string | No | Dirección del ordenamiento: asc o desc. Default: desc |
search | string | No | Búsqueda de texto libre sobre nombre, email y teléfono |
owner_id | string | No | Filtra por ID del responsable asignado |
source | string | No | Filtra por ID o handle de la fuente de origen |
created_after | string | No | Retorna solo contactos creados después de esta fecha (ISO 8601, ej. 2025-01-01T00:00:00Z) |
created_before | string | No | Retorna solo contactos creados antes de esta fecha (ISO 8601) |
Ejemplos
curl -X GET "https://api-crm.zelta.dev/public/v1/contacts?limit=10&sort=created_at&order=desc" \
-H "Authorization: Bearer tu-api-key-aqui"Ejemplo JS
const response = await fetch(
'https://api-crm.zelta.dev/public/v1/contacts?limit=10&sort=created_at&order=desc',
{
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.data); // array de contactos
console.log(data.nextCursor); // cursor para la siguiente páginaEjemplo Py
import requests
response = requests.get(
'https://api-crm.zelta.dev/public/v1/contacts',
params={
'limit': 10,
'sort': 'created_at',
'order': 'desc'
},
headers={
'Authorization': 'Bearer tu-api-key-aqui'
}
)
data = response.json()
contacts = data['data']
next_cursor = data.get('nextCursor')Respuesta exitosa
HTTP 200 OK
{
"data": [
{
"id": "cnp_j3k9mxqz7t",
"firstName": "María",
"lastName": "González",
"email": "[email protected]",
"phone": "+507 6123-4567",
"mobile": null,
"companyId": "cmp_r8v2wlnp4s",
"companyName": "Empresa ABC S.A.",
"position": "Directora de Compras",
"sourceId": "src_q1m5tyk8xn",
"sourceName": "Sitio web",
"score": 72,
"ownerId": "usr_b4h7czjp9e",
"ownerName": "Carlos Rodríguez",
"tags": [
{
"id": "tag_x2f6nsvd3w",
"label": "Cliente VIP",
"handle": "cliente-vip",
"color": "#6366F1"
}
],
"customData": {},
"addressCountryId": 171,
"addressCountryName": "Panamá",
"addressStateId": 1234,
"addressStateName": "Panamá",
"addressCityId": 56789,
"addressCityName": "Ciudad de Panamá",
"createdAt": "2025-03-15T10:30:00.000Z",
"updatedAt": "2025-04-01T08:45:00.000Z"
}
],
"nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI1LTAzLTE1VDEwOjMwOjAwLjAwMFoiLCJpZCI6ImNucF9qM2s5bXhxejd0In0=",
"hasMore": true
}Obtener un contacto
GET /contacts/:idRetorna los datos completos de un contacto específico por su ID.
Ejemplos
curl -X GET "https://api-crm.zelta.dev/public/v1/contacts/cnp_j3k9mxqz7t" \
-H "Authorization: Bearer tu-api-key-aqui"Ejemplo JS
const contactId = 'cnp_j3k9mxqz7t';
const response = await fetch(
`https://api-crm.zelta.dev/public/v1/contacts/${contactId}`,
{
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_KEY}`
}
}
);
const contact = await response.json();Ejemplo Py
import requests
contact_id = 'cnp_j3k9mxqz7t'
response = requests.get(
f'https://api-crm.zelta.dev/public/v1/contacts/{contact_id}',
headers={'Authorization': 'Bearer tu-api-key-aqui'}
)
contact = response.json()Respuesta exitosa
HTTP 200 OK
Retorna el objeto completo del contacto con todos sus campos. Ver .
Crear un contacto
POST /contactsCrea un nuevo contacto en el espacio de trabajo.
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
firstName | string | Sí | Nombre del contacto. Máximo 100 caracteres |
lastName | string | Sí | Apellido del contacto. Máximo 100 caracteres |
email | string | No | Correo electrónico. Debe ser un email válido |
phone | string | No | Teléfono principal (incluir código de país, ej. +507 6123-4567) |
mobile | string | No | Teléfono móvil alternativo |
companyId | string | No | ID de la empresa a la que pertenece el contacto |
position | string | No | Cargo o puesto del contacto en su empresa |
sourceId | string | No | ID de la fuente de origen del contacto (ej. sitio web, referido) |
ownerId | string | No | ID del usuario responsable del contacto. Si se omite, se asigna al usuario que realiza la solicitud |
tagIds | string[] | No | Array de IDs de etiquetas a asignar al contacto |
customData | object | No | Datos de campos personalizados definidos en el espacio de trabajo |
addressCountryId | integer | No | ID del país (tabla de referencia geográfica) |
addressStateId | integer | No | ID del estado o provincia |
addressCityId | integer | No | ID de la ciudad |
Campos personalizados
Los campos en customData deben coincidir con los campos personalizados configurados en Ajustes > Campos personalizados para la entidad contacto. Los campos con isRequired: true son obligatorios.
Ejemplos
curl -X POST "https://api-crm.zelta.dev/public/v1/contacts" \
-H "Authorization: Bearer tu-api-key-aqui" \
-H "Content-Type: application/json" \
-d '{
"firstName": "María",
"lastName": "González",
"email": "[email protected]",
"phone": "+507 6123-4567",
"companyId": "cmp_r8v2wlnp4s",
"position": "Directora de Compras",
"tagIds": ["tag_x2f6nsvd3w"],
"addressCountryId": 171,
"addressStateId": 1234,
"addressCityId": 56789
}'Ejemplo JS
const response = await fetch('https://api-crm.zelta.dev/public/v1/contacts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
firstName: 'María',
lastName: 'González',
email: '[email protected]',
phone: '+507 6123-4567',
companyId: 'cmp_r8v2wlnp4s',
position: 'Directora de Compras',
tagIds: ['tag_x2f6nsvd3w'],
addressCountryId: 171,
addressStateId: 1234,
addressCityId: 56789
})
});
const contact = await response.json();
console.log(contact.id); // ID del nuevo contactoEjemplo Py
import requests
response = requests.post(
'https://api-crm.zelta.dev/public/v1/contacts',
headers={
'Authorization': 'Bearer tu-api-key-aqui',
'Content-Type': 'application/json'
},
json={
'firstName': 'María',
'lastName': 'González',
'email': '[email protected]',
'phone': '+507 6123-4567',
'companyId': 'cmp_r8v2wlnp4s',
'position': 'Directora de Compras',
'tagIds': ['tag_x2f6nsvd3w'],
'addressCountryId': 171,
'addressStateId': 1234,
'addressCityId': 56789
}
)
contact = response.json()
print(contact['id'])Respuesta exitosa
HTTP 201 Created
Retorna el objeto completo del contacto recién creado.
Actualizar un contacto
PUT /contacts/:idActualiza los campos de un contacto existente. Solo se actualizan los campos incluidos en el cuerpo — los campos omitidos conservan su valor actual.
Cuerpo de la solicitud
Todos los campos son opcionales. Ver la tabla de para las definiciones de cada campo.
Ejemplos
curl -X PUT "https://api-crm.zelta.dev/public/v1/contacts/cnp_j3k9mxqz7t" \
-H "Authorization: Bearer tu-api-key-aqui" \
-H "Content-Type: application/json" \
-d '{
"position": "Gerente General",
"phone": "+507 6999-0001"
}'Ejemplo JS
const contactId = 'cnp_j3k9mxqz7t';
const response = await fetch(
`https://api-crm.zelta.dev/public/v1/contacts/${contactId}`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
position: 'Gerente General',
phone: '+507 6999-0001'
})
}
);
const updated = await response.json();Ejemplo Py
import requests
contact_id = 'cnp_j3k9mxqz7t'
response = requests.put(
f'https://api-crm.zelta.dev/public/v1/contacts/{contact_id}',
headers={
'Authorization': 'Bearer tu-api-key-aqui',
'Content-Type': 'application/json'
},
json={
'position': 'Gerente General',
'phone': '+507 6999-0001'
}
)
updated = response.json()Respuesta exitosa
HTTP 200 OK
Retorna el objeto completo del contacto con los campos actualizados.
Eliminar un contacto
DELETE /contacts/:idElimina un contacto de forma lógica (soft delete). El contacto no se borra permanentemente — queda inaccesible desde la API y la interfaz, pero puede recuperarse contactando al soporte.
Ejemplos
curl -X DELETE "https://api-crm.zelta.dev/public/v1/contacts/cnp_j3k9mxqz7t" \
-H "Authorization: Bearer tu-api-key-aqui"Ejemplo JS
const contactId = 'cnp_j3k9mxqz7t';
const response = await fetch(
`https://api-crm.zelta.dev/public/v1/contacts/${contactId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.ZELTA_API_KEY}`
}
}
);
const result = await response.json();
// { "success": true }Ejemplo Py
import requests
contact_id = 'cnp_j3k9mxqz7t'
response = requests.delete(
f'https://api-crm.zelta.dev/public/v1/contacts/{contact_id}',
headers={'Authorization': 'Bearer tu-api-key-aqui'}
)
result = response.json()
# {'success': True}Respuesta exitosa
HTTP 200 OK
{
"success": true
}Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único del contacto |
firstName | string | Nombre del contacto |
lastName | string | Apellido del contacto |
email | string | null | Correo electrónico |
phone | string | null | Teléfono principal |
mobile | string | null | Teléfono móvil alternativo |
companyId | string | null | ID de la empresa asociada |
companyName | string | null | Nombre de la empresa (resuelto por JOIN) |
position | string | null | Cargo del contacto |
sourceId | string | null | ID de la fuente de origen |
sourceName | string | null | Nombre de la fuente (resuelto por JOIN) |
score | integer | Puntuación del contacto (0–100) |
ownerId | string | null | ID del responsable asignado |
ownerName | string | null | Nombre completo del responsable (resuelto por JOIN) |
tags | array | Etiquetas asignadas al contacto |
tags[].id | string | ID de la etiqueta |
tags[].label | string | Nombre visible de la etiqueta |
tags[].handle | string | Identificador slug de la etiqueta |
tags[].color | string | Color hex de la etiqueta |
customData | object | Valores de campos personalizados |
addressCountryId | integer | null | ID del país |
addressCountryName | string | null | Nombre del país |
addressStateId | integer | null | ID del estado o provincia |
addressStateName | string | null | Nombre del estado o provincia |
addressCityId | integer | null | ID de la ciudad |
addressCityName | string | null | Nombre de la ciudad |
createdAt | string | Fecha de creación en formato ISO 8601 |
updatedAt | string | Fecha de última actualización en formato ISO 8601 |
Problemas comunes
| Error | Causa | Solución |
|---|---|---|
401 Unauthorized | API Key inválida o ausente | Verifica que el header Authorization incluya un token válido |
403 Forbidden | Sin permisos suficientes | El rol asociado a tu API Key no tiene el permiso contacts:read o contacts:create |
404 Not Found | Contacto no encontrado | Verifica que el ID sea correcto y pertenezca al espacio de trabajo del token |
422 Unprocessable Entity | Error de validación | Revisa los campos requeridos (firstName, lastName) y el formato del email |
400 — companyId inválido | La empresa no existe | Verifica que el companyId sea correcto y no esté eliminado |
400 — ownerId inválido | El usuario no pertenece al workspace | Usa solo IDs de miembros activos del espacio de trabajo |
Límite de resultados
El parámetro limit acepta un máximo de 100 registros por página. Para conjuntos de datos grandes, itera usando el campo nextCursor hasta que hasMore sea false.
Siguientes pasos
- — Asocia contactos a empresas
- — Vincula contactos a oportunidades de venta
- — Cómo generar y usar tu API Key