API REST
La API REST de Artificialic te permite integrar tu plataforma de mensajería con sistemas externos como CRMs, bots, ERPs o cualquier aplicación que necesite enviar mensajes o gestionar contactos programáticamente.
Autenticación
Todas las solicitudes a la API deben incluir un header Authorization con una clave de API válida:
Authorization: Bearer ak_tu_clave_aqui
Obtener una clave de API
- Ve a Configuración → API en tu dashboard.
- Haz clic en Crear clave.
- Asigna un nombre descriptivo (ej. "Integración CRM") y selecciona los permisos necesarios.
- Copia la clave inmediatamente. Solo se muestra una vez. Si la pierdes, deberás crear una nueva.
Características de seguridad
- Las claves tienen el prefijo
ak_seguido de 64 caracteres hexadecimales. - Se almacenan como un hash SHA-256 — ni siquiera nosotros podemos ver tu clave.
- Puedes revocar una clave en cualquier momento desde el dashboard. La revocación es inmediata.
- Cada clave registra la fecha de último uso para que puedas auditar el acceso.
Importante: Nunca expongas tu clave de API en código del lado del cliente (frontend, apps móviles). Úsala solo desde tu servidor backend.
Scopes (Permisos)
Cada clave de API tiene uno o más scopes que determinan qué operaciones puede realizar. Asigna solo los permisos que necesites (principio de menor privilegio).
| Scope | Descripción | Endpoints |
|---|---|---|
| channels:read | Leer canales conectados | GET /api/v1/channels |
| contacts:read | Listar y ver contactos | GET /api/v1/contacts, GET /api/v1/contacts/:id |
| contacts:write | Crear, editar y eliminar contactos | POST, PATCH, DELETE /api/v1/contacts |
| messages:send | Enviar mensajes de texto | POST /api/v1/messages |
Formato de respuestas
Todas las respuestas son JSON. Las respuestas exitosas envuelven los datos en un campo data:
// Respuesta exitosa
{
"data": {
"id": "clxyz...",
"name": "Juan Pérez",
...
}
}
// Lista con paginación
{
"data": [ ... ],
"nextCursor": "clxyz..." // null si no hay más resultados
}Las respuestas de error usan un campo error con un código máquina y un mensaje legible:
// Respuesta de error
{
"error": {
"code": "UNAUTHORIZED",
"message": "API key inválida o revocada"
}
}Códigos HTTP
| Código | Significado |
|---|---|
| 200 | Operación exitosa |
| 201 | Recurso creado exitosamente |
| 400 | Error de validación en los datos enviados |
| 401 | Clave de API faltante, inválida o revocada |
| 403 | Scope insuficiente o plan sin acceso a API |
| 404 | Recurso no encontrado |
| 409 | Conflicto (ej. contacto duplicado) |
| 500 | Error interno del servidor |
Endpoints — Canales
GET/api/v1/channels
Retorna la lista de canales conectados a tu organización. No incluye credenciales de acceso.
Scope requerido: channels:read
Query params
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| type | string | No | Filtrar por tipo: WHATSAPP, MESSENGER, INSTAGRAM, WEBCHAT |
Respuesta
{
"data": [
{
"id": "cm1abc...",
"name": "WhatsApp Business",
"type": "WHATSAPP",
"status": "CONNECTED",
"profilePictureUrl": "https://...",
"createdAt": "2026-01-15T10:30:00.000Z"
}
]
}Ejemplo cURL
curl -H "Authorization: Bearer ak_tu_clave" \ https://tu-dominio.com/api/v1/channels
Endpoints — Contactos
GET/api/v1/contacts
Lista los contactos de tu organización con paginación basada en cursor.
Scope requerido: contacts:read
Query params
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| search | string | No | Buscar por nombre, teléfono, email o ID externo (case-insensitive) |
| cursor | string | No | ID del último contacto de la página anterior (para paginación) |
| limit | number | No | Contactos por página (default: 20, máximo: 100) |
Respuesta
{
"data": [
{
"id": "cm1def...",
"name": "Juan Pérez",
"phoneNumber": "584121234567",
"externalId": null,
"email": "[email protected]",
"avatarUrl": null,
"createdAt": "2026-02-10T08:00:00.000Z"
}
],
"nextCursor": "cm1ghi..." // null si no hay más páginas
}Paginación
La API usa paginación por cursor. Para obtener la siguiente página, pasa el valor de nextCursor como parámetro cursor:
# Primera página GET /api/v1/contacts?limit=20 # Siguiente página GET /api/v1/contacts?limit=20&cursor=cm1ghi...
Ejemplo cURL
curl -H "Authorization: Bearer ak_tu_clave" \ "https://tu-dominio.com/api/v1/contacts?search=juan&limit=10"
GET/api/v1/contacts/:id
Obtiene un contacto con todas sus conversaciones. Esto es clave para saber qué channelId usar al enviar un mensaje.
Scope requerido: contacts:read
Path params
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | string | Sí | ID del contacto |
Respuesta
{
"data": {
"id": "cm1def...",
"name": "Juan Pérez",
"phoneNumber": "584121234567",
"externalId": null,
"email": "[email protected]",
"avatarUrl": null,
"metadata": null,
"createdAt": "2026-02-10T08:00:00.000Z",
"conversations": [
{
"id": "cm1conv...",
"status": "OPEN",
"channelId": "cm1ch...",
"lastMessageAt": "2026-03-20T14:00:00.000Z",
"channel": {
"name": "WhatsApp Business",
"type": "WHATSAPP"
}
}
]
}
}El array conversations te permite identificar en qué canales tiene conversaciones activas este contacto. Usa el channelId junto con el id del contacto para enviar mensajes.
Ejemplo cURL
curl -H "Authorization: Bearer ak_tu_clave" \ https://tu-dominio.com/api/v1/contacts/cm1def...
POST/api/v1/contacts
Crea un nuevo contacto en tu organización.
Scope requerido: contacts:write
Body (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| name | string | No | Nombre del contacto |
| phoneNumber | string | No | Teléfono en formato internacional (ej. +584121234567) |
| string | No | Correo electrónico |
Nota: Se requiere al menos name o phoneNumber. El número de teléfono se valida como formato E.164 internacional y se almacena sin el signo + (ej. 584121234567).
Respuesta (201 Created)
{
"data": {
"id": "cm1new...",
"name": "María García",
"phoneNumber": "584121234567",
"externalId": null,
"email": null,
"createdAt": "2026-03-23T10:00:00.000Z"
}
}Errores específicos
| HTTP | Código | Causa |
|---|---|---|
| 400 | BAD_REQUEST | Falta name y phoneNumber, o teléfono inválido |
| 409 | CONFLICT | Ya existe un contacto con ese número de teléfono |
Ejemplo cURL
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{"name":"María García","phoneNumber":"+584121234567"}' \
https://tu-dominio.com/api/v1/contactsPATCH/api/v1/contacts/:id
Actualiza uno o más campos de un contacto existente. Solo envía los campos que deseas modificar.
Scope requerido: contacts:write
Path params
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | string | Sí | ID del contacto |
Body (JSON, todos opcionales)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| name | string | No | Nuevo nombre |
| phoneNumber | string | No | Nuevo teléfono |
| string | No | Nuevo email |
Respuesta
{
"data": {
"id": "cm1def...",
"name": "Juan A. Pérez",
"phoneNumber": "584121234567",
"externalId": null,
"email": "[email protected]",
"createdAt": "2026-02-10T08:00:00.000Z"
}
}Ejemplo cURL
curl -X PATCH \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
https://tu-dominio.com/api/v1/contacts/cm1def...DELETE/api/v1/contacts/:id
Elimina un contacto y todas sus conversaciones asociadas. Esta acción es irreversible.
Scope requerido: contacts:write
Path params
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | string | Sí | ID del contacto a eliminar |
Respuesta
{
"success": true
}Ejemplo cURL
curl -X DELETE \ -H "Authorization: Bearer ak_tu_clave" \ https://tu-dominio.com/api/v1/contacts/cm1def...
Endpoints — Mensajes
POST/api/v1/messages
Envía un mensaje de texto o una plantilla a un contacto. Acepta múltiples formas de identificar al contacto y al canal. Si el contacto o la conversación no existen, se crean automáticamente.
Scope requerido: messages:send
Identificación del contacto (al menos uno requerido)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| contactId | string | No | ID de un contacto existente |
| phoneNumber | string | No | Número de teléfono con código de país (ej. +584121234567). Solo para canales WHATSAPP. Auto-crea contacto si no existe. |
| facebookId | string | No | ID de Facebook/Messenger del usuario. Solo para canales MESSENGER. Auto-crea contacto si no existe. |
| instagramId | string | No | ID de Instagram del usuario. Solo para canales INSTAGRAM. Auto-crea contacto si no existe. |
Identificación del canal (al menos uno requerido)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| channelId | string | No | ID de un canal específico |
| channelType | string | No | Tipo de canal: WHATSAPP, MESSENGER, INSTAGRAM, WEBCHAT. Usa el primer canal conectado de ese tipo. |
Contenido del mensaje (exactamente uno requerido)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| content | string | No | Texto plano del mensaje |
| template | object | No | Plantilla a enviar (ver estructura abajo) |
| template.id | string | Sí | ID de la plantilla |
| template.type | string | Sí | "message_template" (Meta/WhatsApp) o "generic_template" (plantilla genérica de la organización) |
| template.variables | object | No | Variables de sustitución: {"1": "valor", "2": "valor"} |
Tipos de plantilla:
message_template— Plantilla de WhatsApp (Meta). Debe estar aprobada. Solo funciona en canales WHATSAPP. Se envía como mensaje de plantilla nativo.generic_template— Plantilla genérica de tu organización. Funciona en cualquier canal. Las variables se sustituyen y se envía como texto plano.
Flujo interno
- Se resuelve el canal (por ID o primer canal conectado del tipo indicado).
- Se resuelve o crea el contacto (por ID, teléfono, facebookId o instagramId).
- Se crea la conversación si no existe (upsert).
- Se verifica la ventana de conversación (ej. 24h para WhatsApp — las plantillas no requieren ventana abierta).
- Se crea el mensaje con estado
PENDING. - Se envía al proveedor del canal.
- Se actualiza el estado a
SENToDELIVERED. - Se emite un evento SSE para actualización en tiempo real.
Respuesta
{
"data": {
"id": "cm1msg...",
"conversationId": "cm1conv...",
"senderType": "AGENT",
"senderId": "cm1user...",
"contentType": "TEXT",
"content": "Hola, tu pedido está listo",
"status": "SENT",
"externalId": "wamid.abc...",
"createdAt": "2026-03-23T10:30:00.000Z",
"updatedAt": "2026-03-23T10:30:01.000Z",
"sender": {
"id": "cm1user...",
"name": "Admin",
"email": "[email protected]",
"avatarUrl": null
}
}
}Errores específicos
| HTTP | Código | Causa |
|---|---|---|
| 400 | BAD_REQUEST | Falta identificador de contacto o canal |
| 400 | BAD_REQUEST | phoneNumber solo válido para WHATSAPP / facebookId para MESSENGER / instagramId para INSTAGRAM |
| 400 | BAD_REQUEST | message_template solo funciona en canales WHATSAPP |
| 400 | BAD_REQUEST | Plantilla no aprobada / Ventana de conversación expirada |
| 404 | NOT_FOUND | Canal, contacto o plantilla no encontrada |
| 400 | BAD_REQUEST | Canal desconectado |
Ventana de conversación: En WhatsApp, solo puedes enviar mensajes de texto libre dentro de las 24 horas posteriores al último mensaje del contacto. Después de ese período, usa una message_template aprobada para reabrir la conversación. Las plantillas genéricas (generic_template) se envían como texto y están sujetas a la misma restricción de ventana.
Ejemplo 1: Texto plano por número de teléfono
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+584121234567",
"channelType": "WHATSAPP",
"content": "Hola, tu pedido está listo para retiro."
}' \
https://tu-dominio.com/api/v1/messagesEjemplo 2: Template de WhatsApp
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+584121234567",
"channelType": "WHATSAPP",
"template": {
"id": "cm1tmpl...",
"type": "message_template",
"variables": { "1": "Juan", "2": "#12345" }
}
}' \
https://tu-dominio.com/api/v1/messagesEjemplo 3: Template genérica
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"contactId": "cm1def...",
"channelId": "cm1ch...",
"template": {
"id": "cm1gen...",
"type": "generic_template",
"variables": { "1": "Juan", "2": "#12345" }
}
}' \
https://tu-dominio.com/api/v1/messagesEjemplo 4: Retrocompatible (contactId + channelId + content)
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"contactId": "cm1def...",
"channelId": "cm1ch...",
"content": "Hola, tu pedido está listo."
}' \
https://tu-dominio.com/api/v1/messagesErrores comunes
| HTTP | Código | Causa probable | Solución |
|---|---|---|---|
| 401 | UNAUTHORIZED | Falta el header Authorization | Agrega Authorization: Bearer ak_... |
| 401 | UNAUTHORIZED | Clave inválida o revocada | Verifica la clave en Configuración → API |
| 403 | FORBIDDEN | Scope insuficiente | Crea una clave nueva con el scope necesario |
| 403 | FORBIDDEN | Plan sin acceso a API | Actualiza tu plan a uno que incluya API dedicada |
| 404 | NOT_FOUND | Recurso no encontrado o no pertenece a tu org | Verifica el ID del recurso |
| 409 | CONFLICT | Duplicado (ej. mismo teléfono) | Busca el contacto existente antes de crear |
Ejemplos de integración
Flujo completo: enviar un mensaje a un contacto
Este ejemplo muestra cómo obtener los canales, buscar un contacto y enviarle un mensaje usando JavaScript (Node.js / fetch):
const API_URL = "https://tu-dominio.com";
const API_KEY = "ak_tu_clave_aqui";
const headers = {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// 1. Obtener canales disponibles
const channelsRes = await fetch(`${API_URL}/api/v1/channels`, { headers });
const { data: channels } = await channelsRes.json();
const whatsappChannel = channels.find(ch => ch.type === "WHATSAPP");
console.log("Canal WhatsApp:", whatsappChannel.id);
// 2. Buscar contacto por teléfono
const contactsRes = await fetch(
`${API_URL}/api/v1/contacts?search=584121234567`,
{ headers }
);
const { data: contacts } = await contactsRes.json();
const contact = contacts[0];
console.log("Contacto:", contact.id, contact.name);
// 3. Enviar mensaje
const msgRes = await fetch(`${API_URL}/api/v1/messages`, {
method: "POST",
headers,
body: JSON.stringify({
contactId: contact.id,
channelId: whatsappChannel.id,
content: "Hola, tu pedido #1234 está listo para retiro.",
}),
});
const { data: message } = await msgRes.json();
console.log("Mensaje enviado:", message.id, "Estado:", message.status);Mismo flujo con cURL
# 1. Listar canales
curl -s -H "Authorization: Bearer ak_tu_clave" \
https://tu-dominio.com/api/v1/channels
# 2. Buscar contacto
curl -s -H "Authorization: Bearer ak_tu_clave" \
"https://tu-dominio.com/api/v1/contacts?search=584121234567"
# 3. Enviar mensaje
curl -X POST \
-H "Authorization: Bearer ak_tu_clave" \
-H "Content-Type: application/json" \
-d '{
"contactId": "CONTACT_ID",
"channelId": "CHANNEL_ID",
"content": "Hola, tu pedido #1234 está listo para retiro."
}' \
https://tu-dominio.com/api/v1/messagesConsejo: Guarda los IDs de canales y contactos frecuentes en tu base de datos para no tener que consultarlos en cada envío.