VitrinaAPI

Enrutar cada mensaje al equipo correcto, con un reloj encima

Equipos, reglas de asignación y SLA como una sola configuración

Una conversación nueva necesita caer en manos de alguien, rápido. Esta receta arma equipos, reglas de asignación y un SLA como una sola configuración, no como piezas sueltas. Al final, un mensaje nuevo sabe a quién pertenece. Y alguien se entera si nadie contestó a tiempo.

Trampa

Dos reglas pueden coincidir con la misma conversación: gana la primera de la lista, nunca la más específica

Las reglas se evalúan en orden y gana la primera que coincide en origen y canal. Una regla sin miembros pasa a la siguiente regla que coincida y, si ninguna coincide, al canal. Una regla con miembros pero nadie conectado no sigue buscando: la conversación queda sin asignar.

Antes de empezar

  • teams:write para crear equipos y sumar miembros.
  • routing:write para reglas de asignación. Es un scope de administrador, ausente del set por defecto de un agente.
  • slas:write y triggers:write para el reloj y lo que dispara al vencerse.
  • Roles y permisos dentro de un equipo están en Equipos y roles; esta receta cubre solo el enrutamiento.

1. Arma el equipo

curl -X POST https://api.vitrinadev.com/api/v1/teams \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Atención Norte" }'
{
  "data": {
    "id": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c",
    "name": "Atención Norte",
    "hours_mode": "workspace",
    "hours": null
  }
}
curl -X POST https://api.vitrinadev.com/api/v1/teams/42f4b285-802b-4d94-ac9e-ab3d5f7d615c/members \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "3197957f-5fb6-4c7a-837d-1fe296bc548d", "role": "member" }'
{ "data": { "team_id": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c", "user_id": "3197957f-5fb6-4c7a-837d-1fe296bc548d", "role": "member" } }

hours_mode empieza en workspace: el equipo hereda el horario general. Para un horario propio, manda hours_mode: "override" junto con hours en la misma llamada. Sin el modo, la ventana se guarda como null y la creación de todos modos responde 201, sin avisar que no quedó nada guardado.

2. Decide qué conversación va a cada equipo

curl -X POST https://api.vitrinadev.com/api/v1/assignment-rules \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Sitio web a Atención Norte", "match_sources": ["website"], "team_id": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c" }'
{ "data": { "id": "01a0ce19-5294-7415-9d9f-70bd68420b80", "name": "Sitio web a Atención Norte", "priority": 0, "match_sources": ["website"], "team_id": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c" } }

Una regla lleva exactamente un destino: team_id o assignee_user_ids, nunca ambos. match_sources y match_channels vacíos son comodín: cubren cualquier origen o canal. Una regla nueva se agrega siempre al final; un priority propio en la creación es un 400. El orden se cambia después, en el paso 3.

3. Ordena las reglas: la primera que coincide, gana

curl -X PUT https://api.vitrinadev.com/api/v1/assignment-rules/order \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ids": ["01a0ce19-6ad5-7eba-8d7e-e90a93f5ad8e", "01a0ce19-5294-7415-9d9f-70bd68420b80"] }'
{
  "data": [
    { "id": "01a0ce19-6ad5-7eba-8d7e-e90a93f5ad8e", "name": "WhatsApp por defecto", "priority": 0 },
    { "id": "01a0ce19-5294-7415-9d9f-70bd68420b80", "name": "Sitio web a Atención Norte", "priority": 1 }
  ]
}

El cambio es atómico: reescribe la prioridad de TODAS las reglas del workspace a la vez, a partir del orden de ids. La lista tiene que traer cada regla exactamente una vez; una lista parcial es un 400 sin aplicar nada, nunca un cambio a medias.

Si ninguna regla coincide, el enrutamiento cae al canal: messaging_account.assignment_mode, configurado desde la aplicación. Si el canal tampoco tiene uno, cae al modo por defecto del workspace. Las reglas con is_portal_default, las tarjetas «Predeterminado por marketplace», siempre se evalúan al final, después de cualquier regla propia.

4. Pon un reloj: define el SLA

curl -X POST https://api.vitrinadev.com/api/v1/slas \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Atención estándar",
    "target_first_response_minutes": 30,
    "target_resolution_minutes": 1440,
    "business_hours_only": true,
    "applies_to": { "channel": "whatsapp" }
  }'
{
  "data": {
    "id": "2dcb37cf-fe55-4f9c-9e2b-c8eedaa4b695",
    "name": "Atención estándar",
    "target_first_response_minutes": 30,
    "target_resolution_minutes": 1440,
    "business_hours_only": true,
    "applies_to": { "channel": "whatsapp" },
    "active": true
  }
}

Los dos objetivos son independientes: una política puede fijar solo primera respuesta, solo resolución, o ambas. business_hours_only pausa el reloj fuera del horario del equipo, el mismo hours/hours_mode del paso 1, en vez de contar tiempo de pared.

applies_to decide a qué conversaciones aplica la política. Es un filtro libre, con las mismas claves que ya usa tu enrutamiento: canal, marca, prioridad.

5. Reacciona cuando el reloj se cumple

curl -X POST https://api.vitrinadev.com/api/v1/triggers \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Avisar SLA incumplido",
    "event": "sla_breached",
    "actions": [{ "type": "assign_team", "value": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c" }]
  }'
{
  "data": {
    "id": "d6b003a7-6641-4415-843a-028940e0681b",
    "name": "Avisar SLA incumplido",
    "event": "sla_breached",
    "actions": [{ "type": "assign_team", "value": "42f4b285-802b-4d94-ac9e-ab3d5f7d615c" }],
    "enabled": true,
    "run_count": 0
  }
}

sla_breached es uno de doce eventos que un trigger puede escuchar. Los otros cubren el resto del ciclo: creada, reabierta, etiquetada, inactiva por horas. Las condiciones son opcionales: sin conditions, la acción corre en cada incumplimiento. Con ellas, se filtra por campo, operador y valor, combinados con all o any. run_count sube en cada disparo, así que sirve para confirmar que el trigger de verdad se activó.

Para confirmarlo sin mirar la aplicación, suscríbete a los eventos sla.breached y conversation.assigned; el catálogo completo de eventos y firmas está en Webhooks.

En la aplicación: las reglas viven en Configuración → Bandeja → Asignación, numeradas con el mismo orden de prioridad que expone PUT /assignment-rules/order.

La pantalla Asignación de Vitrina: seis reglas numeradas por prioridad, una desactivada, y los niveles siguientes de la cascada

El reloj es otra pantalla. Los SLA se definen en Configuración → Bandeja → SLAs, y los triggers en Configuración → IA y automatización → Triggers:

La pantalla SLAs de Vitrina: las políticas del workspace con sus tiempos objetivo y a qué canales y equipos aplican

Guía completa en Manual de plataforma → Enrutar con un reloj encima.

Cuando falla

Reordenar con una lista incompleta responde 400 y no mueve nada:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "ids must list every rule of the workspace exactly once (expected 2, got 1)."
  }
}

El mensaje trae el conteo esperado contra el recibido. Antes de reintentar, vuelve a pedir GET /assignment-rules y arma la lista completa desde ahí, en vez de reordenar solo las reglas que cambiaron.

Lo que queda fuera de esta receta

Las macros son respuestas ya armadas, con pasos automáticos incluidos. Se crean por API igual que una regla. Aplicarlas, en cambio, todavía es una acción del inbox: no hay una llamada equivalente. La creación y sus campos están en Macros y reglas de asignación. El detalle de cada evento y condición de un trigger está en SLA y automatizaciones.

En esta página