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 | Obligatorio | 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 IDs de lista a los que añadir el contacto (tiene prioridad sobre listId). |
custom_fields |
No | Un objeto con sus propios campos de clave/valor. También puede pasarlos como claves de nivel superior. Los campos con un tipo (fecha, número, menú desplegable, casilla de verificación) se validan; consulte Tipos de campos personalizados. |
Se aceptan nombres alternativos comunes cuando falta el estándar: phone o phone_number para el número, first_name y last_name para el nombre, y full_name o name, que se divide en nombre y apellido. Por lo tanto, un error Missing required fields: phoneNumber significa que no llegó ningún valor de teléfono, no que la clave estuviera escrita de forma diferente.
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 en el canal en el que está creando, la llamada de creación no crea uno segundo. La respuesta devuelve un estado HTTP
200y unerror_codede409en el cuerpo, por lo que debe ramificar segúnerror_codeen lugar de según el estado HTTP. El ID del contacto existente se devuelve comocontactId(ydata.contactId), por lo que puede continuar con él de inmediato:{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user.", "contactId": "abc123", "data": { "contactId": "abc123" } }Lo mismo se aplica a una dirección de correo electrónico duplicada en el canal de correo electrónico. Si su puesto de equipo solo puede ver sus propios contactos y el contacto existente no es uno de ellos, el ID se omite; búsquelo con Obtener un contacto por teléfono o correo electrónico en su lugar.
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"
},
"adAttribution": {
"visit_at": "2026-06-09T10:14:02.000Z",
"matched_at": "2026-06-09T10:14:20.000Z",
"match_method": "reference",
"reference": "4827",
"fbc": "fb.1.1749463000000.IwAR...",
"fbp": "fb.1.1749000000000.123456789",
"fbclid": "IwAR...",
"gclid": null,
"gbraid": null,
"wbraid": null,
"utm_source": "facebook",
"utm_medium": "paid",
"utm_campaign": "spring-promo",
"utm_content": null,
"utm_term": null,
"landing_url": "https://yoursite.com/quiz?utm_source=facebook&utm_campaign=spring-promo",
"referrer": "https://l.facebook.com/",
"client_ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 (iPhone; ...)",
"site_key": "YOUR_SITE_KEY",
"visit_id": "visit_abc123"
}
}
}
adAttribution es el clic en un anuncio que llevó a su sitio web, vinculado a este contacto por el script de clics de anuncios, o null cuando no existe ninguno. Los campos se explican en Atribución de clics de anuncios para WhatsApp.
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.
Contactos cuyo número está oculto
Un contacto de WhatsApp Web puede aparecer sin número de teléfono cuando WhatsApp lo oculta del dispositivo vinculado (consulta Contactos cuyo número está oculto). Cada contacto que lees incluye dos campos adicionales para esto: number_hidden (numberHidden en la forma de contacto único) y whatsapp_lid (whatsappLid).
- Para un contacto con número oculto,
phone_numberesnull,number_hiddenestrueywhatsapp_lidcontiene el ID interno de WhatsApp, con el formato153055538446346@lid. No es un número al que se pueda llamar. Haz coincidir el contacto con ese ID desde tu propio sistema y pásalo comophoneNumbera esta búsqueda para encontrar el contacto de nuevo. - Para cualquier otro contacto,
number_hiddenesfalseywhatsapp_lides el ID que WhatsApp informó para el contacto, onull.
Los webhooks de salida y las cargas útiles de automatización que incluyen un contacto llevan los mismos tres campos, y la exportación de contactos a CSV tiene una columna de Número oculto y una de LID de WhatsApp junto al Número de teléfono. Una vez que WhatsApp comparte el número real, el contacto se actualiza solo y phone_number se completa.
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.
Obtener el coste de un contacto
GET /contacts/{contactId}/cost
Devuelve los créditos que ha utilizado un contacto, desglosados por conversación (sesión de chat), por tipo de acción y el coste medio de una conversación gestionada únicamente por la IA frente a una en la que intervino su equipo. Utilícelo para mostrar a un cliente lo que cuesta una conversación.
cURL
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost?apiKey=YOUR_API_KEY"
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.total_credits, data.by_session.length);
Python
import requests
res = requests.get(
"<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost",
headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["total_credits"], data["summary"]["contained"]["avg_credits"])
Respuesta
{
"success": true,
"contact_id": "contact_abc123",
"total_credits": 23,
"refunded_credits": 0,
"unattributed_credits": 0,
"by_session": [
{
"session_id": "hpWKxuTSXIKlylze5Ulx",
"start": "2026-09-21T00:00:00.000Z",
"end": "2026-09-24T17:41:52.052Z",
"status": "ChatSessionClosed",
"tag": null,
"credits": 7,
"actions": 7,
"outcome": "handed_off"
},
{
"session_id": "5kKnkI3DD4THIh2YVjey",
"start": "2026-09-09T00:00:00.000Z",
"end": "2026-09-11T22:42:34.056Z",
"status": "ChatSessionClosed",
"tag": null,
"credits": 13,
"actions": 14,
"outcome": "contained"
}
],
"by_type": [
{ "type": "AI Chat Conclude Evaluation", "credits": 8, "actions": 8 },
{ "type": "AI Tool Use", "credits": 4, "actions": 4 },
{ "type": "AI Message", "credits": 2, "actions": 2 }
],
"summary": {
"contained": { "sessions": 1, "credits": 13, "avg_credits": 13 },
"handed_off": { "sessions": 1, "credits": 7, "avg_credits": 7 },
"no_ai": { "sessions": 0, "credits": 0, "avg_credits": 0 }
},
"sessions_truncated": false
}
Notas de campo:
total_creditscuenta cada cargo realizado al contacto (respuestas de la IA, llamadas a herramientas, etiquetas, seguimientos, resúmenes, pasos de evaluación en segundo plano, tarifas de plantillas y entrega), restando los reembolsos del contacto. Las concesiones de créditos, recargas y renovaciones nunca pertenecen a un contacto. Las pruebas en el entorno de pruebas (Playground) quedan excluidas.- Un cargo se asigna a la conversación en cuya ventana cae. Una ventana abarca desde el inicio de la conversación hasta que esta se cierra o comienza la siguiente, por lo que ningún cargo se cuenta dos veces. Los cargos fuera de toda ventana se encuentran en
unattributed_credits(y en el total). outcome:containedsignifica que la IA respondió y nadie de su equipo tuvo que intervenir;handed_offsignifica que alguien envió un mensaje después de que la IA hubiera respondido, o que la IA alertó a un humano;no_aisignifica que la IA nunca respondió en esa conversación.- Se enumeran hasta 200 conversaciones, empezando por las más recientes;
sessions_truncatedestruecuando hay más. - Para un cliente de agencia, pase
sub_account_id; las cifras se expresan en los propios créditos de ese cliente. creditsUseden Obtener estadísticas de contacto es un contador acumulativo que se mantiene en el contacto. Este endpoint reconstruye la cifra a partir del libro mayor de créditos, por lo que ambas pueden diferir ligeramente, sobre todo en contactos antiguos.- Los miembros del equipo necesitan el permiso de facturación. Un contacto que no esté en su cuenta devuelve
404;503significa que las cifras no se pueden calcular en este momento, inténtelo de nuevo en breve.
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",
"number_hidden": false,
"whatsapp_lid": null,
"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",
"ad_attribution": null
}
],
"next_cursor": "contact_abc123"
}
Cada contacto contiene ad_attribution: el clic en un anuncio que llevó a su sitio web, vinculado al contacto, con los mismos campos que adAttribution en Obtener un contacto por ID, o null cuando no existe ninguno.
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ápido, ciclo y cliente potencial 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 texto libre sobre el cliente potencial. |
custom_fields |
Un objeto de campos personalizados. Combinado por clave: solo se escriben las claves que envíe, el resto de los campos personalizados existentes se mantienen. También puede pasar claves de campos personalizados en el nivel superior. Los campos tipados se validan y un valor incorrecto rechaza la solicitud con field_errors; consulte Tipos de campos personalizados. |
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 gestionan la etiqueta en sí (cambiar su 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 tag_id en el nivel superior de la respuesta 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; sin embargo, cuando envía una listId, el contacto que ya tiene se agrega a esa lista y se cuenta en added_to_list, por lo que una reimportación llena la lista sin cambiar nada en el contacto. 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 agregan tags y el contacto se añade a listId. El canal, el número de teléfono y las etiquetas de bot nunca se modifican 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 | Obligatorio | Descripción |
|---|---|---|
contacts |
Sí | Matriz de registros de contacto (máx. 500). |
listId |
No | Lista a la que se añadirá cada contacto en esta solicitud: creados, actualizados y los que se omitieron como duplicados por igual. 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": [],
"added_to_list": 0,
"added_to_list_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": [],
"added_to_list": 1,
"added_to_list_contact_ids": ["contact_def456"],
"skipped": [
{ "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
]
}
La fila omitida aún se unió a listId, que es lo que cuenta added_to_list; no se tocó nada más en ese contacto. Con updateExisting: true, la misma solicitud informa sobre el contacto existente en 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.
Enviar un seguimiento de IA a un contacto
POST /contacts/{contactId}/ai-follow-up
Hace que un agente de IA escriba un mensaje con su propia voz, a partir de la conversación hasta el momento y una instrucción que usted proporcione, y lo envía por el canal del contacto. Es lo mismo que el paso de automatización Enviar seguimiento de IA y cuesta los mismos créditos que un seguimiento de IA.
| Campo | Obligatorio | Descripción |
|---|---|---|
instruction |
Sí | Qué debe hacer el mensaje, en palabras sencillas, hasta 2000 caracteres. Por ejemplo: “Recuérdales que su enlace reservado caduca esta noche y pregúntales si quieren confirmarlo”. |
agentId |
No | El agente que escribe el mensaje. Déjelo vacío para usar el agente que ya gestiona el contacto. |
mustContain |
No | Una lista de hasta 5 frases o enlaces (cada uno de hasta 500 caracteres) que el mensaje debe incluir palabra por palabra. Si la IA omite uno, no se envía nada y skipReason es text_check_failed. |
respectMessagingWindow |
No | true predeterminado. En WhatsApp Business API, Instagram y Messenger, omítalo cuando el último mensaje del contacto tenga más de 24 horas de antigüedad. |
skipWhenAiPaused |
No | true predeterminado. Omítalo cuando la IA esté desactivada para este contacto. |
cURL
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instruction": "Remind them their reserved link expires tonight and ask if they want to lock it in",
"mustContain": ["https://example.com/r/abc123"]
}'
JavaScript
const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
instruction: "Remind them their reserved link expires tonight and ask if they want to lock it in",
mustContain: ["https://example.com/r/abc123"],
}),
});
const data = await res.json();
if (!data.sent) console.log("Skipped:", data.skipReason);
Respuesta cuando se envió el mensaje:
{
"sent": true,
"messageId": "ai-follow-up-aB3dE5fG7hI9jK1l",
"text": "Hi Ada, quick reminder: your reserved spot is held until 20:00 tonight. Want to lock it in? https://example.com/r/abc123",
"agentId": "agent_abc123"
}
Respuesta cuando se omitió:
{
"sent": false,
"skipReason": "window_closed"
}
Ambos devuelven el estado 200. skipReason es uno de no_agent (ningún agente gestiona el contacto y no se proporcionó ninguno), dnd (no molestar), ai_paused, window_closed (fuera de la ventana de 24 horas, o el contacto nunca le escribió por ese canal), no_credits, text_check_failed (faltaba un elemento mustContain), no_text (la IA no escribió nada útil) o linkedin_gap (un lead de LinkedIn recibió un mensaje automático hace menos de 24 horas; la respuesta entonces también incluye nextAllowedAt, el momento en que se puede enviar el siguiente). Un salto es una respuesta normal, no un error, así que verifique sent antes de asumir que el mensaje se envió. Una llamada omitida no consume créditos.
Un cuerpo al que le falta instruction o que tiene un campo del tipo incorrecto recibe un 400. Un contacto o agente que no esté en su cuenta recibe un 404. Los errores se devuelven como { "success": false, "error": "..." }. La clave de un miembro del equipo necesita acceso de edición a los contactos.
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 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. Cuando el contacto ya estaba con este agente y su último mensaje ya ha sido respondido, no se inicia ninguna respuesta: la respuesta incluye aiResponseTriggered: false y un aiTriggerError indicando el motivo. |
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í).
| Campo | Obligatorio | Descripción |
|---|---|---|
agentId |
Sí | El agente de IA que debe hacerse cargo, o null para borrar la asignación. |
contactIds |
Uno de los tres | Hasta 500 ID de contacto para mover. |
filter |
Uno de los tres | Seleccione los contactos en el servidor en lugar de enumerarlos, empezando por los más recientes. Acepta las mismas claves que los filtros del endpoint de recuento: agentId (o none), channel, tag, listId, botActive, status. |
rules |
Uno de los tres | Un objeto de reglas de lista inteligente: consulte La forma de smart_rules. |
limit |
No | Cuántos contactos mover en esta llamada cuando se selecciona con filter o rules. De 1 a 500, el valor predeterminado es 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
}
Listar campos personalizados
GET /users/me/custom-fields: devuelve las claves de campo personalizadas registradas en su cuenta (las que se muestran como columnas y en el formulario de contacto), para que una integración pueda ofrecerlas como selector en lugar de tener que adivinarlas.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY"
Respuesta (200)
{ "success": true, "custom_fields": ["tier", "company", "birthday"] }
Establezca la lista con PUT /users/me/custom-fields y la misma forma de matriz.
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.
Tipos de campos personalizados
Cada campo personalizado en una cuenta tiene un tipo: text (el predeterminado), number, date, select (un menú desplegable con opciones fijas) o boolean (una casilla de verificación). Un campo tipado se valida en cada escritura (creación, actualización, importación, el motor de automatizaciones y la propia recopilación de campos de la IA) y su valor se almacena en una forma canónica, de modo que los filtros y las condiciones de automatización comparan elementos equivalentes:
| Tipo | Almacenado como | Aceptado en la entrada |
|---|---|---|
text |
Lo que envíe | Cualquier cosa (comportamiento sin cambios) |
number |
Un número JSON | 42, "42", "19,99" |
date |
"YYYY-MM-DD" |
2026-02-01, una fecha-hora ISO (se conserva la parte de la fecha), 01/02/2026 (día primero o mes primero según el formato de fecha de la cuenta; un valor inequívoco como 13/02/2026 siempre gana), February 1, 2026, 1 de febrero de 2026 |
select |
Una de las opciones del campo, con la propia ortografía de la opción | Cualquier coincidencia de una opción que no distinga entre mayúsculas y minúsculas |
boolean |
true / false |
true/false, yes/no, 1/0, si/no, ja/nein … |
Enviar null o una cadena vacía borra un campo tipado. Las claves que no están registradas en la cuenta y los campos text se almacenan exactamente como se envían.
Leer las definiciones de campo
GET /users/me/custom-fields
{
"success": true,
"custom_fields": ["company", "birthday", "plan", "vip"],
"definitions": [
{ "key": "company", "type": "text" },
{ "key": "birthday", "type": "date" },
{ "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
{ "key": "vip", "type": "boolean" }
]
}
custom_fields es la lista simple de claves (sin cambios para clientes antiguos); definitions lleva el tipo por clave.
Establecer las definiciones de campo
PUT /users/me/custom-fields
Reemplaza toda la lista. Cada entrada es una clave simple (mantiene el tipo que el campo ya tiene, text para uno nuevo) o un objeto con key, type y, para select, options (de 1 a 100 opciones únicas, hasta 64 caracteres cada una). Hasta 50 campos; las claves usan letras minúsculas, dígitos y guiones bajos y comienzan con una letra o guion bajo.
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customFields": [
"company",
{ "key": "birthday", "type": "date" },
{ "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
{ "key": "vip", "type": "boolean" }
]
}'
Cambiar el tipo de un campo no reescribe los valores ya almacenados en los contactos; se validan la próxima vez que se escriben.
Errores de validación
Una creación o actualización de contacto cuyos valores de campo personalizados no se ajustan a su tipo se rechaza en su totalidad con un 400:
{
"success": false,
"error": "Invalid custom field value(s): birthday: \"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01).",
"field_errors": [
{ "key": "birthday", "message": "\"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01)." }
]
}
En una importación masiva, el registro con el valor incorrecto se notifica como fallido con el mismo mensaje y el resto del lote se procesa.
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.