API de contactos
Un contacto es una persona a la que envías mensajes: su nombre, número de teléfono, correo electrónico, canal, etiquetas, campos personalizados y las listas y campañas a las que pertenece. La API de contactos te permite crear contactos, buscarlos, actualizarlos, etiquetarlos, importarlos de forma masiva y eliminarlos, todo ello sin usar el panel de control.
Todas las rutas de esta página son relativas a la URL base:
https://api.youraiconnector.com/v1
Por lo tanto, /contacts significa https://api.youraiconnector.com/v1/contacts.
¿Eres nuevo en la API? Lee primero Acceso a la API: cubre cómo generar tu clave de API, las tres formas de autenticación, los límites de frecuencia y el formato de error. Todo lo que aparece en esta página asume que ya tienes una clave de API funcional.
Acerca de los ID de contacto
Cada contacto tiene un ID único. El ID que recibes al crear un contacto (en data.contactId) es el mismo ID que utilizas en cualquier otro lugar: para obtener, actualizar, etiquetar, enviar un mensaje o eliminar ese contacto. Guárdalo una vez y reutilízalo.
No tienes que crear un contacto para obtener su ID. También puedes buscarlo por número de teléfono o correo electrónico (consulta Obtener un contacto), o navegar por todos tus contactos (consulta Listar contactos). Cada una de esas opciones devuelve el mismo ID.
Crear un contacto
POST /contacts
Añade un nuevo contacto a tu cuenta. Se requiere un número de teléfono con código de país; un correo electrónico por sí solo no es suficiente. Todo lo demás es opcional.
Opcionalmente, puedes añadir el nuevo contacto directamente a una o más listas con listId (una sola lista) o listIds (una matriz). Si se envían ambos, listIds tiene prioridad.
Cualquier campo que envíe que no sea uno de los campos de creación estándar enumerados en la tabla de campos Crear un contacto a continuación (phoneNumber, firstName, lastName, email, channel, is_bot_active, is_private, lead_profile, listId, listIds, custom_fields) se almacena automáticamente como un campo personalizado, por lo que una carga útil plana de una herramienta como Make o Zapier funciona sin anidamiento. También puede pasar un objeto custom_fields explícito.
| Campo | Requerido | Descripción |
|---|---|---|
phoneNumber |
Sí | El número de teléfono del contacto, con código de país (p. ej., +15551234567). |
firstName |
No | Nombre. |
lastName |
No | Apellido. |
email |
No | Dirección de correo electrónico. |
channel |
No | Canal de mensajería. Uno de whatsapp, sms, whatsapp_web. El valor predeterminado es whatsapp. |
is_bot_active |
No | Si el asistente de IA responde a este contacto. El valor predeterminado es true. |
is_private |
No | Marcar el contacto como privado. Cuando es true, el asistente de IA se desactiva para ellos. El valor predeterminado es false. |
lead_profile |
No | Notas de texto libre sobre el cliente potencial. |
listId |
No | Un único ID de lista al que añadir el contacto. |
listIds |
No | Una matriz de ID de lista a los que añadir el contacto (tiene prioridad sobre listId). |
custom_fields |
No | Un objeto con tus propios campos de clave/valor. También puedes pasarlos como claves de nivel superior. |
cURL
curl -X POST "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"is_bot_active": true,
"listIds": ["list123", "list456"]
}'
JavaScript
const res = await fetch("https://api.youraiconnector.com/v1/contacts", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
phoneNumber: "+15551234567",
firstName: "Jane",
lastName: "Smith",
email: "jane@example.com",
is_bot_active: true,
listIds: ["list123", "list456"],
}),
});
const data = await res.json();
console.log(data.data.contactId);
Python
import requests
res = requests.post(
"https://api.youraiconnector.com/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"phoneNumber": "+15551234567",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"is_bot_active": True,
"listIds": ["list123", "list456"],
},
)
print(res.json()["data"]["contactId"])
Respuesta
{
"success": true,
"data": {
"message": "Successfully created new contact",
"contactId": "contact_abc123",
"listsAdded": ["list123", "list456"]
}
}
El ID del nuevo contacto se encuentra en data.contactId. Las listas a las que fue añadido se devuelven en data.listsAdded.
No se crean duplicados. Si ya existe un contacto con el mismo número de teléfono, la llamada de creación no lo crea ni lo devuelve. La respuesta regresa con un estado HTTP
200y unerror_codede409en el cuerpo, así que realice la bifurcación enerror_codeen lugar de en el estado HTTP:{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user." }Para trabajar con un contacto existente después de un
error_codede409, búsquelo con Obtener un contacto por teléfono o correo electrónico —GET /contacts?phoneNumber=...— y reutilice el ID que devuelve.
Las grafías equivalentes de WhatsApp cuentan como el mismo número. Algunos países tienen dos grafías válidas para la misma línea móvil y WhatsApp puede informar cualquiera de ellas: México (
+52…y la+521…heredada), Brasil (con o sin el noveno dígito) y Argentina (con o sin el9después del+54). La comprobación de duplicados al crear yGET /contacts?phoneNumber=coincide en ambas grafías, por lo que obtendrá el contacto existente independientemente de la forma que envíe. Elphone_numberalmacenado en el contacto nunca se sobrescribe.
Obtener un contacto por teléfono o correo electrónico
GET /contacts?phoneNumber=... o GET /contacts?email=...
Busca un único contacto y devuelve el objeto de contacto completo y enriquecido, incluyendo sus listas, etiquetas y campañas resueltas en pares { id, name }, además del último mensaje intercambiado.
Pasa o bien phoneNumber (en formato internacional) o bien email. Si no pasas ninguno, este mismo endpoint cambia al modo Listar contactos.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?phoneNumber=%2B15551234567&apiKey=YOUR_API_KEY"
JavaScript
const phone = encodeURIComponent("+15551234567");
const res = await fetch(`https://api.youraiconnector.com/v1/contacts?phoneNumber=${phone}`, {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"phoneNumber": "+15551234567"},
)
print(res.json()["contact"])
Respuesta
{
"success": true,
"contactId": "contact_abc123",
"contact": {
"id": "contact_abc123",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"phoneNumber": "+15551234567",
"channel": "whatsapp",
"isBotActive": true,
"isPrivate": false,
"doNotDisturb": false,
"lead_profile": null,
"avatarUrl": "https://example.com/photo.jpg",
"customFields": {},
"lists": [{ "id": "list123", "name": "VIP customers" }],
"tags": [{ "id": "tagHotLead", "name": "Hot lead" }],
"campaigns": [{ "id": "campaign789", "name": "Spring promo" }],
"currentCampaign": { "id": "campaign789", "name": "Spring promo" },
"lastMessage": {
"direction": "inbound",
"body": "Sounds good, thanks!",
"status": "received",
"timestamp": "2026-06-09T10:21:00.000Z"
}
}
}
El ID del contacto se devuelve tanto en el nivel superior (contactId) como dentro del objeto (contact.id). Si no hay coincidencias, obtienes un 404 con { "success": false, "message": "Contact not found" }.
avatarUrles la foto de perfil del contacto, obtenida de WhatsApp o Meta cuando le envían un mensaje. Es de solo lectura: no puede establecerla y aparece comonullpara los contactos que no tienen foto o que le contactan a través de un canal que no la comparte. Trate el enlace como temporal en lugar de almacenarlo, ya que algunos de estos enlaces de fotos caducan y se actualizan automáticamente. (En el punto final de la lista a continuación, el mismo valor se denominaavatar_url.)
Números de teléfono en URLs. Un signo
+en una cadena de consulta debe estar codificado en la URL como%2B; de lo contrario, se interpreta como un espacio. Los ejemplos anteriores hacen esto por ti.
Obtener un contacto por ID
GET /contacts/{contactId}
Cuando ya tenga el ID de un contacto, recupérelo directamente. La estructura de la respuesta es idéntica a la de la búsqueda anterior.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["contact"])
Un ID de contacto que no existe en su cuenta devuelve un 404.
Obtener estadísticas de contacto
GET /contacts/{contactId}/stats
Devuelve estadísticas agregadas de mensajes para un contacto: totales, respuestas de IA frente a humanas, créditos gastados y marcas de tiempo del primer y último mensaje.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.totalMessages, data.creditsUsed);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats",
headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["totalMessages"], data["creditsUsed"])
Respuesta
{
"success": true,
"totalMessages": 48,
"sent": 21,
"received": 27,
"aiReplies": 18,
"humanReplies": 3,
"creditsUsed": 34,
"botMessageCount": 18,
"firstMessageAt": "2026-05-01T09:00:00.000Z",
"lastMessageAt": "2026-06-09T10:21:00.000Z"
}
botMessageCount es el mismo contador de mensajes de IA que el botón “restablecer” en la aplicación pone a cero para un contacto. creditsUsed es el total de créditos acumulados para este contacto, no solo los números de esta respuesta. Un ID de contacto que no existe en su cuenta devuelve un 404.
Listar contactos
GET /contacts
Llame a GET /contacts sin phoneNumber ni email para paginar a través de todos sus contactos, empezando por los más recientes. Cada página devuelve resúmenes compactos de los contactos (las listas, etiquetas y campañas se devuelven como matrices de ID en lugar de objetos completos) y un next_cursor.
| Parámetro de consulta | Descripción |
|---|---|
limit |
Tamaño de página. El valor predeterminado es 50, el máximo es 100. |
cursor |
El valor next_cursor de la página anterior. Omítalo en la primera página. |
listId |
Opcional. Solo devuelve los contactos que pertenecen a esta lista. |
Para recorrer cada página: realice la primera llamada sin un cursor y, a continuación, siga pasando el next_cursor devuelto como cursor. Deténgase cuando next_cursor sea null; eso significa que no hay más resultados.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&apiKey=YOUR_API_KEY"
# next page:
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&cursor=contact_abc123&apiKey=YOUR_API_KEY"
JavaScript
async function listAllContacts() {
const all = [];
let cursor = null;
do {
const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
all.push(...data.contacts);
cursor = data.next_cursor;
} while (cursor);
return all;
}
Python
import requests
def list_all_contacts():
all_contacts = []
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
params=params,
)
data = res.json()
all_contacts.extend(data["contacts"])
cursor = data["next_cursor"]
if not cursor:
break
return all_contacts
Respuesta
{
"success": true,
"contacts": [
{
"id": "contact_abc123",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"phone_number": "+15551234567",
"channel": "whatsapp",
"is_bot_active": true,
"is_private": false,
"do_not_disturb": false,
"avatar_url": "https://example.com/photo.jpg",
"custom_fields": {},
"created_at": "2026-06-01T09:00:00.000Z",
"list_ids": ["list123"],
"tag_ids": ["tagHotLead"],
"campaign_ids": ["campaign789"],
"current_campaign_id": "campaign789"
}
],
"next_cursor": "contact_abc123"
}
Nota: Filtrar por un listId que no existe en su cuenta devuelve un 404. Un cursor no válido devuelve un 400.
Contar contactos
GET /contacts/count
Devuelve cuántos contactos coinciden con un filtro, además de un desglose por canal, sin necesidad de paginarlos. Esta es la llamada correcta para cualquier pregunta de tipo “cuántos”: un widget de panel, una automatización o preguntar a Champ. Todos los filtros son opcionales y combinar varios reduce el recuento (un contacto debe coincidir con todos los que envíes).
| Parámetro de consulta | Descripción |
|---|---|
agentId |
Solo contactos asignados a este agente de IA. Pasa none para contactos sin agente asignado (aquellos que son atendidos por el agente predeterminado del canal). |
channel |
Solo contactos en este canal, p. ej., whatsapp, messenger, instagram, sms, email, chat_widget. |
tag |
Solo contactos que tengan esta etiqueta, por nombre de etiqueta (las mayúsculas/minúsculas no importan). Un nombre de etiqueta que no tengas devuelve un 404. |
listId |
Solo contactos en esta lista. |
botActive |
true o false: solo contactos cuyo asistente de IA esté activado o desactivado. |
status |
Solo contactos con este estado, p. ej., Lead. |
rules |
Un objeto de reglas JSON codificado en URL, usando la misma estructura que una lista inteligente (consulta La estructura smart_rules más abajo). No se puede combinar con los otros filtros. |
Si no envías ningún filtro, obtendrás el número total de contactos en tu cuenta.
cURL
# everything
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?apiKey=YOUR_API_KEY"
# only the contacts one agent handles on Messenger
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?agentId=agent_xyz789&channel=messenger&apiKey=YOUR_API_KEY"
JavaScript
const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count");
url.searchParams.set("agentId", "agent_xyz789");
url.searchParams.set("channel", "messenger");
const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
console.log(data.total);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"agentId": "agent_xyz789", "channel": "messenger"},
)
data = res.json()
print(data["total"])
Respuesta
{
"success": true,
"total": 3423,
"by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
"filters": { "agentId": "agent_xyz789" }
}
by_channel divide el mismo total por canal; los contactos que no están en ningún canal se cuentan bajo none. filters devuelve los filtros que se aplicaron, para que puedas verificar que la llamada hizo lo que pretendías.
Nota: Enviar rules junto con cualquier otro filtro, o un valor rules que no sea JSON válido, devuelve un 400. Un nombre de etiqueta o ID de lista que no exista en tu cuenta devuelve un 404.
Actualizar un contacto
PUT /contacts/{contactId}
Actualiza un contacto existente. Solo se modifican los campos que incluya; omita todo lo que no desee cambiar. Debe enviar al menos un campo, o recibirá un 400 (“No hay campos para actualizar”).
| Campo | Descripción |
|---|---|
firstName |
Nombre. |
lastName |
Apellido. |
email |
Dirección de correo electrónico. |
is_bot_active |
Si el asistente de IA responde a este contacto. |
is_private |
Marcar como privado. Establecer esto en true también desactiva el asistente de IA. |
do_not_disturb |
Pausar el alcance automatizado a este contacto. También evita que la IA responda. |
follow_ups_disabled |
Detener todos los seguimientos automatizados para este contacto (rápidos, de ciclo y de clientes potenciales en frío) mientras la IA sigue respondiendo a los mensajes que envían. Útil una vez que alguien ha comprado. Permanece desactivado hasta que lo vuelva a establecer en false. |
lead_profile |
Notas de cliente potencial en texto libre. |
custom_fields |
Un objeto de campos personalizados. Combinado por clave: solo se escriben las claves que envía, el resto de los campos personalizados existentes se mantienen. También puede pasar claves de campos personalizados en el nivel superior. |
cURL
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "firstName": "Jane", "do_not_disturb": true }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
method: "PUT",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ firstName: "Jane", do_not_disturb: true }),
});
const data = await res.json();
console.log(data.message);
Python
import requests
res = requests.put(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"firstName": "Jane", "do_not_disturb": True},
)
print(res.json()["message"])
Respuesta
{
"success": true,
"message": "Contact updated successfully"
}
Los campos personalizados se combinan, no se reemplazan. Enviar
{ "custom_fields": { "tier": "gold" } }solo establecetier; cualquier otro campo personalizado en el contacto permanece exactamente como estaba. Para eliminar un campo personalizado por completo en todos los contactos, utilice Eliminar un campo personalizado.
Agregar o eliminar etiquetas
POST /contacts/{contactId}/tags
Agrega y/o elimina etiquetas en un solo contacto en una llamada. Pase los ID de las etiquetas en addTagIds y removeTagIds. Al menos uno de los dos debe estar completo.
Las etiquetas ya deben existir en su cuenta; créelas primero a través del punto de conexión de etiquetas. Si el contacto o alguna etiqueta referenciada no existe, recibirá un 404.
| Campo | Descripción |
|---|---|
addTagIds |
Matriz de IDs de etiquetas para añadir al contacto. |
removeTagIds |
Matriz de IDs de etiquetas para eliminar del contacto. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"] }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
addTagIds: ["tagHotLead"],
removeTagIds: ["tagColdLead"],
}),
});
const data = await res.json();
console.log(data.added, data.removed);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"]},
)
data = res.json()
print(data["added"], data["removed"])
Respuesta
{
"success": true,
"contact_id": "contact_abc123",
"added": 1,
"removed": 1
}
Administre su biblioteca de etiquetas
Estos endpoints administran la etiqueta en sí (cambiarle el nombre o eliminarla de su cuenta), a diferencia de aplicar o eliminar una etiqueta en un contacto (consulte Agregar o eliminar etiquetas arriba). Cada etiqueta en su cuenta tiene un ID (tagId): el que se muestra en el administrador de etiquetas de su panel de control y el que se devuelve como data.tag_id cuando crea una etiqueta con POST /tags y un cuerpo JSON de { "name": "..." } (sin phoneNumber, email o contactId).
Actualizar una etiqueta
PUT /tags/{tagId}
Envíe solo los campos que va a cambiar.
| Campo | Descripción |
|---|---|
name |
El nombre de la etiqueta. |
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagHotLead?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Hot lead (Q3)" }'
Respuesta
{ "success": true, "tag_id": "tagHotLead" }
Un tagId que no existe en su cuenta devuelve un 404.
Eliminar una etiqueta
DELETE /tags/{tagId}
Elimina una etiqueta por ID. Esto no se puede deshacer: los contactos que tengan la etiqueta simplemente la perderán. Eliminar una etiqueta que ya no existe (o que nunca existió) devuelve 200 con deleted: 0 en lugar de un 404, ya que no hay nada que enumerar.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"
Respuesta
{ "success": true, "deleted": 1 }
Eliminar varias etiquetas a la vez
DELETE /tags
| Campo | Descripción |
|---|---|
tagIds |
Matriz de IDs de etiquetas a eliminar (máximo 1000). |
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tagIds": ["tagColdLead", "tagUnsubscribed"] }'
Respuesta
{ "success": true, "deleted": 2 }
Los IDs que no existen o que pertenecen a otra cuenta se omiten silenciosamente y no se cuentan en deleted.
Establecer una bandera de forma masiva
POST /contacts/bulk-flag
Establece una bandera booleana en muchos contactos a la vez. Hasta 500 IDs de contacto por solicitud. Los IDs que no existen en su cuenta se omiten y se cuentan en skipped.
| Campo | Descripción |
|---|---|
contactIds |
Matriz de IDs de contacto a actualizar (máx. 500). |
field |
Qué bandera establecer. Uno de bot_active (asistente de IA activado/desactivado), dnd (pausar alcance automatizado), spam, private. |
value |
El valor booleano al que establecer la bandera. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contactIds": ["contactId1", "contactId2"],
"field": "bot_active",
"value": false
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
contactIds: ["contactId1", "contactId2"],
field: "bot_active",
value: false,
}),
});
const data = await res.json();
console.log(data.updated, data.skipped);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"contactIds": ["contactId1", "contactId2"],
"field": "bot_active",
"value": False,
},
)
data = res.json()
print(data["updated"], data["skipped"])
Respuesta
{
"success": true,
"updated": 2,
"skipped": 0
}
Importar contactos de forma masiva
POST /contacts/import
Crea hasta 500 contactos en una sola llamada desde una matriz JSON. Cada registro necesita un phone_number en formato internacional; todo lo demás es opcional. Los registros con números de teléfono no válidos o canales no admitidos se omiten (no se crean), y cada registro omitido se informa con su índice y motivo, para que pueda corregir solo los fallos y volver a intentarlo.
Los números de teléfono que ya existen en su cuenta se omiten como duplicate de forma predeterminada. Envíe updateExisting: true para actualizar esos contactos en su lugar: los campos presentes en el registro sobrescriben los del contacto (first_name, last_name, email, lead_profile y custom_fields se combinan clave por clave), se añaden tags y el contacto se añade a listId. El canal, el número de teléfono y las banderas de bot nunca se cambian en un contacto existente.
Opcionalmente, puede añadir cada contacto importado (o actualizado) a una lista con listId, establecer un defaultChannel para los registros que no especifiquen uno, y etiquetar los registros con tags (nombres de etiquetas: las etiquetas que faltan se crean, las existentes se comparan sin distinguir entre mayúsculas y minúsculas).
Campos de nivel superior
| Campo | Requerido | Descripción |
|---|---|---|
contacts |
Sí | Matriz de registros de contacto (máx. 500). |
listId |
No | Lista a la que añadir cada contacto importado (y actualizado). Debe ser una lista en su cuenta. |
defaultChannel |
No | Canal aplicado a los registros que omiten channel. Uno de whatsapp, sms, whatsapp_web. El valor predeterminado es whatsapp. |
updateExisting |
No | true para actualizar los contactos cuyo número de teléfono ya existe en lugar de omitirlos como duplicate. El valor predeterminado es false. |
Campos por registro
| Campo | Requerido | Descripción |
|---|---|---|
phone_number |
Sí | Número de teléfono en formato internacional (se añade un + inicial si falta). |
first_name |
No | Nombre. |
last_name |
No | Apellido. |
email |
No | Dirección de correo electrónico. |
channel |
No | Uno de whatsapp, sms, whatsapp_web. Recurre a defaultChannel. |
is_bot_active |
No | Si el asistente de IA responde. El valor predeterminado es true. |
is_private |
No | Marcar como privado. El valor predeterminado es false. |
lead_profile |
No | Notas de cliente potencial en texto libre. |
custom_fields |
No | Objeto de claves y valores de campos personalizados. |
tags |
No | Matriz de nombres de etiquetas (también funciona una sola cadena "a; b"). Las etiquetas que no existen se crean; las existentes se comparan ignorando mayúsculas y minúsculas. Máx. 25 por registro. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{ "phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"] },
{ "phone_number": "+12025551235", "first_name": "Bob" }
],
"listId": "list123",
"defaultChannel": "whatsapp_web",
"updateExisting": true
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
contacts: [
{ phone_number: "+12025551234", first_name: "Ann", last_name: "Lee", tags: ["vip", "newsletter"] },
{ phone_number: "+12025551235", first_name: "Bob" },
],
listId: "list123",
defaultChannel: "whatsapp_web",
updateExisting: true,
}),
});
const data = await res.json();
console.log(`Imported ${data.imported}, updated ${data.updated}, skipped ${data.skipped.length}`);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"contacts": [
{"phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"]},
{"phone_number": "+12025551235", "first_name": "Bob"},
],
"listId": "list123",
"defaultChannel": "whatsapp_web",
"updateExisting": True,
},
)
data = res.json()
print(f"Imported {data['imported']}, updated {data['updated']}, skipped {len(data['skipped'])}")
Respuesta
{
"success": true,
"imported": 2,
"contact_ids": ["contact_abc123", "contact_def456"],
"updated": 0,
"updated_contact_ids": [],
"skipped": []
}
Si algunos registros no se pueden crear, aparecen en skipped con el motivo (aquí sin updateExisting, por lo que se omite el número existente):
{
"success": true,
"imported": 1,
"contact_ids": ["contact_abc123"],
"updated": 0,
"updated_contact_ids": [],
"skipped": [
{ "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
]
}
Con updateExisting: true, la misma solicitud informa del contacto existente bajo updated / updated_contact_ids en su lugar.
Posibles motivos de omisión: invalid_record, missing_phone_number, invalid_phone_number, invalid_channel, duplicate_in_request, duplicate, contact_limit_reached, create_failed.
Límites del plan. Si el límite de contactos de su plan no permite esta cantidad de nuevos contactos, toda la solicitud se rechaza de antemano con un
403. Si se alcanza el límite a mitad del proceso, los registros restantes se devuelven como omitidos con el motivocontact_limit_reached.
Importar contactos desde un archivo CSV
Para importaciones más grandes de lo que permite la importación masiva (hasta aproximadamente 50,000 filas), encole un trabajo de importación asíncrono para un archivo CSV que ya se encuentre en el almacenamiento de su cuenta y, a continuación, realice sondeos hasta que se complete.
Iniciar la importación
POST /contacts/import-csv
| Campo | Obligatorio | Descripción |
|---|---|---|
csvStoragePath |
Sí | Ruta de almacenamiento del archivo CSV, bajo users/{your account id}/imports/, que termina en .csv. |
listName |
Sí | Crea (o reutiliza) una lista con este nombre y añade a ella cada contacto importado. |
existingListRefs |
No | Matriz de IDs de listas existentes a las que también se añadirá cada contacto importado. |
defaultChannel |
No | Canal aplicado a las filas que no especifican uno. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"csvStoragePath": "users/abc123/imports/leads.csv",
"listName": "Webinar signups"
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
csvStoragePath: "users/abc123/imports/leads.csv",
listName: "Webinar signups",
}),
});
const data = await res.json();
console.log(data.job_id);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"csvStoragePath": "users/abc123/imports/leads.csv",
"listName": "Webinar signups",
},
)
job_id = res.json()["job_id"]
Respuesta (202 — la importación está en cola, aún no ha finalizado)
{
"success": true,
"job_id": "csvimp_abc123",
"status": "queued"
}
Cómo llevar el archivo al almacenamiento. Este endpoint inicia y realiza el seguimiento del trabajo de importación; no acepta una carga por sí mismo. El archivo CSV debe estar ya en
csvStoragePathantes de llamarlo; el propio importador de CSV del panel de control realiza esto como primer paso.
Consultar el trabajo de importación
GET /contacts/import-csv/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv/csvimp_abc123?apiKey=YOUR_API_KEY"
Respuesta
{
"success": true,
"job_id": "csvimp_abc123",
"status": "completed",
"imported": 812,
"updated": 0,
"skipped": 14,
"errors": [],
"error_message": null
}
status avanza a través de queued → processing → completed, o failed con el motivo en error_message. Un jobId que no existe en su cuenta devuelve un 404.
Exportar contactos
Inicia una exportación CSV asíncrona de sus contactos y devuelve un trabajo que debe sondear para verificar su finalización.
Iniciar la exportación
POST /contacts/export
| Campo | Obligatorio | Descripción |
|---|---|---|
listId |
No | Exportar solo los contactos que pertenecen a esta lista. |
contactIds |
No | Exportar solo estos ID de contacto específicos. |
Si deja ambos campos vacíos, se exportarán todos los contactos de su cuenta.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "listId": "list123" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ listId: "list123" }),
});
const data = await res.json();
console.log(data.job_id);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"listId": "list123"},
)
job_id = res.json()["job_id"]
Respuesta (202 — la exportación está en cola)
{
"success": true,
"job_id": "export_abc123",
"status": "queued"
}
Consultar el estado del trabajo de exportación
GET /contacts/export/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"
Respuesta
{
"success": true,
"job_id": "export_abc123",
"status": "completed",
"export_id": "exp_xyz789",
"contact_count": 812,
"error_message": null
}
Una vez que
statusesté"completed", recibiráexport_idycontact_count. La descarga del archivo CSV generado se realiza desde la página de Exportaciones de su panel de control.
Enviar un mensaje a un contacto
POST /contacts/{contactId}/send-message
Envía un mensaje a un contacto existente en el canal en el que ya se encuentre. El mensaje se pone en cola y se entrega en segundo plano; la respuesta confirma que fue aceptado, no que ya se haya entregado.
| Campo | Obligatorio | Descripción |
|---|---|---|
body |
Sí | El texto del mensaje a enviar. |
mediaUrl |
No | URL de un archivo multimedia para adjuntar. |
mediaContentType |
No | Tipo MIME del archivo multimedia adjunto (p. ej., image/jpeg). |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "body": "Hi! Your appointment is confirmed." }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ body: "Hi! Your appointment is confirmed." }),
});
const data = await res.json();
console.log(data.messageId);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"body": "Hi! Your appointment is confirmed."},
)
print(res.json()["messageId"])
Respuesta
{
"success": true,
"messageId": "aB3dE5fG7hI9jK1lM2nO",
"contactId": "contact_abc123",
"channel": "whatsapp",
"message": "Message created successfully. Delivery is being processed."
}
¿No puedes enviar mensajes ahora? Si el contacto tiene activado el modo no molestar o privado, o no se encuentra en un canal que pueda recibir mensajes salientes, la solicitud se rechaza con un
422y unerrorexplicativo.
Para enviar mensajes mediante número de teléfono, ID de Instagram u otra identidad de canal en lugar de un ID de contacto —y para obtener más información sobre mensajería en general—, consulta la API de mensajes.
Asignar un agente de IA a un contacto
POST /contacts/{contactId}/assign-agent
Mueve una conversación existente a un agente de IA diferente, a partir del siguiente mensaje. Es lo mismo que Asignar agente de IA en el menú de un chat, y el mismo paso que utiliza la acción Asignar agente de IA o campaña en las Automatizaciones.
| Campo | Obligatorio | Descripción |
|---|---|---|
agentId |
Sí | El ID del agente de IA que debe tomar el control, o null para borrar la asignación y que la conversación vuelva a la bandeja de entrada de su equipo. |
triggerAIResponse |
No | true hace que el agente recién asignado responda de inmediato a los últimos mensajes sin respuesta del contacto. El valor predeterminado es false. |
Cuidado con
triggerAIResponse: true: envía un mensaje al contacto en ese mismo momento, así que úsalo solo cuando quieras que reciban el mensaje ahora. En Messenger e Instagram, ese mensaje fallará si el contacto te escribió por última vez hace más de 24 horas.
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "agentId": "agent_xyz789" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ agentId: "agent_xyz789" }),
});
const data = await res.json();
console.log(data.data.agentId);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"agentId": "agent_xyz789"},
)
print(res.json()["data"]["agentId"])
Respuesta
{
"success": true,
"data": {
"contactId": "contact_abc123",
"agentId": "agent_xyz789",
"aiResponseTriggered": false
}
}
El agente debe pertenecer a la misma cuenta que el contacto; de lo contrario, la solicitud será rechazada con un
404o403. Encuentre los ID de los agentes en la página de Agentes de IA (la URL de cada agente termina con su ID).
Asignar un agente de IA a muchos contactos
POST /contacts/bulk-assign-agent
Mueve muchas conversaciones a un agente de IA diferente en una sola llamada, o borra la asignación para todos ellos con null. Es puramente un cambio de enrutamiento: no se envía ningún mensaje y el agente no responde a nadie. Cada contacto simplemente recibe el nuevo agente la próxima vez que escribe. (Por eso no hay triggerAIResponse aquí).
| Field | Required | Description |
|---|---|---|
agentId |
Yes | The AI agent that should take over, or null to clear the assignment. |
contactIds |
One of the three | Up to 500 contact IDs to move. |
filter |
One of the three | Pick the contacts on the server instead of listing them, newest first. Takes the same keys as the count endpoint’s filters: agentId (or none), channel, tag, listId, botActive, status. |
rules |
One of the three | A smart-list rules object — see The smart_rules shape. |
limit |
No | How many contacts to move in this call when you select with filter or rules. 1 to 500, defaults to 500. |
Envía exactamente uno de contactIds, filter o rules.
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_xyz789",
"filter": { "agentId": "agent_abc123", "channel": "messenger" }
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
agentId: "agent_xyz789",
filter: { agentId: "agent_abc123", channel: "messenger" },
}),
});
const data = await res.json();
console.log(data.updated, data.remaining);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"agentId": "agent_xyz789",
"filter": {"agentId": "agent_abc123", "channel": "messenger"},
},
)
data = res.json()
print(data["updated"], data["remaining"])
Respuesta
{
"success": true,
"agentId": "agent_xyz789",
"matched": 3415,
"updated": 500,
"skipped": 0,
"remaining": 2915,
"filters": { "agentId": "agent_abc123" }
}
matched es cuántos contactos encontró la selección en total, updated cuántos fueron movidos por esta llamada, skipped cuántos de los IDs que enviaste no se encontraron en tu cuenta y remaining cuántos siguen coincidiendo ahora que la llamada ha terminado.
Mover a todos. Debido a que una llamada mueve como máximo 500 contactos, un grupo grande requiere varias llamadas. Usa un filtro que deje de coincidir con un contacto una vez que se haya movido (por ejemplo, filter: { "agentId": "agent_abc123" } mientras asignas a agent_xyz789) y repite exactamente la misma llamada hasta que remaining devuelva 0. Cuando pasas contactIds en su lugar, remaining es siempre 0.
Asignar un contacto a un departamento
POST /contacts/{contactId}/department
“Asignar este cliente potencial a Ventas”: archiva un contacto en un departamento específico y, de forma predeterminada, se lo entrega a la persona de ese departamento que actualmente tenga menos contactos. Esto es independiente de asignar un agente de IA: un departamento responde a “qué equipo es el propietario de esto”, un agente responde a “qué IA responde a esto”, y configurar uno nunca elimina el otro.
| Campo | Obligatorio | Descripción |
|---|---|---|
department_id |
Sí | El departamento en el que archivar el contacto. Pase null para borrarlo. |
hand_to_member |
No | También entregar el contacto a la persona con menos carga de trabajo en ese departamento. El valor predeterminado es true. Nunca reasigna un contacto que ya pertenece a alguien. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "department_id": "dept_sales" }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ department_id: "dept_sales" }),
});
const data = await res.json();
console.log(data.assigned_to);
Python
import requests
res = requests.post(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"department_id": "dept_sales"},
)
print(res.json()["assigned_to"])
Respuesta
{
"success": true,
"department_id": "dept_sales",
"assigned_to": "member_uid_123"
}
assigned_to es null cuando el contacto ya pertenecía a alguien, o si pasó hand_to_member: false.
Vincular un contacto a través de canales
“Continuar en WhatsApp” (o SMS) busca o crea el contacto de esta persona en otro canal basado en teléfono y vincula ambos, de modo que el resto de la aplicación los reconozca como la misma persona.
Vincular a otro canal
POST /contacts/{contactId}/link-channel
| Campo | Obligatorio | Descripción |
|---|---|---|
channel |
Sí | El canal al que vincular. Uno de whatsapp, whatsapp_web, sms. |
phoneNumber |
No | Número de teléfono a utilizar en el nuevo canal. Por defecto, utiliza el número del contacto de origen. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link-channel?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "channel": "sms" }'
Respuesta
{
"success": true,
"data": {
"contact_id": "contact_def456",
"person_id": "person_xyz789",
"created": true
}
}
created le indica si se creó un nuevo contacto para el canal de destino o si se encontró y vinculó uno existente. Llamar a esto una segunda vez es seguro: devuelve el mismo contact_id con created: false en lugar de crear un duplicado.
Un 422 significa que la cuenta no puede realizar este vínculo en este momento: el contacto ya está en esa familia de canales, no tiene número de teléfono que utilizar o no hay ningún remitente conectado para el canal de destino. Un 409 significa que los dos contactos ya están vinculados a dos personas diferentes; primero debe desvincular uno.
Listar las conversaciones vinculadas de un contacto
GET /contacts/{contactId}/linked
Devuelve las otras conversaciones que corresponden a la misma persona que este contacto. Un contacto no vinculado devuelve una matriz vacía, no un 404; “esta persona no tiene otros canales” es un estado normal.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"
Respuesta
{
"success": true,
"data": [
{
"contact_id": "contact_def456",
"channel": "sms",
"custom_channel": null,
"first_name": "Jane",
"last_name": "Smith",
"phone_number": "+15551234567",
"last_message": "Sounds good, thanks!",
"last_message_timestamp": "2026-06-09T10:21:00.000Z",
"linked_from": {
"contact_id": "contact_abc123",
"channel": "whatsapp",
"linked_at": "2026-06-01T09:00:00.000Z",
"reason": "continue_on_channel"
}
}
]
}
Desvincular un contacto
DELETE /contacts/{contactId}/link
Elimina este contacto de su persona, de forma unilateral; cualquier otro contacto que siga vinculado a esa persona mantiene su vínculo, por lo que desvincular uno de tres no disuelve el grupo.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"
Respuesta
{ "success": true }
Obtener la foto de perfil de un contacto
POST /contacts/{contactId}/profile-pic
Obtiene (y almacena en caché) la foto de perfil de WhatsApp o Meta del contacto bajo demanda; la misma foto que se devuelve como avatarUrl en Obtener un contacto, actualizada.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"
Respuesta
{
"success": true,
"avatar_url": "https://example.com/photo.jpg",
"cached": false
}
cached: true significa que la URL proviene de una obtención reciente en lugar de una búsqueda nueva en el proveedor; las imágenes se almacenan en caché durante 7 días, y un contacto del que el proveedor informa que no tiene una foto accesible se almacena en caché como no disponible durante 24 horas. Cuando no hay ninguna imagen que obtener, se omite avatar_url y message explica el motivo.
Etiquetado automático de contactos con IA
Ejecuta las reglas de etiquetas de su cuenta sobre el historial completo de conversaciones de uno o más contactos y aplica (o elimina) etiquetas exactamente igual que el etiquetado en tiempo real que se ejecuta durante un chat en vivo: mismas reglas, mismo coste de crédito por etiqueta.
Iniciar una ejecución
POST /contacts/auto-tag
| Campo | Obligatorio | Descripción |
|---|---|---|
scope |
Sí | "contacts" para etiquetar contactos específicos, o "agent" para etiquetar cada conversación gestionada actualmente por un agente de IA. |
contact_ids |
Obligatorio cuando scope es "contacts" |
Matriz de IDs de contacto, de 1 a 500. |
agent_id |
Obligatorio cuando scope es "agent" |
El agente de IA cuyas conversaciones se van a etiquetar. Cuando scope es "contacts", esto es opcional y solo restringe qué reglas de etiqueta del agente se ejecutan. |
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "scope": "contacts", "contact_ids": ["contact_abc123", "contact_def456"] }'
Un único contacto se ejecuta en línea y devuelve el resultado inmediatamente:
{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }
Dos o más contactos (o scope: "agent") se ejecutan como un trabajo en segundo plano y devuelven 202 inmediatamente:
{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }
Consultar una ejecución
GET /contacts/auto-tag/run
Devuelve la ejecución actual (o más reciente) de la cuenta, para que pueda consultar el progreso sin tener que realizar el seguimiento de run_id usted mismo.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"
Respuesta
{
"success": true,
"run": {
"run_id": "m1x2y3-a1b2c3d4",
"status": "running",
"total": 214,
"processed": 58,
"tagged_contacts": 12,
"tags_applied": 15,
"tags_removed": 2,
"credits_charged": 15
}
}
run es null cuando la cuenta nunca ha iniciado una. status pasa de "running" a "completed" o "failed".
Solo puede haber una ejecución masiva en curso por cuenta a la vez; iniciar una segunda mientras otra está en ejecución devuelve 409 con error_code: "auto_tag_run_in_progress". Quedarse sin créditos en una ejecución de un solo contacto devuelve 402 con error_code: "insufficient_credits"; una ejecución masiva, en cambio, se detiene antes de tiempo e informa de hasta dónde llegó en run.
Eliminar un contacto
DELETE /contacts/{contactId}
Elimina permanentemente un contacto por ID, junto con su historial de mensajes. Esto no se puede deshacer. Para eliminar varios contactos en una sola llamada, utilice Eliminar contactos a continuación.
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.success);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
Respuesta
{
"success": true
}
Un ID de contacto que no existe en su cuenta, o que pertenece a una cuenta diferente, devuelve un 404.
Eliminar contactos
DELETE /contacts
Elimina permanentemente uno o varios contactos por ID en una sola llamada (hasta 500 ID). Los ID que no existen en tu cuenta se omiten y se contabilizan en skipped. Esta acción no se puede deshacer.
| Campo | Descripción |
|---|---|
contactIds |
Matriz de ID de contacto a eliminar (máx. 500). |
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "contactIds": ["contactId1", "contactId2"] }'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts", {
method: "DELETE",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ contactIds: ["contactId1", "contactId2"] }),
});
const data = await res.json();
console.log(`Deleted ${data.deleted}, skipped ${data.skipped}`);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"contactIds": ["contactId1", "contactId2"]},
)
data = res.json()
print(f"Deleted {data['deleted']}, skipped {data['skipped']}")
Respuesta
{
"success": true,
"deleted": 2,
"skipped": 0
}
Eliminar un campo personalizado
DELETE /contacts/custom-fields/{fieldKey}
Elimina una clave de campo personalizado de todos los contactos de su cuenta. Utilice esto para realizar una limpieza después de renombrar o retirar un campo personalizado. La clave solo puede contener letras, números, guiones bajos y guiones. Devuelve cuántos contactos fueron actualizados. Esta acción no se puede deshacer.
cURL
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh", {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(`Removed from ${data.updated} contacts`);
Python
import requests
res = requests.delete(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh",
headers={"X-API-Key": "YOUR_API_KEY"},
)
print(f"Removed from {res.json()['updated']} contacts")
Respuesta
{
"success": true,
"updated": 42
}
Nota: Una clave de campo con caracteres no admitidos devuelve un 400.
Listas
Las listas agrupan contactos. Una lista puede ser estática (tú decides quién está en ella) o inteligente (la pertenencia se calcula a partir de reglas y se mantiene actualizada automáticamente; consulta Organización de listas y contactos).
| Campo | Descripción |
|---|---|
name |
Obligatorio al crear. Hasta 100 caracteres. |
status |
live (predeterminado) o draft. En minúsculas. |
contact_ids |
Matriz de IDs de contacto para incluir en la lista. Solo listas estáticas. |
type |
static (predeterminado) o smart. |
smart_rules |
El conjunto de reglas: obligatorio cuando type es smart. Ver más abajo. |
Crear una lista
POST /lists
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Hot leads (active)",
"type": "smart",
"smart_rules": {
"match": "all",
"conditions": [
{ "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
{ "field": "last_activity_at", "op": "within_last", "value": { "amount": 90, "unit": "days" } }
]
}
}'
Respuesta
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 3, "removed": 0, "total": 3 }
}
Una lista inteligente se evalúa en línea, en la misma solicitud, por lo que evaluation te indica exactamente quién terminó en ella. En una lista estática, evaluation es null.
Actualizar una lista
PUT /lists/{listId}
Envía solo los campos que vas a cambiar. Cambiar smart_rules vuelve a evaluar la lista inmediatamente y devuelve el mismo objeto evaluation.
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/list_abc123?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "smart_rules": { "match": "any", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead", "tagWebinar"] } ] } }'
Puedes cambiar una lista entre los dos tipos:
- Estática → inteligente: envía
{ "type": "smart", "smart_rules": { … } }. Las reglas se aplican al instante. - Inteligente → estática: envía
{ "type": "static" }. Las reglas se eliminan y quien esté en la lista permanece en ella.
La estructura de smart_rules
{
"match": "all",
"conditions": [
{ "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
{ "field": "channel", "op": "is_any", "value": ["whatsapp", "sms"] },
{ "field": "last_incoming_message_at", "op": "not_within_last", "value": { "amount": 7, "unit": "days" } },
{ "field": "created_at", "op": "after", "value": "2026-01-01" },
{ "field": "is_bot_active", "op": "is", "value": true },
{ "field": "email", "op": "is_set" },
{ "field": "custom_field", "key": "Plan", "op": "eq", "value": "pro" }
]
}
match—all(todas las condiciones deben ser verdaderas) oany(al menos una).conditions— de 1 a 20 condiciones, cada una con un máximo de 100 valores, cadenas de hasta 200 caracteres.
field |
op |
value |
|---|---|---|
tags |
has_any, has_all, has_none |
matriz de IDs de etiquetas |
lists |
in_any, not_in_any |
matriz de IDs de listas (solo listas estáticas: una lista inteligente no puede crearse a partir de otra lista inteligente) |
channel |
is_any, is_none |
matriz de canales |
status |
is_any, is_none |
matriz de estados de contacto |
created_at, last_activity_at, last_incoming_message_at, last_outgoing_message_at, first_ai_interaction_at, last_ai_interaction_at |
within_last, not_within_last |
{ "amount": 1–3650, "unit": "hours" | "days" } |
| mismos campos de fecha | before, after |
fecha ISO ("2026-01-01", comparada como días completos) o fecha y hora ISO completa ("2026-01-01T14:30:00Z", comparada con el momento exacto) |
| mismos campos de fecha | is_set, not_set |
— |
has_interacted_with_ai |
is |
true / false — true coincide con los contactos a los que la IA ha enviado mensajes al menos una vez (alguna vez) |
is_bot_active, do_not_disturb, is_private, has_ever_responded |
is |
true / false |
email, phone_number, first_name, last_name |
is_set, not_set, contains, not_contains |
cadena para los formularios contains |
current_campaign_id, assigned_agent |
is_any, is_none, is_set, not_set |
matriz de IDs para los formularios is_any / is_none |
custom_field (más un key) |
eq, neq, contains, not_contains, is_set, not_set |
cadena para los formularios de valor |
not_within_last también coincide con contactos para los que nunca se estableció la fecha (“hace más de N, o nunca”), y las comparaciones de texto ignoran las mayúsculas y minúsculas.
Interacción de la IA. has_interacted_with_ai es la marca de tiempo de por vida: true para cada contacto al que su IA haya enviado al menos un mensaje, false para todos los demás (incluidos los contactos a los que solo su equipo ha respondido). Se marca en el primer mensaje de la IA a un contacto y nunca se borra, por lo que desactivar las respuestas de la IA del contacto o moverlo a otra campaña no la restablece. Para un período — “los contactos que mi IA gestionó este mes”, la pregunta de facturación habitual — utilice el rango last_ai_interaction_at en su lugar:
{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }
No confunda ninguno de los dos con is_bot_active (la IA tiene permiso para responder, no que lo haya hecho) o has_ever_responded (el contacto respondió, a cualquiera). Las mismas dos marcas se devuelven en cada contacto como first_ai_interaction_at / last_ai_interaction_at, y todo el conjunto de reglas también funciona en GET /contacts?rules=, por lo que puede contar las coincidencias sin crear una lista.
Previsualizar un conjunto de reglas
POST /lists/preview
Cuenta y muestra una muestra de los contactos que coincidirían con un conjunto de reglas, sin crear ni cambiar nada. Úselo para verificar la coherencia de las reglas antes de guardarlas.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/preview?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "smart_rules": { "match": "all", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead"] } ] } }'
Respuesta
{
"success": true,
"count": 3,
"sample": [
{
"id": "contact_abc123",
"first_name": "Sofia",
"last_name": "Martinez",
"phone_number": "+31600000000",
"email": "sofia@example.com",
"channel": "whatsapp"
}
]
}
sample contiene hasta 10 contactos, ordenados por actividad más reciente primero.
Volver a ejecutar una lista inteligente ahora
POST /lists/{listId}/evaluate
Fuerza una reevaluación inmediata (lo mismo que hace Actualizar ahora en el panel de control). Las listas inteligentes ya se actualizan cuando un contacto cambia y cada 15 minutos para las reglas basadas en tiempo, por lo que esto solo es necesario cuando desea el resultado ahora mismo.
Respuesta
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 2, "removed": 1, "total": 4 }
}
evaluation.skipped: true significa que ya se estaba ejecutando otra evaluación de la misma lista y esta llamada no hizo nada.
Las listas inteligentes rechazan miembros seleccionados manualmente
Los endpoints de membresía devuelven 409 con "This is a smart list — its members are computed from its rules. Edit the rules instead." cuando la lista de destino es inteligente. Esto cubre POST /contacts/lists, DELETE /contacts/lists, POST /contacts/lists/batch, contact_ids en POST /lists y PUT /lists/{listId}, además de elegir una lista inteligente como destino de importación CSV. Cambie las reglas en su lugar.
Llamar a POST /lists/{listId}/evaluate en una lista estática también es un 409: no tiene reglas que ejecutar.
Errores de la API de contactos
Los endpoints de contactos devuelven el sobre de error estándar:
{
"success": false,
"error": "Contact not found"
}
Algunos endpoints también incluyen error_code, que generalmente coincide con el estado HTTP; la única excepción es el caso de contacto duplicado a continuación, donde el estado HTTP es 200 y solo error_code lleva el 409. Los códigos específicos para los endpoints de contacto:
| Código | Cuándo ocurre en un endpoint de contacto |
|---|---|
400 |
Solicitud incorrecta: un campo faltante/inválido, cuerpo vacío, cursor incorrecto o más de 500 IDs en un lote. |
402 |
No hay suficientes créditos para completar una ejecución de etiquetado con IA en un contacto (error_code: "insufficient_credits"). |
404 |
El contacto, la lista o la etiqueta no se encontraron en su cuenta. |
409 |
Ya existe un contacto con ese número de teléfono (al crear). Se devuelve como error_code en el cuerpo con un estado HTTP de 200, así que bifurque en error_code aquí. También se devuelve cuando una ejecución de etiquetado automático masivo ya está en curso (error_code: "auto_tag_run_in_progress"), o cuando vincular un contacto a otro canal uniría dos contactos que ya están vinculados a dos personas diferentes. |
422 |
El contacto no puede recibir un mensaje en este momento (no molestar, privado o canal no compatible). En el endpoint de vinculación de canal, también cubre la falta de número de teléfono, un emparejamiento de canal no compatible o la falta de un remitente conectado para el canal de destino. |
Un 403 en un endpoint de contacto también puede significar un problema de límite de contactos o de permiso de lista en lugar de acceso al plan. Los códigos compartidos que puede devolver cualquier endpoint — 401, 403 (su plan no incluye acceso a la API), 429 (límite de tasa) y 500 — se enumeran con orientación sobre reintentos en Errores y paginación.
Próximos pasos
- API de mensajes — envíe mensajes por identidad de canal y gestione conversaciones.
- Referencia de la API — lista completa de endpoints, incluyendo etiquetas y listas.
- Acceso a la API — autenticación, límites de tasa y manejo de errores.