Runbook operativo para todo lo relacionado con la integración Zoho (Calendar + Mail) del workspace tras el pivot s68 (17-05-2026).
Arquitectura en una frase
D1 sigue siendo SoT único para tasks (KanbanMini intacto). Zoho Calendar + Mail entran como canales de acción explícita desde TaskModal — la task es el hub, Zoho es el brazo ejecutor. Per-user OAuth (cada miembro del staff autoriza su propia cuenta).
Quién debe leer esto
- Edu / Dani / Txell — para autorizar su cuenta Zoho o re-autorizar tras cambio de scopes.
- Operador de incidentes — si algún evento Calendar no aparece, falla un cierre de task, o el panel
/settings/integrations/zohomuestra “sesión desconocida”.
Para el usuario final
Autorizar mi cuenta Zoho (primera vez)
- Inicia sesión en el workspace con tu email
@esfericlabs.com. - Visita
https://workspace.crearack.com/settings/integrations/zoho/. - En tu propia card pulsa “Conectar Zoho”.
- Zoho te pide login con tus credenciales y muestra el consent screen. Acepta todos los permisos.
- Volverás a la página con tu card marcada como “Conectado”.
Crear una task con evento Calendar (manual)
- Modal “Nueva tarea” → título + assignees + due_date.
- Despliega ”+ Crear evento” → ajusta start/end → “Añadir a la tarea”.
- (Opcional) ”+ Vincular email” → busca un email del Mail → selecciona.
- Pulsa Crear.
- La task aparece en KanbanMini con badge
1 evento(o2 eventos).
Crear/cerrar una task (automático)
Cuando creas una task con assignees + due_date, automáticamente:
- Aparece un evento all-day “Inicio · [task]” hoy en el Calendar de cada implicado.
- Aparece un evento all-day “Fin previsto · [task]” en la fecha límite.
Cuando pasas la task a Completada:
- “Fin previsto” desaparece.
- Aparece “Cerrado · [task]” en hoy.
Cuando borras la task:
- Todos los eventos auto desaparecen del Calendar de cada implicado.
Truco operativo
Si refrescas el Zoho Calendar y no aparecen los cambios, recarga con Ctrl+Shift+R o reset de sesión. El cliente Zoho cachea aggressivamente. Los eventos sí están creados — es solo cosmética del navegador.
Para el administrador (Edu)
Autorizar Zoho en nombre de Dani o Txell (modo admin)
Útil para pre-configurar las cuentas antes del onboarding.
1. mail.zoho.eu → avatar → Sign out (CADA VEZ entre miembros)
2. (cerrar la pestaña de mail.zoho.eu)
3. https://workspace.crearack.com/api/oauth/zoho/start?as=<TeamMember>
donde <TeamMember> = Edu | Dani | Txell
4. Zoho pide login fresh → metes credenciales del miembro correcto → Accept
5. Verifica en /settings/integrations/zoho que la card muestra el email correcto
6. Repetir desde 1 para el siguiente miembro
FOOTGUN crítico: si NO haces logout en mail.zoho.eu entre cada ?as=, Zoho usa la sesión activa anterior y el token se guarda mapeado al wrong team_member. Ver memoria feedback_zoho_logout_between_admin_oauth.
Revocar el token de un miembro
Solo Edu puede revocar otros (endpoint admin):
https://workspace.crearack.com/api/zoho/admin-revoke?member=Dani
https://workspace.crearack.com/api/zoho/admin-revoke?member=Txell
Cada miembro puede revocar el suyo desde /settings/integrations/zoho con el botón “Revocar” en su propia card.
Verificar estado de las 3 cuentas
https://workspace.crearack.com/api/zoho/status
Devuelve JSON con members[] (los 3) + current (el actor del request).
Diagnosticar fallo de DELETE/UPDATE auto-events
https://workspace.crearack.com/api/zoho/calendar/debug-close
Auto-detecta la task más reciente con auto_end_planned y ejecuta el DELETE síncrono devolviendo el response Zoho raw. Útil cuando un cierre de task no borra el evento “Fin previsto” en Zoho.
Para el operador de incidentes
Síntoma: el panel dice “sesión actual desconocida” / botón Re-autorizar no aparece
Causa: el header Cf-Access-Authenticated-User-Email no llega y el mapeo STAFF_EMAIL_TO_NAME en functions/_lib/staff.ts no resuelve al usuario.
Fix:
- Verificar que el email CF Access del usuario está en
STAFF_EMAIL_TO_NAME. Si no, actualizar el código. - Si está, comprobar que CF Access tiene “Include identity in JWT” activado. El fallback JWT decode requiere el claim
emailen el JWT.
Ver memoria footguns_staff_email_mapping_workspace.
Síntoma: la task se cierra pero el evento “Fin previsto” sigue en Zoho
Causa probable: scope ZohoCalendar.event.DELETE no autorizado en el token actual, o el header etag no se está mandando en el DELETE.
Diagnóstico:
/api/zoho/calendar/debug-close→ mira el JSON.- Si devuelve
status: 400 ETAG_MISSING→ bug en el código (regresión). - Si devuelve
status: 401o403→ token sin scope DELETE. Re-autorizar. - Si devuelve
status: 204o200→ arreglado, sigue normal.
- Si devuelve
- Verificar que todos los miembros tienen scope DELETE/UPDATE en su autorización actual (re-autorizar tras cualquier cambio en
ZOHO_SCOPES).
Ver memoria footguns_zoho_calendar_etag_required.
Síntoma: las 3 cards muestran la misma cuenta Zoho
Causa: Edu autorizó las 3 cuentas sin hacer logout en mail.zoho.eu entre cada ?as=. Zoho usó la misma sesión activa para los 3 consents.
Fix:
- Revocar los tokens cruzados:
admin-revoke?member=Dani+admin-revoke?member=Txell. - Re-autorizar siguiendo el protocolo arriba (logout entre cada).
Síntoma: hooks auto fallan silenciosamente
Los hooks onTaskCreated / onTaskUpdated / onTaskDeleted corren en context.waitUntil (background). Si fallan, log con console.warn. Para inspeccionar:
wrangler tail crearacksl-workspace(en local con auth).- Filtrar por
[task-zoho-auto]. - Identificar la causa (scope, ETag, calendar no encontrado, etc).
Schema D1 relevante
| Tabla | Propósito |
|---|---|
zoho_oauth_tokens | Tokens per-user. PK team_member. Columnas: zoho_email, refresh_token, access_token, dc, granted_by_cf_email. |
zoho_oauth_state | State efímero anti-CSRF del flujo OAuth (TTL 1h). |
task_calendar_events | Vínculos task ↔ evento Calendar. PK (task_id, zoho_event_uid). Columna link_type distingue manual de los 3 tipos auto. |
task_emails | Vínculos task ↔ mensaje Mail. PK (task_id, zoho_message_id). |
Endpoints relevantes
| Endpoint | Función |
|---|---|
GET /api/me | Identidad del actor (resuelve email + team_member). |
GET /api/zoho/status | Estado de las 3 cuentas + actor actual. |
DELETE /api/zoho/status | Revocar token del actor (no de otros). |
GET /api/oauth/zoho/start[?as=X] | Inicia OAuth para sí mismo o (admin Edu) para otro. |
GET /api/oauth/zoho/callback | Callback Zoho. Público (en PUBLIC_PATHS). |
GET /api/zoho/admin-revoke?member=X | Revocar token de cualquier miembro (solo Edu). |
GET /api/zoho/calendar[?from=&to=] | Eventos del calendar del actor. |
POST /api/zoho/calendar | Crear evento manual con organizer + attendees. |
GET /api/zoho/mail/search?q=&from=&subject= | Picker emails para vincular. |
GET /api/zoho/mail/compose-url?subject=&body= | Deep-link Zoho Mail compose. |
GET/POST/DELETE /api/tasks/{id}/links/calendar | CRUD vínculos eventos por task. |
GET/POST/DELETE /api/tasks/{id}/links/email | CRUD vínculos emails por task. |
GET /api/zoho/calendar/debug-close | Debug DELETE síncrono de auto_end_planned. Solo Edu. |
Scopes Zoho activos (s68)
ZohoCalendar.event.READ— leer eventosZohoCalendar.event.CREATE— crear eventosZohoCalendar.event.UPDATE— mover fechas / editar eventosZohoCalendar.event.DELETE— borrar eventosZohoCalendar.calendar.READ— listar calendariosZohoMail.messages.READ— buscar y leer emails para vincularZohoMail.accounts.READ— identidad de la cuenta Mail
Cualquier cambio en estos scopes requiere re-autorización de las 3 cuentas.
Limitaciones conocidas
- Cambiar assignees de una task después de crear NO propaga a los eventos auto ya creados. Los nuevos assignees no aparecen como attendees.
- Refresh UI Zoho lento: los cambios aparecen tras refresh manual del navegador (~10-30 s).
- Sesión Zoho compartida en navegador entre cuentas → ojo al modo admin
?as=(ver footgun). - Multi-region: solo testeado en
dc=eu. Paradc=comodc=indebería funcionar igual pero sin verificación.
Véase también
- [[decision—20260517—integracion-zoho-calendar-mail-pivot]]
- [[decision—20260516—integracion-zoho-workspace]]
- [[entity—ops—catalogo-servicios-externos]]