Apariencia
Gestión de entrevistas (recruiter)
El recruiter es quien agenda, confirma y cierra entrevistas entre candidatos y empresas. Es una capa de coordinación crítica.
Agendar una entrevista
Desde el pipeline
Dentro de una tarjeta de asignación en etapa presented o interviewing, el botón "Agendar entrevista" abre ScheduleInterviewDialog.
Endpoint: POST /api/v1/vacancies/{vid}/assignments/{aid}/interviews
Body:
json
{
"title": "Primera entrevista técnica",
"scheduled_at": "2026-04-22T10:00:00-06:00",
"duration_minutes": 60,
"timezone": "America/Mexico_City",
"mode": "online",
"meeting_url": "https://meet.google.com/abc-defg-hij",
"meeting_provider": "google_meet",
"meeting_id": "abc-defg-hij",
"round": 1
}Validaciones
scheduled_atdebe estar en el futuro.duration_minutesentre 15 y 480.mode∈{online, presencial, telefono}.- Si
mode = online,meeting_urlrequerido. - Si
mode = presencial,locationrequerido.
Qué hace el backend
- Verifica que la vacante esté en estado que admita entrevistas (
con_candidatos_asignados,entrevistas_en_curso). - Crea la entrevista con
state = propuesta. - Calcula
roundautomáticamente si no se provee:max(round existente) + 1. - Dentro de una transacción, si la vacante estaba en
con_candidatos_asignados, la mueve aentrevistas_en_curso. - Dispara
InterviewScheduledNotificationa:- El candidato
- Los owners/managers de la empresa (miembros con rol interno
owneromanager)
- Responde 201.
Código: App\Services\InterviewService::schedule().
Modificar una entrevista
Confirmar
Endpoint: POST /api/v1/interviews/{id}/confirm
- Solo desde
propuesta. - Cambia a
confirmada. - Idempotente: si ya estaba
confirmada, no hace nada. - Dispara
InterviewConfirmedNotification.
Reprogramar
Endpoint: POST /api/v1/interviews/{id}/reschedule
json
{
"new_scheduled_at": "2026-04-24T14:00:00-06:00",
"reason": "Conflicto en agenda del entrevistador",
"duration_minutes": 45,
"meeting_url": "https://meet.google.com/new-link"
}- Disponible desde
propuestaoconfirmada. - Crea un registro en
interview_reschedulescon:requested_by(el recruiter)previous_scheduled_atnew_scheduled_atreason
- Actualiza la entrevista:
scheduled_at = new_scheduled_at,state = propuesta, y los otros campos opcionales. - Notifica a todas las partes.
Este flujo permite iteraciones: candidato propone una nueva fecha, recruiter re-agenda, empresa confirma, etc. El histórico queda completo.
Cancelar
Endpoint: POST /api/v1/interviews/{id}/cancelBody: {"reason": "motivo"}.
- Disponible desde cualquier no-terminal.
- Cambia a
cancelada. - Prepend
[cancelado] motivoalrecruiter_feedbackde la entrevista. - Notifica a todas las partes.
Marcar como realizada
Endpoint: POST /api/v1/interviews/{id}/complete
json
{
"feedback": "Entrevista muy positiva. Demostró solidez técnica...",
"recommendation": "advance" // o "reject", "second_interview"
}- Solo desde
confirmada. - Cambia a
realizada. - Guarda
recruiter_feedback,recommendationyrating. - No notifica automáticamente al candidato (el feedback es privado).
- Si la recomendación es
reject, sugiere al recruiter mover la asignación arejected.
Visibilidad del feedback
recruiter_feedback y company_feedback se exponen en InterviewResource sólo a recruiter, admin y company_user. Los candidatos no los ven (es información interna de evaluación). En la UI, la InterviewCard muestra una sección colapsable "Resultado de la entrevista" con la recomendación (badge), el rating (X/10) y el texto del feedback cuando la entrevista está en realizada o cuando hay feedback acumulado en una cancelada con motivo.
Marcar no_asisto
Endpoint: POST /api/v1/interviews/{id}/no-show
- Solo desde
confirmada. - Cambia a
no_asisto. - Impacta negativamente el rating del candidato (campo futuro).
- Notifica al candidato con oportunidad de explicar.
Vista de agenda
URL: /recruiter/agenda (opcional, fase 2)
Calendario visual con todas las entrevistas del recruiter agrupadas por día. Útil para ver carga semanal.
En el MVP la vista es por-vacante (dentro del pipeline).
Filtros en el listado de entrevistas
Endpoint GET /api/v1/interviews:
| Filtro | Ejemplo |
|---|---|
state | propuesta, confirmada, realizada, cancelada, no_asisto, reprogramada |
from, to | rango de fechas (ISO 8601) |
assignment_id | de una asignación específica |
La página /me/entrevistas (recruiter y company_user comparten la misma) expone estos filtros en UI:
- Tabs Próximas (
propuesta/confirmada/reprogramada), Historial (realizada/cancelada/no_asisto), Todas — agrupan estados localmente. - Búsqueda — input que filtra cliente-side por nombre del candidato o
code/titlede la vacante. - Estado — select que mapea a
?state=del endpoint (Todos los estadoslo omite). - Rango de fechas —
Desde/Hastaque se mandan comofrom/to. - Limpiar filtros — reset rápido cuando hay alguno activo.
Cada cambio dispara una nueva query de TanStack (la queryKey incluye los filtros). El backend pagina a 30 por defecto; si la lista crece más allá de eso, añadir per_page y un botón "Cargar más" en una iteración futura.
Notificaciones disparadas
| Evento | Notificación | Destinatarios |
|---|---|---|
| Schedule nueva | InterviewScheduledNotification | candidato + company_user |
| Confirmada | InterviewConfirmedNotification | candidato + company_user + recruiter |
| Reprogramada | InterviewRescheduledNotification | todas las partes |
| Cancelada | InterviewCancelledNotification | todas las partes |
| Realizada | — | (silenciosa al candidato) |
| No asistió | InterviewNoShowNotification | candidato |
Todas vía email + in-app.
Integración con calendario (Fase 2)
Pendiente: generar archivos .ics para adjuntar al correo de confirmación. Esto permite que el candidato y el company_user agreguen la entrevista a su Google Calendar / Outlook con un click.
Performance y diseño
- El endpoint
GET /interviewsusa eager loading deassignment.candidateProfile.useryassignment.vacancy.company. - Índices DB:
(vacancy_assignment_id, state),scheduled_at,state. - Paginación de 20 con
per_pagemáximo 50.
Errores comunes
| Error | HTTP | Causa |
|---|---|---|
| "Vacante no admite entrevistas" | 422 | Estado de vacancy no compatible |
| "No puedes agendar en el pasado" | 422 | scheduled_at < now() |
| "Entrevista ya realizada" | 422 | Intento de modificar una en estado terminal |
| "Transición no permitida" | 422 | FSM rechaza |
Siguiente
Cierre del kit del recruiter: Favoritos y notas →

