{"openapi":"3.1.0","info":{"title":"Projekt Republic API","description":"API **contract-first** y **multi-tenant** de Projekt Republic (3XA). Todo el dominio vive\nbajo `/api/v1`; `/health` y `/ready` van sin prefijo (infra, no producto).\n\n### Autenticación\n\nAutentícate con un **Personal Access Token (PAT)** con formato `pjk_live_…`,\ncreado por un *owner/admin* de la organización (Ajustes → API keys). Envíalo en\ncada petición como:\n\n```\nAuthorization: Bearer pjk_live_…\n```\n\nEl token se muestra **una sola vez** al crearlo (en la base de datos solo se\nguarda su hash); guárdalo en un gestor de secretos y revócalo si se filtra. La\nsesión web usa cookies HttpOnly; los PATs son para scripts, SDKs e integraciones.\n\n### Organización (multi-tenant)\n\nCada recurso vive bajo su organización: la ruta lleva el `{org_id}` y el servidor\nvalida la pertenencia del portador. Un PAT solo alcanza la organización que lo\nemitió; los no-miembros reciben `404` (sin filtrar la existencia del recurso).\n\n### Errores\n\nTODA respuesta no-2xx usa el mismo envelope:\n\n```json\n{ \"error\": { \"code\": \"not_found\", \"message\": \"Resource not found\", \"request_id\": \"…\" } }\n```\n\nLas validaciones de entrada devuelven `422` con `code: \"validation_error\"` y un\n`message` que enumera los campos inválidos (`campo: motivo`). Códigos estándar:\n`401` (unauthorized), `403` (forbidden), `404` (not_found), `409` (conflict),\n`422` (validation_error), `429` (too_many_requests).\n\n### Rate limits\n\nLos endpoints sensibles (autenticación) están limitados por IP y por cuenta con\nuna ventana fija; superarlos devuelve `429` con el envelope estándar. El límite\nes *fail-open*: si el backend de límites no está disponible, no se bloquea la\npetición. El header `X-Request-ID` acompaña a cada respuesta para trazabilidad.\n","version":"0.1.0"},"servers":[{"url":"https://api.projektrepublic.com/api/v1","description":"Production"}],"paths":{"/health":{"get":{"tags":["health"],"summary":"Health","description":"Liveness: el proceso responde (no toca dependencias).","operationId":"health_health","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthOut"}}}}}}},"/ready":{"get":{"tags":["health"],"summary":"Ready","description":"Readiness: SELECT 1 contra la DB y PING contra Redis.\n\nLa sesión llega por la dependency `get_db_session` (no por la factory\nglobal): los tests pueden overridearla y el CI no necesita un MySQL real\npara el smoke de readiness.","operationId":"health_ready","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthOut"}}}},"503":{"description":"Service Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/version":{"get":{"tags":["health"],"summary":"Version","description":"Metadata de build (versión/commit/entorno). Endpoint público de infra,\nsin dependencias externas — nunca expone secretos, solo texto de build\nfijado en build/deploy time (o \"unknown\" si no se configuró).","operationId":"health_version","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionOut"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":["oauth-as"],"summary":"Oauth As Metadata","description":"Metadata del AS (RFC 8414).","operationId":"getOauthAsMetadata","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Getoauthasmetadata"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["oauth-as"],"summary":"Oauth Resource Metadata","description":"Metadata del protected resource /mcp (RFC 9728).","operationId":"getOauthProtectedResourceMetadata","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Getoauthprotectedresourcemetadata"}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["oauth-as"],"summary":"Oauth Resource Metadata Mcp","description":"Misma metadata; ruta derivada del path del recurso (RFC 9728 §3.1).","operationId":"getOauthProtectedResourceMetadataMcp","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Getoauthprotectedresourcemetadatamcp"}}}}}}},"/api/v1/auth/register":{"post":{"tags":["auth"],"summary":"Register","operationId":"auth_register","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Login","operationId":"auth_login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh","operationId":"auth_refresh","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","description":"Cierra sesión. Idempotente y en la allowlist del cerrojo read-only de\nimpersonation (PJKT-2035): bajo impersonation el logout debe seguir\nfuncionando (vía de escape) y además TERMINA la impersonation activa\n(revoca + audita via=logout + borra su cookie) — cerrar sesión lo cierra TODO.","operationId":"auth_logout","responses":{"204":{"description":"Successful Response"}}}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"Me","description":"Usuario actual.\n\nSi la petición se autenticó con un PAT Bearer (`request.state.pat`),\nresuelve el nombre de la key y devuelve el scope en `api_key`.\nEn sesiones por cookie `api_key` es null.","operationId":"auth_me","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/email-change/confirm":{"get":{"tags":["auth"],"summary":"Email Change Confirm","description":"Canjea el enlace de verificación y aplica el cambio de email.\n\nPÚBLICO (el token de la URL ES la credencial: quien lo tiene demostró\ncontrolar el buzón nuevo). En éxito redirige (302) a\n`{WEB_URL}/login?email_changed=1`; token manipulado/caducado → 400\n`invalid_token`; email ocupado por otro usuario → 409 `email_in_use` (ambos\ncon el envelope estándar). GET a propósito: es un enlace de correo.","operationId":"auth_email_change_confirm","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","description":"Token firmado de cambio de email.","title":"Token"},"description":"Token firmado de cambio de email."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Email cambiado → `{WEB_URL}/login?email_changed=1`."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/impersonation/activate":{"get":{"tags":["auth"],"summary":"Impersonation Activate","description":"Canjea el token de activación (single-use) por la cookie HttpOnly de sesión.\n\nPÚBLICO (el token de la URL ES la credencial; no exige sesión previa).\nNavegación top-level desde el panel ops: canjea el token de activación por un\ntoken de SESIÓN nuevo (el de la URL queda inservible tras esto), fija la cookie\ncon ESE token de sesión —nunca el que viajó por la URL— en el dominio de la web\ny redirige (302) a `{WEB_URL}/dashboard`. Token inválido/caducado/revocado/ya\nactivado → 302 a `{WEB_URL}/login?error=impersonation_invalid` (sin cookie, sin\nfiltrar detalle), mismo patrón que el callback OAuth. GET a propósito: es de\nsolo lectura respecto al negocio (solo emite una cookie) y pasa el cerrojo\nread-only del middleware.","operationId":"auth_impersonation_activate","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","description":"Token opaco de la sesión de impersonation.","title":"Token"},"description":"Token opaco de la sesión de impersonation."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Redirige a la web (con cookie en éxito)."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/impersonation/exit":{"get":{"tags":["auth"],"summary":"Impersonation Exit","description":"\"Salir\" del banner: revoca la sesión y BORRA la cookie de impersonation.\n\nPÚBLICO e idempotente. GET a propósito (el banner navega aquí; una escritura\nquedaría bloqueada por el cerrojo read-only). Siempre limpia la cookie (aunque\nla sesión ya no exista) y, si sigue viva, la revoca + audita\n`platform.impersonation_ended` (via=exit). Redirige a `{WEB_URL}/login`.","operationId":"auth_impersonation_exit","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Limpia la cookie de impersonation y redirige."}}}},"/api/v1/me/profile":{"patch":{"tags":["auth"],"summary":"Update My Profile","operationId":"auth_update_my_profile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyProfileUpdateIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/stats":{"get":{"tags":["auth"],"summary":"Get My Stats","operationId":"auth_get_my_stats","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyStatsOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/onboarding":{"get":{"tags":["auth"],"summary":"Mi estado de onboarding","description":"Estado de onboarding (diálogo de bienvenida, tours guiados y progreso del asistente de puesta en marcha) del usuario autenticado, persistido a nivel de USUARIO como fuente de verdad cross-device. Un usuario que aún no ha guardado nada recibe el estado vacío (welcome_seen=false, tours_completed=[], version=0, setup=null), nunca 404.","operationId":"getMyOnboarding","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStateOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"tags":["auth"],"summary":"Guardar mi estado de onboarding","description":"Reemplaza POR COMPLETO el estado de onboarding del usuario (sustitución, no merge: el frontend envía siempre el estado completo). Idempotente. Usado al descartar el welcome, al completar/saltar un tour, al avanzar por el asistente de puesta en marcha y al reiniciar los tours desde Ajustes. Única excepción a la sustitución total: omitir `setup` conserva el progreso guardado del asistente; `setup: null` lo borra.","operationId":"setMyOnboarding","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStateIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStateOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/dashboard-layout":{"get":{"tags":["auth"],"summary":"Mi layout del dashboard","description":"Layout del dashboard (qué widgets, en qué orden y tamaño) del usuario autenticado, persistido a nivel de USUARIO como fuente de verdad cross-device. Un usuario que aún no ha guardado nada recibe el estado vacío (version=1, items=[]), nunca 404: el frontend lo reconcilia contra su catálogo de widgets.","operationId":"getMyDashboardLayout","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardLayoutOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"tags":["auth"],"summary":"Guardar mi layout del dashboard","description":"Reemplaza POR COMPLETO el layout del dashboard del usuario (sustitución, no merge: el frontend envía siempre el layout completo). Idempotente.","operationId":"setMyDashboardLayout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardLayoutIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardLayoutOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/ui-preferences":{"put":{"tags":["auth"],"summary":"Guardar mis preferencias de UI","description":"Actualiza las preferencias de UI del usuario autenticado (fondo de ambiente Pro y estilo de las cards), persistidas a nivel de USUARIO como fuente de verdad cross-device. Actualización parcial: solo se aplican los campos presentes; `ambient_background: null` quita el fondo de ambiente. Idempotente. Devuelve el usuario actualizado.","operationId":"setMyUiPreferences","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UiPreferencesUpdateIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/sessions":{"get":{"tags":["auth"],"summary":"Dispositivos/sesiones conectados","description":"Sesiones activas del usuario autenticado (cada refresh token vivo = un dispositivo/navegador), con IP de origen, User-Agent y última actividad. La sesión de esta petición se marca con `current=true`. Nunca expone el token; ordenadas por actividad reciente primero.","operationId":"listMySessions","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SessionOut"},"type":"array","title":"Response Listmysessions"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/sessions/{session_id}":{"delete":{"tags":["auth"],"summary":"Cerrar sesión de un dispositivo","description":"Revoca el refresh token de una sesión del usuario (cierra sesión en ese dispositivo). Sesión inexistente, ya revocada o de otro usuario → 404. Se permite revocar la sesión actual (el usuario deja de estar autenticado en este dispositivo al caducar su access token).","operationId":"revokeMySession","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/weather":{"get":{"tags":["auth"],"summary":"Condición meteorológica del usuario","description":"Consulta las condiciones actuales en Open-Meteo para lat/lon dados. Si el usuario tiene `weather_override` fijado, se devuelve ese pin. Nunca devuelve 5xx aunque el proveedor falle (unavailable=true con HTTP 200). SSRF-safe: el host está fijo en api.open-meteo.com; el usuario solo aporta lat/lon numéricos validados.","operationId":"getMyWeather","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","maximum":90.0,"minimum":-90.0,"description":"Latitud (-90..90)","title":"Lat"},"description":"Latitud (-90..90)"},{"name":"lon","in":"query","required":true,"schema":{"type":"number","maximum":180.0,"minimum":-180.0,"description":"Longitud (-180..180)","title":"Lon"},"description":"Longitud (-180..180)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/auth/login-code":{"post":{"tags":["auth"],"summary":"Request Login Code","description":"Envía un código de 6 dígitos al correo.\n\nResponde SIEMPRE lo mismo, exista la cuenta o no: cualquier diferencia\nobservable —mensaje, código de estado o tiempo— convertiría esto en un\ndetector de cuentas. Pedir un código invalida el anterior de ese email.","operationId":"requestLoginCode","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCodeRequestIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCodeRequestOut"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/login-code/verify":{"post":{"tags":["auth"],"summary":"Verify Login Code","description":"Canjea el código y abre sesión (mismas cookies que un login normal).\n\nTodos los fallos —sin código, caducado, equivocado o agotado— devuelven el\nMISMO `invalid_login_code`: distinguirlos delataría si esa cuenta tiene una\npetición de acceso en curso.","operationId":"verifyLoginCode","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginCodeVerifyIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/2fa":{"get":{"tags":["auth"],"summary":"Two Factor Status","description":"Estado del segundo factor de la cuenta (para la pantalla de seguridad).","operationId":"auth_two_factor_status","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorStatusOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/2fa/setup":{"post":{"tags":["auth"],"summary":"Start Setup","description":"Genera el secreto y el QR. NO activa nada hasta confirmar con un código.","operationId":"auth_start_setup","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorSetupOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/2fa/enable":{"post":{"tags":["auth"],"summary":"Enable","description":"Confirma el alta y devuelve los códigos de recuperación (solo aquí).","operationId":"auth_enable","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoveryCodesOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/2fa/disable":{"post":{"tags":["auth"],"summary":"Disable","description":"Desactiva el segundo factor. Exige un código válido, no solo la sesión:\nsi bastara con estar dentro, robar una sesión bastaría para quitarlo.","operationId":"auth_disable","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeIn"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/2fa/recovery-codes":{"post":{"tags":["auth"],"summary":"Regenerate Recovery Codes","description":"Emite una lista nueva e invalida la anterior. Exige código válido.","operationId":"auth_regenerate_recovery_codes","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoveryCodesOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/2fa/verify":{"post":{"tags":["auth"],"summary":"Verify Challenge","description":"Canjea el reto pendiente y abre la sesión de verdad.\n\nEs el único endpoint que acepta la cookie de reto, y lo primero que hace al\nacertar es borrarla: un reto es de un solo uso, y dejarlo vivo permitiría\nreabrir sesión con él durante los cinco minutos siguientes.","operationId":"auth_verify_challenge","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFactorCodeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/step-up/methods":{"get":{"tags":["auth"],"summary":"Factores disponibles para confirmar","operationId":"getStepUpMethods","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpMethodsOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/step-up/passkey/begin":{"post":{"tags":["auth"],"summary":"Iniciar confirmación con passkey","operationId":"stepUpPasskeyBegin","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Stepuppasskeybegin"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/step-up/passkey/complete":{"post":{"tags":["auth"],"summary":"Completar confirmación con passkey","operationId":"stepUpPasskeyComplete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpPasskeyCompleteIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResultOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/step-up/totp":{"post":{"tags":["auth"],"summary":"Confirmar con código 2FA (TOTP o recuperación)","operationId":"stepUpTotp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpCodeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResultOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/step-up/email/request":{"post":{"tags":["auth"],"summary":"Enviarme un código de confirmación por email","operationId":"stepUpEmailRequest","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericMessageOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/step-up/email/verify":{"post":{"tags":["auth"],"summary":"Confirmar con el código recibido por email","operationId":"stepUpEmailVerify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpCodeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResultOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/oauth/{provider}/start":{"get":{"tags":["auth"],"summary":"Oauth Start","operationId":"auth_oauth_start","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}},{"name":"client","in":"query","required":false,"schema":{"enum":["web","ops"],"type":"string","default":"web","title":"Client"}}],"responses":{"302":{"description":"Redirección a la URL de autorización del proveedor.","content":{"application/json":{"schema":{}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/oauth/{provider}/callback":{"get":{"tags":["auth"],"summary":"Oauth Callback","operationId":"auth_oauth_callback","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"oauth_state","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Oauth State"}},{"name":"oauth_client","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Oauth Client"}}],"responses":{"302":{"description":"Sesión abierta → /dashboard, o /login?error=… en fallo.","content":{"application/json":{"schema":{}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/register":{"post":{"tags":["oauth-as"],"summary":"Register Client","description":"Registro dinámico de cliente público (RFC 7591); errores en formato RFC.","operationId":"registerOauthClient","requestBody":{"content":{"application/json":{"schema":{"properties":{"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"token_endpoint_auth_method":{"anyOf":[{"type":"string","const":"none"},{"type":"null"}],"title":"Token Endpoint Auth Method"},"grant_types":{"anyOf":[{"items":{"type":"string","enum":["authorization_code","refresh_token"]},"type":"array"},{"type":"null"}],"title":"Grant Types"},"response_types":{"anyOf":[{"items":{"type":"string","const":"code"},"type":"array"},{"type":"null"}],"title":"Response Types"},"scope":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Scope"}},"type":"object","required":["redirect_uris"],"title":"ClientRegisterIn","description":"Metadata de registro dinámico de cliente (RFC 7591)."}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientRegisterOut"}}}},"400":{"description":"Metadata inválida (`invalid_client_metadata` / `invalid_redirect_uri`).","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error"]}}}}}}},"/api/v1/oauth/authorize":{"get":{"tags":["oauth-as"],"summary":"Authorize","description":"Valida y redirige al consent de la web (o al redirect_uri con ?error=).","operationId":"oauthAuthorize","parameters":[{"name":"response_type","in":"query","required":false,"schema":{"type":"string","enum":["code"],"default":"","title":"Response Type"}},{"name":"client_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Client Id"}},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string","default":"","title":"Redirect Uri"}},{"name":"code_challenge","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["S256"],"title":"Code Challenge Method"}},{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"resource","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Oauthauthorize"}}}},"302":{"description":"Redirección al consent, o al redirect_uri con `?error=`."},"400":{"description":"Cliente o `redirect_uri` inválidos (sin redirección).","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"error_description":{"type":"string"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/consent/{request_id}":{"get":{"tags":["oauth-as"],"summary":"Consent Info","description":"Qué cliente pide acceso (para pintar la pantalla de consentimiento).","operationId":"getOauthConsentInfo","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentInfoOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["oauth-as"],"summary":"Decide Consent","description":"Aprueba (emite code ligado al usuario de la sesión) o deniega.","operationId":"decideOauthConsent","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentDecisionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentDecisionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/token":{"post":{"tags":["oauth-as"],"summary":"Token","description":"Canje de authorization code / rotación de refresh (RFC 6749, form-encoded).","operationId":"oauthToken","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_oauthToken"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponseOut"}}}},"400":{"description":"`invalid_grant`, `invalid_request` o `unsupported_grant_type`.","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error"]}}}},"401":{"description":"Cliente desconocido (`invalid_client`).","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error"]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/authorizations":{"get":{"tags":["oauth-as"],"summary":"List My Authorizations","description":"Autorizaciones OAuth activas del usuario de la sesión (apps conectadas).","operationId":"listMyOauthAuthorizations","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OAuthAuthorizationOut"},"type":"array","title":"Response Listmyoauthauthorizations"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/oauth/authorizations/{authorization_id}":{"delete":{"tags":["oauth-as"],"summary":"Revoke My Authorization","description":"Revoca una autorización PROPIA: el consent + todos sus tokens vivos.\n\nSolo el dueño (identidad de la sesión); una autorización de otro usuario o\ninexistente → 404 opaco.","operationId":"revokeMyOauthAuthorization","parameters":[{"name":"authorization_id","in":"path","required":true,"schema":{"type":"string","title":"Authorization Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/passkey/register/begin":{"post":{"tags":["auth"],"summary":"Passkey Register Begin","operationId":"auth_passkey_register_begin","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebAuthnOptionsOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/passkey/register/complete":{"post":{"tags":["auth"],"summary":"Passkey Register Complete","operationId":"auth_passkey_register_complete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyRegisterCompleteIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/passkey/login/begin":{"post":{"tags":["auth"],"summary":"Passkey Login Begin","operationId":"auth_passkey_login_begin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyLoginBeginIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebAuthnOptionsOut"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/passkey/login/complete":{"post":{"tags":["auth"],"summary":"Passkey Login Complete","operationId":"auth_passkey_login_complete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyLoginCompleteIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserOut"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/passkey/credentials":{"get":{"tags":["auth"],"summary":"List Passkeys","operationId":"auth_list_passkeys","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PasskeyOut"},"type":"array","title":"Response Auth List Passkeys"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/auth/passkey/credentials/{credential_id}":{"delete":{"tags":["auth"],"summary":"Revoke Passkey","operationId":"auth_revoke_passkey","parameters":[{"name":"credential_id","in":"path","required":true,"schema":{"type":"string","title":"Credential Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations":{"get":{"tags":["organizations"],"summary":"List Organizations","operationId":"organizations_list_organizations","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OrganizationOut"},"type":"array","title":"Response Organizations List Organizations"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["organizations"],"summary":"Create Organization","operationId":"organizations_create_organization","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationCreateIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}":{"get":{"tags":["organizations"],"summary":"Get Organization","operationId":"organizations_get_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["organizations"],"summary":"Update Organization","operationId":"organizations_update_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["organizations"],"summary":"Delete Organization","description":"Borra la organización (soft-delete). Solo el `owner`; un `admin` recibe 403.\n\nSolo con sesión de navegador: autenticada con credenciales de API\n(`Authorization: Bearer` — PAT `pjk_live_…` u OAuth `pjk_oat_…`) responde 403\n`interactive_session_required`, incluso siendo `owner`. Arrastra toda la org y\nesas credenciales viven desatendidas en scripts, apps de terceros y el\nconector MCP, así que exige una persona delante.\n\nTras el borrado la organización deja de ser accesible (403\n`organization_suspended` en el resto de endpoints org-scoped) y deja de\nlistarse en `GET /organizations`. Lo que cuelga de ella no se borra: el\nborrado físico es la purga del panel de plataforma. Ver\n`OrganizationService.delete`.","operationId":"organizations_delete_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/members":{"get":{"tags":["organizations"],"summary":"List Organization Members","description":"Lista paginada de miembros de la organización. Requiere member+.\n\nLos campos sensibles de cada miembro (`phone`, `hire_date`,\n`employment_type`, `hourly_cost_rate`, `hourly_bill_rate`) solo se\nrellenan para manager+/admin+; para un `member` normal vienen en `null`.","operationId":"organizations_list_organization_members","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MemberOut"},"title":"Response Organizations List Organization Members"}}},"headers":{"X-Total-Count":{"description":"Número total de miembros de la organización (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/members/bulk":{"post":{"tags":["organizations"],"summary":"Bulk Update Members","description":"Actualización masiva de RRHH (department_id, position, manager_id, is_active).\n\nAplica el mismo UPSERT de ficha employee que el PATCH individual. `is_active`\npermite activar/desactivar a los seleccionados sin eliminar la membership.\nRequiere manager+. Respuesta honesta: {ok, errors[{user_id, reason}]}.","operationId":"bulkUpdateMembers","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberBulkUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberBulkResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/members/{user_id}":{"patch":{"tags":["organizations"],"summary":"Update Member","description":"Actualiza rol y/o campos RRHH de un miembro.\n\n- `role`: requiere owner/admin. Solo un owner puede asignar/revocar owner.\n- Campos RRHH (department, position, phone, hire_date, employment_type,\n  is_active): requieren manager+.\n\nConceder `owner` es una TRANSFERENCIA DE PROPIEDAD: además del step-up sudo del\ndecorador, exige una confirmación FRESCA (PJKT-2111).","operationId":"organizations_update_member","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["organizations"],"summary":"Remove Member","operationId":"organizations_remove_member","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/members/invite":{"post":{"tags":["organizations"],"summary":"Invite Member","description":"Invita a un usuario a la organización por email o nombre.\n\n- Usuario ya registrado y NO miembro → se añade directamente, response\n  contiene `member`.\n- Email desconocido → invitación pendiente por email, response contiene\n  `invite`. Si ya había una invitación pendiente vigente para ese email,\n  se devuelve la existente (idempotente).\n- Usuario ya miembro → 409 `already_member`.\n- Requiere rol admin o superior.","operationId":"inviteMember","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMemberIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/members/invite-bulk":{"post":{"tags":["organizations"],"summary":"Bulk Invite Members","description":"Alta en lote: una lista de correos con un mismo rol.\n\n200 con UNA FILA POR CORREO (`invited`, `added`, `already_member`,\n`already_invited`, `duplicate`, `invalid_email`, `plan_limit_reached`).\nNunca un éxito o fracaso global: ningún correo problemático aborta el lote.\nRequiere rol admin o superior.","operationId":"bulkInviteMembers","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkInviteIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkInviteResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/members/invites":{"get":{"tags":["organizations"],"summary":"List Org Invites","description":"Lista las invitaciones pendientes (no aceptadas, no caducadas). Admin+.","operationId":"listOrgInvites","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InviteOut"},"title":"Response Listorginvites"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/members/invites/{invite_id}":{"delete":{"tags":["organizations"],"summary":"Revoke Org Invite","description":"Revoca una invitación pendiente. Admin+.","operationId":"revokeOrgInvite","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/org-chart":{"get":{"tags":["organizations"],"summary":"Get Org Chart","description":"Árbol jerárquico de empleados (organigrama).\n\nDevuelve los empleados de la org organizados en árbol según la cadena\nmanager_id. Los nodos sin manager_id o cuyo manager no pertenece a la org\nson raíces. Cada nodo incluye sus reportes directos en `children`. Requiere\nmember+.","operationId":"getOrgChart","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrgChartNode"},"title":"Response Getorgchart"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/export":{"get":{"tags":["organizations"],"summary":"Export Org Data","description":"Exporta los datos de la organización como ZIP con CSVs.\n\nRequiere rol owner o admin. Incluye: projects.csv, tasks.csv,\nclients.csv, invoices.csv, expenses.csv. Excluye registros en papelera.","operationId":"organizations_export_org_data","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/domains":{"get":{"tags":["organizations"],"summary":"List Organization Domains","description":"Dominios corporativos de la organización (owner/admin, plan Enterprise).","operationId":"organizations_list_organization_domains","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationDomainOut"},"title":"Response Organizations List Organization Domains"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations"],"summary":"Claim Organization Domain","description":"Reclama un dominio y envía un código a una dirección de ese mismo dominio.\n\nNo concede nada: hasta verificarlo, el dominio no captura a nadie. Un dominio\npúblico (gmail, outlook…) se rechaza con 422 `public_domain`, y uno ya\nreclamado con 409 `domain_taken` — sin decir por quién.","operationId":"organizations_claim_organization_domain","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainClaimIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/domains/{domain_id}/verify":{"post":{"tags":["organizations"],"summary":"Verify Organization Domain","description":"Comprueba el código del correo. De un solo uso, caduca en 30 min.","operationId":"organizations_verify_organization_domain","parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"type":"string","title":"Domain Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainVerifyIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/domains/{domain_id}":{"patch":{"tags":["organizations"],"summary":"Set Organization Domain Enforcement","description":"Activa o desactiva el modo obligatorio de un dominio ya verificado.\n\nAl activarlo, los usuarios ya registrados con ese correo quedan vinculados\ncomo `member` y dejan de poder crear organizaciones propias. Lo que ya tenían\nNO se toca.","operationId":"organizations_set_organization_domain_enforcement","parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"type":"string","title":"Domain Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainEnforceIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDomainOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["organizations"],"summary":"Release Organization Domain","description":"Suelta el dominio: deja de capturar altas y vuelve a estar reclamable.\n\nLas membresías ya creadas se conservan: quien entró por el dominio sigue en la\norganización.","operationId":"organizations_release_organization_domain","parameters":[{"name":"domain_id","in":"path","required":true,"schema":{"type":"string","title":"Domain Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invites/{token}":{"get":{"tags":["organizations"],"summary":"Get Invite By Token","description":"Ficha pública de una invitación, para pintar la pantalla de aceptación.\n\nPÚBLICO a propósito: quien abre el enlace del correo no tiene por qué tener\nsesión —ni cuenta—. El token no autentica ni abre sesión; solo resuelve qué\ninvitación es. 200 también con `status` `expired`/`accepted`.","operationId":"getInviteByToken","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitePreviewOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invites/{token}/accept":{"post":{"tags":["organizations"],"summary":"Accept Invite","description":"Acepta la invitación con la sesión iniciada. Idempotente.\n\nExige que el correo de la sesión sea el invitado (403\n`invite_email_mismatch` si no): el enlace identifica la invitación, no a la\npersona.","operationId":"acceptInvite","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/audit":{"get":{"tags":["audit"],"summary":"List Organization Audit","description":"Historial de auditoría de ESTA organización (self-service). Requiere admin+.\n\nExcluye siempre las acciones internas `platform.*` del panel OPS\n(impersonation, purga, suspensión, plan, feature flags…): son operaciones\ndel operador de la plataforma SOBRE la organización, no actividad propia\nde sus miembros.","operationId":"audit_list_organization_audit","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por código de acción exacto.","title":"Action"},"description":"Filtrar por código de acción exacto."},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por el usuario que ejecutó la acción.","title":"Actor Id"},"description":"Filtrar por el usuario que ejecutó la acción."},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"created_at >= (ISO 8601, inclusive).","title":"Date From"},"description":"created_at >= (ISO 8601, inclusive)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"created_at <= (ISO 8601, inclusive).","title":"Date To"},"description":"created_at <= (ISO 8601, inclusive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrgAuditLogEntryOut"},"title":"Response Audit List Organization Audit"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/entitlements":{"get":{"tags":["billing"],"summary":"Get Entitlements","description":"Plan efectivo de la organización + features + límites + uso.","operationId":"billing_get_entitlements","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/modules":{"get":{"tags":["billing"],"summary":"List Organization Modules","description":"Módulos que el plan de la organización incluye, con su interruptor (admin+).","operationId":"billing_list_organization_modules","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationModuleOut"},"title":"Response Billing List Organization Modules"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/modules/{key}":{"put":{"tags":["billing"],"summary":"Set Organization Module","description":"Enciende o apaga un módulo para toda la organización (admin+).\n\nApagar NO borra nada: los datos del módulo siguen ahí y volver a encenderlo\nlos devuelve tal cual. Encender tampoco CONCEDE nada — solo retira el apagado\nde la organización—, así que un plan que no incluya el módulo recibe 422\n`module_not_available` en los dos sentidos.","operationId":"billing_set_organization_module","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationModuleSetIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationModuleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/billing/checkout-session":{"post":{"tags":["billing"],"summary":"Create Checkout Session","description":"Crea una sesión de Stripe Checkout para subir de plan (admin+).\n\nExige datos fiscales de la organización (422 `fiscal_data_required`) y admite\nun `promo_code` opcional; si el código no vale, la sesión NO se crea (422\n`promo_*`) en vez de cobrar el precio completo sin avisar.","operationId":"billing_create_checkout_session","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRedirect"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/v1/organizations/{org_id}/billing/promo/{code}":{"get":{"tags":["billing"],"summary":"Resolve Promo Code","description":"Qué concede un código promocional a ESTA organización (admin+).\n\nSirve para que el checkout pueda anunciar el beneficio antes de pagar. Aplica\nlas mismas validaciones que el checkout —caducidad, usos, plan y canje previo\nde la organización—, así que un código inservible se ve aquí y no al pagar.","operationId":"billing_resolve_promo_code","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoCodeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/billing/portal-session":{"post":{"tags":["billing"],"summary":"Create Portal Session","description":"Crea una sesión del Stripe Billing Portal para gestionar/cancelar (admin+).","operationId":"billing_create_portal_session","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingRedirect"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Recibe los eventos de Stripe (firma verificada) y aplica el cambio de plan.","operationId":"billing_stripe_webhook","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingWebhookAck"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Service Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/organizations/{org_id}/crossorg/orgs/search":{"get":{"tags":["crossorg"],"summary":"Search Orgs To Link","operationId":"searchOrgsToLink","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por nombre o slug.","title":"Q"},"description":"Filtra por nombre o slug."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrgSummaryOut"},"title":"Response Searchorgstolink"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crossorg/link-requests":{"post":{"tags":["crossorg"],"summary":"Request Org Link","operationId":"requestOrgLink","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgLinkRequestIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgLinkOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["crossorg"],"summary":"List Org Links","operationId":"listOrgLinks","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"direction","in":"query","required":false,"schema":{"enum":["incoming","outgoing"],"type":"string","description":"incoming | outgoing.","default":"incoming","title":"Direction"},"description":"incoming | outgoing."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrgLinkOut"},"title":"Response Listorglinks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crossorg/link-requests/{link_id}/accept":{"post":{"tags":["crossorg"],"summary":"Accept Org Link","operationId":"acceptOrgLink","parameters":[{"name":"link_id","in":"path","required":true,"schema":{"type":"string","title":"Link Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgLinkOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crossorg/link-requests/{link_id}/reject":{"post":{"tags":["crossorg"],"summary":"Reject Org Link","operationId":"rejectOrgLink","parameters":[{"name":"link_id","in":"path","required":true,"schema":{"type":"string","title":"Link Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgLinkOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crossorg/link-requests/{link_id}/revoke":{"post":{"tags":["crossorg"],"summary":"Revoke Org Link","operationId":"revokeOrgLink","parameters":[{"name":"link_id","in":"path","required":true,"schema":{"type":"string","title":"Link Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgLinkOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/shared":{"get":{"tags":["crossorg"],"summary":"List Shared Projects","operationId":"listSharedProjects","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SharedProjectOut"},"title":"Response Listsharedprojects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/shares":{"post":{"tags":["crossorg"],"summary":"Share Project","operationId":"shareProject","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectShareCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectShareOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["crossorg"],"summary":"List Project Shares","operationId":"listProjectShares","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectShareOut"},"title":"Response Listprojectshares"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/shares/{share_id}":{"delete":{"tags":["crossorg"],"summary":"Unshare Project","operationId":"unshareProject","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"share_id","in":"path","required":true,"schema":{"type":"string","title":"Share Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}":{"get":{"tags":["crossorg"],"summary":"Get Shared Project","operationId":"getSharedProject","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedProjectDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks":{"get":{"tags":["crossorg"],"summary":"List Shared Project Tasks","operationId":"listSharedProjectTasks","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskOut"},"title":"Response Listsharedprojecttasks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crossorg"],"summary":"Create Shared Task","description":"Crea una tarea en un proyecto compartido con permiso 'full'.\n\n`org_id` del path = organización LLAMANTE (guest). La org dueña del proyecto\nse resuelve desde project_shares. Re-verifica: enlace aceptado + 'full' share.","operationId":"createSharedTask","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks/{task_id}":{"get":{"tags":["crossorg"],"summary":"Get Shared Task","operationId":"getSharedTask","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crossorg"],"summary":"Update Shared Task","description":"Actualiza status/priority/assignee_id/title/description de una tarea compartida.\n\nCampos limitados (bounded write): el cuerpo es `SharedTaskUpdateIn` —los cinco\ncampos que declara el contrato— y no `TaskUpdateIn`, que anunciaba nueve más\nque el service tiraba sin decir nada. Re-verifica acceso 'full' en cada llamada.","operationId":"updateSharedTask","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedTaskUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crossorg"],"summary":"Delete Shared Task","description":"Borra una tarea de un proyecto compartido con permiso 'full'.\n\n`org_id` del path = organización LLAMANTE (guest). Re-verifica enlace\naceptado + 'full'. 404 si la tarea no pertenece al proyecto compartido.","operationId":"deleteSharedTask","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks/{task_id}/subtasks":{"get":{"tags":["crossorg"],"summary":"List Shared Task Subtasks","operationId":"listSharedTaskSubtasks","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskOut"},"title":"Response Listsharedtasksubtasks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks/{task_id}/comments":{"get":{"tags":["crossorg"],"summary":"List Shared Task Comments","operationId":"listSharedTaskComments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskCommentOut"},"title":"Response Listsharedtaskcomments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crossorg"],"summary":"Create Shared Task Comment","description":"Crea un comentario en una tarea de un proyecto compartido con permiso 'full'.\n\nAtribuido al usuario llamante. El cuerpo es solo `body` —lo que declara el\ncontrato—: `parent_id` y `visibility` se aceptaban y se descartaban.\nRe-verifica acceso 'full' en cada llamada.","operationId":"createSharedTaskComment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedTaskCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/board-columns":{"get":{"tags":["crossorg"],"summary":"List Shared Project Board Columns","operationId":"listSharedProjectBoardColumns","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BoardColumnOut"},"title":"Response Listsharedprojectboardcolumns"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/sprints":{"get":{"tags":["crossorg"],"summary":"List Shared Project Sprints","operationId":"listSharedProjectSprints","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SprintOut"},"title":"Response Listsharedprojectsprints"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks/{task_id}/attachments":{"get":{"tags":["crossorg"],"summary":"List Shared Task Attachments","operationId":"listSharedTaskAttachments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskAttachmentOut"},"title":"Response Listsharedtaskattachments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/tasks/{task_id}/attachments/{attachment_id}/download":{"get":{"tags":["crossorg"],"summary":"Download Shared Task Attachment","description":"Descarga los bytes de un adjunto de una tarea de proyecto compartido (lectura).","operationId":"downloadSharedTaskAttachment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/shared-projects/{project_id}/time-entries":{"get":{"tags":["crossorg"],"summary":"List Shared Project Time Entries","operationId":"listSharedProjectTimeEntries","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"task_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por tarea (UUID).","title":"Task Id"},"description":"Filtra por tarea (UUID)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntryOut"},"title":"Response Listsharedprojecttimeentries"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crossorg"],"summary":"Create Shared Time Entry","description":"Imputa tiempo en un proyecto compartido con permiso 'full'.\n\nAtribuido al usuario llamante (guest). `task_id` opcional, validado al proyecto.","operationId":"createSharedTimeEntry","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryProjectCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/external-collaborators":{"get":{"tags":["crossorg"],"summary":"List External Collaborators","description":"Lista los colaboradores externos del proyecto (side dueño). member+.","operationId":"listExternalCollaborators","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalCollaboratorOut"},"title":"Response Listexternalcollaborators"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crossorg"],"summary":"Add External Collaborator","description":"Añade un colaborador externo al proyecto. admin+. Requiere enlace aceptado.","operationId":"addExternalCollaborator","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCollaboratorCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCollaboratorOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/external-collaborators/{collab_id}":{"delete":{"tags":["crossorg"],"summary":"Remove External Collaborator","description":"Revoca el acceso de un colaborador externo. admin+.","operationId":"removeExternalCollaborator","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"collab_id","in":"path","required":true,"schema":{"type":"string","title":"Collab Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/link-org":{"post":{"tags":["crossorg"],"summary":"Link Client Org","description":"Vincula un cliente CRM a una organización Projekt real. admin+.\nRequiere enlace ACEPTADO entre la org actual y la org destino.","operationId":"linkClientOrg","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientLinkOrgIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientLinkOrgOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crossorg"],"summary":"Unlink Client Org","description":"Elimina el vínculo de un cliente CRM con una org Projekt. admin+.","operationId":"unlinkClientOrg","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/mywork/cross-org":{"get":{"tags":["crossorg"],"summary":"Get Cross Org My Work","description":"Tareas de proyectos COMPARTIDOS CON ESTA ORG asignadas al usuario actual.\n\nComplementa el endpoint normal /my-work (que solo devuelve tareas de la propia\norg) con las tareas cross-tenant a las que el usuario tiene acceso.","operationId":"getCrossOrgMyWork","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyWorkTaskOut"},"title":"Response Getcrossorgmywork"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects":{"get":{"tags":["projects"],"summary":"List Projects","operationId":"projects_list_projects","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectOut"},"title":"Response Projects List Projects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Create Project","operationId":"projects_create_project","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/trash":{"get":{"tags":["projects"],"summary":"List Project Trash","description":"Lista proyectos en papelera (soft-deleted) para esta organización.","operationId":"projects_list_project_trash","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectOut"},"title":"Response Projects List Project Trash"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/by-key/{key}":{"get":{"tags":["projects"],"summary":"Get Project By Key","description":"Resolve a project by its org-scoped key (case-insensitive).","operationId":"getProjectByKey","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}":{"get":{"tags":["projects"],"summary":"Get Project","operationId":"projects_get_project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["projects"],"summary":"Update Project","operationId":"projects_update_project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["projects"],"summary":"Delete Project","description":"Soft-delete: mueve el proyecto a la papelera (restaurable).\n\nSolo con sesión de navegador: con credenciales de API (PAT u OAuth) responde\n403 `interactive_session_required`. Aunque sea reversible, un proyecto borrado\npor error desde un script o desde la IA del conector MCP saca de circulación\ntareas, tiempos y adjuntos sin nadie delante.","operationId":"projects_delete_project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/restore":{"post":{"tags":["projects"],"summary":"Restore Project","description":"Restaura un proyecto de la papelera.","operationId":"projects_restore_project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/purge":{"delete":{"tags":["projects"],"summary":"Purge Project","description":"Hard-delete permanente de un proyecto en la papelera (owner/admin only).\n\nSolo con sesión de navegador: con credenciales de API (PAT u OAuth) responde\n403 `interactive_session_required`. Es irreversible.","operationId":"projects_purge_project","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/visibility":{"put":{"tags":["projects"],"summary":"Set Project Visibility","description":"Fija la visibilidad del proyecto (`organization`|`restricted`). admin/manager.","operationId":"projects_set_project_visibility","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVisibilityUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/visibility-departments":{"get":{"tags":["projects"],"summary":"List Project Visibility Departments","description":"Departamentos que pueden ver este proyecto restringido (set M:N). admin/manager.","operationId":"projects_list_project_visibility_departments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVisibilityDepartmentOut"},"title":"Response Projects List Project Visibility Departments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["projects"],"summary":"Set Project Visibility Departments","description":"Reemplaza el set de departamentos de visibilidad del proyecto. admin/manager.\n\nAcepta `departments` (con nivel por departamento) o `department_ids` (forma\nhistórica, equivale a `write`). Cada department_id debe pertenecer a la MISMA\norg (422 si no).","operationId":"projects_set_project_visibility_departments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVisibilityDepartmentsUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVisibilityDepartmentOut"},"title":"Response Projects Set Project Visibility Departments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/announcements":{"get":{"tags":["projects"],"summary":"List Project Announcements","description":"Avisos del proyecto: los fijados primero, luego por fecha descendente.","operationId":"projects_list_project_announcements","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectAnnouncementOut"},"title":"Response Projects List Project Announcements"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Create Project Announcement","description":"Publica un aviso y lo notifica a la audiencia del proyecto.\n\nRequiere ESCRITURA sobre el proyecto: un departamento de solo lectura recibe\n403 `read_only_project`.","operationId":"projects_create_project_announcement","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectAnnouncementCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectAnnouncementOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/announcements/{announcement_id}":{"delete":{"tags":["projects"],"summary":"Delete Project Announcement","description":"Borra un aviso: solo su autor o un admin/owner de la organización.","operationId":"projects_delete_project_announcement","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"announcement_id","in":"path","required":true,"schema":{"type":"string","title":"Announcement Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/followers/me":{"get":{"tags":["projects"],"summary":"Get Project Follow State","description":"Estado de seguimiento del usuario autenticado sobre este proyecto.","operationId":"projects_get_project_follow_state","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFollowStateOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["projects"],"summary":"Set Project Follow State","description":"Sigue o silencia los avisos del proyecto. Idempotente.\n\n`following=false` silencia sin borrar el vínculo, para que el auto-follow no\nreactive lo que el usuario apagó a propósito.","operationId":"projects_set_project_follow_state","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFollowStateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFollowStateOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/followers":{"get":{"tags":["projects"],"summary":"List Project Followers","description":"Seguidores del proyecto, incluidos los silenciados. manager+.","operationId":"projects_list_project_followers","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectFollowerOut"},"title":"Response Projects List Project Followers"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/members":{"get":{"tags":["projects"],"summary":"List Project Members","description":"Lista los miembros explícitos del proyecto. admin/manager.","operationId":"projects_list_project_members","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectMemberOut"},"title":"Response Projects List Project Members"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Add Project Member","description":"Añade un miembro explícito (debe ser miembro de la org). admin/manager.","operationId":"projects_add_project_member","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMemberCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMemberOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/members/{user_id}":{"delete":{"tags":["projects"],"summary":"Remove Project Member","description":"Quita un miembro explícito del proyecto. admin/manager.","operationId":"projects_remove_project_member","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/project-folders":{"get":{"tags":["projects"],"summary":"List Project Folders","description":"Carpetas de la organización con su recuento de proyectos.\n\nBasta con ser miembro: quien trabaja necesita ver cómo está organizado el\nlistado aunque no pueda tocarlo. Fuera de Business responde 403\n`feature_not_available`.","operationId":"projects_list_project_folders","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectFolderOut"},"title":"Response Projects List Project Folders"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Create Project Folder","description":"Crea una carpeta (owner/admin). 409 `folder_name_taken` si el nombre existe.","operationId":"projects_create_project_folder","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/project-folders/{folder_id}":{"patch":{"tags":["projects"],"summary":"Update Project Folder","description":"Renombra, recolorea o reordena una carpeta (owner/admin).","operationId":"projects_update_project_folder","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}},"delete":{"tags":["projects"],"summary":"Delete Project Folder","description":"Borra la carpeta (owner/admin).\n\nSus proyectos quedan SUELTOS, nunca se borran: una carpeta es una etiqueta de\norganización, no un contenedor de propiedad.","operationId":"projects_delete_project_folder","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/project-folders/{folder_id}/pin":{"put":{"tags":["projects"],"summary":"Pin Project Folder","description":"Fija la carpeta como filtro por defecto de QUIEN LLAMA.\n\nPreferencia por (usuario, organización), no edición de la carpeta: basta con\nser miembro y poder verla. Una sola fijada a la vez; idempotente.","operationId":"projects_pin_project_folder","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["projects"],"summary":"Unpin Project Folder","description":"Desfija la carpeta: el listado vuelve a abrirse en «Todas». Idempotente.","operationId":"projects_unpin_project_folder","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/project-folders/{folder_id}/departments":{"get":{"tags":["projects"],"summary":"List Project Folder Departments","description":"Departamentos cuyos miembros ven la carpeta restringida (owner/admin).","operationId":"projects_list_project_folder_departments","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentOut"},"title":"Response Projects List Project Folder Departments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["projects"],"summary":"Set Project Folder Departments","description":"Reemplaza el set COMPLETO de departamentos con acceso (owner/admin).\n\nSemántica PUT: el set queda exactamente como los ids enviados; lista vacía =\nsolo owner/admin ven la carpeta.","operationId":"projects_set_project_folder_departments","parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectFolderDepartmentsIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentOut"},"title":"Response Projects Set Project Folder Departments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/profiles/{user_id}":{"get":{"tags":["auth"],"summary":"Get Public Profile","description":"Perfil de una persona, si su privacidad lo permite.\n\n404 —y no 403— cuando no se puede ver: un 403 confirmaría que la cuenta\nexiste, y con unas cuantas peticiones se podría enumerar quién tiene cuenta.\n\nLos datos de rendimiento son agregados PELADOS: nunca nombres de proyecto,\ncliente ni tarea.","operationId":"auth_get_public_profile","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProfileOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/profiles/{user_id}/posts":{"get":{"tags":["auth"],"summary":"List Profile Posts","description":"Publicaciones del perfil. Su visibilidad es la DEL PERFIL, no por post.","operationId":"auth_list_profile_posts","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserPostOut"},"title":"Response Auth List Profile Posts"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/profiles/{user_id}/follow":{"post":{"tags":["auth"],"summary":"Follow Profile","description":"Sigue a alguien. Idempotente y sin aprobación por su parte.\n\nSolo se puede seguir a quien te deja ver su perfil; si no, 404.","operationId":"auth_follow_profile","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["auth"],"summary":"Unfollow Profile","description":"Deja de seguir. Idempotente: si no lo seguía, responde 204 igualmente.","operationId":"auth_unfollow_profile","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/posts":{"post":{"tags":["auth"],"summary":"Create My Post","description":"Publica en el perfil propio. Quién lo verá lo decide `profile_visibility`.","operationId":"auth_create_my_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPostCreateIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPostOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/posts/{post_id}":{"delete":{"tags":["auth"],"summary":"Delete My Post","description":"Borra una publicación PROPIA (borrado suave, para poder moderar después).","operationId":"auth_delete_my_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks":{"get":{"tags":["tasks"],"summary":"List Tasks","operationId":"tasks_list_tasks","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20,"pattern":"^[a-z0-9_]+$"},{"type":"null"}],"description":"Filtra por status.","title":"Status"},"description":"Filtra por status."},{"name":"priority","in":"query","required":false,"schema":{"anyOf":[{"enum":["low","medium","high","urgent"],"type":"string"},{"type":"null"}],"description":"Filtra por prioridad.","title":"Priority"},"description":"Filtra por prioridad."},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"enum":["epic","story","task","bug","spike","chore"],"type":"string"},{"type":"null"}],"description":"Filtra por tipo.","title":"Type"},"description":"Filtra por tipo."},{"name":"assignee_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filtra por asignado.","title":"Assignee Id"},"description":"Filtra por asignado."},{"name":"created_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filtra por creador.","title":"Created By"},"description":"Filtra por creador."},{"name":"sprint_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filtra por sprint.","title":"Sprint Id"},"description":"Filtra por sprint."},{"name":"tag_id","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string","format":"uuid"}},{"type":"null"}],"description":"Filtra por etiqueta (repetible; AL MENOS una).","title":"Tag Id"},"description":"Filtra por etiqueta (repetible; AL MENOS una)."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda de texto en título/descripción.","title":"Q"},"description":"Búsqueda de texto en título/descripción."},{"name":"due_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"due_date ≤ esta fecha.","title":"Due Before"},"description":"due_date ≤ esta fecha."},{"name":"due_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"due_date ≥ esta fecha.","title":"Due After"},"description":"due_date ≥ esta fecha."},{"name":"parent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Subtareas directas de este id.","title":"Parent Id"},"description":"Subtareas directas de este id."},{"name":"root_only","in":"query","required":false,"schema":{"type":"boolean","description":"Solo tareas raíz (sin parent).","default":false,"title":"Root Only"},"description":"Solo tareas raíz (sin parent)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["created","updated","due","priority","number","title"],"type":"string","description":"Campo de orden.","default":"created","title":"Sort"},"description":"Campo de orden."},{"name":"order","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","description":"Sentido del orden.","default":"asc","title":"Order"},"description":"Sentido del orden."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskOut"},"title":"Response Tasks List Tasks"}}},"headers":{"X-Total-Count":{"description":"Número total de tareas del proyecto (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Create Task","operationId":"tasks_create_task","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/search":{"post":{"tags":["tasks"],"summary":"Search Tasks","description":"Ejecuta un saved_filter guardado (entidad tasks) o un blob inline.","operationId":"searchTasks","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSearchIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskOut"},"title":"Response Searchtasks"}}},"headers":{"X-Total-Count":{"description":"Total de tareas que cumplen el filtro (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/bulk":{"post":{"tags":["tasks"],"summary":"Bulk Task Operation","description":"Aplica una operación a un lote de tareas del proyecto (transaccional).","operationId":"bulkTaskOperation","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBulkIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBulkResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/by-number/{number}":{"get":{"tags":["tasks"],"summary":"Get Task By Number","description":"Resolve a task by its per-project sequential number.","operationId":"getTaskByNumber","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"number","in":"path","required":true,"schema":{"type":"integer","title":"Number"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}":{"get":{"tags":["tasks"],"summary":"Get Task","operationId":"tasks_get_task","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["tasks"],"summary":"Update Task","operationId":"tasks_update_task","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}},"headers":{"X-Open-Blockers":{"description":"Solo al CERRAR la tarea (`status: done`) y solo si es > 0: cuántas tareas que la bloquean seguían abiertas. Es un AVISO, no un rechazo — el cambio ya se ha aplicado.","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["tasks"],"summary":"Delete Task","operationId":"tasks_delete_task","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/tags":{"put":{"tags":["tasks"],"summary":"Set Task Tags","operationId":"tasks_set_task_tags","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTaskTagsIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/subtasks":{"get":{"tags":["tasks"],"summary":"List Subtasks","operationId":"listSubtasks","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubtaskListOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/activity":{"get":{"tags":["tasks"],"summary":"Get Task Activity","description":"Feed de actividad (bitácora) de la tarea, newest-first (tope 200, paginable).","operationId":"getTaskActivity","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskActivityEntry"},"title":"Response Gettaskactivity"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/status-time":{"get":{"tags":["tasks"],"summary":"Get Task Status Time","description":"Tiempo acumulado por estado de la tarea (incluye estados personalizados).","operationId":"getTaskStatusTime","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusTimeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/my-work":{"get":{"tags":["tasks"],"summary":"Get My Work","operationId":"getMyWork","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por status","title":"Status"},"description":"Filtrar por status"},{"name":"overdue","in":"query","required":false,"schema":{"type":"boolean","description":"Solo tareas vencidas","default":false,"title":"Overdue"},"description":"Solo tareas vencidas"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyWorkTaskOut"},"title":"Response Getmywork"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/work":{"get":{"tags":["tasks"],"summary":"Get Me Work","description":"Vista global \"Mi trabajo\": tareas asignadas al usuario en todas sus organizaciones.","operationId":"getMeWork","parameters":[{"name":"organization_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Limita el resultado a una organización de la que el usuario es miembro.","title":"Organization Id"},"description":"Limita el resultado a una organización de la que el usuario es miembro."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por status","title":"Status"},"description":"Filtrar por status"},{"name":"overdue","in":"query","required":false,"schema":{"type":"boolean","description":"Solo tareas vencidas","default":false,"title":"Overdue"},"description":"Solo tareas vencidas"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MyWorkTaskOut"},"title":"Response Getmework"}}},"headers":{"X-Total-Count":{"description":"Total de tareas que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/work/counts":{"get":{"tags":["tasks"],"summary":"Get Me Work Counts","description":"Conteo de tareas abiertas asignadas al usuario, agrupado por organización.","operationId":"getMeWorkCounts","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MeWorkOrgCountOut"},"type":"array","title":"Response Getmeworkcounts"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/attachments":{"post":{"tags":["tasks"],"summary":"Upload Task Attachment","description":"Upload a file attachment (multipart/form-data). Max 10 MB. Returns metadata only.","operationId":"uploadTaskAttachment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_uploadTaskAttachment"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskAttachmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Request Entity Too Large"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["tasks"],"summary":"List Task Attachments","description":"List attachment metadata for a task (never includes file bytes).","operationId":"listTaskAttachments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskAttachmentOut"},"title":"Response Listtaskattachments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/attachments/{attachment_id}/download":{"get":{"tags":["tasks"],"summary":"Download Task Attachment","description":"Download the raw bytes of an attachment. Requires org membership (not public).","operationId":"downloadTaskAttachment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"File bytes with original content-type.","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tasks"],"summary":"Delete Task Attachment","description":"Delete an attachment. Only the uploader or admin+ may delete.","operationId":"deleteTaskAttachment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/dependencies":{"get":{"tags":["tasks"],"summary":"List Task Dependencies","operationId":"listTaskDependencies","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskDependencyOut"},"title":"Response Listtaskdependencies"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Add Task Dependency","operationId":"addTaskDependency","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDependencyCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskDependencyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/dependencies/{dep_id}":{"delete":{"tags":["tasks"],"summary":"Remove Task Dependency","operationId":"removeTaskDependency","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"dep_id","in":"path","required":true,"schema":{"type":"string","title":"Dep Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/custom-field-defs":{"get":{"tags":["tasks"],"summary":"List Custom Field Defs","description":"List all custom field definitions for the organization.\n\nSin `?entity` (PJKT-2471): era un parámetro que el contrato no declaraba y\ncuyo único valor útil era el respaldo. Ahora que el alta no deja crear\ndefiniciones de otra entidad, `?entity=cualquier_cosa` solo podía devolver\nuna lista vacía. El motivo entero, en `schemas.CustomFieldDefCreateIn`.","operationId":"listCustomFieldDefs","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldDefOut"},"title":"Response Listcustomfielddefs"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tasks"],"summary":"Create Custom Field Def","description":"Create a custom field definition (admin+ only).","operationId":"createCustomFieldDef","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldDefCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldDefOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/custom-field-defs/{def_id}":{"patch":{"tags":["tasks"],"summary":"Update Custom Field Def","description":"Update a custom field definition (admin+ only).","operationId":"updateCustomFieldDef","parameters":[{"name":"def_id","in":"path","required":true,"schema":{"type":"string","title":"Def Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldDefUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldDefOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["tasks"],"summary":"Delete Custom Field Def","description":"Delete a custom field definition and all its values (admin+ only).","operationId":"deleteCustomFieldDef","parameters":[{"name":"def_id","in":"path","required":true,"schema":{"type":"string","title":"Def Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/custom-fields":{"get":{"tags":["tasks"],"summary":"Get Task Custom Fields","description":"Get all custom field values for a task.","operationId":"getTaskCustomFields","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldValueOut"},"title":"Response Gettaskcustomfields"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["tasks"],"summary":"Set Task Custom Fields","description":"Bulk upsert custom field values for a task. Validates value against field type.","operationId":"setTaskCustomFields","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTaskCustomFieldsIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldValueOut"},"title":"Response Settaskcustomfields"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/board-columns":{"get":{"tags":["projects"],"summary":"List Board Columns","operationId":"listBoardColumns","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BoardColumnOut"},"title":"Response Listboardcolumns"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Create Board Column","operationId":"createBoardColumn","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/board-columns/{column_id}":{"patch":{"tags":["projects"],"summary":"Update Board Column","operationId":"updateBoardColumn","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","title":"Column Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["projects"],"summary":"Delete Board Column","operationId":"deleteBoardColumn","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"column_id","in":"path","required":true,"schema":{"type":"string","title":"Column Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/board-columns/reorder":{"put":{"tags":["projects"],"summary":"Reorder Board Columns","operationId":"reorderBoardColumns","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardColumnReorderIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BoardColumnOut"},"title":"Response Reorderboardcolumns"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/board":{"get":{"tags":["projects"],"summary":"Get Board","description":"Tablero unificado: columnas + tareas agrupadas + WIP en una sola llamada.","operationId":"getBoard","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"swimlane","in":"query","required":false,"schema":{"enum":["none","assignee","priority","type"],"type":"string","description":"Agrupación en carriles por columna.","default":"none","title":"Swimlane"},"description":"Agrupación en carriles por columna."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/saved-filters":{"get":{"tags":["saved-filters"],"summary":"List Saved Filters","operationId":"listSavedFilters","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SavedFilterOut"},"title":"Response Listsavedfilters"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["saved-filters"],"summary":"Create Saved Filter","operationId":"createSavedFilter","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedFilterCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedFilterOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/saved-filters/{filter_id}":{"patch":{"tags":["saved-filters"],"summary":"Update Saved Filter","operationId":"updateSavedFilter","parameters":[{"name":"filter_id","in":"path","required":true,"schema":{"type":"string","title":"Filter Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedFilterUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedFilterOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["saved-filters"],"summary":"Delete Saved Filter","operationId":"deleteSavedFilter","parameters":[{"name":"filter_id","in":"path","required":true,"schema":{"type":"string","title":"Filter Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/reminders":{"get":{"tags":["reminders"],"summary":"List Reminders","operationId":"listReminders","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"enum":["pending","done","all"],"type":"string","description":"Qué recordatorios devolver: pendientes (defecto), hechos o todos.","default":"pending","title":"Status"},"description":"Qué recordatorios devolver: pendientes (defecto), hechos o todos."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReminderOut"},"title":"Response Listreminders"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["reminders"],"summary":"Create Reminder","operationId":"createReminder","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/reminders/{reminder_id}":{"patch":{"tags":["reminders"],"summary":"Update Reminder","operationId":"updateReminder","parameters":[{"name":"reminder_id","in":"path","required":true,"schema":{"type":"string","title":"Reminder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["reminders"],"summary":"Delete Reminder","operationId":"deleteReminder","parameters":[{"name":"reminder_id","in":"path","required":true,"schema":{"type":"string","title":"Reminder Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/sla-policies":{"get":{"tags":["sla"],"summary":"List Sla Policies","description":"Políticas de SLA de la organización. Requiere member+.","operationId":"listSlaPolicies","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SlaPolicyOut"},"title":"Response Listslapolicies"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["sla"],"summary":"Create Sla Policy","description":"Crea una política de SLA. Requiere manager+.","operationId":"createSlaPolicy","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicyCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/sla-policies/{policy_id}":{"get":{"tags":["sla"],"summary":"Get Sla Policy","operationId":"getSlaPolicy","parameters":[{"name":"policy_id","in":"path","required":true,"schema":{"type":"string","title":"Policy Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["sla"],"summary":"Update Sla Policy","description":"Actualización parcial. Descarta los relojes VIVOS que medía la política\n(los cerrados se conservan intactos). Requiere manager+.","operationId":"updateSlaPolicy","parameters":[{"name":"policy_id","in":"path","required":true,"schema":{"type":"string","title":"Policy Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicyUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["sla"],"summary":"Delete Sla Policy","description":"Elimina la política. Los relojes ya CERRADOS sobreviven con la copia de la\npolítica con la que se midieron. Requiere manager+.","operationId":"deleteSlaPolicy","parameters":[{"name":"policy_id","in":"path","required":true,"schema":{"type":"string","title":"Policy Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/sla-clocks":{"get":{"tags":["sla"],"summary":"List Sla Clocks","description":"Relojes de varias tareas de una vez (lo que necesita un LISTADO).\n\nNo recalcula: devuelve los valores materializados. El porqué —y qué campo\nenvejece por ello— está en el service. Requiere member+.","operationId":"listSlaClocks","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"task_ids","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Ids"}},{"name":"open","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Open"}},{"name":"breached","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Breached"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSlaClockOut"},"title":"Response Listslaclocks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/tasks/{task_id}/sla":{"get":{"tags":["sla"],"summary":"Get Task Sla","description":"Relojes de SLA de una tarea (uno por objetivo aplicable).\n\nLos relojes vivos se refrescan al leer; los cerrados se devuelven tal y como\nquedaron. Requiere member+.","operationId":"getTaskSla","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSlaClockOut"},"title":"Response Gettasksla"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/request-types":{"get":{"tags":["support"],"summary":"List Request Types","description":"Tipos de petición de la organización. Requiere member+.","operationId":"listRequestTypes","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"only_active","in":"query","required":false,"schema":{"type":"boolean","description":"Solo los que hoy se ofrecen al solicitante.","default":false,"title":"Only Active"},"description":"Solo los que hoy se ofrecen al solicitante."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RequestTypeOut"},"title":"Response Listrequesttypes"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["support"],"summary":"Create Request Type","description":"Crea un tipo de petición. Requiere manager+.","operationId":"createRequestType","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTypeCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTypeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/request-types/{request_type_id}":{"get":{"tags":["support"],"summary":"Get Request Type","operationId":"getRequestType","parameters":[{"name":"request_type_id","in":"path","required":true,"schema":{"type":"string","title":"Request Type Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTypeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["support"],"summary":"Update Request Type","description":"Actualiza un tipo de petición. Requiere manager+.","operationId":"updateRequestType","parameters":[{"name":"request_type_id","in":"path","required":true,"schema":{"type":"string","title":"Request Type Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTypeUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTypeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}},"delete":{"tags":["support"],"summary":"Delete Request Type","description":"Borra un tipo de petición. Requiere manager+.","operationId":"deleteRequestType","parameters":[{"name":"request_type_id","in":"path","required":true,"schema":{"type":"string","title":"Request Type Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/support-requests":{"get":{"tags":["support"],"summary":"List Support Requests","description":"Peticiones de la organización, la más reciente primero. Requiere member+.","operationId":"listSupportRequests","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por cliente solicitante.","title":"Client Id"},"description":"Filtra por cliente solicitante."},{"name":"request_type_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por tipo.","title":"Request Type Id"},"description":"Filtra por tipo."},{"name":"channel","in":"query","required":false,"schema":{"anyOf":[{"enum":["portal","email","phone","internal","api"],"type":"string"},{"type":"null"}],"description":"Filtra por canal de entrada.","title":"Channel"},"description":"Filtra por canal de entrada."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportRequestOut"},"title":"Response Listsupportrequests"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["support"],"summary":"Create Support Request","description":"Registra una petición desde dentro (llamada, aviso presencial). member+.","operationId":"createSupportRequest","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRequestCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/support-requests/{task_id}":{"get":{"tags":["support"],"summary":"Get Support Request","operationId":"getSupportRequest","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/support/saved-replies":{"get":{"tags":["support"],"summary":"List Support Saved Replies","description":"Respuestas guardadas de la organización, por título. Requiere member+.","operationId":"listSupportSavedReplies","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportSavedReplyOut"},"title":"Response Listsupportsavedreplies"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["support"],"summary":"Create Support Saved Reply","description":"Crea una respuesta guardada. Requiere manager+.","operationId":"createSupportSavedReply","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportSavedReplyCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportSavedReplyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/support/saved-replies/{reply_id}":{"patch":{"tags":["support"],"summary":"Update Support Saved Reply","description":"Actualiza una respuesta guardada. Requiere manager+.","operationId":"updateSupportSavedReply","parameters":[{"name":"reply_id","in":"path","required":true,"schema":{"type":"string","title":"Reply Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportSavedReplyUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportSavedReplyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}},"delete":{"tags":["support"],"summary":"Delete Support Saved Reply","description":"Borra una respuesta guardada. Requiere manager+.","operationId":"deleteSupportSavedReply","parameters":[{"name":"reply_id","in":"path","required":true,"schema":{"type":"string","title":"Reply Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/support/email-channels":{"get":{"tags":["support"],"summary":"List Support Email Channels","description":"Buzones de correo entrante de la organización. Requiere member+.","operationId":"listSupportEmailChannels","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportEmailChannelOut"},"title":"Response Listsupportemailchannels"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["support"],"summary":"Create Support Email Channel","description":"Da de alta una dirección de entrada. Requiere manager+.","operationId":"createSupportEmailChannel","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportEmailChannelCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportEmailChannelOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/support/email-channels/{channel_id}":{"patch":{"tags":["support"],"summary":"Update Support Email Channel","description":"Actualiza un buzón. Requiere manager+.","operationId":"updateSupportEmailChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportEmailChannelUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportEmailChannelOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["support"],"summary":"Delete Support Email Channel","description":"Borra un buzón. Requiere manager+.","operationId":"deleteSupportEmailChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/support/email/inbound/{provider}":{"post":{"tags":["support"],"summary":"Inbound Email Webhook","description":"Receptor de correo entrante. PÚBLICO, autenticado por la firma del proveedor.\n\nSe lee el cuerpo CRUDO (`request.body()`) y no un modelo Pydantic: la firma se\ncalcula sobre esos bytes exactos, y dejar que FastAPI parsee y reserialice el\nJSON cambia un espacio y rompe la verificación. Es el error clásico de esta\nintegración.","operationId":"inboundEmailWebhook","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboundEmailAck"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/v1/organizations/{org_id}/support/ingest-keys":{"get":{"tags":["support"],"summary":"List Support Ingest Keys","description":"Credenciales de ingesta de la organización. Nunca devuelve el token.","operationId":"listSupportIngestKeys","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportIngestKeyOut"},"title":"Response Listsupportingestkeys"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["support"],"summary":"Create Support Ingest Key","description":"Acuña una credencial de ingesta. El token sale UNA vez, aquí. manager+.","operationId":"createSupportIngestKey","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestKeyCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestKeyCreatedOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/support/ingest-keys/{key_id}":{"delete":{"tags":["support"],"summary":"Revoke Support Ingest Key","description":"Revoca la credencial: deja de valer en la petición siguiente. manager+.","operationId":"revokeSupportIngestKey","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/support/ingest/form":{"get":{"tags":["support"],"summary":"Get Support Ingest Form","description":"El formulario que esta credencial rellena: nombre y campos, ya resueltos.\n\nExiste para que integrar no sea copiar UUIDs a mano de la pantalla de ajustes\nal código de otra web. Está acotado a SU tipo de petición: no ve la cola, ni\nlas peticiones, ni los demás tipos de la organización.","operationId":"getSupportIngestForm","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestFormOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/support/ingest/requests":{"post":{"tags":["support"],"summary":"Create Ingested Support Request","description":"Abre una petición de soporte desde una web externa.\n\nLa organización, la cola y la prioridad salen de la credencial; un\n`organization_id` en el cuerpo no existe y no se leería. Con\n`idempotency_key`, reintentar la misma entrega devuelve la petición original\ncon **200** y `created: false`, en vez de abrir una segunda con 201.","operationId":"createIngestedSupportRequest","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestRequestIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestAcceptedOut"}}}},"200":{"description":"Entrega REPETIDA: la misma `idempotency_key` ya se usó. Devuelve la petición original con `created: false` y no abre ninguna nueva.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportIngestAcceptedOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/sprints":{"get":{"tags":["sprints"],"summary":"List Sprints","operationId":"sprints_list_sprints","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SprintOut"},"title":"Response Sprints List Sprints"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["sprints"],"summary":"Create Sprint","operationId":"sprints_create_sprint","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/sprints/{sprint_id}/stats":{"get":{"tags":["sprints"],"summary":"Sprint Stats","operationId":"sprints_sprint_stats","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintStatsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/sprints/{sprint_id}":{"get":{"tags":["sprints"],"summary":"Get Sprint","operationId":"sprints_get_sprint","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["sprints"],"summary":"Update Sprint","operationId":"sprints_update_sprint","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["sprints"],"summary":"Delete Sprint","operationId":"sprints_delete_sprint","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/sprints/{sprint_id}/tasks":{"post":{"tags":["sprints"],"summary":"Assign Sprint Tasks","operationId":"sprints_assign_sprint_tasks","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignSprintTasksIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintStatsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/sprints/{sprint_id}/burndown":{"get":{"tags":["sprints"],"summary":"Sprint Burndown","operationId":"sprints_sprint_burndown","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintBurndownOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/sprints/{sprint_id}/complete":{"post":{"tags":["sprints"],"summary":"Complete Sprint","operationId":"completeSprint","parameters":[{"name":"sprint_id","in":"path","required":true,"schema":{"type":"string","title":"Sprint Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintCompleteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/velocity":{"get":{"tags":["sprints"],"summary":"Project Velocity","operationId":"projectVelocity","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"description":"Solo los últimos N sprints completados (media móvil); por defecto, histórico completo.","title":"Limit"},"description":"Solo los últimos N sprints completados (media móvil); por defecto, histórico completo."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SprintVelocityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/comments":{"get":{"tags":["task-comments"],"summary":"List Task Comments","operationId":"task-comments_list_task_comments","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskCommentOut"},"title":"Response Task-Comments List Task Comments"}}},"headers":{"X-Total-Count":{"description":"Total de comentarios de la tarea (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["task-comments"],"summary":"Create Task Comment","operationId":"task-comments_create_task_comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/comments/{comment_id}":{"patch":{"tags":["task-comments"],"summary":"Update Task Comment","operationId":"task-comments_update_task_comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["task-comments"],"summary":"Delete Task Comment","operationId":"task-comments_delete_task_comment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels":{"post":{"tags":["chat"],"summary":"Create Channel","description":"Crea un canal (DM idempotente / grupo / de proyecto). El creador es owner.","operationId":"createChatChannel","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chat"],"summary":"List Channels","description":"Lista mis canales (donde soy miembro) en la org, no archivados.","operationId":"listChatChannels","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChannelOut"},"title":"Response Listchatchannels"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/browse":{"get":{"tags":["chat"],"summary":"Browse Channels","description":"Explora canales persistentes: públicos de la org + privados donde soy miembro.","operationId":"browseChatChannels","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","description":"Incluir también los canales archivados.","default":false,"title":"Include Archived"},"description":"Incluir también los canales archivados."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChannelOut"},"title":"Response Browsechatchannels"}}},"headers":{"X-Total-Count":{"description":"Total de canales visibles (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/join":{"post":{"tags":["chat"],"summary":"Join Channel","description":"Me une a un canal público (idempotente si ya soy miembro).","operationId":"joinChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/members/{user_id}":{"patch":{"tags":["chat"],"summary":"Update Member Role","description":"Promociona/degrada moderadores (solo type=channel; owner del canal u org admin).","operationId":"updateChatChannelMemberRole","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberRoleIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat"],"summary":"Remove Member","description":"Quita un miembro del canal (a mí mismo siempre; a otros solo el owner).","operationId":"removeChatChannelMember","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}":{"delete":{"tags":["chat"],"summary":"Delete Channel","description":"Elimina un canal estilo Discord (owner del canal u org admin; resto 422).","operationId":"deleteChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chat"],"summary":"Get Channel","description":"Detalle de un canal + sus miembros (solo si soy miembro).","operationId":"getChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["chat"],"summary":"Update Channel","description":"Edita el nombre/icono de un canal group/project (solo miembros; DM no).","operationId":"updateChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/unarchive":{"post":{"tags":["chat"],"summary":"Unarchive Channel","description":"Desarchiva el canal (misma autorización que archivar).","operationId":"unarchiveChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/members":{"post":{"tags":["chat"],"summary":"Add Members","description":"Añade miembros (de la org) a un canal group/project.","operationId":"addChatChannelMembers","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMembersIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/archive":{"post":{"tags":["chat"],"summary":"Archive Channel","description":"Archiva el canal (owner; en type=channel también org admin). Bloquea escritura.","operationId":"archiveChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/external/people":{"get":{"tags":["chat"],"summary":"Search External People","description":"Personas de otras organizaciones con las que se puede hablar.\n\n`q` es OBLIGATORIO y de 2 caracteres mínimo, y lo impone FastAPI (422 antes\nde tocar el service): sin él, esto sería un censo paginable de todas las\npersonas de todas las organizaciones abiertas. El tope de 50 es por lo mismo\n— buscar a alguien concreto es el caso de uso; barrer el directorio no.","operationId":"searchExternalChatPeople","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalPersonOut"},"title":"Response Searchexternalchatpeople"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/external/conversations":{"post":{"tags":["chat"],"summary":"Open External Conversation","description":"Abre (o devuelve, si ya existía) el directo con alguien de otra org.","operationId":"openExternalChatConversation","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalDmIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/unread-count":{"get":{"tags":["chat"],"summary":"Unread Count","description":"Total de mensajes no leídos en todos mis canales (badge del dock).","operationId":"chatUnreadCount","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__chat__schemas__UnreadCountOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/read":{"post":{"tags":["chat"],"summary":"Mark Read","description":"Marca el canal como leído (avanza el watermark; limpia no-leído manual).","operationId":"markChatChannelRead","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/unread":{"post":{"tags":["chat"],"summary":"Mark Unread","description":"Marca el canal como NO leído a mano.","operationId":"markChatChannelUnread","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages":{"post":{"tags":["chat"],"summary":"Send Message","description":"Envía un mensaje al canal (solo miembros). Notifica en tiempo real (SSE).","operationId":"sendChatMessage","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chat"],"summary":"List Messages","description":"Lista mensajes del canal (cronológico, paginado por cursor `before_id`).","operationId":"listChatMessages","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"before_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cursor: mensajes anteriores a este id.","title":"Before Id"},"description":"Cursor: mensajes anteriores a este id."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageOut"},"title":"Response Listchatmessages"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages/{message_id}":{"patch":{"tags":["chat"],"summary":"Edit Message","description":"Edita un mensaje (solo el autor).","operationId":"editChatMessage","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageEditIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat"],"summary":"Delete Message","description":"Borra un mensaje (autor o owner del canal).","operationId":"deleteChatMessage","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages/{message_id}/reactions":{"post":{"tags":["chat"],"summary":"Add Reaction","description":"Reacciona con un emoji (idempotente). Devuelve el mensaje con reacciones.","operationId":"addChatReaction","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages/{message_id}/reactions/{emoji}":{"delete":{"tags":["chat"],"summary":"Remove Reaction","description":"Quita mi reacción de ese emoji. Devuelve el mensaje con reacciones.","operationId":"removeChatReaction","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"emoji","in":"path","required":true,"schema":{"type":"string","title":"Emoji"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/pins":{"get":{"tags":["chat"],"summary":"List Pins","description":"Mensajes pineados vigentes del canal (para el banner), recién pineados primero.","operationId":"listChatPins","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageOut"},"title":"Response Listchatpins"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages/{message_id}/pin":{"post":{"tags":["chat"],"summary":"Pin Message","description":"Pinea un mensaje del canal con la duración elegida (1w/15d/30d/siempre).","operationId":"pinChatMessage","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat"],"summary":"Unpin Message","description":"Quita el pin de un mensaje del canal. Devuelve el mensaje actualizado.","operationId":"unpinChatMessage","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/typing":{"post":{"tags":["chat"],"summary":"Typing","description":"Avisa (efímero, sin BD) de que estoy escribiendo en el canal.","operationId":"chatTyping","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/mute":{"patch":{"tags":["chat"],"summary":"Mute Channel","description":"Silencia/activa el canal para mí (sin emails de no-leído si está silenciado).","operationId":"muteChatChannel","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MuteIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/heartbeat":{"post":{"tags":["chat"],"summary":"Heartbeat","description":"Marca mi presencia online (TTL ~30 s). El cliente lo repite periódicamente.","operationId":"chatHeartbeat","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/status":{"patch":{"tags":["chat"],"summary":"Set Status","description":"Fija mi estado de chat (online/away/busy/offline).","operationId":"setChatStatus","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusIn"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/presence":{"get":{"tags":["chat"],"summary":"Presence","description":"Presencia (online/last_seen/status) de `user_ids` (acotada a mi org).","operationId":"chatPresence","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"user_ids","in":"query","required":false,"schema":{"type":"string","description":"CSV de user_ids a consultar.","default":"","title":"User Ids"},"description":"CSV de user_ids a consultar."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserPresenceOut"},"title":"Response Chatpresence"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/messages/{message_id}/attachments":{"post":{"tags":["chat"],"summary":"Upload Attachment","description":"Adjunta un fichero a un mensaje propio (multipart). Máx 10 MB, tipos permitidos.\n\nDevuelve el mensaje con la lista de adjuntos. Solo el autor del mensaje adjunta;\ntamaño/extensión inválidos → 422 (envelope estándar).","operationId":"uploadChatAttachment","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_uploadChatAttachment"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/attachments/{attachment_id}/download":{"get":{"tags":["chat"],"summary":"Download Attachment","description":"Descarga los bytes de un adjunto. Requiere ser miembro del canal (si no, 404).","operationId":"downloadChatAttachment","parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Bytes del adjunto con su content-type original.","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/stream":{"get":{"tags":["chat"],"summary":"Stream de chat en tiempo real (SSE)","description":"SSE de mensajes de mis canales (tiempo real por Redis; poll de fallback).","operationId":"streamChat","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Server-Sent Events (text/event-stream). Emite `event: message` por cada mensaje nuevo en cualquiera de mis canales, y `: ping` heartbeat.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/voice-channels":{"get":{"tags":["chat"],"summary":"List Voice Channels","description":"Canales de audio visibles para mí, cada uno con quién está dentro ahora.","operationId":"listChatVoiceChannels","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","description":"Incluir también los canales de audio archivados.","default":false,"title":"Include Archived"},"description":"Incluir también los canales de audio archivados."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VoiceChannelOut"},"title":"Response Listchatvoicechannels"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/voice":{"get":{"tags":["chat"],"summary":"Get Voice Room","description":"Quién está en el audio de este canal (se puede mirar sin entrar).","operationId":"getChatVoiceRoom","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceRoomOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["chat"],"summary":"Leave Voice Room","description":"Salgo del audio (idempotente).","operationId":"leaveChatVoiceRoom","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/voice/join":{"post":{"tags":["chat"],"summary":"Join Voice Room","description":"Entro al audio: token LiveKit de la sala permanente + censo actual.","operationId":"joinChatVoiceRoom","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceJoinOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/v1/organizations/{org_id}/chat/channels/{channel_id}/voice/heartbeat":{"post":{"tags":["chat"],"summary":"Voice Heartbeat","description":"Sigo dentro: refresca mi TTL en el censo.","operationId":"chatVoiceHeartbeat","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/meetings":{"post":{"tags":["meetings"],"summary":"Create Meeting","description":"Crea una reunión (agendada si trae `scheduled_start`, si no instantánea).","operationId":"createMeeting","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["meetings"],"summary":"List Meetings","description":"Lista las reuniones de la organización (más recientes primero).","operationId":"listMeetings","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MeetingOut"},"title":"Response Listmeetings"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/invitations":{"get":{"tags":["meetings"],"summary":"List My Meeting Invitations","description":"Mis invitaciones a reuniones en esta org (con el resumen de la reunión).","operationId":"listMyMeetingInvitations","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MeetingInvitationOut"},"title":"Response Listmymeetinginvitations"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/capabilities":{"get":{"tags":["meetings"],"summary":"Get Meeting Capabilities","description":"¿Está LiveKit configurado en esta instalación? (para avisar antes del 503).","operationId":"getMeetingCapabilities","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingCapabilitiesOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}":{"get":{"tags":["meetings"],"summary":"Get Meeting","description":"Detalle de una reunión que ALCANZO.\n\nDe mi organización, o colgada de un canal de chat que alcanzo desde ella —\nque es como una llamada ENTRE organizaciones llega al otro lado, cuya\norganización nunca fue la de la reunión.\n\n403 `external_messaging_disabled` si una de las dos cerró la puerta.","operationId":"getMeeting","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["meetings"],"summary":"Update Meeting","description":"Cambia título y horas. Anfitrión u owner/admin.\n\nSolo toca lo que llega: omitir un campo lo deja como estaba y mandarlo a\n`null` lo borra. 422 `meeting_already_started` si alguien ya entró.","operationId":"updateMeeting","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["meetings"],"summary":"Delete Meeting","description":"La elimina, con sus invitaciones y admisiones. Anfitrión u owner/admin.\n\nBorra de verdad y no marca «cancelada»: una reunión que nunca llegó a ocurrir\nno es histórico de nada, y dejarla como fila muerta obligaría a filtrarla en\nel calendario, en el listado, en la ficha del cliente y en el timeline. El\nhistórico de las que SÍ ocurrieron lo sostienen `started_at`/`ended_at`, y a\nésas las protege el 422.","operationId":"deleteMeeting","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/token":{"post":{"tags":["meetings"],"summary":"Mint Meeting Token","description":"Firma un token de acceso LiveKit para unirme a la sala.\n\n403 `admission_required` si soy un invitado a la espera de admisión, 403\n`admission_denied` si el anfitrión me rechazó, 503 si LiveKit no está\nconfigurado en el entorno.","operationId":"mintMeetingToken","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinTokenOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/end":{"post":{"tags":["meetings"],"summary":"End Meeting","description":"Da la reunión por terminada y le pone hora de fin.\n\nIDEMPOTENTE: lo llama `onDisconnected` de la sala, que dispara una vez por\nparticipante. La primera llamada fija la hora; el resto devuelve la misma\nreunión. 200 y no 204 porque el cliente quiere la fila al día —con su\n`ended_at`— para pintar la duración sin volver a preguntar.","operationId":"endMeeting","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/invites":{"post":{"tags":["meetings"],"summary":"Create Meeting Invites","description":"Invita a miembros de la org a la reunión (solo el anfitrión o un admin).","operationId":"createMeetingInvites","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingInviteCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MeetingInviteOut"},"title":"Response Createmeetinginvites"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["meetings"],"summary":"List Meeting Invites","description":"Lista las invitaciones de una reunión.","operationId":"listMeetingInvites","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MeetingInviteOut"},"title":"Response Listmeetinginvites"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/invites/respond":{"post":{"tags":["meetings"],"summary":"Respond Meeting Invite","description":"Acepta o rechaza mi propia invitación a la reunión.","operationId":"respondMeetingInvite","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingInviteRespondIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingInviteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/knock":{"post":{"tags":["meetings"],"summary":"Knock Meeting","description":"Pide entrar a la sala (sala de espera). Avisa al anfitrión.","operationId":"knockMeeting","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingAdmissionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/admissions":{"get":{"tags":["meetings"],"summary":"List Meeting Admissions","description":"Solicitudes pendientes de admisión (solo el anfitrión o un admin).","operationId":"listMeetingAdmissions","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MeetingAdmissionOut"},"title":"Response Listmeetingadmissions"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/meetings/{meeting_id}/admissions/{admission_id}":{"post":{"tags":["meetings"],"summary":"Decide Meeting Admission","description":"Admite o rechaza una solicitud de entrada (solo el anfitrión o un admin).","operationId":"decideMeetingAdmission","parameters":[{"name":"meeting_id","in":"path","required":true,"schema":{"type":"string","title":"Meeting Id"}},{"name":"admission_id","in":"path","required":true,"schema":{"type":"string","title":"Admission Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingAdmissionDecideIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingAdmissionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/meetings/by-slug/{slug}":{"get":{"tags":["meetings"],"summary":"Get Meeting By Slug","description":"Resuelve el meeting del link único `/meet/{slug}`.\n\nDos formas de alcanzarlo: ser miembro de su organización, o serlo del canal\ndel que cuelga (la llamada entre organizaciones). 403\n`external_messaging_disabled` si la puerta se cerró — el enlace sigue siendo\npulsable en el correo de ayer.","operationId":"getMeetingBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/okr/objectives":{"post":{"tags":["okr"],"summary":"Create Objective","description":"Crea un objetivo (opcionalmente con sus KRs iniciales). Requiere manager+.","operationId":"createObjective","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectiveCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectiveOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["okr"],"summary":"List Objectives","description":"Lista los objetivos de la organización (con sus KRs y progreso derivado).\n\nFiltros opcionales por `period`, `status` y `parent_id` (los hijos\nalineados bajo un objetivo padre concreto). Requiere manager+.","operationId":"listObjectives","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Period"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["active","done","archived"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"parent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectiveOut"},"title":"Response Listobjectives"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/okr/objectives/{objective_id}":{"get":{"tags":["okr"],"summary":"Get Objective","description":"Detalle de un objetivo con sus KRs (solo de mi organización).","operationId":"getObjective","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectiveOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["okr"],"summary":"Update Objective","description":"Edita un objetivo (parcial). Requiere manager+.","operationId":"updateObjective","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectiveUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ObjectiveOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["okr"],"summary":"Delete Objective","description":"Borra un objetivo y sus KRs (cascade). Requiere manager+.","operationId":"deleteObjective","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/okr/objectives/{objective_id}/key-results":{"post":{"tags":["okr"],"summary":"Create Key Result","description":"Añade un resultado clave a un objetivo. Requiere manager+.","operationId":"createKeyResult","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/okr/objectives/{objective_id}/key-results/{key_result_id}":{"patch":{"tags":["okr"],"summary":"Update Key Result","description":"Edita un resultado clave (mover `current_value` refleja el avance). manager+.","operationId":"updateKeyResult","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"key_result_id","in":"path","required":true,"schema":{"type":"string","title":"Key Result Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["okr"],"summary":"Delete Key Result","description":"Borra un resultado clave. Requiere manager+.","operationId":"deleteKeyResult","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"key_result_id","in":"path","required":true,"schema":{"type":"string","title":"Key Result Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/okr/objectives/{objective_id}/key-results/{key_result_id}/snapshots":{"post":{"tags":["okr"],"summary":"Create Key Result Snapshot","description":"Registra un punto de progreso (mueve `current_value` y deja rastro\nhistórico). Requiere manager+.","operationId":"createKeyResultSnapshot","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"key_result_id","in":"path","required":true,"schema":{"type":"string","title":"Key Result Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultSnapshotCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyResultSnapshotOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["okr"],"summary":"List Key Result Snapshots","description":"Historial de progreso del KR en orden cronológico (para una gráfica de\nevolución). Requiere manager+.","operationId":"listKeyResultSnapshots","parameters":[{"name":"objective_id","in":"path","required":true,"schema":{"type":"string","title":"Objective Id"}},{"name":"key_result_id","in":"path","required":true,"schema":{"type":"string","title":"Key Result Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KeyResultSnapshotOut"},"title":"Response Listkeyresultsnapshots"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/tags":{"get":{"tags":["tags"],"summary":"List Tags","operationId":"tags_list_tags","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagOut"},"title":"Response Tags List Tags"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tags"],"summary":"Create Tag","operationId":"tags_create_tag","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/tags/{tag_id}":{"patch":{"tags":["tags"],"summary":"Update Tag","operationId":"tags_update_tag","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["tags"],"summary":"Delete Tag","operationId":"tags_delete_tag","parameters":[{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","title":"Tag Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices":{"get":{"tags":["finance"],"summary":"List Invoices","operationId":"finance_list_invoices","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["draft","sent","paid","overdue","cancelled"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date >= (ISO).","title":"Date From"},"description":"issue_date >= (ISO)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date <= (ISO).","title":"Date To"},"description":"issue_date <= (ISO)."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en invoice_number o client_name (case-insensitive).","title":"Search"},"description":"Busca en invoice_number o client_name (case-insensitive)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["created_at","number","client","status","issue_date","due_date","total"],"type":"string","default":"created_at","title":"Sort"}},{"name":"dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Dir"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceOut"},"title":"Response Finance List Invoices"}}},"headers":{"X-Total-Count":{"description":"Total de facturas que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["finance"],"summary":"Create Invoice","operationId":"finance_create_invoice","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/from-time":{"post":{"tags":["finance"],"summary":"Generar una factura borrador desde el tiempo facturable de un proyecto","description":"Agrega las horas facturables (no facturadas aún) de un proyecto en una\nfactura DRAFT — una línea por persona (o por tarea) × su tarifa de\nfacturación — y marca esas horas como facturadas para no duplicarlas (admin+).\n\n422 `no_billable_time` si no hay tiempo facturable sin facturar en el rango.","operationId":"createInvoiceFromTime","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceFromTimeIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/export":{"get":{"tags":["finance"],"summary":"Export Invoices Csv","description":"Exporta TODAS las facturas que cumplen los filtros como CSV UTF-8.\n\nIgnora paginación — exporta todas las filas. Requiere ser miembro.\nColumnas: número, cliente, estado, fecha_emision, fecha_venc, subtotal, iva, total, moneda.","operationId":"exportInvoicesCsv","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["draft","sent","paid","overdue","cancelled"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date >= (ISO).","title":"Date From"},"description":"issue_date >= (ISO)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date <= (ISO).","title":"Date To"},"description":"issue_date <= (ISO)."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en número o cliente.","title":"Search"},"description":"Busca en número o cliente."}],"responses":{"200":{"description":"CSV de facturas con cabecera (UTF-8).","content":{"application/json":{"schema":{}},"text/csv":{}},"headers":{"Content-Disposition":{"description":"attachment; filename=\"facturas.csv\"","schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/by-number/{invoice_number}":{"get":{"tags":["finance"],"summary":"Get Invoice By Number","description":"Resuelve una factura por su número legible (p. ej. INV-00076) dentro de la org.\n\nAlternativa a la URL por UUID para construir URLs amigables tipo\n`/finance/invoices/INV-00076`. El número es único por organización.","operationId":"getInvoiceByNumber","parameters":[{"name":"invoice_number","in":"path","required":true,"schema":{"type":"string","title":"Invoice Number"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}":{"get":{"tags":["finance"],"summary":"Get Invoice","operationId":"finance_get_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Invoice","operationId":"finance_update_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Invoice","operationId":"finance_delete_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices/bulk":{"post":{"tags":["finance"],"summary":"Bulk Invoice Action","operationId":"finance_bulk_invoice_action","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceBulkActionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/renumber":{"post":{"tags":["finance"],"summary":"Renumerar la serie de facturas de forma correlativa","operationId":"renumberInvoices","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRenumberIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRenumberResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/approve":{"post":{"tags":["finance"],"summary":"Approve Invoice","operationId":"finance_approve_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/reject":{"post":{"tags":["finance"],"summary":"Reject Invoice","operationId":"finance_reject_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/mark-paid":{"post":{"tags":["finance"],"summary":"Mark Invoice Paid","operationId":"finance_mark_invoice_paid","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/void":{"post":{"tags":["finance"],"summary":"Void Invoice","operationId":"finance_void_invoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/refund":{"post":{"tags":["finance"],"summary":"Reembolsar una factura cobrada (total o parcial)","description":"Emite una factura RECTIFICATIVA por el importe reembolsado.\n\n201 y no 200 a propósito: la respuesta es un recurso NUEVO (la rectificativa,\ncon su propio id y su número de la serie `REC-`), no la factura original\nmodificada.","operationId":"refundInvoice","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRefundIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/refunds":{"get":{"tags":["finance"],"summary":"Reembolsos de una factura","operationId":"listInvoiceRefunds","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRefundsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/pdf":{"get":{"tags":["finance"],"summary":"Download Invoice Pdf","description":"Genera y descarga el PDF de la factura.\n\nEl engine es fpdf2 (puro Python). El logo de la organización se incrusta\núnicamente si está disponible en base64 — nunca se hace fetch de URLs remotas.","operationId":"downloadInvoicePdf","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"PDF de la factura (A4).","content":{"application/json":{"schema":{}},"application/pdf":{}},"headers":{"Content-Disposition":{"description":"attachment; filename=\"{invoice_number}.pdf\"","schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/share":{"post":{"tags":["finance"],"summary":"Create Invoice Share Token","description":"Genera un enlace público de solo lectura para la factura.\n\nEl token crudo solo se devuelve en esta respuesta — no se puede recuperar\ndespués. Guarda el enlace (`public_url`) inmediatamente.\nRequiere rol admin o superior (igual que la aprobación de facturas).","operationId":"createInvoiceShareToken","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceShareCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceShareOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["finance"],"summary":"List Invoice Share Tokens","description":"Lista todos los share tokens activos (no revocados) de la factura.","operationId":"listInvoiceShareTokens","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceShareOut"},"title":"Response Listinvoicesharetokens"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/share/{share_id}":{"delete":{"tags":["finance"],"summary":"Revoke Invoice Share Token","description":"Revoca un share token (el enlace público deja de funcionar inmediatamente).","operationId":"revokeInvoiceShareToken","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"share_id","in":"path","required":true,"schema":{"type":"string","title":"Share Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/expenses":{"get":{"tags":["finance"],"summary":"List Expenses","operationId":"finance_list_expenses","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","approved","rejected","paid","void"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"date >= (ISO).","title":"Date From"},"description":"date >= (ISO)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"date <= (ISO).","title":"Date To"},"description":"date <= (ISO)."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en description o vendor (case-insensitive).","title":"Search"},"description":"Busca en description o vendor (case-insensitive)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["date","number","description","category","amount","status"],"type":"string","default":"date","title":"Sort"}},{"name":"dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Dir"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExpenseOut"},"title":"Response Finance List Expenses"}}},"headers":{"X-Total-Count":{"description":"Número total de gastos que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["finance"],"summary":"Create Expense","operationId":"finance_create_expense","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/export":{"get":{"tags":["finance"],"summary":"Export Expenses Csv","description":"Exporta TODOS los gastos que cumplen los filtros como CSV UTF-8.\n\nIgnora paginación — exporta todas las filas. Requiere ser miembro.\nColumnas: descripcion, categoria, estado, fecha, importe, moneda, proveedor.","operationId":"exportExpensesCsv","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","approved","rejected","paid","void"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"date >= (ISO).","title":"Date From"},"description":"date >= (ISO)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"date <= (ISO).","title":"Date To"},"description":"date <= (ISO)."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en descripción o proveedor.","title":"Search"},"description":"Busca en descripción o proveedor."}],"responses":{"200":{"description":"CSV de gastos con cabecera (UTF-8).","content":{"application/json":{"schema":{}},"text/csv":{}},"headers":{"Content-Disposition":{"description":"attachment; filename=\"gastos.csv\"","schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/by-number/{number}":{"get":{"tags":["finance"],"summary":"Get Expense By Number","description":"Resuelve un gasto por su número secuencial (p. ej. 42 → EXP-0042) dentro de la org.\n\nAlternativa a la URL por UUID para construir URLs amigables tipo\n`/finance/expenses/42`. El número es único por organización.","operationId":"getExpenseByNumber","parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"integer","title":"Number"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}":{"get":{"tags":["finance"],"summary":"Get Expense","operationId":"finance_get_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Expense","operationId":"finance_update_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Expense","operationId":"finance_delete_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/expenses/bulk":{"post":{"tags":["finance"],"summary":"Bulk Expense Action","operationId":"finance_bulk_expense_action","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseBulkActionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/approve":{"post":{"tags":["finance"],"summary":"Approve Expense","operationId":"finance_approve_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/reject":{"post":{"tags":["finance"],"summary":"Reject Expense","operationId":"finance_reject_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/mark-paid":{"post":{"tags":["finance"],"summary":"Mark Paid Expense","operationId":"finance_mark_paid_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/void":{"post":{"tags":["finance"],"summary":"Void Expense","operationId":"finance_void_expense","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/summary":{"get":{"tags":["finance"],"summary":"Get Finance Summary","operationId":"finance_get_finance_summary","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__finance__schemas__FinanceSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/trend":{"get":{"tags":["finance"],"summary":"Finance Trend","operationId":"finance_finance_trend","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FinanceTrendPointOut"},"title":"Response Finance Finance Trend"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/trend/{month}/invoices":{"get":{"tags":["finance"],"summary":"Finance Trend Invoices","description":"Abre `income` de un punto de la tendencia: las facturas que lo suman (admin+).\n\nPasa el MISMO `from`/`to` de la tendencia: es lo que recorta el primer y el\núltimo mes, y con otro rango el desglose explicaría un total distinto del que\nse está mirando. `income` viene de la misma consulta agregada que la serie, así\nque no cambia al paginar.","operationId":"financeTrendInvoices","parameters":[{"name":"month","in":"path","required":true,"schema":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$","description":"Mes `YYYY-MM` del punto de la tendencia que se abre.","title":"Month"},"description":"Mes `YYYY-MM` del punto de la tendencia que se abre."},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DesgloseDeLoFacturadoOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/expenses-by-category":{"get":{"tags":["finance"],"summary":"Finance Expenses By Category","operationId":"finance_finance_expenses_by_category","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExpenseCategoryStatOut"},"title":"Response Finance Finance Expenses By Category"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/invoice-status-distribution":{"get":{"tags":["finance"],"summary":"Finance Invoice Status Distribution","operationId":"finance_finance_invoice_status_distribution","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceStatusStatOut"},"title":"Response Finance Finance Invoice Status Distribution"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/aging":{"get":{"tags":["finance"],"summary":"Finance Aging","operationId":"finance_finance_aging","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgingReportOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/revenue-by-client":{"get":{"tags":["finance"],"summary":"Finance Revenue By Client","description":"Ingresos agrupados por cliente en el rango (facturas sent/paid/overdue).\n\nAgrupa por ficha de cliente (client_id) cuando existe; si no hay ficha\nvinculada, agrupa por client_name del snapshot. Excluye draft y cancelled.\nOrdenado de mayor a menor total.","operationId":"financeRevenueByClient","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RevenueByClientRowOut"},"title":"Response Financerevenuebyclient"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/profitability/by-project":{"get":{"tags":["finance"],"summary":"Finance Profitability By Project","description":"Rentabilidad por proyecto: ingreso vs coste laboral + gastos (admin+).\n\nPara cada proyecto con actividad (tiempo, factura o gasto) en el rango:\nrevenue (facturas sent/paid/overdue), cost_labor (horas × tarifa de coste\ndel miembro; 0 si no tiene tarifa), cost_expenses (gastos aprobados),\nmargin y margin_pct. `from`/`to` opcionales; sin ellos, all-time. Cada\nentidad se filtra por SU propia fecha. Ordenado por margen desc.","operationId":"profitabilityByProject","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectProfitabilityOut"},"title":"Response Profitabilitybyproject"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/profitability/by-client":{"get":{"tags":["finance"],"summary":"Finance Profitability By Client","description":"Rentabilidad por cliente: roll-up de la rentabilidad por proyecto (admin+).\n\nAgrega `revenue`/`cost`/`margin` de `profitabilityByProject` por el\n`client_id` de cada proyecto (misma fórmula, no la recalcula). Los\nproyectos sin cliente vinculado se agrupan en una fila \"Sin cliente\"\n(`client_id=null`) en vez de excluirse. `from`/`to` opcionales; sin\nellos, all-time. Ordenado por margen desc.","operationId":"profitabilityByClient","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientProfitabilityOut"},"title":"Response Profitabilitybyclient"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/utilization":{"get":{"tags":["finance"],"summary":"Finance Team Utilization","description":"Utilización por miembro: horas facturables sobre horas totales (admin+).\n\nUna fila por usuario con tiempo registrado en el rango; utilization_pct =\nbillable/total×100 (0 si no hay horas). `from`/`to` opcionales (all-time).","operationId":"teamUtilization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamUtilizationOut"},"title":"Response Teamutilization"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-invoices":{"get":{"tags":["finance"],"summary":"List Recurring Invoices","operationId":"finance_list_recurring_invoices","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecurringInvoiceOut"},"title":"Response Finance List Recurring Invoices"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["finance"],"summary":"Create Recurring Invoice","operationId":"finance_create_recurring_invoice","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-invoices/due":{"get":{"tags":["finance"],"summary":"List Due Recurring Invoices","operationId":"finance_list_due_recurring_invoices","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecurringInvoiceOut"},"title":"Response Finance List Due Recurring Invoices"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/recurring-invoices/{recurring_id}":{"get":{"tags":["finance"],"summary":"Get Recurring Invoice","operationId":"finance_get_recurring_invoice","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Recurring Invoice","operationId":"finance_update_recurring_invoice","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Recurring Invoice","operationId":"finance_delete_recurring_invoice","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/recurring-invoices/{recurring_id}/toggle":{"post":{"tags":["finance"],"summary":"Toggle Recurring Invoice","operationId":"finance_toggle_recurring_invoice","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-invoices/{recurring_id}/generate":{"post":{"tags":["finance"],"summary":"Generate Recurring Invoice","operationId":"finance_generate_recurring_invoice","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-expenses":{"get":{"tags":["finance"],"summary":"List Recurring Expenses","operationId":"finance_list_recurring_expenses","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecurringExpenseOut"},"title":"Response Finance List Recurring Expenses"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["finance"],"summary":"Create Recurring Expense","operationId":"finance_create_recurring_expense","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-expenses/due":{"get":{"tags":["finance"],"summary":"List Due Recurring Expenses","operationId":"finance_list_due_recurring_expenses","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecurringExpenseOut"},"title":"Response Finance List Due Recurring Expenses"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/recurring-expenses/{recurring_id}":{"get":{"tags":["finance"],"summary":"Get Recurring Expense","operationId":"finance_get_recurring_expense","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Recurring Expense","operationId":"finance_update_recurring_expense","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Recurring Expense","operationId":"finance_delete_recurring_expense","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/recurring-expenses/{recurring_id}/toggle":{"post":{"tags":["finance"],"summary":"Toggle Recurring Expense","operationId":"finance_toggle_recurring_expense","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/recurring-expenses/{recurring_id}/generate":{"post":{"tags":["finance"],"summary":"Generate Recurring Expense","operationId":"finance_generate_recurring_expense","parameters":[{"name":"recurring_id","in":"path","required":true,"schema":{"type":"string","title":"Recurring Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/settings":{"get":{"tags":["finance"],"summary":"Get Finance Settings","description":"Preferencias de finanzas de la organización.\n\nHoy solo el interruptor de los recordatorios de cobro automáticos. Una\norganización que nunca lo tocó responde `false` con `updated_at: null` — y no\nse crea ninguna fila al mirar.","operationId":"getFinanceSettings","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceSettingsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["finance"],"summary":"Update Finance Settings","description":"Enciende o apaga los recordatorios de cobro automáticos de la organización.\n\nCon el interruptor encendido, un barrido diario escribe a los clientes con\nfacturas vencidas (1, 7 y 15 días tras el vencimiento) desde la dirección que\nconsta en cada factura. Requiere admin+ y queda en auditoría: no es una\npreferencia personal, es una política sobre lo que sale a nombre de la\nempresa.","operationId":"updateFinanceSettings","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceSettingsUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceSettingsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/reminders":{"get":{"tags":["finance"],"summary":"List Invoice Reminders","description":"Recordatorios de cobro ya enviados de una factura, más reciente primero.\n\nCada fila dice qué hito se cubrió (1/7/15), a qué dirección salió y cuándo.\nEs la respuesta a «¿por qué ha recibido mi cliente este correo?».","operationId":"listInvoiceReminders","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceReminderOut"},"title":"Response Listinvoicereminders"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/invoices/{token}":{"get":{"tags":["finance"],"summary":"Get Public Invoice","description":"Devuelve la vista pública de una factura sin requerir autenticación.\n\nEl token es la credencial (generado con `POST …/share`). Devuelve 404 si el\ntoken no existe, ha sido revocado o ha expirado. Rate-limited por IP.","operationId":"getPublicInvoice","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInvoiceOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/invoices/{token}/pdf":{"get":{"tags":["finance"],"summary":"Get Public Invoice Pdf","description":"Descarga el PDF de la factura pública (sin autenticación, rate-limited).","operationId":"getPublicInvoicePdf","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"PDF de la factura (público).","content":{"application/json":{"schema":{}},"application/pdf":{}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/quotes":{"get":{"tags":["quotes"],"summary":"List Quotes","operationId":"quotes_list_quotes","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["draft","sent","approved","rejected","converted","expired"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por cliente CRM vinculado (UUID de la ficha de cliente).","title":"Client Id"},"description":"Filtra por cliente CRM vinculado (UUID de la ficha de cliente)."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en quote_number o client_name (case-insensitive).","title":"Search"},"description":"Busca en quote_number o client_name (case-insensitive)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["created_at","number","client","status","issue_date","expires_at","total"],"type":"string","default":"created_at","title":"Sort"}},{"name":"dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Dir"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/QuoteOut"},"title":"Response Quotes List Quotes"}}},"headers":{"X-Total-Count":{"description":"Total de presupuestos que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["quotes"],"summary":"Create Quote","operationId":"quotes_create_quote","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}":{"get":{"tags":["quotes"],"summary":"Get Quote","operationId":"quotes_get_quote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["quotes"],"summary":"Update Quote","operationId":"quotes_update_quote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["quotes"],"summary":"Delete Quote","operationId":"quotes_delete_quote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/send":{"post":{"tags":["quotes"],"summary":"Send Quote","description":"Marca el presupuesto como enviado (draft → sent).","operationId":"sendQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/share":{"post":{"tags":["quotes"],"summary":"Create Quote Share Token","description":"Genera un enlace público de solo lectura para el presupuesto.\n\nEl token crudo solo se devuelve en esta respuesta — no se puede recuperar\ndespués. Guarda el enlace (`public_url`) inmediatamente. Requiere admin+.","operationId":"createQuoteShareToken","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteShareCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteShareOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/convert":{"post":{"tags":["quotes"],"summary":"Convert Quote To Invoice","description":"Convierte un presupuesto APROBADO en factura (422 si no está aprobado).\n\nCon `deposit_percent` la factura es solo ese % del total (línea 'Depósito\nX%'); sin él se copian todas las líneas. Devuelve el presupuesto ya\n`converted` con `converted_invoice_id` apuntando a la nueva factura.","operationId":"convertQuoteToInvoice","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteConvertIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/convert-to-invoice":{"post":{"tags":["quotes"],"summary":"Create Invoice From Quote","description":"Convierte un presupuesto APROBADO en factura y DEVUELVE LA FACTURA.\n\nCierra el flujo comercial→cobro. Idempotente: si el presupuesto ya se\nconvirtió y la factura sigue viva, devuelve ESA factura sin crear otra (no\nduplica ante reintentos). `422` si el presupuesto no está `approved`. Con\n`deposit_percent` la factura cubre solo ese anticipo. Requiere admin+.","operationId":"createInvoiceFromQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteConvertIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/pdf":{"get":{"tags":["quotes"],"summary":"Download Quote Pdf","description":"Genera y descarga el PDF del presupuesto (motor fpdf2, sin I/O de red).","operationId":"downloadQuotePdf","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}},"application/pdf":{}},"headers":{"Content-Disposition":{"description":"attachment; filename=\"{quote_number}.pdf\"","schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/project":{"get":{"tags":["quotes"],"summary":"Get Quote Project","description":"Proyecto abierto desde el presupuesto; 404 si todavía no hay ninguno.","operationId":"getQuoteProject","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["quotes"],"summary":"Create Project From Quote","description":"Abre el proyecto con el que se ejecuta un presupuesto aceptado.\n\nArrastra cliente, nombre y una descripción con el importe y las líneas; los\nimportes NO se copian como campos del proyecto (siguen viviendo en el\npresupuesto y la factura). **Idempotente**: si el presupuesto ya tiene\nproyecto devuelve ESE, sin crear otro. `409` si el que había está en la\npapelera, `422` si el presupuesto no está aceptado. Requiere admin+.","operationId":"createProjectFromQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteProjectCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/document":{"get":{"tags":["quotes"],"summary":"Get Quote Document","description":"Documento (`kind: file`) con el PDF archivado; 404 si aún no se archivó.","operationId":"getQuoteDocument","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["quotes"],"summary":"Archive Quote Document","description":"Guarda el PDF del presupuesto aceptado en Documentos de la organización.\n\nVa a la carpeta «Presupuestos aceptados» (restringida: solo owner/admin la\nven, el mismo umbral con el que se ven los presupuestos). Normalmente no hace\nfalta llamarlo —el archivado corre solo tras la aceptación—; esto lo fuerza al\nmomento. **Idempotente**: si ya está archivado devuelve ESE documento.\nRequiere admin+.","operationId":"archiveQuoteDocument","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/public/quotes/{token}":{"get":{"tags":["quotes"],"summary":"Get Public Quote","description":"Devuelve la vista pública de un presupuesto sin requerir autenticación.\n\nEl token es la credencial (generado con `POST …/share`). Devuelve 404 si el\ntoken no existe, ha sido revocado o ha expirado. Rate-limited por IP.","operationId":"getPublicQuote","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicQuoteOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/quotes/{token}/approve":{"post":{"tags":["quotes"],"summary":"Approve Public Quote","description":"Aprueba (firma) un presupuesto vía link público, sin autenticación.\n\nRegistra la aceptación: estado `approved`, identidad del firmante (nombre y\nemail, ambos obligatorios), IP y un `signature_hash` SHA-256 del contenido +\nidentidad + timestamp. Devuelve 409 si el cliente ya decidió (aceptado,\nconvertido o rechazado) y 422 si el presupuesto caducó. Rate-limited por IP.","operationId":"approvePublicQuote","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteApproveIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicQuoteOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/v1/public/quotes/{token}/reject":{"post":{"tags":["quotes"],"summary":"Reject Public Quote","description":"Rechaza un presupuesto vía link público, sin autenticación.\n\nHermano de `/approve`: mismas guardas (token vivo, un solo uso, caducado no\nse decide) y mismo sellado de identidad, con `rejection_reason` opcional del\npropio cliente. Devuelve 409 si ya decidió y 422 si caducó. Rate-limited por\nIP. La respuesta es la vista PÚBLICA: nunca incluye notas internas.","operationId":"rejectPublicQuote","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRejectIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicQuoteOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/v1/public/quotes/{token}/pdf":{"get":{"tags":["quotes"],"summary":"Download Public Quote Pdf","description":"Descarga el PDF del presupuesto desde el token público (sin auth).","operationId":"downloadPublicQuotePdf","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/quotes/{quote_id}/signature":{"get":{"tags":["quotes"],"summary":"Get Quote Signature","description":"Expediente completo de la firma de un presupuesto (admin+).","operationId":"getQuoteSignature","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentSignatureOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/signatures/{token}/code":{"post":{"tags":["quotes"],"summary":"Request Signature Code","description":"Envía al cliente el código de seis dígitos con el que podrá firmar.\n\nEl destino es el correo que ya consta en el documento; la respuesta solo\ndevuelve esa dirección enmascarada.","operationId":"requestSignatureCode","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatureCodeSentOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/v1/public/signatures/{token}/sign":{"post":{"tags":["quotes"],"summary":"Sign Public Document","description":"Firma el documento (aceptándolo o rechazándolo) con el código recibido.\n\nLa IP y el agente los toma el servidor de la petición, nunca del cuerpo:\nforman parte de la prueba y un dato que declara el firmante no prueba nada.","operationId":"signPublicDocument","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentSignIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDocumentSignatureOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/v1/public/signatures/{token}/pdf":{"get":{"tags":["quotes"],"summary":"Download Signed Document Pdf","description":"Devuelve el PDF firmado tal y como se archivó (no se regenera).","operationId":"downloadSignedDocumentPdf","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}},"application/pdf":{}},"headers":{"Content-Disposition":{"description":"attachment; filename=\"{quote_number}-firmado.pdf\"","schema":{"type":"string"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/portal-links":{"post":{"tags":["portal"],"summary":"Create Client Portal Link","description":"Emite un enlace del portal para el cliente (solo lectura del cliente).\n\nEl token crudo (`token`) y la URL (`portal_url`) solo se devuelven en esta\nrespuesta — no se pueden recuperar después. Guárdalos inmediatamente.\nRequiere admin+.","operationId":"createClientPortalLink","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLinkCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalLinkOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["portal"],"summary":"List Client Portal Links","description":"Lista los enlaces del portal del cliente (sin el token crudo).","operationId":"listClientPortalLinks","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalLinkOut"},"title":"Response Listclientportallinks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/portal-links/{link_id}":{"delete":{"tags":["portal"],"summary":"Revoke Client Portal Link","description":"Revoca un enlace del portal (queda inutilizable). Requiere admin+.","operationId":"revokeClientPortalLink","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"link_id","in":"path","required":true,"schema":{"type":"string","title":"Link Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}":{"get":{"tags":["portal"],"summary":"Get Portal Summary","description":"Portada del portal: cliente, marca de la org y recuentos.\n\nEl token es la credencial (emitido con `POST …/portal-links`). 404 si el\ntoken no existe, ha sido revocado o ha expirado. Rate-limited por IP.","operationId":"getPortalSummary","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSummaryOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/invoices":{"get":{"tags":["portal"],"summary":"Get Portal Invoices","description":"Facturas del cliente del token: emitidas, cobradas y vencidas.\n\nLos borradores y las ANULADAS no se exponen (`portal/visibility.py` declara\nla lista completa con el motivo de cada exclusión). Rate-limited por IP.","operationId":"getPortalInvoices","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalInvoiceOut"},"title":"Response Getportalinvoices"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/quotes":{"get":{"tags":["portal"],"summary":"Get Portal Quotes","description":"Presupuestos del cliente del token, incluido su histórico cerrado.\n\nSe exponen enviados, aceptados, rechazados, convertidos y caducados; los\nborradores no (`portal/visibility.py`). Rate-limited por IP.","operationId":"getPortalQuotes","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalQuoteOut"},"title":"Response Getportalquotes"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/contracts":{"get":{"tags":["portal"],"summary":"Get Portal Contracts","description":"Contratos del cliente del token, incluido su histórico cerrado.\n\nSe exponen activos, firmados, vencidos, resueltos y anulados; los borradores\nno (`portal/visibility.py`, que explica por qué un contrato anulado sí se ve\ny una factura anulada no). Rate-limited por IP.","operationId":"getPortalContracts","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalContractOut"},"title":"Response Getportalcontracts"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/invoices/{invoice_number}/approve":{"post":{"tags":["portal"],"summary":"Approve Portal Invoice","description":"Aprueba (firma) una factura vía el link público, sin autenticación.\n\nRegistra la aceptación: identidad del firmante, IP y un `signature_hash`\nSHA-256 del contenido + identidad + timestamp (no cambia el status de la\nfactura). 404 si no existe o no es de este cliente; 409 si ya fue aprobada.\nRate-limited por IP.","operationId":"approvePortalInvoice","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"invoice_number","in":"path","required":true,"schema":{"type":"string","title":"Invoice Number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalApproveIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalInvoiceOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/contracts/{contract_number}/sign":{"post":{"tags":["portal"],"summary":"Sign Portal Contract","description":"Firma un contrato vía el link público, sin autenticación.\n\nRegistra la firma: identidad del firmante, IP y un `signature_hash` SHA-256\ndel contenido + identidad + timestamp; pasa el status a \"signed\". 404 si no\nexiste o no es de este cliente; 409 si ya fue firmado. Rate-limited por IP.","operationId":"signPortalContract","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"contract_number","in":"path","required":true,"schema":{"type":"string","title":"Contract Number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalApproveIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalContractOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/identify":{"post":{"tags":["portal"],"summary":"Request Portal Access Code","description":"Pide el código de acceso para una dirección de correo.\n\nResponde SIEMPRE lo mismo, exista o no ese contacto: contestar distinto\nconvertiría el enlace en un comprobador de quién trabaja en esa empresa. El\ncódigo solo sale hacia el buzón si de verdad hay a quién mandárselo, y\nsiempre a la dirección REGISTRADA en la ficha, nunca a la que se teclee.","operationId":"requestPortalAccessCode","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalIdentifyIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCodeSentOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/verify":{"post":{"tags":["portal"],"summary":"Verify Portal Access Code","description":"Canjea el código y abre la sesión de la persona.\n\nEl token de sesión NO viaja en el cuerpo: sale solo en la cookie HttpOnly.\nDevolverlo aquí lo pondría al alcance de cualquier script de la página y\ndesharía justo lo que la cookie protege.","operationId":"verifyPortalAccessCode","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalVerifyIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalIdentityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/me":{"get":{"tags":["portal"],"summary":"Get Portal Identity","description":"Quién está dentro. 401 `portal_identity_required` si no hay sesión válida.","operationId":"getPortalIdentity","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalIdentityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/session":{"delete":{"tags":["portal"],"summary":"End Portal Session","description":"Cierra la sesión (ordenador compartido). Idempotente: 204 aunque no hubiera.\n\nRevoca la fila Y borra la cookie. Las dos cosas: borrar solo la cookie\ndejaría la sesión viva para quien se hubiera quedado con una copia, y\nrevocar solo la fila dejaría al navegador mandando una credencial muerta.","operationId":"endPortalSession","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"204":{"description":"Successful Response"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/request-types":{"get":{"tags":["portal"],"summary":"Get Portal Request Types","description":"Formularios que hoy se ofrecen, con sus campos. Requiere sesión.","operationId":"getPortalRequestTypes","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalRequestTypeOut"},"title":"Response Getportalrequesttypes"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/requests":{"get":{"tags":["portal"],"summary":"Get Portal Requests","description":"Las peticiones que abrió ESTA persona. Requiere sesión.","operationId":"getPortalRequests","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalRequestOut"},"title":"Response Getportalrequests"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["portal"],"summary":"Create Portal Request","description":"Abre una petición del tipo elegido, con sus campos. Requiere sesión.","operationId":"createPortalRequest","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/public/portal/{token}/requests/{request_id}":{"get":{"tags":["portal"],"summary":"Get Portal Request","description":"La petición y su conversación PÚBLICA. 404 si no es suya. Requiere sesión.","operationId":"getPortalRequest","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/portal/{token}/requests/{request_id}/comments":{"post":{"tags":["portal"],"summary":"Create Portal Request Comment","description":"Responde en la conversación de su petición. Requiere sesión.","operationId":"createPortalRequestComment","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/public/portal/{token}/requests/{request_id}/attachments":{"get":{"tags":["portal"],"summary":"Get Portal Request Attachments","description":"Los ficheros que el CLIENTE puso en su petición. Requiere sesión.\n\nLos que subió el EQUIPO a la misma tarea no salen: un adjunto no tiene\ncolumna de visibilidad y un comentario sí, con defecto interno (el porqué\ncompleto, en `PortalService.list_request_attachments`).","operationId":"getPortalRequestAttachments","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PortalRequestAttachmentOut"},"title":"Response Getportalrequestattachments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["portal"],"summary":"Create Portal Request Attachment","description":"Adjunta un fichero a SU petición (base64). Requiere sesión.\n\nMismos límites que cualquier subida del producto (regla 18): 10 MiB,\nallow-list de extensiones y verificación de los BYTES contra la extensión.\n422 `invalid_attachment` si el fichero no pasa — al otro lado hay una persona\nque puede reintentar, así que se le dice.","operationId":"createPortalRequestAttachment","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestAttachmentIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalRequestAttachmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/public/portal/{token}/requests/{request_id}/attachments/{attachment_id}/download":{"get":{"tags":["portal"],"summary":"Download Portal Request Attachment","description":"Descarga AUTENTICADA del adjunto de su petición (regla 18).\n\nNunca una ruta pública a los bytes: el fichero se sirve por aquí, y aquí se\ncomprueba antes que la persona está identificada, que la petición es suya y\nque el adjunto cuelga de esa petición. 404 en cualquiera de los tres fallos.","operationId":"downloadPortalRequestAttachment","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"portal_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Session"}}],"responses":{"200":{"description":"El fichero.","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/supplier-invoices":{"get":{"tags":["finance"],"summary":"List Supplier Invoices","operationId":"listSupplierInvoices","deprecated":true,"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["received","approved","rejected","paid","void"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"}},{"name":"supplier","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en supplier_name o number (case-insensitive).","title":"Supplier"},"description":"Busca en supplier_name o number (case-insensitive)."},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date >= (ISO).","title":"Date From"},"description":"issue_date >= (ISO)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"issue_date <= (ISO).","title":"Date To"},"description":"issue_date <= (ISO)."},{"name":"sort","in":"query","required":false,"schema":{"enum":["created_at","issue_date","due_date","total","supplier","status"],"type":"string","default":"created_at","title":"Sort"}},{"name":"dir","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Dir"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplierInvoiceOut"},"title":"Response Listsupplierinvoices"}}},"headers":{"X-Total-Count":{"description":"Total de facturas de proveedor que cumplen los filtros.","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["finance"],"summary":"Create Supplier Invoice","operationId":"createSupplierInvoice","deprecated":true,"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/supplier-invoices/bulk":{"post":{"tags":["finance"],"summary":"Bulk Supplier Invoice Action","operationId":"bulkSupplierInvoiceAction","deprecated":true,"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceBulkActionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/supplier-invoices/{invoice_id}":{"get":{"tags":["finance"],"summary":"Get Supplier Invoice","operationId":"getSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Supplier Invoice","operationId":"updateSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Supplier Invoice","operationId":"deleteSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/supplier-invoices/{invoice_id}/approve":{"post":{"tags":["finance"],"summary":"Approve Supplier Invoice","operationId":"approveSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/supplier-invoices/{invoice_id}/reject":{"post":{"tags":["finance"],"summary":"Reject Supplier Invoice","operationId":"rejectSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/supplier-invoices/{invoice_id}/mark-paid":{"post":{"tags":["finance"],"summary":"Mark Paid Supplier Invoice","operationId":"markPaidSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/supplier-invoices/{invoice_id}/void":{"post":{"tags":["finance"],"summary":"Void Supplier Invoice","operationId":"voidSupplierInvoice","deprecated":true,"parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierInvoiceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/ap-aging":{"get":{"tags":["finance"],"summary":"Ap Aging","operationId":"apAging","deprecated":true,"parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApAgingReportOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/fiscal-export":{"get":{"tags":["finance"],"summary":"Fiscal Export","operationId":"fiscalExport","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","description":"Periodo: YYYY (ej. 2026), YYYY-Qn (ej. 2026-Q1), o YYYY-MM (ej. 2026-07).","title":"Period"},"description":"Periodo: YYYY (ej. 2026), YYYY-Qn (ej. 2026-Q1), o YYYY-MM (ej. 2026-07)."}],"responses":{"200":{"description":"ZIP con export-fiscal-{period}.xlsx y resumen-fiscal-{period}.pdf.","content":{"application/json":{"schema":{}},"application/zip":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/tax/modelo-303":{"get":{"tags":["finance"],"summary":"Get Modelo 303","operationId":"getModelo303","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","description":"Periodo: YYYY, YYYY-Qn o YYYY-MM.","title":"Period"},"description":"Periodo: YYYY, YYYY-Qn o YYYY-MM."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Modelo303Out"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/tax/modelo-347":{"get":{"tags":["finance"],"summary":"Get Modelo 347","operationId":"getModelo347","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"year","in":"query","required":true,"schema":{"type":"integer","description":"Ejercicio fiscal (ej. 2026).","title":"Year"},"description":"Ejercicio fiscal (ej. 2026)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Modelo347Out"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/tax/modelo-349":{"get":{"tags":["finance"],"summary":"Get Modelo 349","operationId":"getModelo349","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"year","in":"query","required":true,"schema":{"type":"integer","description":"Ejercicio fiscal (ej. 2026).","title":"Year"},"description":"Ejercicio fiscal (ej. 2026)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Modelo349Out"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/budgets":{"get":{"tags":["finance"],"summary":"List Budgets","operationId":"listBudgets","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por período (YYYY, YYYY-Qn, YYYY-MM).","title":"Period"},"description":"Filtrar por período (YYYY, YYYY-Qn, YYYY-MM)."},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por proyecto.","title":"Project Id"},"description":"Filtrar por proyecto."},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por categoría.","title":"Category"},"description":"Filtrar por categoría."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BudgetOut"},"title":"Response Listbudgets"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["finance"],"summary":"Create Budget","operationId":"createBudget","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/budgets/{budget_id}":{"get":{"tags":["finance"],"summary":"Get Budget","operationId":"getBudget","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string","title":"Budget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Budget","operationId":"updateBudget","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string","title":"Budget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Budget","operationId":"deleteBudget","parameters":[{"name":"budget_id","in":"path","required":true,"schema":{"type":"string","title":"Budget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/budget-vs-actual":{"get":{"tags":["finance"],"summary":"Budget Vs Actual","operationId":"budgetVsActual","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","description":"Período: YYYY, YYYY-Qn o YYYY-MM.","title":"Period"},"description":"Período: YYYY, YYYY-Qn o YYYY-MM."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetVsActualOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/cost-centers":{"get":{"tags":["finance"],"summary":"List Cost Centers","operationId":"listCostCenters","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CostCenterOut"},"title":"Response Listcostcenters"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["finance"],"summary":"Create Cost Center","operationId":"createCostCenter","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/cost-centers/{cost_center_id}":{"get":{"tags":["finance"],"summary":"Get Cost Center","operationId":"getCostCenter","parameters":[{"name":"cost_center_id","in":"path","required":true,"schema":{"type":"string","title":"Cost Center Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Cost Center","operationId":"updateCostCenter","parameters":[{"name":"cost_center_id","in":"path","required":true,"schema":{"type":"string","title":"Cost Center Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Cost Center","operationId":"deleteCostCenter","parameters":[{"name":"cost_center_id","in":"path","required":true,"schema":{"type":"string","title":"Cost Center Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/cost-center-rollup":{"get":{"tags":["finance"],"summary":"Cost Center Rollup","operationId":"costCenterRollup","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","description":"Período: YYYY, YYYY-Qn o YYYY-MM.","title":"Period"},"description":"Período: YYYY, YYYY-Qn o YYYY-MM."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostCenterRollupOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets":{"get":{"tags":["finance"],"summary":"List Fixed Assets","operationId":"listFixedAssets","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FixedAssetOut"},"title":"Response Listfixedassets"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["finance"],"summary":"Create Fixed Asset","operationId":"createFixedAsset","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixedAssetCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixedAssetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets/register":{"get":{"tags":["finance"],"summary":"Assets Register","operationId":"assetsRegister","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"as_of","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha de referencia (ISO). Defecto: hoy (UTC).","title":"As Of"},"description":"Fecha de referencia (ISO). Defecto: hoy (UTC)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetsRegisterSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets/refresh-prices":{"post":{"tags":["finance"],"summary":"Refresh Asset Prices","description":"Actualiza el valor de mercado de todos los activos crypto/etf de la org.","operationId":"refreshAssetPrices","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetPriceRefreshOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets/{asset_id}":{"get":{"tags":["finance"],"summary":"Get Fixed Asset","operationId":"getFixedAsset","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixedAssetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["finance"],"summary":"Update Fixed Asset","operationId":"updateFixedAsset","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixedAssetUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixedAssetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Fixed Asset","operationId":"deleteFixedAsset","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets/{asset_id}/amortization":{"get":{"tags":["finance"],"summary":"Asset Amortization","operationId":"assetAmortization","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"as_of","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha de referencia (ISO). Defecto: hoy (UTC).","title":"As Of"},"description":"Fecha de referencia (ISO). Defecto: hoy (UTC)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetAmortizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/finance/fixed-assets/{asset_id}/refresh-price":{"post":{"tags":["finance"],"summary":"Refresh Asset Price","description":"Actualiza el valor de mercado de un único activo crypto/etf.","operationId":"refreshAssetPrice","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetPriceRefreshOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients":{"get":{"tags":["clients"],"summary":"List Clients","operationId":"clients_list_clients","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por nombre, email o empresa.","title":"Q"},"description":"Filtra por nombre, email o empresa."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtra por estado activo.","title":"Is Active"},"description":"Filtra por estado activo."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientOut"},"title":"Response Clients List Clients"}}},"headers":{"X-Total-Count":{"description":"Total de clientes que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["clients"],"summary":"Create Client","operationId":"clients_create_client","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/clients/trash":{"get":{"tags":["clients"],"summary":"List Client Trash","description":"Lista clientes en papelera (soft-deleted).","operationId":"clients_list_client_trash","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientOut"},"title":"Response Clients List Client Trash"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}":{"get":{"tags":["clients"],"summary":"Get Client","operationId":"clients_get_client","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["clients"],"summary":"Update Client","operationId":"clients_update_client","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["clients"],"summary":"Delete Client","description":"Soft-delete: mueve el cliente a la papelera (restaurable).","operationId":"clients_delete_client","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/restore":{"post":{"tags":["clients"],"summary":"Restore Client","description":"Restaura un cliente de la papelera.","operationId":"clients_restore_client","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/purge":{"delete":{"tags":["clients"],"summary":"Purge Client","description":"Hard-delete permanente de un cliente en papelera (admin only).","operationId":"clients_purge_client","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/timeline":{"get":{"tags":["clients"],"summary":"List Client Timeline","description":"Todo lo que ha pasado con el cliente, en orden cronológico inverso.\n\nReúne facturas, presupuestos, gastos imputados, oportunidades, proyectos,\ncontratos, hitos de contrato y peticiones de soporte. Cada fuente se consulta\nSOLO si su app está disponible para la organización: una organización sin CRM\nno recibe un 403, recibe el resto de su timeline.\n\nPaginación EXACTA sobre una ventana acotada: `offset + limit` no puede pasar\nde 600 (422 `timeline_window_exceeded`). El porqué, en\n`clients/timeline_service.py`.","operationId":"listClientTimeline","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"types","in":"query","required":false,"schema":{"type":"array","items":{"enum":["invoice","quote","expense","deal","crm_note","project","contract","contract_milestone","request","meeting"],"type":"string"},"description":"Tipos a incluir. Vacío = todos los que la organización tenga disponibles.","default":[],"title":"Types"},"description":"Tipos a incluir. Vacío = todos los que la organización tenga disponibles."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientTimelineEntryOut"},"title":"Response Listclienttimeline"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/contacts":{"get":{"tags":["clients"],"summary":"List Client Contacts","description":"Personas de contacto del cliente (el principal primero). Requiere admin+.","operationId":"listClientContacts","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtra por estado activo.","title":"Is Active"},"description":"Filtra por estado activo."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientContactOut"},"title":"Response Listclientcontacts"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["clients"],"summary":"Create Client Contact","description":"Da de alta una persona de contacto. Requiere admin+.","operationId":"createClientContact","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientContactCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientContactOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/clients/{client_id}/contacts/{contact_id}":{"get":{"tags":["clients"],"summary":"Get Client Contact","operationId":"getClientContact","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","title":"Contact Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientContactOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["clients"],"summary":"Update Client Contact","operationId":"updateClientContact","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","title":"Contact Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientContactUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientContactOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}},"delete":{"tags":["clients"],"summary":"Delete Client Contact","description":"Borra la ficha. Para dar de baja a quien se fue, usa `is_active=false`.","operationId":"deleteClientContact","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"contact_id","in":"path","required":true,"schema":{"type":"string","title":"Contact Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/suppliers":{"get":{"tags":["suppliers"],"summary":"List Suppliers","description":"Lista de proveedores con stats agregadas (invoice_count, total_spend).","operationId":"suppliers_list_suppliers","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por nombre (case-insensitive).","title":"Name"},"description":"Filtra por nombre (case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplierWithStatsOut"},"title":"Response Suppliers List Suppliers"}}},"headers":{"X-Total-Count":{"description":"Total de proveedores que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["suppliers"],"summary":"Create Supplier","description":"Crea un proveedor en la organización. Requiere admin+.","operationId":"suppliers_create_supplier","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/suppliers/cleanup":{"post":{"tags":["suppliers"],"summary":"Cleanup Suppliers","description":"Elimina los proveedores fantasma (sin gastos ni facturas AP). Requiere admin+.\n\nUn proveedor se considera \"fantasma\" si:\n- No tiene ningún gasto con supplier_id == su id.\n- No tiene ningún gasto con vendor coincidente (case-insensitive, sin FK).\n- No tiene ninguna supplier_invoice vinculada (por FK o por nombre).\nDevuelve {deleted, kept}.","operationId":"cleanupSuppliers","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierCleanupResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/suppliers/{supplier_id}":{"get":{"tags":["suppliers"],"summary":"Get Supplier","operationId":"suppliers_get_supplier","parameters":[{"name":"supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Supplier Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["suppliers"],"summary":"Update Supplier","description":"Actualización parcial del proveedor. Requiere admin+.","operationId":"suppliers_update_supplier","parameters":[{"name":"supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Supplier Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["suppliers"],"summary":"Delete Supplier","description":"Elimina el proveedor permanentemente. Requiere admin+.","operationId":"suppliers_delete_supplier","parameters":[{"name":"supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Supplier Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/pipelines":{"get":{"tags":["crm"],"summary":"List Pipelines","operationId":"crm_list_pipelines","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PipelineOut"},"title":"Response Crm List Pipelines"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm"],"summary":"Create Pipeline","operationId":"crm_create_pipeline","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/pipelines/{pipeline_id}":{"get":{"tags":["crm"],"summary":"Get Pipeline","operationId":"crm_get_pipeline","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm"],"summary":"Update Pipeline","operationId":"crm_update_pipeline","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PipelineOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crm"],"summary":"Delete Pipeline","operationId":"crm_delete_pipeline","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/pipelines/{pipeline_id}/stages":{"get":{"tags":["crm"],"summary":"List Stages","operationId":"crm_list_stages","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StageOut"},"title":"Response Crm List Stages"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["crm"],"summary":"Create Stage","operationId":"crm_create_stage","parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/stages/{stage_id}":{"patch":{"tags":["crm"],"summary":"Update Stage","operationId":"crm_update_stage","parameters":[{"name":"stage_id","in":"path","required":true,"schema":{"type":"string","title":"Stage Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StageOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crm"],"summary":"Delete Stage","operationId":"crm_delete_stage","parameters":[{"name":"stage_id","in":"path","required":true,"schema":{"type":"string","title":"Stage Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/deals":{"get":{"tags":["crm"],"summary":"List Deals","operationId":"crm_list_deals","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"pipeline_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pipeline Id"}},{"name":"stage_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["open","won","lost"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"owner_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Id"}},{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en title (case-insensitive).","title":"Search"},"description":"Busca en title (case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DealOut"},"title":"Response Crm List Deals"}}},"headers":{"X-Total-Count":{"description":"Total de filas que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["crm"],"summary":"Create Deal","operationId":"crm_create_deal","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/deals/{deal_id}":{"get":{"tags":["crm"],"summary":"Get Deal","operationId":"crm_get_deal","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm"],"summary":"Update Deal","operationId":"crm_update_deal","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crm"],"summary":"Delete Deal","operationId":"crm_delete_deal","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/deals/{deal_id}/stage":{"patch":{"tags":["crm"],"summary":"Move Deal","operationId":"crm_move_deal","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealMoveIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/deals/{deal_id}/close":{"post":{"tags":["crm"],"summary":"Close Deal","operationId":"crm_close_deal","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealCloseIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/deals/{deal_id}/convert-to-project":{"post":{"tags":["crm"],"summary":"Convert Deal To Project","description":"Handoff Deal→Proyecto (idempotente): crea/enlaza un proyecto con el cliente\ndel deal. Requiere admin+.","operationId":"crm_convert_deal_to_project","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/leads":{"get":{"tags":["crm"],"summary":"List Leads","operationId":"crm_list_leads","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["new","contacted","qualified","converted","lost"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en name, email o company (case-insensitive).","title":"Search"},"description":"Busca en name, email o company (case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LeadOut"},"title":"Response Crm List Leads"}}},"headers":{"X-Total-Count":{"description":"Total de filas que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["crm"],"summary":"Create Lead","operationId":"crm_create_lead","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/leads/{lead_id}":{"get":{"tags":["crm"],"summary":"Get Lead","operationId":"crm_get_lead","parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"string","title":"Lead Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["crm"],"summary":"Update Lead","operationId":"crm_update_lead","parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"string","title":"Lead Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["crm"],"summary":"Delete Lead","operationId":"crm_delete_lead","parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"string","title":"Lead Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/leads/{lead_id}/convert":{"post":{"tags":["crm"],"summary":"Convert Lead","operationId":"crm_convert_lead","parameters":[{"name":"lead_id","in":"path","required":true,"schema":{"type":"string","title":"Lead Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/activities":{"get":{"tags":["crm"],"summary":"List Activities","operationId":"crm_list_activities","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"deal_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deal Id"}},{"name":"lead_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lead Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CrmActivityOut"},"title":"Response Crm List Activities"}}},"headers":{"X-Total-Count":{"description":"Total de filas que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["crm"],"summary":"Create Activity","operationId":"crm_create_activity","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmActivityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/crm/activities/{activity_id}":{"delete":{"tags":["crm"],"summary":"Delete Activity","operationId":"crm_delete_activity","parameters":[{"name":"activity_id","in":"path","required":true,"schema":{"type":"string","title":"Activity Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/analytics/pipeline":{"get":{"tags":["crm"],"summary":"Crm Pipeline Metrics","operationId":"crm_crm_pipeline_metrics","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"pipeline_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pipeline Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PipelineMetricOut"},"title":"Response Crm Crm Pipeline Metrics"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/analytics/funnel":{"get":{"tags":["crm"],"summary":"Crm Funnel","operationId":"crm_crm_funnel","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"pipeline_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pipeline Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FunnelStageOut"},"title":"Response Crm Crm Funnel"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/crm/analytics/summary":{"get":{"tags":["crm"],"summary":"Crm Summary","operationId":"crm_crm_summary","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrmSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/departments":{"get":{"tags":["departments"],"summary":"List Departments","description":"Lista paginada de departamentos. Requiere member+.","operationId":"departments_list_departments","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentOut"},"title":"Response Departments List Departments"}}},"headers":{"X-Total-Count":{"description":"Total de departamentos de la organización (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["departments"],"summary":"Create Department","description":"Crea un departamento en la organización. Requiere manager+.","operationId":"departments_create_department","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/departments/{department_id}":{"get":{"tags":["departments"],"summary":"Get Department","operationId":"departments_get_department","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["departments"],"summary":"Update Department","description":"Actualización parcial del departamento. Requiere manager+.","operationId":"departments_update_department","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["departments"],"summary":"Delete Department","description":"Elimina el departamento permanentemente. employees.department_id → NULL.\nEl string employees.department se conserva. Requiere manager+.","operationId":"departments_delete_department","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/departments/{department_id}/projects":{"get":{"tags":["departments"],"summary":"List Department Projects","description":"Proyectos del departamento (permiso DERIVADO proyecto↔departamento).\n\nmanager+ ve siempre; un member normal solo si pertenece al departamento.\nAditivo: no altera el acceso org-wide de `/projects`.","operationId":"departments_list_department_projects","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectOut"},"title":"Response Departments List Department Projects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/departments/{department_id}/visibility-projects":{"get":{"tags":["departments"],"summary":"List Department Visibility Projects","description":"Proyectos a los que ESTE departamento da acceso (set M:N de visibilidad,\ndirección inversa a la gestión desde el proyecto). admin/manager.\n\nOJO: distinto de `/departments/{id}/projects` (Wave 5, proyectos cuyo\n`department_id` \"dueño\" es este departamento). Aquí se gestiona qué proyectos\nRESTRINGIDOS ve el departamento — la misma fuente de verdad que la cara\n\"proyecto\" en `/projects/{id}/visibility-departments`.","operationId":"departments_list_department_visibility_projects","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentVisibilityProjectOut"},"title":"Response Departments List Department Visibility Projects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["departments"],"summary":"Set Department Visibility Projects","description":"Reemplaza el set de proyectos a los que este departamento da acceso.\nadmin/manager. Acepta `projects` (con nivel por proyecto) o `project_ids`\n(forma histórica, equivale a `write`). Cada project_id debe pertenecer a la\nMISMA org (422 si no).","operationId":"departments_set_department_visibility_projects","parameters":[{"name":"department_id","in":"path","required":true,"schema":{"type":"string","title":"Department Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartmentVisibilityProjectsUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DepartmentVisibilityProjectOut"},"title":"Response Departments Set Department Visibility Projects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/employees":{"get":{"tags":["employees"],"summary":"List Employees","operationId":"employees_list_employees","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por nombre, apellidos o email.","title":"Q"},"description":"Filtra por nombre, apellidos o email."},{"name":"department","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por departamento.","title":"Department"},"description":"Filtra por departamento."},{"name":"is_active","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtra por estado activo.","title":"Is Active"},"description":"Filtra por estado activo."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmployeeOut"},"title":"Response Employees List Employees"}}},"headers":{"X-Total-Count":{"description":"Total de empleados que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["employees"],"summary":"Create Employee","operationId":"employees_create_employee","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/employees/bulk":{"post":{"tags":["employees"],"summary":"Bulk Employee Action","operationId":"employees_bulk_employee_action","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeBulkActionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeBulkActionResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/employees/department-stats":{"get":{"tags":["employees"],"summary":"Employee Department Stats","operationId":"employees_employee_department_stats","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EmployeeDepartmentStatOut"},"title":"Response Employees Employee Department Stats"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/employees/{employee_id}":{"get":{"tags":["employees"],"summary":"Get Employee","operationId":"employees_get_employee","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["employees"],"summary":"Update Employee","operationId":"employees_update_employee","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["employees"],"summary":"Delete Employee","operationId":"employees_delete_employee","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/leave-requests":{"get":{"tags":["hr"],"summary":"List Leave Requests","operationId":"listLeaveRequests","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","approved","rejected"],"type":"string"},{"type":"null"}],"description":"Filtra por estado: pending, approved, rejected.","title":"Status"},"description":"Filtra por estado: pending, approved, rejected."},{"name":"employee_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por ID de empleado.","title":"Employee Id"},"description":"Filtra por ID de empleado."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha inicio >= (start_date).","title":"From"},"description":"Fecha inicio >= (start_date)."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha fin <= (end_date).","title":"To"},"description":"Fecha fin <= (end_date)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LeaveRequestOut"},"title":"Response Listleaverequests"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["hr"],"summary":"Create Leave Request","operationId":"createLeaveRequest","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/leave-balances":{"get":{"tags":["hr"],"summary":"Leave Balances","operationId":"leaveBalances","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"year","in":"query","required":true,"schema":{"type":"integer","description":"Año para el que se calculan los balances (YYYY).","title":"Year"},"description":"Año para el que se calculan los balances (YYYY)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LeaveBalanceOut"},"title":"Response Leavebalances"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/absence-calendar":{"get":{"tags":["hr"],"summary":"Absence Calendar","operationId":"absenceCalendar","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inicio del rango (inclusive).","title":"Start"},"description":"Inicio del rango (inclusive)."},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Fin del rango (inclusive).","title":"End"},"description":"Fin del rango (inclusive)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AbsenceOut"},"title":"Response Absencecalendar"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/leave-requests/{request_id}":{"get":{"tags":["hr"],"summary":"Get Leave Request","operationId":"getLeaveRequest","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["hr"],"summary":"Cancel Leave Request","operationId":"cancelLeaveRequest","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/leave-requests/{request_id}/approve":{"post":{"tags":["hr"],"summary":"Approve Leave Request","operationId":"approveLeaveRequest","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveDecisionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/leave-requests/{request_id}/reject":{"post":{"tags":["hr"],"summary":"Reject Leave Request","operationId":"rejectLeaveRequest","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveDecisionIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaveRequestOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/timesheets/week":{"get":{"tags":["hr"],"summary":"Get Timesheet Week","operationId":"getTimesheetWeek","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"week_start","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Cualquier fecha de la semana (YYYY-MM-DD); se normaliza al primer día de esa semana según `week_start_day` de la organización. El submit sí exige ese primer día exacto.","title":"Week Start"},"description":"Cualquier fecha de la semana (YYYY-MM-DD); se normaliza al primer día de esa semana según `week_start_day` de la organización. El submit sí exige ese primer día exacto."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ID del usuario (admin+ puede especificar otro usuario).","title":"User Id"},"description":"ID del usuario (admin+ puede especificar otro usuario)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimesheetWeekOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/timesheets/submit":{"post":{"tags":["hr"],"summary":"Submit Timesheet","operationId":"submitTimesheet","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimesheetSubmitIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimesheetPeriodOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/timesheets":{"get":{"tags":["hr"],"summary":"List Timesheets","operationId":"listTimesheets","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["draft","submitted","approved","rejected"],"type":"string"},{"type":"null"}],"description":"Filtra por estado: draft, submitted, approved, rejected.","title":"Status"},"description":"Filtra por estado: draft, submitted, approved, rejected."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por usuario (admin+).","title":"User Id"},"description":"Filtra por usuario (admin+)."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"week_start >= fecha.","title":"From"},"description":"week_start >= fecha."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"week_start <= fecha.","title":"To"},"description":"week_start <= fecha."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimesheetPeriodOut"},"title":"Response Listtimesheets"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/timesheets/overview":{"get":{"tags":["hr"],"summary":"List Timesheet Overview","operationId":"listTimesheetOverview","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por usuario (manager+; un member solo puede pedir el suyo).","title":"User Id"},"description":"Filtra por usuario (manager+; un member solo puede pedir el suyo)."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"week_start >= fecha (primer día de semana, inclusive).","title":"From"},"description":"week_start >= fecha (primer día de semana, inclusive)."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"week_start <= fecha (primer día de semana, inclusive).","title":"To"},"description":"week_start <= fecha (primer día de semana, inclusive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimesheetOverviewRowOut"},"title":"Response Listtimesheetoverview"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/timesheets/{period_id}/approve":{"post":{"tags":["hr"],"summary":"Approve Timesheet","operationId":"approveTimesheet","parameters":[{"name":"period_id","in":"path","required":true,"schema":{"type":"string","title":"Period Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimesheetPeriodOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/timesheets/{period_id}/reject":{"post":{"tags":["hr"],"summary":"Reject Timesheet","operationId":"rejectTimesheet","parameters":[{"name":"period_id","in":"path","required":true,"schema":{"type":"string","title":"Period Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimesheetPeriodOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/payroll/runs":{"get":{"tags":["payroll"],"summary":"List Payroll Runs","operationId":"listPayrollRuns","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PayrollRunOut"},"title":"Response Listpayrollruns"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["payroll"],"summary":"Create Payroll Run","operationId":"createPayrollRun","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayrollRunCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayrollRunOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/payroll/runs/{run_id}/process":{"post":{"tags":["payroll"],"summary":"Process Payroll Run","operationId":"processPayrollRun","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayrollRunWithPayslipsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/payroll/runs/{run_id}":{"get":{"tags":["payroll"],"summary":"Get Payroll Run","operationId":"getPayrollRun","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayrollRunWithPayslipsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["payroll"],"summary":"Delete Payroll Run","operationId":"deletePayrollRun","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/payroll/runs/{run_id}/payslips":{"get":{"tags":["payroll"],"summary":"List Payslips","operationId":"listPayslips","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PayslipOut"},"title":"Response Listpayslips"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/payroll/payslips/{payslip_id}":{"get":{"tags":["payroll"],"summary":"Get Payslip","operationId":"getPayslip","parameters":[{"name":"payslip_id","in":"path","required":true,"schema":{"type":"string","title":"Payslip Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayslipOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/schedules":{"get":{"tags":["payroll"],"summary":"Schedules Overview","operationId":"schedulesOverview","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"weekday","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":6,"minimum":0},{"type":"null"}],"description":"Filtra por día de la semana (0=Lunes … 6=Domingo).","title":"Weekday"},"description":"Filtra por día de la semana (0=Lunes … 6=Domingo)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SchedulesOverviewItemOut"},"title":"Response Schedulesoverview"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/hr/schedules/employees/{employee_id}":{"get":{"tags":["payroll"],"summary":"List Work Schedule","operationId":"listWorkSchedule","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkScheduleEmployeeSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["payroll"],"summary":"Set Work Schedule","operationId":"setWorkSchedule","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkScheduleBulkSetIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkScheduleEmployeeSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/schedules/employees/{employee_id}/entries":{"post":{"tags":["payroll"],"summary":"Add Schedule Entry","operationId":"addScheduleEntry","parameters":[{"name":"employee_id","in":"path","required":true,"schema":{"type":"string","title":"Employee Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkScheduleEntryIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkScheduleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/hr/schedules/entries/{entry_id}":{"delete":{"tags":["payroll"],"summary":"Remove Schedule Entry","operationId":"removeScheduleEntry","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/time-entries":{"get":{"tags":["time-entries"],"summary":"List Time Entries","operationId":"time-entries_list_time_entries","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por proyecto.","title":"Project Id"},"description":"Filtra por proyecto."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por autor. member: solo su propio id (otro autor = forbidden); admin/owner: cualquier autor u omitido = toda la organización.","title":"User Id"},"description":"Filtra por autor. member: solo su propio id (otro autor = forbidden); admin/owner: cualquier autor u omitido = toda la organización."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha imputada desde (inclusive).","title":"From"},"description":"Fecha imputada desde (inclusive)."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha imputada hasta (inclusive).","title":"To"},"description":"Fecha imputada hasta (inclusive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntryOut"},"title":"Response Time-Entries List Time Entries"}}},"headers":{"X-Total-Count":{"description":"Total de registros que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["time-entries"],"summary":"Create Time Entry","operationId":"time-entries_create_time_entry","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/time-entries":{"post":{"tags":["time-entries"],"summary":"Create Project Time Entry","operationId":"time-entries_create_project_time_entry","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryProjectCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/time-entries/start":{"post":{"tags":["time-entries"],"summary":"Start Time Entry","operationId":"time-entries_start_time_entry","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryStartIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryRunningOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/time-entries/active":{"get":{"tags":["time-entries"],"summary":"Get Active Time Entry","operationId":"time-entries_get_active_time_entry","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveTimerOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/time-entries/summary":{"get":{"tags":["time-entries"],"summary":"Get Time Entries Summary","operationId":"time-entries_get_time_entries_summary","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por proyecto.","title":"Project Id"},"description":"Filtra por proyecto."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por autor. member: solo su propio id (otro autor = forbidden); admin/owner: cualquier autor u omitido = toda la organización.","title":"User Id"},"description":"Filtra por autor. member: solo su propio id (otro autor = forbidden); admin/owner: cualquier autor u omitido = toda la organización."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha imputada desde (inclusive).","title":"From"},"description":"Fecha imputada desde (inclusive)."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha imputada hasta (inclusive).","title":"To"},"description":"Fecha imputada hasta (inclusive)."},{"name":"group_by","in":"query","required":false,"schema":{"enum":["project","user","none"],"type":"string","description":"Dimensión de desglose de `groups`.","default":"none","title":"Group By"},"description":"Dimensión de desglose de `groups`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntrySummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/time-entries/{entry_id}/stop":{"post":{"tags":["time-entries"],"summary":"Stop Time Entry","operationId":"time-entries_stop_time_entry","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/time-entries/{entry_id}":{"get":{"tags":["time-entries"],"summary":"Get Time Entry","operationId":"time-entries_get_time_entry","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["time-entries"],"summary":"Update Time Entry","operationId":"time-entries_update_time_entry","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["time-entries"],"summary":"Delete Time Entry","operationId":"time-entries_delete_time_entry","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","title":"Entry Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/usage/me":{"get":{"tags":["metering"],"summary":"My Usage","description":"Mi consumo del mes en curso, un item por recurso.\n\nSalen TODOS los recursos, incluidos los que esta edición no ofrece — con\n`included: null`. Es lo que deja a la pantalla decir «Kern es de Pro» en vez\nde no enseñar nada: un hueco en blanco se lee como que algo falló.","operationId":"myUsage","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageMeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/usage":{"get":{"tags":["metering"],"summary":"Usage Breakdown","description":"Quién ha gastado qué, de mayor a menor. Admin+.\n\nSolo aparece quien tiene consumo: un listado con ochenta personas a cero para\nencontrar a las tres que gastan es un listado que nadie lee, y el que falta\nse deduce.","operationId":"usageBreakdown","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"resource","in":"query","required":true,"schema":{"enum":["ia_tokens","video_minutos"],"type":"string","title":"Resource"}},{"name":"period","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}$"},{"type":"null"}],"description":"`YYYY-MM`. Por defecto, el mes en curso.","title":"Period"},"description":"`YYYY-MM`. Por defecto, el mes en curso."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageBreakdownOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/usage/history":{"get":{"tags":["metering"],"summary":"Usage History","description":"El total de la organización, mes a mes. Admin+.\n\nLos meses SIN consumo no salen —no hay fila que sumar—, así que quien pinte\nla serie tiene que rellenar los huecos con cero: una gráfica que se salta un\nmes vacío miente sobre la pendiente.","operationId":"usageHistory","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"resource","in":"query","required":true,"schema":{"enum":["ia_tokens","video_minutos"],"type":"string","title":"Resource"}},{"name":"months","in":"query","required":false,"schema":{"type":"integer","maximum":24,"minimum":1,"default":12,"title":"Months"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageHistoryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/workforce/capacity":{"get":{"tags":["workforce"],"summary":"Workforce Capacity","description":"Conciliación de capacidad de la organización para una semana.\n\nPor cada miembro: horas esperadas (modelo híbrido: override manual → horario\ndel empleado → horario del departamento → horario por defecto de la org),\nfestivos y ausencias que descuentan, capacidad neta, horas imputadas y delta\n(imputado − neto). Requiere manager+. El coste/tarifa NO se expone aquí, solo horas.","operationId":"workforceCapacity","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"week_start","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Cualquier fecha de la semana (YYYY-MM-DD); se normaliza al primer día de esa semana según `week_start_day` de la organización (0 lunes, 5 sábado, 6 domingo), no a un lunes fijo. Devuelve la conciliación horas esperadas vs imputadas vs ausencias de todos los miembros para esa semana.","title":"Week Start"},"description":"Cualquier fecha de la semana (YYYY-MM-DD); se normaliza al primer día de esa semana según `week_start_day` de la organización (0 lunes, 5 sábado, 6 domingo), no a un lunes fijo. Devuelve la conciliación horas esperadas vs imputadas vs ausencias de todos los miembros para esa semana."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/workforce/schedule":{"get":{"tags":["workforce"],"summary":"Workforce Get Schedule","description":"Horario laboral por defecto de la organización (base del modelo híbrido).\n\nLo puede leer cualquier miembro de la organización.","operationId":"workforceGetSchedule","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgScheduleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["workforce"],"summary":"Workforce Set Schedule","description":"Fija (reemplaza) el horario por defecto de la organización. Requiere manager+.\n\n`weekly_schedule: null` borra el horario por defecto.","operationId":"workforceSetSchedule","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgScheduleUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgScheduleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/documents/trash":{"get":{"tags":["documents"],"summary":"List Document Trash","description":"Lista documentos en papelera (soft-deleted).","operationId":"documents_list_document_trash","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentOut"},"title":"Response Documents List Document Trash"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents":{"get":{"tags":["documents"],"summary":"List Documents","operationId":"documents_list_documents","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por proyecto.","title":"Project Id"},"description":"Filtra por proyecto."},{"name":"parent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por documento padre (hijos directos).","title":"Parent Id"},"description":"Filtra por documento padre (hijos directos)."},{"name":"folder_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por carpeta.","title":"Folder Id"},"description":"Filtra por carpeta."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"enum":["page","file"],"type":"string"},{"type":"null"}],"description":"Filtra por tipo: `page` (markdown) o `file` (fichero).","title":"Kind"},"description":"Filtra por tipo: `page` (markdown) o `file` (fichero)."},{"name":"is_template","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"`true` = solo plantillas; `false` = solo lo que no lo es; omitido = todo.","title":"Is Template"},"description":"`true` = solo plantillas; `false` = solo lo que no lo es; omitido = todo."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por título (case-insensitive).","title":"Q"},"description":"Búsqueda por título (case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentOut"},"title":"Response Documents List Documents"}}},"headers":{"X-Total-Count":{"description":"Total de documentos que cumplen los filtros (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["documents"],"summary":"Create Document","operationId":"documents_create_document","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/documents/upload":{"post":{"tags":["documents"],"summary":"Upload Document File","description":"Sube un fichero como documento (`multipart/form-data`, campo `file`).\n\nMáximo 10 MB; imágenes, PDF y ofimática (docx/xlsx/pptx/csv/txt/zip). Se\nvalida el CONTENIDO real, no solo la extensión.","operationId":"uploadDocumentFile","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_uploadDocumentFile"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Request Entity Too Large"}}}},"/api/v1/organizations/{org_id}/documents/from-template":{"post":{"tags":["documents"],"summary":"Create Document From Template","description":"Crea un documento copiando una plantilla de la organización.\n\nSe declara ANTES que `/documents/{document_id}` a propósito: aunque hoy no\nhaya un `POST` sobre esa ruta y no puedan chocar, FastAPI resuelve por orden\nde declaración y `from-template` encajaría como `document_id` el día que lo\nhaya.","operationId":"createDocumentFromTemplate","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentFromTemplateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/documents/{document_id}":{"get":{"tags":["documents"],"summary":"Get Document","operationId":"documents_get_document","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["documents"],"summary":"Update Document","operationId":"documents_update_document","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["documents"],"summary":"Delete Document","description":"Soft-delete: mueve el documento a la papelera (restaurable).","operationId":"documents_delete_document","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/download":{"get":{"tags":["documents"],"summary":"Download Document File","description":"Descarga autenticada del fichero de un documento `kind='file'`.","operationId":"downloadDocumentFile","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"El fichero.","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/restore":{"post":{"tags":["documents"],"summary":"Restore Document","description":"Restaura un documento de la papelera.","operationId":"documents_restore_document","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/purge":{"delete":{"tags":["documents"],"summary":"Purge Document","description":"Hard-delete permanente de un documento en papelera (admin only).","operationId":"documents_purge_document","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/versions":{"get":{"tags":["documents"],"summary":"List Document Versions","operationId":"documents_list_document_versions","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentVersionOut"},"title":"Response Documents List Document Versions"}}},"headers":{"X-Total-Count":{"description":"Total de versiones del documento (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/versions/{version_id}":{"get":{"tags":["documents"],"summary":"Get Document Version","operationId":"documents_get_document_version","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentVersionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/versions/{version_id}/restore":{"post":{"tags":["documents"],"summary":"Restore Document Version","operationId":"documents_restore_document_version","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/template":{"post":{"tags":["documents"],"summary":"Mark Document As Template","description":"Marca el documento como plantilla de la organización. Requiere admin+.\n\n200 y no 201: no nace un recurso nuevo, cambia el estado de uno que ya\nexiste. Idempotente.","operationId":"markDocumentAsTemplate","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["documents"],"summary":"Retire Document Template","description":"Retira la plantilla (el documento sigue existiendo). Requiere admin+.","operationId":"retireDocumentTemplate","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/publication":{"get":{"tags":["documents"],"summary":"Get Document Publication","description":"Estado de publicación del documento, CON el enlace si está publicado.\n\nAquí está la diferencia deliberada con `POST …/quotes/{id}/share`, que solo\nenseña el enlace una vez: el de un documento se puede volver a consultar,\nporque se DERIVA de `APP_SECRET` en vez de guardarse. El porqué completo, en\nla cabecera de `publishing.py`. Requiere admin+.","operationId":"getDocumentPublication","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPublicationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["documents"],"summary":"Publish Document","description":"Publica el documento y devuelve el enlace público. Requiere admin+.\n\n200 y no 201 porque es idempotente: la publicación es un singleton del\ndocumento y publicar dos veces devuelve el mismo enlace. Con `rotate: true`\nel anterior muere para siempre.","operationId":"publishDocument","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPublishIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentPublicationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["documents"],"summary":"Unpublish Document","description":"Apaga el enlace público (idempotente). Requiere admin+.\n\nNO destruye el enlace: volver a publicar devuelve el mismo. Para matarlo de\nverdad —un enlace filtrado— hay que publicar con `rotate: true`.","operationId":"unpublishDocument","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/documents/{token}":{"get":{"tags":["documents"],"summary":"Get Public Document","description":"Documento publicado, sin autenticación. La credencial es el token.\n\nUn solo 404 para todo (token inexistente, despublicado, rotado, documento en\npapelera): al visitante anónimo no se le confirma que un token haya\nexistido. Devuelve SOLO título, contenido y fecha de modificación.","operationId":"getPublicDocument","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDocumentOut"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/comments":{"get":{"tags":["document-comments"],"summary":"List Document Comments","operationId":"document-comments_list_document_comments","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentCommentOut"},"title":"Response Document-Comments List Document Comments"}}},"headers":{"X-Total-Count":{"description":"Total de comentarios del documento (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["document-comments"],"summary":"Create Document Comment","operationId":"document-comments_create_document_comment","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/documents/{document_id}/comments/{comment_id}":{"patch":{"tags":["document-comments"],"summary":"Update Document Comment","operationId":"document-comments_update_document_comment","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCommentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["document-comments"],"summary":"Delete Document Comment","operationId":"document-comments_delete_document_comment","parameters":[{"name":"document_id","in":"path","required":true,"schema":{"type":"string","title":"Document Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/dashboard/stats":{"get":{"tags":["dashboard"],"summary":"Dashboard Stats","operationId":"dashboard_dashboard_stats","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardStatsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/dashboard/activity":{"get":{"tags":["dashboard"],"summary":"Dashboard Activity","operationId":"dashboard_dashboard_activity","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DashboardActivityItemOut"},"title":"Response Dashboard Dashboard Activity"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/api-keys":{"get":{"tags":["api-keys"],"summary":"List Api Keys","operationId":"api-keys_list_api_keys","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por proyecto (UUID). Omitir = todas las keys.","title":"Project Id"},"description":"Filtra por proyecto (UUID). Omitir = todas las keys."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyOut"},"title":"Response Api-Keys List Api Keys"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["api-keys"],"summary":"Create Api Key","operationId":"api-keys_create_api_key","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/api-keys/{key_id}":{"delete":{"tags":["api-keys"],"summary":"Revoke Api Key","operationId":"api-keys_revoke_api_key","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/vault":{"get":{"tags":["vault"],"summary":"List Vault Items","operationId":"vault_list_vault_items","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VaultItemOut"},"title":"Response Vault List Vault Items"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["vault"],"summary":"Create Vault Item","operationId":"vault_create_vault_item","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultItemCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultItemOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/vault/{item_id}/reveal":{"get":{"tags":["vault"],"summary":"Reveal Vault Secret","operationId":"vault_reveal_vault_secret","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultSecretOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/vault/{item_id}":{"patch":{"tags":["vault"],"summary":"Update Vault Item","operationId":"vault_update_vault_item","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultItemUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultItemOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["vault"],"summary":"Delete Vault Item","operationId":"vault_delete_vault_item","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/store/apps":{"get":{"tags":["store"],"summary":"List Organization Store Apps","description":"El catálogo con lo que esta organización tiene instalado (cualquier miembro).","operationId":"store_list_organization_store_apps","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StoreAppForOrganizationOut"},"title":"Response Store List Organization Store Apps"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/store/apps/{app_key}/install":{"post":{"tags":["store"],"summary":"Install Store App","description":"Instala la app en la organización (owner/admin). 201 si la creó, 200 si ya estaba.","operationId":"store_install_store_app","parameters":[{"name":"app_key","in":"path","required":true,"schema":{"type":"string","title":"App Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppForOrganizationOut"}}}},"200":{"description":"La app ya estaba instalada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppForOrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["store"],"summary":"Uninstall Store App","description":"Desinstala la app (owner/admin). 422 `app_has_dependencies` si hay vínculos vivos.","operationId":"store_uninstall_store_app","parameters":[{"name":"app_key","in":"path","required":true,"schema":{"type":"string","title":"App Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppForOrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/store/apps/{app_key}/trial":{"post":{"tags":["store"],"summary":"Start Store App Trial","description":"Arranca la prueba gratuita de una app (owner/admin). Una vez por organización.\n\nNO pide tarjeta y NO necesita Stripe configurado: es una regla del modelo de\nnegocio («trial sin tarjeta»), no un atajo. 409 `trial_already_used` si esta\norganización ya la gastó.","operationId":"store_start_store_app_trial","parameters":[{"name":"app_key","in":"path","required":true,"schema":{"type":"string","title":"App Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppSubscriptionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/organizations/{org_id}/store/apps/{app_key}/subscribe":{"post":{"tags":["store"],"summary":"Subscribe Store App","description":"Suscribe la organización al tramo premium de una app (owner/admin).\n\nDevuelve la suscripción ya activa cuando la app se pudo añadir como item a\nla suscripción de Stripe que la organización ya tenía, o un `checkout_url`\ncuando es su primera compra y hace falta una tarjeta. Exactamente uno de los\ndos campos viene relleno.","operationId":"store_subscribe_store_app","parameters":[{"name":"app_key","in":"path","required":true,"schema":{"type":"string","title":"App Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreSubscribeIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreSubscribeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}},"delete":{"tags":["store"],"summary":"Unsubscribe Store App","description":"Cancela al fin de periodo (owner/admin): sigue viva hasta `renueva_en`.\n\nNo corta en seco lo que ya está cobrado (§8) y no depende de la flag de\nrollout: quien compró tiene que poder darse de baja siempre.","operationId":"store_unsubscribe_store_app","parameters":[{"name":"app_key","in":"path","required":true,"schema":{"type":"string","title":"App Key"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppSubscriptionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/v1/organizations/{org_id}/store/subscriptions":{"get":{"tags":["store"],"summary":"List Organization Subscriptions","description":"Lo que la organización paga o está probando — la página «Mis apps».\n\nCualquier miembro, igual que el catálogo con estado. Incluye las MUERTAS\n(`viva: false`): un trial caducado es justo lo que esa pantalla tiene que\npoder enseñar.","operationId":"store_list_organization_subscriptions","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StoreAppSubscriptionOut"},"title":"Response Store List Organization Subscriptions"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/store/apps":{"get":{"tags":["store"],"summary":"List Store Apps","description":"Catálogo público de apps: modelo, precios y reparto gratis/premium.","operationId":"store_list_store_apps","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/StoreAppOut"},"type":"array","title":"Response Store List Store Apps"}}}}}}},"/api/v1/organizations/{org_id}/notifications/stream":{"get":{"tags":["notifications","notifications"],"summary":"Stream de notificaciones en tiempo real (SSE)","description":"Real-time Server-Sent Events stream for the authenticated user's notifications.\n\nRegisters a per-user SSE stream that emits:\n- An initial ``unread`` event with the current badge count.\n- A ``notification`` event for every new notification persisted after\n  the connection was opened.\n- A heartbeat comment (`: ping`) every ~15 s.\n\nReal-time updates arrive via a Redis ping (``notif:{user_id}``); a DB poll\nevery ~20 s is a fallback for when Redis is unavailable. Short-lived sessions\nonly (never holds a connection across sleep). Stops cleanly on client\ndisconnect (``await request.is_disconnected()`` guard in every slice).","operationId":"streamNotifications","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Server-Sent Events stream (text/event-stream). The stream never closes on its own — the client should keep the connection open and reconnect if needed.\n\n**Event shapes**\n\n```\nretry: 30000\nevent: unread\ndata: {\"unread\": <int>}\n\nevent: notification\ndata: <Notification JSON — same shape as GET /notifications items>\n\n: ping\n\n```\n\n- `retry` — reconnection delay in ms (30000), sent with the first frame. It keeps the browser's automatic reconnect from landing inside a deploy window (where it would get a 502, which closes an `EventSource` permanently).\n- `unread` — emitted once on connect with the current unread count.\n- `notification` — emitted for each new notification created after the connection was established (real-time via Redis ping; ~20 s poll fallback).\n- `: ping` — heartbeat comment every ~15 s to keep proxies alive.\n\nAuth: same cookie session as the other /notifications endpoints. Use native `EventSource` on the frontend (openapi-fetch does not consume SSE).","content":{"text/event-stream":{"schema":{"type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notifications":{"get":{"tags":["notifications"],"summary":"List Notifications","operationId":"listNotifications","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de notificaciones a devolver (1..200, por defecto 50).","default":50,"title":"Limit"},"description":"Máximo de notificaciones a devolver (1..200, por defecto 50)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NotificationOut"},"title":"Response Listnotifications"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notifications/unread-count":{"get":{"tags":["notifications"],"summary":"Unread Notification Count","operationId":"unreadNotificationCount","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__notifications__schemas__UnreadCountOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notifications/read-all":{"post":{"tags":["notifications"],"summary":"Mark All Notifications Read","operationId":"markAllNotificationsRead","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__notifications__schemas__UnreadCountOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Notification Read","operationId":"markNotificationRead","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notifications/{notification_id}":{"delete":{"tags":["notifications"],"summary":"Delete Notification","operationId":"deleteNotification","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/notification-preferences":{"get":{"tags":["notifications"],"summary":"List Notification Preferences","description":"Preferencia EFECTIVA del usuario actual en cada categoría del registro.\n\nModelo opt-out: sin fila guardada, la categoría sale con todo activado. Cada\nelemento lleva además sus `channel_limits` (eventos con techo declarado).","operationId":"listNotificationPreferences","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NotificationPreferenceOut"},"title":"Response Listnotificationpreferences"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["notifications"],"summary":"Upsert Notification Preference","description":"Ajusta (upsert) la preferencia de UNA categoría del usuario actual.\n\nCada usuario gestiona solo las suyas (el autor es siempre `tenant.user`).","operationId":"upsertNotificationPreference","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferenceIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferenceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/push-subscriptions":{"post":{"tags":["notifications"],"summary":"Register Push Subscription","description":"Registra la suscripción Web Push de este navegador para el usuario actual.\nIdempotente por `endpoint` (re-suscribirse no duplica).","operationId":"registerPushSubscription","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSubscriptionIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSubscriptionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["notifications"],"summary":"Delete Push Subscription","description":"Da de baja la suscripción de este navegador (por endpoint) del usuario.","operationId":"deletePushSubscription","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushSubscriptionDeleteIn"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/device-tokens":{"post":{"tags":["notifications"],"summary":"Register Device Token","description":"Registra el token de push nativo (APNs/FCM) de este dispositivo para el\nusuario actual. Idempotente por `token` (re-registrar no duplica).","operationId":"registerDeviceToken","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["notifications"],"summary":"Delete Device Token","description":"Da de baja el token de este dispositivo (por token) del usuario.","operationId":"deleteDeviceToken","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenDeleteIn"}}}},"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/unsubscribe":{"get":{"tags":["notifications"],"summary":"Unsubscribe Email Category","description":"Da de baja el EMAIL de una categoría (clic desde el email). Verifica solo\nel token firmado; conserva in_app/web_push. Idempotente (400 si el token no\nes válido).","operationId":"unsubscribeEmailCategory","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","description":"Token firmado de baja.","title":"Token"},"description":"Token firmado de baja."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Unsubscribeemailcategory"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["notifications"],"summary":"Unsubscribe Email Category Post","description":"Variante POST (RFC 8058 List-Unsubscribe-Post = One-Click).","operationId":"unsubscribeEmailCategoryPost","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","description":"Token firmado de baja.","title":"Token"},"description":"Token firmado de baja."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Unsubscribeemailcategorypost"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/vapid-public-key":{"get":{"tags":["notifications"],"summary":"Get Vapid Public Key","description":"Clave pública VAPID (applicationServerKey) para suscribirse a Web Push.\n\nPública por diseño (la clave pública VAPID se expone al navegador). Sin auth.\nDevuelve `\"\"` si el servidor no tiene Web Push configurado — el front lo trata\ncomo \"push deshabilitado\" y no ofrece suscripción.","operationId":"getVapidPublicKey","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VapidPublicKeyOut"}}}}}}},"/api/v1/organizations/{org_id}/search":{"get":{"tags":["search"],"summary":"Global Search","description":"Búsqueda cross-entidad dentro de la organización.\n\nBusca simultáneamente en tareas, proyectos, clientes, facturas, gastos,\ndocumentos, empleados, proveedores y miembros.  Devuelve una lista plana\nde hits con un campo `type` como discriminador.  Cap máximo de 5 resultados\npor tipo (se ignora `limit` > 5 a nivel de tipo; el param sigue siendo útil\npara reducir a menos).  Las entidades financieras (invoice, expense,\nsupplier) solo aparecen si el rol del llamante es member o superior.","operationId":"search_global_search","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"q","in":"query","required":true,"schema":{"type":"string","description":"Término de búsqueda (mínimo 1 carácter).","title":"Q"},"description":"Término de búsqueda (mínimo 1 carácter)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Máximo de resultados por tipo de entidad.","default":8,"title":"Limit"},"description":"Máximo de resultados por tipo de entidad."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SearchHit"},"title":"Response Search Global Search"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/me/calendar-feed":{"get":{"tags":["calendar"],"summary":"Get Calendar Feed","description":"Devuelve (y genera si es la primera vez) el token de calendario ICS del\nusuario autenticado más la URL de suscripción lista para pegar en\nGoogle/Apple Calendar.","operationId":"calendar_get_calendar_feed","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/calendar/{token}.ics":{"get":{"tags":["calendar"],"summary":"Get Calendar Ics","description":"Feed ICS público (sin cookie): el token es la credencial.\n\nDevuelve un VCALENDAR con las tareas asignadas al usuario propietario del\ntoken que tengan due_date. Compatible con Google Calendar y Apple Calendar\n(suscripción «Add by URL»).","operationId":"calendar_get_calendar_ics","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Feed iCalendar (RFC 5545).","content":{"text/calendar":{"schema":{"type":"string"}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/calendar/events":{"get":{"tags":["calendar"],"summary":"List Calendar Events","description":"Eventos del calendario de la organización en [start, end]: festivos\n(recurrentes expandidos) + ausencias aprobadas que solapan. Requiere member+.","operationId":"listCalendarEvents","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inicio del rango (inclusive, YYYY-MM-DD).","title":"Start"},"description":"Inicio del rango (inclusive, YYYY-MM-DD)."},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Fin del rango (inclusive, YYYY-MM-DD).","title":"End"},"description":"Fin del rango (inclusive, YYYY-MM-DD)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CalendarEventOut"},"title":"Response Listcalendarevents"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/calendar/business-time/minutes":{"get":{"tags":["calendar"],"summary":"Business Minutes Between Endpoint","description":"Minutos laborables entre dos instantes según la jornada de la organización.\n\nDescuenta noches, fines de semana y festivos (los recurrentes proyectados al\naño que toque) y se calcula en la zona horaria de la organización, así que\nlos cambios de hora no lo desplazan. Requiere member+.","operationId":"businessMinutesBetween","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Instante inicial (ISO 8601; naive = UTC).","title":"Start"},"description":"Instante inicial (ISO 8601; naive = UTC)."},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Instante final (ISO 8601; naive = UTC).","title":"End"},"description":"Instante final (ISO 8601; naive = UTC)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessMinutesOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/calendar/business-time/due":{"get":{"tags":["calendar"],"summary":"Business Time Due","description":"Vencimiento: instante en el que se cumplen N minutos laborables desde `start`.\n\nEs el cálculo de un compromiso de servicio («4 horas laborables de\nrespuesta») y sirve igual para estimar una fecha de entrega. Requiere member+.","operationId":"businessTimeDue","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"minutes","in":"query","required":true,"schema":{"type":"integer","maximum":100000,"minimum":0,"description":"Minutos laborables a sumar (p. ej. 240 = 4 horas laborables).","title":"Minutes"},"description":"Minutos laborables a sumar (p. ej. 240 = 4 horas laborables)."},{"name":"start","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Instante desde el que contar (ISO 8601; naive = UTC). Por defecto, ahora.","title":"Start"},"description":"Instante desde el que contar (ISO 8601; naive = UTC). Por defecto, ahora."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessDueOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/calendar/holidays":{"get":{"tags":["calendar"],"summary":"List Holidays","description":"Lista de festivos de la organización ordenados por fecha. Requiere member+.","operationId":"listHolidays","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HolidayOut"},"title":"Response Listholidays"}}},"headers":{"X-Total-Count":{"description":"Total de festivos de la organización (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["calendar"],"summary":"Create Holiday","description":"Crea un festivo. La fecha es única por organización. Requiere manager+.","operationId":"createHoliday","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/calendar/holidays/import":{"post":{"tags":["calendar"],"summary":"Import Holidays","description":"Importa festivos en bloque desde filas de CSV (`fecha`, `nombre`, `ambito`).\n\nValida cada fila; una fila inválida devuelve su error sin abortar el resto.\nIdempotente por (organización, fecha): omite las fechas ya existentes.\nDevuelve un resumen {created, skipped, errors}. Requiere manager+.","operationId":"importHolidays","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayImportIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayImportResult"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/calendar/holidays/{holiday_id}":{"get":{"tags":["calendar"],"summary":"Get Holiday","operationId":"getHoliday","parameters":[{"name":"holiday_id","in":"path","required":true,"schema":{"type":"string","title":"Holiday Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["calendar"],"summary":"Update Holiday","description":"Actualización parcial del festivo. Requiere manager+.","operationId":"updateHoliday","parameters":[{"name":"holiday_id","in":"path","required":true,"schema":{"type":"string","title":"Holiday Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HolidayOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["calendar"],"summary":"Delete Holiday","description":"Elimina el festivo permanentemente. Requiere manager+.","operationId":"deleteHoliday","parameters":[{"name":"holiday_id","in":"path","required":true,"schema":{"type":"string","title":"Holiday Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/gantt":{"get":{"tags":["roadmap"],"summary":"Project Gantt","description":"Gantt bars for all tasks in a project, including dependency links.\n\nTasks without dates are included with null start_date/due_date.","operationId":"projectGantt","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GanttResponse"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/roadmap":{"get":{"tags":["roadmap"],"summary":"Org Roadmap","description":"Org-level timeline: each active project with start, end and progress.\n\nSorted by project start date (nulls last).","operationId":"orgRoadmap","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoadmapItem"},"title":"Response Orgroadmap"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/evm":{"get":{"tags":["roadmap"],"summary":"Project Evm","description":"Earned Value Management summary for a project (effort-hours).\n\nBAC, EV, AC, PV, CPI, SPI, EAC, ETC, VAC — all in hours (2 dp).\nCPI and SPI are null when their denominator is zero.","operationId":"projectEvm","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"as_of","in":"query","required":false,"schema":{"type":"string","format":"date","description":"Reference date for EVM (YYYY-MM-DD). Defaults to today.","title":"As Of"},"description":"Reference date for EVM (YYYY-MM-DD). Defaults to today."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvmSummary"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/contracts":{"get":{"tags":["contracts"],"summary":"List Contracts","operationId":"listContracts","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["draft","active","signed","expired","terminated","cancelled"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"contract_type","in":"query","required":false,"schema":{"anyOf":[{"enum":["service","employment","nda","framework","lease","other"],"type":"string"},{"type":"null"}],"title":"Contract Type"}},{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Busca en contract_number o title (case-insensitive).","title":"Search"},"description":"Busca en contract_number o title (case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContractOut"},"title":"Response Listcontracts"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"post":{"tags":["contracts"],"summary":"Create Contract","operationId":"createContract","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/contracts/{contract_id}":{"get":{"tags":["contracts"],"summary":"Get Contract","operationId":"getContract","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["contracts"],"summary":"Update Contract","operationId":"updateContract","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["contracts"],"summary":"Delete Contract","operationId":"deleteContract","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/contracts/from-template/{template_id}":{"post":{"tags":["contracts"],"summary":"Create Contract From Template","operationId":"createContractFromTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/contract-templates":{"get":{"tags":["contracts"],"summary":"List Contract Templates","operationId":"listContractTemplates","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContractTemplateOut"},"title":"Response Listcontracttemplates"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contracts"],"summary":"Create Contract Template","operationId":"createContractTemplate","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplateCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplateOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/contract-templates/{template_id}":{"get":{"tags":["contracts"],"summary":"Get Contract Template","operationId":"getContractTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplateOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["contracts"],"summary":"Update Contract Template","operationId":"updateContractTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplateUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractTemplateOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["contracts"],"summary":"Delete Contract Template","operationId":"deleteContractTemplate","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/contracts/{contract_id}/milestones":{"get":{"tags":["contracts"],"summary":"List Milestones","operationId":"listMilestones","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MilestoneOut"},"title":"Response Listmilestones"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contracts"],"summary":"Create Milestone","operationId":"createMilestone","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilestoneCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilestoneOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/contracts/{contract_id}/milestones/{milestone_id}":{"patch":{"tags":["contracts"],"summary":"Update Milestone","operationId":"updateMilestone","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"milestone_id","in":"path","required":true,"schema":{"type":"string","title":"Milestone Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilestoneUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilestoneOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["contracts"],"summary":"Delete Milestone","operationId":"deleteMilestone","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","title":"Contract Id"}},{"name":"milestone_id","in":"path","required":true,"schema":{"type":"string","title":"Milestone Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/bi/dashboards":{"get":{"tags":["bi"],"summary":"List Bi Dashboards","operationId":"listBiDashboards","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BiDashboardOut"},"title":"Response Listbidashboards"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["bi"],"summary":"Create Bi Dashboard","operationId":"createBiDashboard","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiDashboardCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiDashboardOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/bi/dashboards/{dashboard_id}":{"get":{"tags":["bi"],"summary":"Get Bi Dashboard","operationId":"getBiDashboard","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiDashboardOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["bi"],"summary":"Update Bi Dashboard","operationId":"updateBiDashboard","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiDashboardUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiDashboardOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["bi"],"summary":"Delete Bi Dashboard","operationId":"deleteBiDashboard","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/bi/dashboards/{dashboard_id}/widgets":{"get":{"tags":["bi"],"summary":"List Bi Widgets","operationId":"listBiWidgets","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BiWidgetOut"},"title":"Response Listbiwidgets"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["bi"],"summary":"Create Bi Widget","operationId":"createBiWidget","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/bi/dashboards/{dashboard_id}/widgets/{widget_id}":{"patch":{"tags":["bi"],"summary":"Update Bi Widget","operationId":"updateBiWidget","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"widget_id","in":"path","required":true,"schema":{"type":"string","title":"Widget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["bi"],"summary":"Delete Bi Widget","operationId":"deleteBiWidget","parameters":[{"name":"dashboard_id","in":"path","required":true,"schema":{"type":"string","title":"Dashboard Id"}},{"name":"widget_id","in":"path","required":true,"schema":{"type":"string","title":"Widget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/bi/widgets/{widget_id}/data":{"get":{"tags":["bi"],"summary":"Bi Widget Data","operationId":"biWidgetData","parameters":[{"name":"widget_id","in":"path","required":true,"schema":{"type":"string","title":"Widget Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetDataOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/bi/query":{"post":{"tags":["bi"],"summary":"Bi Adhoc Query","operationId":"biAdHocQuery","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiAdHocQueryIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiWidgetDataOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/automations":{"get":{"tags":["automations"],"summary":"List Automation Rules","operationId":"listAutomationRules","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutomationRuleOut"},"title":"Response Listautomationrules"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["automations"],"summary":"Create Automation Rule","operationId":"createAutomationRule","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRuleCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRuleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/automations/{rule_id}":{"get":{"tags":["automations"],"summary":"Get Automation Rule","operationId":"getAutomationRule","parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","title":"Rule Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRuleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["automations"],"summary":"Update Automation Rule","operationId":"updateAutomationRule","parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","title":"Rule Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRuleUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationRuleOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["automations"],"summary":"Delete Automation Rule","operationId":"deleteAutomationRule","parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","title":"Rule Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/automations/{rule_id}/test":{"post":{"tags":["automations"],"summary":"Test Automation Rule","description":"Dry-run de la regla contra `task_id`. No aplica ningún cambio.","operationId":"testAutomationRule","parameters":[{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","title":"Rule Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationTestIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutomationActionResult"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/agency/overview":{"get":{"tags":["agency"],"summary":"Resumen de agencia multi-cliente","description":"Roll-up por cliente: proyectos (contratos vinculados), tareas abiertas y resumen financiero (facturado y pendiente) de cada cliente de la org. También devuelve totales globales de la organización. Requiere ser miembro. NOTA DE ESQUEMA: los totales financieros agrupan las facturas por `invoices.client_id` (FK estable); las que no tienen ficha vinculada se atribuyen por coincidencia de texto `invoices.client_name = clients.name`. Los proyectos se cuentan como contratos con client_id.","operationId":"agencyOverview","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyOverviewOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings":{"get":{"tags":["holdings"],"summary":"Listar mis holdings","description":"Holdings en los que el usuario tiene fila en `holding_members`. El rol en la matriz NO da acceso por sí solo.","operationId":"listMyHoldings","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/HoldingOut"},"type":"array","title":"Response Listmyholdings"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["holdings"],"summary":"Crear un holding","description":"Crea un grupo empresarial cuya MATRIZ es una organización existente. Lo crea el `owner` de esa organización (404 si no es miembro, 403 si no es owner). La matriz queda unida a su propio holding (un consolidado la incluye) y el creador entra como `owner` en `holding_members`. 409 si esa organización ya es matriz de un holding o ya pertenece a uno. Requiere la feature `holdings` (plan Holding).","operationId":"createHolding","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingCreateIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}":{"get":{"tags":["holdings"],"summary":"Detalle de un holding","description":"404 homogéneo si no existe o el usuario no es miembro del holding.","operationId":"getHolding","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["holdings"],"summary":"Renombrar un holding","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta).","operationId":"updateHolding","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/subsidiaries":{"post":{"tags":["holdings"],"summary":"Adjuntar una filial al holding","description":"Adjunta una organización al holding (`organizations.holding_id`). DOBLE LLAVE: hay que ser `owner` de la organización MATRIZ **y** `owner`/`admin` de la organización que se adjunta — no se puede absorber una organización ajena conociendo su UUID (404 si no eres miembro de ella, 403 si lo eres pero no la administras). 409 si ya pertenece a un holding o si es matriz de otro.","operationId":"attachHoldingSubsidiary","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingSubsidiaryAttachIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/subsidiaries/{organization_id}":{"delete":{"tags":["holdings"],"summary":"Soltar una filial del holding","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta). Pone `organizations.holding_id = NULL`: la filial desaparece del consolidado en la petición siguiente (revocación inmediata). 409 al intentar soltar la organización MATRIZ.","operationId":"detachHoldingSubsidiary","parameters":[{"name":"organization_id","in":"path","required":true,"schema":{"type":"string","title":"Organization Id"}},{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/members":{"get":{"tags":["holdings"],"summary":"Listar miembros del holding","description":"Quién tiene acceso al consolidado. Sin PII: solo `user_id` y rol. Visible para cualquier miembro del holding.","operationId":"listHoldingMembers","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HoldingMemberOut"},"title":"Response Listholdingmembers"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["holdings"],"summary":"Dar acceso al holding a un usuario","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta). El destinatario debe pertenecer a alguna organización del grupo (404 en caso contrario: el acceso consolidado no se concede a un usuario ajeno). 409 si ya es miembro.","operationId":"addHoldingMember","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingMemberCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingMemberOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/members/{member_id}":{"patch":{"tags":["holdings"],"summary":"Cambiar el rol de un miembro del holding","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta). 409 si dejaría al holding sin ningún `owner`.","operationId":"updateHoldingMember","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}},{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingMemberUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingMemberOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["holdings"],"summary":"Quitar acceso al holding","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta). 409 si dejaría al holding sin ningún `owner`.","operationId":"removeHoldingMember","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}},{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/visibility":{"get":{"tags":["holdings"],"summary":"Configuración de visibilidad por filial","description":"Qué bloques de datos (`finance`, `projects`, `people`) ve el holding de cada FILIAL. Por defecto TODO visible: una filial sin configuración guardada devuelve los tres bloques a `true`. La organización MATRIZ no aparece (no se oculta datos a sí misma).\n\nLo lee cualquier miembro del holding —el panel necesita saber qué falta para no pintar un 0 engañoso—, pero cambiarlo exige ser `owner` de la MATRIZ.","operationId":"listHoldingVisibility","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HoldingVisibilityOut"},"title":"Response Listholdingvisibility"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/visibility/{organization_id}":{"patch":{"tags":["holdings"],"summary":"Cambiar qué datos ve el holding de una filial","description":"AUTORIZACIÓN: solo el `owner` de la organización MATRIZ del holding (ser `owner` en `holding_members` no basta). PATCH parcial: los bloques que no se envían no se tocan. Ocultar un bloque lo retira de `/summary` y `/finance` en la petición siguiente, y ADEMÁS deja de sumar en los totales consolidados (si siguiera sumando, el dato oculto se deduciría restando).\n\nLa configuración solo puede RESTRINGIR: nunca hace visible una organización ajena al holding ni habilita ninguna escritura. 404 si la organización no es filial de este holding, 409 si es la MATRIZ (`cannot_configure_parent_org`).","operationId":"updateHoldingVisibility","parameters":[{"name":"organization_id","in":"path","required":true,"schema":{"type":"string","title":"Organization Id"}},{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingVisibilityUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingVisibilityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/summary":{"get":{"tags":["holdings"],"summary":"Resumen consolidado del holding","description":"Consolidado de actividad del grupo (matriz + filiales), SOLO LECTURA: nº de filiales, proyectos totales/activos, tareas por estado y personas. `people_total` cuenta usuarios DISTINTOS: quien tenga membresía en dos filiales cuenta UNA vez (por eso NO coincide con la suma de `people` de cada fila). Devuelve además el desglose por organización, que es lo que pinta la tabla del panel. Sin PII de empleados: solo agregados.\n\n**Visibilidad**: si el holding ha ocultado el bloque `projects` o `people` de una filial, sus contadores salen a 0, el bloque aparece en `hidden_blocks` de esa fila y NO suma en los totales (se marca `has_hidden_data`): dejarlo sumar permitiría deducir el dato oculto por diferencia.","operationId":"getHoldingSummary","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingSummaryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/holdings/{holding_id}/finance":{"get":{"tags":["holdings"],"summary":"Finanzas consolidadas del holding","description":"Ingresos, gastos y neto consolidados del grupo, SOLO LECTURA.\n\n**Neteo intercompañía**: las facturas emitidas entre empresas del mismo holding se excluyen por los DOS lados (el gasto espejo en la receptora y el ingreso equivalente en la emisora). Sin ese neteo el P&L del grupo saldría inflado. `gross_*` son los importes antes de netear y `intercompany_*_excluded` la diferencia.\n\n**Divisa**: se consolida POR DIVISA (una fila por `currency`); no se aplica ninguna conversión ni se suman divisas distintas.\n\n**Visibilidad**: una filial con el bloque `finance` oculto viaja con `currencies: []` y `finance_visible: false`, y no aporta NI UN importe a los totales (que quedan parciales, con `has_hidden_data: true`).","operationId":"getHoldingFinance","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"string","title":"Holding Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha inicial (issue_date/date).","title":"From"},"description":"Fecha inicial (issue_date/date)."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Fecha final (issue_date/date).","title":"To"},"description":"Fecha final (issue_date/date)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingFinanceOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/copilot/chat":{"post":{"tags":["copilot"],"summary":"Copilot Chat","description":"Conversa con el copiloto IA (solo lectura) sobre los datos de la org.\n\nCualquier miembro puede usarlo. Las herramientas que ejecuta aplican sus\npropias policies: si el usuario no tiene acceso a finanzas, el copiloto lo\ncomunica en vez de mostrar datos. Devuelve el texto final más la lista de\nherramientas de lectura ejecutadas (transparencia).\n\nSi el copiloto no está configurado (sin `ANTHROPIC_API_KEY` o\n`COPILOT_ENABLED=false`) responde 503 `copilot_unavailable`.","operationId":"copilot_copilot_chat","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotChatIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotChatOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"description":"Límite de uso de Kern superado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Copiloto no configurado (falta ANTHROPIC_API_KEY o desactivado).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/organizations/{org_id}/copilot/chat/stream":{"post":{"tags":["copilot"],"summary":"Copilot Chat Stream","description":"Conversa con Kern con STREAMING (SSE): el texto se transmite según se genera.\n\nMismo body, permisos (member+) y degradación que `POST /copilot/chat`. Emite\neventos Server-Sent Events (`event: X\\ndata: <json>\\n\\n`):\n- `event: tool` `{\"name\": \"<tool>\"}` cada vez que se ejecuta una herramienta.\n- `event: delta` `{\"text\": \"<fragmento>\"}` por cada fragmento de la respuesta.\n- `event: done` con `{reply, tool_calls, proposed_actions, conversation_id}`\n  al terminar (la persistencia se hace justo antes, igual que en `chat()`).\n\nEl streaming real (token a token) es SOLO con el proveedor Anthropic; con\nOpenRouter se cae al chat no-streaming y se emite un único `event: done`.\n\nSi el copiloto no está configurado (sin `ANTHROPIC_API_KEY` /\n`OPENROUTER_API_KEY` o `COPILOT_ENABLED=false`) responde 503 antes de abrir el\nstream. El stream usa su propia sesión de BD (no la de la request), como el\nstream de notificaciones.","operationId":"copilot_copilot_chat_stream","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopilotChatIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"description":"Límite de uso de Kern superado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Copiloto no configurado (falta ANTHROPIC_API_KEY o desactivado).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/organizations/{org_id}/copilot/alerts":{"get":{"tags":["copilot"],"summary":"Copilot Alerts","description":"Avisos deterministas de Kern por módulo (SIN IA), ordenados por gravedad.\n\nCualquier miembro puede pedirlos. Cada aviso respeta los permisos de su\nmódulo: los de finanzas, presupuestos y rentabilidad de proyectos solo\naparecen si el usuario es admin+; las tareas vencidas del propio usuario, a\ncualquier miembro. Un módulo sin nada que avisar (o al que el usuario no tiene\nacceso) simplemente no añade avisos. No consume el modelo ni requiere API key.","operationId":"copilot_copilot_alerts","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KernAlertOut"},"title":"Response Copilot Copilot Alerts"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/copilot/conversations":{"get":{"tags":["copilot"],"summary":"Copilot Conversations","description":"Histórico de conversaciones del usuario con Kern en esta org.\n\nSolo las del propio usuario, más recientes primero, con el número de mensajes\nde cada una. Cualquier miembro puede consultarlo.","operationId":"copilot_copilot_conversations","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CopilotConversationOut"},"title":"Response Copilot Copilot Conversations"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/copilot/conversations/{conversation_id}":{"get":{"tags":["copilot"],"summary":"Copilot Conversation Messages","description":"Mensajes de una conversación PROPIA del usuario, ordenados cronológicamente.\n\n404 si la conversación no existe o no pertenece al usuario/org.","operationId":"copilot_copilot_conversation_messages","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CopilotMessageOut"},"title":"Response Copilot Copilot Conversation Messages"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/attachments":{"post":{"tags":["attachments"],"summary":"Upload Attachment","description":"Sube un fichero adjunto (multipart/form-data, campo `file`).\n\n- Máximo 10 MB.\n- Extensiones permitidas: png, jpg, jpeg, webp, gif, pdf.\n- Las imágenes se validan con Pillow (protección anti-polyglot).\n- El nombre almacenado es aleatorio (nunca el nombre del cliente).\n- Si se pasa `entity_type`+`entity_id`, valida que la entidad pertenece\n  a la organización (protección IDOR).","operationId":"uploadAttachment","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Tipo de entidad (invoice, expense, task…)","title":"Entity Type"},"description":"Tipo de entidad (invoice, expense, task…)"},{"name":"entity_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UUID de la entidad.","title":"Entity Id"},"description":"UUID de la entidad."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_uploadAttachment"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__attachments__schemas__AttachmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Request Entity Too Large"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["attachments"],"summary":"List Attachments","description":"Lista los adjuntos de una entidad concreta (validación IDOR incluida).","operationId":"listAttachments","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","description":"Tipo de entidad (invoice, expense, task…)","title":"Entity Type"},"description":"Tipo de entidad (invoice, expense, task…)"},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","description":"UUID de la entidad.","title":"Entity Id"},"description":"UUID de la entidad."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/app__modules__attachments__schemas__AttachmentOut"},"title":"Response Listattachments"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/attachments/base64":{"post":{"tags":["attachments"],"summary":"Upload Attachment Base64","description":"Adjunta un fichero (base64, JSON) a una entidad org-scoped con adjuntos:\n`invoice`, `expense`, `task`, `supplier_invoice`, `payslip` o `client`.\n\nNo exige `project_id`, por eso permite adjuntar por MCP a ingresos y gastos\n(que no cuelgan de un proyecto). Valida pertenencia a la organización (IDOR)\ny, para documentos financieros/nómina, exige rol admin+. Mismos límites que\nel resto: 10 MB, png/jpg/jpeg/webp/gif/pdf, verificación Pillow en imágenes.","operationId":"uploadAttachmentBase64","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentUploadBase64"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__attachments__schemas__AttachmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Request Entity Too Large"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/attachments/{stored_name}/download":{"get":{"tags":["attachments"],"summary":"Download Attachment","description":"Descarga autenticada del adjunto (PJKT-2016).\n\nSustituye a `/uploads/attachments/…`, que servía nginx gateado con\n`auth_request`. Aquel camino sigue vivo para las filas que aún no se han\nmovido; las nuevas apuntan aquí desde `file_url`.","operationId":"downloadAttachment","parameters":[{"name":"stored_name","in":"path","required":true,"schema":{"type":"string","title":"Stored Name"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"El fichero.","content":{"application/json":{"schema":{}},"application/octet-stream":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/attachments/{attachment_id}":{"delete":{"tags":["attachments"],"summary":"Delete Attachment","description":"Borra un adjunto. Solo el uploader o admin+ puede borrar.","operationId":"deleteAttachment","parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/attachments":{"post":{"tags":["attachments"],"summary":"Upload Project Attachment","description":"Adjunta un fichero (base64, JSON) a una entidad de ESTE proyecto (`task`\no `supplier_invoice`). Al llevar `{project_id}` en el path funciona con un\nPAT scoped a ese proyecto (el endpoint org-level lo rechaza por scope).","operationId":"uploadProjectAttachment","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentBase64In"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__modules__attachments__schemas__AttachmentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Request Entity Too Large"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/comments":{"get":{"tags":["finance"],"summary":"List Invoice Comments","description":"Lista comentarios de una factura (hilo plano, recientes primero).","operationId":"listInvoiceComments","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FinanceCommentOut"},"title":"Response Listinvoicecomments"}}},"headers":{"X-Total-Count":{"description":"Total de comentarios de la factura (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["finance"],"summary":"Create Invoice Comment","description":"Crea un comentario (o respuesta) en una factura.","operationId":"createInvoiceComment","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/invoices/{invoice_id}/comments/{comment_id}":{"patch":{"tags":["finance"],"summary":"Update Invoice Comment","description":"Edita un comentario (solo el autor o admin+).","operationId":"updateInvoiceComment","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Invoice Comment","description":"Borra un comentario (solo el autor o admin+).","operationId":"deleteInvoiceComment","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/comments":{"get":{"tags":["finance"],"summary":"List Expense Comments","description":"Lista comentarios de un gasto (hilo plano, recientes primero).","operationId":"listExpenseComments","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de elementos a devolver (1..200).","default":200,"title":"Limit"},"description":"Máximo de elementos a devolver (1..200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Número de filas a saltar (paginación). 0 = primera página.","default":0,"title":"Offset"},"description":"Número de filas a saltar (paginación). 0 = primera página."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FinanceCommentOut"},"title":"Response Listexpensecomments"}}},"headers":{"X-Total-Count":{"description":"Total de comentarios del gasto (sin paginar).","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["finance"],"summary":"Create Expense Comment","description":"Crea un comentario (o respuesta) en un gasto.","operationId":"createExpenseComment","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/organizations/{org_id}/expenses/{expense_id}/comments/{comment_id}":{"patch":{"tags":["finance"],"summary":"Update Expense Comment","description":"Edita un comentario de gasto (solo el autor o admin+).","operationId":"updateExpenseComment","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceCommentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["finance"],"summary":"Delete Expense Comment","description":"Borra un comentario de gasto (solo el autor o admin+).","operationId":"deleteExpenseComment","parameters":[{"name":"expense_id","in":"path","required":true,"schema":{"type":"string","title":"Expense Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/integrations/github/oauth/start":{"get":{"tags":["github"],"summary":"Iniciar flujo OAuth GitHub para conectar la integración","description":"Genera un state JWT firmado (TTL 10 min) y devuelve la URL de autorización de GitHub. scope='repo' — GitHub OAuth clásico no ofrece un scope de solo lectura para repos privados; 'repo' es el mínimo necesario. Requiere manager+.","operationId":"githubOauthStart","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubOauthStartOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/integrations/github":{"put":{"tags":["github"],"summary":"Conectar/actualizar integración GitHub de la org","description":"Valida el token contra GitHub (GET /user), lo cifra con Fernet y lo persiste. Devuelve `{connected: true, login, token_last4}`. Requiere admin+.","operationId":"github_connect_github_integration","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubConnectIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubIntegrationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["github"],"summary":"Estado de la integración GitHub","description":"Devuelve `{connected, login?, token_last4?}`. No expone el token; el login se cachea en la tabla.","operationId":"github_get_github_integration_status","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubIntegrationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["github"],"summary":"Desconectar integración GitHub","description":"Elimina el token cifrado de la org. Requiere admin+.","operationId":"github_disconnect_github_integration","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/integrations/github/webhook":{"post":{"tags":["github"],"summary":"Generar o rotar el secreto del webhook de GitHub de la org","description":"Genera (o rota) el secreto del webhook y devuelve la URL pública y el secreto. El secreto se muestra UNA sola vez; el servidor solo guarda su forma cifrada. Configura ambos en GitHub. Requiere manager+ e integración conectada.","operationId":"provisionGithubWebhook","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubWebhookConfig"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/integrations/github/repos":{"get":{"tags":["github"],"summary":"Listar repos del token GitHub","description":"Lista los repos de la cuenta GitHub (hasta 50, ordenados por último push). Filtro substring por `query`. 409 si no hay integración conectada.","operationId":"github_list_github_repos","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"query","in":"query","required":false,"schema":{"type":"string","description":"Filtro substring por nombre completo del repo.","default":"","title":"Query"},"description":"Filtro substring por nombre completo del repo."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GithubRepoSlim"},"title":"Response Github List Github Repos"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/repository":{"post":{"tags":["github"],"summary":"Vincular repositorio GitHub a un proyecto","description":"Verifica que el repo existe en la cuenta de GitHub, obtiene la rama predeterminada y persiste el vínculo. Requiere admin+.","operationId":"github_link_repository","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectRepositoryIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectRepositoryOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"get":{"tags":["github"],"summary":"Listar repositorios vinculados a un proyecto","operationId":"github_list_project_repositories","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProjectRepositoryOut"},"title":"Response Github List Project Repositories"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/repository/{link_id}":{"delete":{"tags":["github"],"summary":"Desvincular repositorio de un proyecto","description":"Requiere admin+.","operationId":"github_unlink_repository","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"link_id","in":"path","required":true,"schema":{"type":"string","title":"Link Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/github/activity":{"get":{"tags":["github"],"summary":"Actividad GitHub del proyecto","description":"Agrega commits, PRs open y PRs merged de todos los repos vinculados. Respuestas cacheadas en memoria 60 s. 409 si no hay integración GitHub. member+.","operationId":"github_get_project_github_activity","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectGithubActivityOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/github/delivery-metrics":{"get":{"tags":["github"],"summary":"Métricas de entrega (DORA-lite) del proyecto","description":"Calcula on-demand métricas de entrega DORA-lite a partir de los PRs mergeados de todos los repos vinculados: `lead_time_avg_hours` (media de merged_at − created_at en horas) y `merged_count`. Reusa el cache de actividad (60 s). Sin integración, sin repos o sin PRs mergeados → lead_time null y merged_count 0. Tolerante a rate-limit (omite el repo que falla). `window_note` explica la limitación de la ventana. member+.","operationId":"githubDeliveryMetrics","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryMetricsOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/tasks/{task_id}/github-links":{"get":{"tags":["github"],"summary":"Commits y PRs de GitHub vinculados a una tarea","description":"Busca en la GitHub Search API los commits y PRs que mencionan la referencia de la tarea ({key}-{number}) en cada repo vinculado al proyecto. Cache in-memory 120 s por (repo, referencia). Si GitHub devuelve 403 rate-limit: rate_limited=true, listas vacías (NO 429 al cliente). Sin integración o sin repos vinculados: repos=[]. 404 si la tarea no pertenece al proyecto/org. member+.","operationId":"github_get_task_github_links","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskGithubLinksOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/projects/{project_id}/github/pulls/{number}/review":{"get":{"tags":["github"],"summary":"Diff y comentarios de revisión de un pull request (solo lectura)","description":"Trae los ficheros cambiados del PR (GET /pulls/{n}/files) y sus comentarios de revisión (GET /pulls/{n}/comments) para enseñarlos junto a la tarea enlazada. SOLO LECTURA por diseño: no existe el POST hermano, porque un comentario escrito desde aquí lo firmaría el token de la integración de la organización y no la persona que revisa. El repo va en `repo` y tiene que estar vinculado a ESE proyecto de ESA org, o 404. Topes dichos en la respuesta: 30 ficheros, 12 KB de parche por fichero, 200 KB en total, 100 comentarios. Cache in-memory 120 s. 429 solo si la cuota de GitHub se agota ya al leer el PR. member+.","operationId":"getGithubPullReview","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"number","in":"path","required":true,"schema":{"type":"integer","title":"Number"}},{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"repo","in":"query","required":true,"schema":{"type":"string","minLength":3,"description":"Nombre completo del repo (owner/name).","title":"Repo"},"description":"Nombre completo del repo (owner/name)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubPullReviewOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/v1/auth/oauth/github/callback/repo-integration":{"get":{"tags":["github"],"summary":"Callback OAuth GitHub (redirección de navegador)","description":"Recibe el code y state de GitHub tras la autorización del usuario. Valida el state (firma JWT, expiración, purpose y user_id == sesión activa). Intercambia el code por un access_token de GitHub (POST access_token, timeout 8s). Llama GET /user para obtener el login. UPSERT en org_github_integrations con el token cifrado. Redirige 302 al frontend; errores como ?github=error&reason=<code>. NUNCA se filtra el detalle del error de GitHub en la URL.","operationId":"githubOauthCallback","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Código de autorización de GitHub.","title":"Code"},"description":"Código de autorización de GitHub."},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"JWT de state generado por /oauth/start.","title":"State"},"description":"JWT de state generado por /oauth/start."}],"responses":{"302":{"description":"Redirección de navegador. Éxito → {web_url}/projects?github=connected. Error → {web_url}/projects?github=error&reason=<code>. Este endpoint es navegación directa del browser, no se debe llamar con fetch.","content":{"application/json":{"schema":{}}},"headers":{"Location":{"schema":{"type":"string"},"description":"URL de destino tras completar el flujo OAuth."}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/integrations/github/webhook/{webhook_id}":{"post":{"tags":["github"],"summary":"Receptor de webhooks de GitHub (verificación de firma HMAC)","description":"Endpoint PÚBLICO (sin cookie ni PAT). Verifica `X-Hub-Signature-256` (HMAC-SHA256 del cuerpo crudo) con el secreto por-integración en tiempo constante y RECHAZA con 401 si falta o no valida ANTES de procesar. La org se resuelve por `webhook_id` (opaco), nunca por el payload. Idempotente por `X-GitHub-Delivery`. Procesa push/pull_request/issues. 404 si webhook_id desconocido.","operationId":"githubWebhook","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GithubWebhookAck"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/places/autocomplete":{"get":{"tags":["places"],"summary":"Autocompletar direcciones via Google Places","description":"Proxy de la API Google Places (New) — `POST /places:autocomplete`. La API key de Google NUNCA se expone al cliente; solo sale del backend. Requiere GOOGLE_MAPS_API_KEY en el entorno del servidor. Filtra por regiones es/ad/fr/pt con languageCode=es. `session_token` agrupa las llamadas de autocompletar + /details en una única sesión para el modelo de facturación por sesión de Google. Si `input` tiene menos de 3 caracteres, devuelve lista vacía sin llamar a Google. Cachea la respuesta en Redis 5 min por `input` exacto (no re-paga la misma query).","operationId":"placesAutocomplete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"input","in":"query","required":true,"schema":{"type":"string","description":"Texto parcial a autocompletar.","title":"Input"},"description":"Texto parcial a autocompletar."},{"name":"session_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Token de sesión de Google Places (billing por sesión).","title":"Session Token"},"description":"Token de sesión de Google Places (billing por sesión)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlaceSuggestion"},"title":"Response Placesautocomplete"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"description":"Límite de peticiones a Places superado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"Error upstream de Google Places.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Google Maps API key no configurada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/places/details":{"get":{"tags":["places"],"summary":"Detalles de dirección via Google Places","description":"Proxy de la API Google Places (New) — `GET /places/{place_id}`. La API key de Google NUNCA se expone al cliente; solo sale del backend. Parsea `addressComponents` para extraer campos de dirección estructurados (calle, número, ciudad, CP, provincia, país). `session_token` cierra la sesión de autocompletar iniciada en /autocomplete, lo que aplica el precio por sesión de Google en lugar de por llamada. Cachea la respuesta en Redis 30 min por `place_id` (no re-paga el mismo lugar).","operationId":"placesDetails","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"place_id","in":"query","required":true,"schema":{"type":"string","description":"ID del lugar devuelto por /autocomplete.","title":"Place Id"},"description":"ID del lugar devuelto por /autocomplete."},{"name":"session_token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Token de sesión de Google Places para cerrar la sesión de billing.","title":"Session Token"},"description":"Token de sesión de Google Places para cerrar la sesión de billing."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceDetails"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"description":"Límite de peticiones a Places superado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"Error upstream de Google Places.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Google Maps API key no configurada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/places/staticmap":{"get":{"tags":["places"],"summary":"Mapa estático via Google Static Maps","description":"Proxy de `GET https://maps.googleapis.com/maps/api/staticmap` que devuelve la imagen PNG del mapa server-side. La API key NUNCA llega al navegador y, si GOOGLE_MAPS_SIGNING_SECRET está configurado, la URL hacia Google se firma con HMAC-SHA1 (URL signing secret) añadiendo `&signature=` — la firma tampoco se expone al cliente. La imagen se sirve con `Cache-Control: private, max-age=3600`.","operationId":"placesStaticMap","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"center","in":"query","required":true,"schema":{"type":"string","maxLength":500,"description":"Dirección o coordenadas 'lat,lng' a centrar.","title":"Center"},"description":"Dirección o coordenadas 'lat,lng' a centrar."},{"name":"zoom","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"description":"Nivel de zoom (1-20).","default":16,"title":"Zoom"},"description":"Nivel de zoom (1-20)."},{"name":"size","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,4}x\\d{1,4}$","description":"Tamaño 'WxH' en píxeles (máximo 1280x1280, se recorta).","default":"640x320","title":"Size"},"description":"Tamaño 'WxH' en píxeles (máximo 1280x1280, se recorta)."},{"name":"marker","in":"query","required":false,"schema":{"type":"boolean","description":"Añade un marker coral 3XA sobre el centro del mapa.","default":true,"title":"Marker"},"description":"Añade un marker coral 3XA sobre el centro del mapa."},{"name":"scale","in":"query","required":false,"schema":{"type":"integer","maximum":2,"minimum":1,"description":"Densidad de píxeles (1-2).","default":2,"title":"Scale"},"description":"Densidad de píxeles (1-2)."}],"responses":{"200":{"description":"Imagen PNG del mapa estático.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"429":{"description":"Límite de peticiones a Places superado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"Error upstream de Google Places.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Google Maps API key no configurada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/overview":{"get":{"tags":["platform"],"summary":"Platform Overview","description":"Contadores globales de la plataforma.","operationId":"platform_platform_overview","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOverviewOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/search":{"get":{"tags":["platform"],"summary":"Platform Search","description":"Búsqueda global (paleta ⌘K): orgs, usuarios y proveedores, máx. 5 por tipo.\n\nEl `min_length=2` lo valida FastAPI → 422 `validation_error` con el envelope.","operationId":"platform_platform_search","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"description":"Término de búsqueda (mínimo 2 caracteres).","title":"Q"},"description":"Término de búsqueda (mínimo 2 caracteres)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSearchResultOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/health":{"get":{"tags":["platform"],"summary":"Platform Health","description":"Salud agregada de infra: colas, scheduler, DB, Redis y tasa de errores.","operationId":"platform_platform_health","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformHealthOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/health/queues":{"get":{"tags":["platform"],"summary":"Platform Health Queues","description":"Profundidad (backlog + dead-letter) por cola de trabajos Dramatiq.","operationId":"platform_platform_health_queues","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformQueueDepthOut"},"type":"array","title":"Response Platform Platform Health Queues"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/jobs":{"get":{"tags":["platform"],"summary":"Platform List Jobs","description":"Jobs programados: última ejecución, duración y resultado por job (PJKT-2029).","operationId":"platform_platform_list_jobs","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformJobStatusOut"},"type":"array","title":"Response Platform Platform List Jobs"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/queues":{"get":{"tags":["platform"],"summary":"Platform List Queues","description":"Estado por cola Dramatiq: backlog, diferidos y dead-letter (PJKT-2027).","operationId":"platform_platform_list_queues","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/QueueStatusOut"},"type":"array","title":"Response Platform Platform List Queues"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Service Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/organizations":{"get":{"tags":["platform"],"summary":"Platform List Organizations","description":"Listado global de organizaciones.","operationId":"platform_platform_list_organizations","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por nombre o slug.","title":"Search"},"description":"Búsqueda por nombre o slug."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["active","suspended","deleted"],"type":"string"},{"type":"null"}],"description":"Filtrar por estado de cuenta. Ausente = todas (comportamiento histórico).","title":"Status"},"description":"Filtrar por estado de cuenta. Ausente = todas (comportamiento histórico)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformOrganizationOut"},"title":"Response Platform Platform List Organizations"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/usage":{"get":{"tags":["platform"],"summary":"Platform Usage","description":"Consumo por organización (PJKT-2048): storage, tareas, documentos,\nadjuntos y miembros, orden descendente por el campo pedido.","operationId":"platform_platform_usage","parameters":[{"name":"order_by","in":"query","required":false,"schema":{"enum":["storage","tasks","members"],"type":"string","description":"Campo por el que ordenar (descendente).","default":"storage","title":"Order By"},"description":"Campo por el que ordenar (descendente)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformOrgUsageOut"},"title":"Response Platform Platform Usage"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/trash":{"get":{"tags":["platform"],"summary":"Platform List Trash Organizations","description":"Papelera (PJKT-2046): orgs soft-deleted, con cuánta gracia queda hasta\nser purgables. Borradas más recientemente primero.","operationId":"platform_platform_list_trash_organizations","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformTrashOrganizationOut"},"title":"Response Platform Platform List Trash Organizations"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/restore":{"post":{"tags":["platform"],"summary":"Platform Restore Organization","description":"Restaura una org de la papelera (`deleted_at` → NULL); sus miembros\nrecuperan el acceso. 404 si no está borrada; 409 `already_purged` si ya no\nexiste físicamente. Auditado `platform.org_restored`.","operationId":"platform_platform_restore_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganizationOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}":{"get":{"tags":["platform"],"summary":"Platform Get Organization","description":"Detalle modo dios de una organización: miembros + contadores + actividad.","operationId":"platform_platform_get_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganizationDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["platform"],"summary":"Platform Update Organization","description":"PATCH parcial de la ficha de la org (nombre + datos fiscales). El slug no\nse toca. Auditado `platform.org_updated` con before→after por campo.","operationId":"platform_platform_update_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganizationUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganizationDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}},"delete":{"tags":["platform"],"summary":"Platform Soft Delete Organization","description":"SOFT-delete de una organización (fija `deleted_at`). Reversible, complementa\nel bulk de W3 (single-org); arranca el periodo de gracia de cara a una purga.\nIdempotente: si ya estaba soft-deleted, no-op. Auditado.","operationId":"platform_platform_soft_delete_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/stats/timeseries":{"get":{"tags":["platform"],"summary":"Platform Timeseries","description":"Series diarias (UTC) del dashboard: altas, logins y errores por día.","operationId":"platform_platform_timeseries","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":7,"description":"Ventana en días (7–90).","default":30,"title":"Days"},"description":"Ventana en días (7–90)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformTimeseriesPointOut"},"title":"Response Platform Platform Timeseries"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users":{"get":{"tags":["platform"],"summary":"Platform List Users","description":"Listado global de usuarios.","operationId":"platform_platform_list_users","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por email o nombre.","title":"Search"},"description":"Búsqueda por email o nombre."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["active","suspended","banned","deleted"],"type":"string"},{"type":"null"}],"description":"Filtrar por estado de cuenta. Ausente = todos (comportamiento histórico).","title":"Status"},"description":"Filtrar por estado de cuenta. Ausente = todos (comportamiento histórico)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformUserOut"},"title":"Response Platform Platform List Users"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}":{"get":{"tags":["platform"],"summary":"Platform Get User","description":"Detalle de un usuario: perfil + membresías + passkeys + sesiones + push.","operationId":"platform_platform_get_user","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["platform"],"summary":"Platform Update User","description":"Edita campos directos del usuario (hoy solo el nombre). El email no: exige\nre-verificación vía `POST /users/{user_id}/email-change`.","operationId":"platform_platform_update_user","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/users/{user_id}/email-change":{"post":{"tags":["platform"],"summary":"Platform Request User Email Change","description":"Pide el cambio de email: manda el enlace de verificación al buzón NUEVO.\nEl email solo cambia cuando su dueño confirma el enlace (PJKT-2033).","operationId":"platform_platform_request_user_email_change","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserEmailChangeIn"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformEmailChangeRequestedOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/platform/users/{user_id}/passkeys/{passkey_id}":{"delete":{"tags":["platform"],"summary":"Platform Revoke User Passkey","description":"Revoca una passkey del usuario.","operationId":"platform_platform_revoke_user_passkey","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"passkey_id","in":"path","required":true,"schema":{"type":"string","title":"Passkey Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}/2fa/disable":{"post":{"tags":["platform"],"summary":"Platform Disable User Two Factor","description":"Desactiva el 2FA del usuario (idempotente; avisa al dueño por email).","operationId":"platform_platform_disable_user_two_factor","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformTwoFactorDisabledOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}/resend-login-code":{"post":{"tags":["platform"],"summary":"Platform Resend User Login Code","description":"Reenvía el código de acceso del login normal al email del usuario.","operationId":"platform_platform_resend_user_login_code","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformLoginCodeResentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}/erase":{"post":{"tags":["platform"],"summary":"Platform Erase User","description":"Borrado GDPR (PJKT-2047): anonimiza al usuario de forma irreversible y\ndevuelve el informe. Exige `confirm` = «ELIMINAR» y `reason` (→422). Bloqueos\n409: platform admin, owner único de una org, o ya borrado. Auditado\n`platform.user_erased` con el informe y el motivo.","operationId":"platform_platform_erase_user","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserEraseIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErasureReportOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/platform/users/{user_id}/sessions":{"delete":{"tags":["platform"],"summary":"Platform Revoke User Sessions","description":"Revoca TODAS las sesiones activas del usuario (logout remoto).","operationId":"platform_platform_revoke_user_sessions","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}/sessions/{session_id}":{"delete":{"tags":["platform"],"summary":"Platform Revoke User Session","description":"Revoca UNA sesión concreta del usuario; el resto sigue viva.","operationId":"platform_platform_revoke_user_session","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/{user_id}/push-devices/{device_id}":{"delete":{"tags":["platform"],"summary":"Platform Delete User Push Device","description":"Borra una suscripción Web Push o un token nativo del usuario.","operationId":"platform_platform_delete_user_push_device","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"device_id","in":"path","required":true,"schema":{"type":"string","title":"Device Id"}},{"name":"kind","in":"query","required":true,"schema":{"enum":["web","native"],"type":"string","description":"Canal del registro a borrar.","title":"Kind"},"description":"Canal del registro a borrar."}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/users/bulk":{"post":{"tags":["platform"],"summary":"Platform Bulk Users","description":"Suspende, reactiva o borra (soft) hasta 100 usuarios en lote (cross-tenant).\n\n`soft_delete` anonimiza el email y NO tiene papelera (a diferencia de las\norgs): es irreversible, así que escala a confirmación FRESCA.","operationId":"platform_platform_bulk_users","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserBulkAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/organizations/bulk":{"post":{"tags":["platform"],"summary":"Platform Bulk Organizations","description":"Suspende, reactiva o borra (soft) hasta 100 organizaciones en lote.","operationId":"platform_platform_bulk_organizations","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrgBulkAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/suppliers/bulk":{"post":{"tags":["platform"],"summary":"Platform Bulk Suppliers","description":"Borra o fusiona hasta 100 proveedores en lote (merge cross-org prohibido).\n\n`delete` es borrado FÍSICO de filas de clientes: mismo nivel que borrar UNA\nentrada del catálogo global (fresca), que destruye estrictamente menos.","operationId":"platform_platform_bulk_suppliers","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierBulkAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkActionResultOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/suppliers/{supplier_id}":{"patch":{"tags":["platform"],"summary":"Platform Update Supplier","description":"Edita un proveedor org-scoped (PJKT-2042). La org no se puede cambiar.","operationId":"platform_platform_update_supplier","parameters":[{"name":"supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Supplier Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/{org_id}/projects":{"get":{"tags":["platform"],"summary":"Platform List Org Projects","description":"Proyectos de una organización (N1, auditado).","operationId":"platform_platform_list_org_projects","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","description":"Incluir los proyectos en la papelera.","default":false,"title":"Include Deleted"},"description":"Incluir los proyectos en la papelera."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformProjectOut"},"title":"Response Platform Platform List Org Projects"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/projects/{project_id}":{"get":{"tags":["platform"],"summary":"Platform Get Org Project","description":"Detalle de un proyecto: metadatos, contadores y miembros (N1, auditado).","operationId":"platform_platform_get_org_project","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformProjectDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["platform"],"summary":"Platform Delete Org Project","description":"Manda un proyecto del cliente a SU papelera (soft-delete, fase 3).\n\nSolo desde navegador: el gate de `/platform/*` ya rechaza PAT y OAuth, así\nque no hace falta `InteractiveOnlyDep` (sería una segunda cerradura en la\nmisma puerta). Auditado `platform.project_soft_deleted` con actor, IP,\nagente, organización, proyecto y motivo.","operationId":"platform_platform_delete_org_project","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformProjectDeleteIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformProjectDeletionOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/{org_id}/projects/{project_id}/restore":{"post":{"tags":["platform"],"summary":"Platform Restore Org Project","description":"Saca un proyecto de la papelera del cliente. Espejo del restore de orgs.\n\nSIN step-up a propósito (criterio de la cabecera: la barrera protege lo\ndestructivo, no lo que lo deshace) y sin el límite del plan, que sí aplica en\nel restore del producto — el porqué está en el service.","operationId":"platform_platform_restore_org_project","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformProjectDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/tasks":{"get":{"tags":["platform"],"summary":"Platform List Org Tasks","description":"Tareas de una organización (N1, auditado).","operationId":"platform_platform_list_org_tasks","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"project_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por proyecto.","title":"Project Id"},"description":"Filtrar por proyecto."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por estado.","title":"Status"},"description":"Filtrar por estado."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por título.","title":"Search"},"description":"Búsqueda por título."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformTaskOut"},"title":"Response Platform Platform List Org Tasks"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/tasks/{task_id}":{"get":{"tags":["platform"],"summary":"Platform Get Org Task","description":"Detalle de una tarea + historial de estados (N1, auditado).","operationId":"platform_platform_get_org_task","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformTaskDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/tasks/{task_id}/content":{"get":{"tags":["platform"],"summary":"Platform Get Org Task Content","description":"Contenido de una tarea (N2): exige `X-Access-Reason` y se audita aparte.\n\nEl motivo llega crudo desde la cabecera y lo valida el SERVICE: el «sin\nmotivo no se sirve» es una regla del canal de lecturas, no algo que cada\nendpoint pueda olvidarse de declarar.","operationId":"platform_platform_get_org_task_content","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"X-Access-Reason","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Motivo por el que se accede al contenido del cliente. Obligatorio en los endpoints de nivel N2; queda en la auditoría.","title":"X-Access-Reason"},"description":"Motivo por el que se accede al contenido del cliente. Obligatorio en los endpoints de nivel N2; queda en la auditoría."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformTaskContentOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/errors/{error_id}/issue":{"post":{"tags":["platform"],"summary":"Platform Create Error Issue","description":"Crea (o devuelve) la issue de Projekt asociada al error (PJKT-2043).","operationId":"platform_platform_create_error_issue","parameters":[{"name":"error_id","in":"path","required":true,"schema":{"type":"string","title":"Error Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformErrorIssueOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/suppliers":{"get":{"tags":["platform"],"summary":"Platform List Suppliers","description":"Proveedores de TODAS las organizaciones.","operationId":"platform_platform_list_suppliers","parameters":[{"name":"organization_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por organización.","title":"Organization Id"},"description":"Filtrar por organización."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por nombre.","title":"Search"},"description":"Búsqueda por nombre."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformSupplierOut"},"title":"Response Platform Platform List Suppliers"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Supplier","description":"Crea un proveedor en la organización indicada.","operationId":"platform_platform_create_supplier","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/users/{user_id}/impersonation":{"post":{"tags":["platform"],"summary":"Platform Create Impersonation","description":"Abre una sesión de impersonation (solo lectura) sobre el usuario.\n\nMotivo obligatorio. Prohibido impersonar a otro platform admin (403). Devuelve\nel token opaco (UNA vez) y la `activation_url` que el panel abre para fijar la\ncookie en la web. Auditado `platform.impersonation_started`.","operationId":"platform_platform_create_impersonation","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonationCreateIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonationCreatedOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/impersonation":{"get":{"tags":["platform"],"summary":"Platform List Impersonations","description":"Sesiones de impersonation activas (no revocadas ni caducadas), forense.","operationId":"platform_platform_list_impersonations","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ImpersonationSessionOut"},"type":"array","title":"Response Platform Platform List Impersonations"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/impersonation/{session_id}":{"delete":{"tags":["platform"],"summary":"Platform Revoke Impersonation","description":"Revoca una sesión de impersonation AL INSTANTE. Auditado\n`platform.impersonation_ended`. Idempotente.","operationId":"platform_platform_revoke_impersonation","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/errors":{"get":{"tags":["platform"],"summary":"Platform List Errors","description":"Errores capturados por el middleware, más recientes primero.","operationId":"platform_platform_list_errors","parameters":[{"name":"status","in":"query","required":false,"schema":{"enum":["open","resolved","all"],"type":"string","description":"Filtro de estado.","default":"open","title":"Status"},"description":"Filtro de estado."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ErrorEventOut"},"title":"Response Platform Platform List Errors"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/errors/groups":{"get":{"tags":["platform"],"summary":"Platform List Error Groups","description":"Errores agrupados por firma (excepción + ruta), última ocurrencia primero.","operationId":"platform_platform_list_error_groups","parameters":[{"name":"status","in":"query","required":false,"schema":{"enum":["open","resolved","all"],"type":"string","description":"Filtro de estado.","default":"open","title":"Status"},"description":"Filtro de estado."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ErrorGroupOut"},"title":"Response Platform Platform List Error Groups"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/errors/groups/resolve":{"post":{"tags":["platform"],"summary":"Platform Resolve Error Group","description":"Marca resueltas TODAS las ocurrencias abiertas de la firma. Idempotente.","operationId":"platform_platform_resolve_error_group","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorGroupResolveIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorGroupResolveResultOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/errors/{error_id}":{"patch":{"tags":["platform"],"summary":"Platform Update Error","description":"Marca un error como resuelto o lo reabre.","operationId":"platform_platform_update_error","parameters":[{"name":"error_id","in":"path","required":true,"schema":{"type":"string","title":"Error Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEventUpdateIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEventOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/audit":{"get":{"tags":["platform"],"summary":"Platform List Audit","description":"Audit log global (solo lectura).","operationId":"platform_platform_list_audit","parameters":[{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por acción exacta.","title":"Action"},"description":"Filtrar por acción exacta."},{"name":"organization_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por organización.","title":"Organization Id"},"description":"Filtrar por organización."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por actor (alias de actor_id, compat).","title":"User Id"},"description":"Filtrar por actor (alias de actor_id, compat)."},{"name":"actor_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por el usuario que ejecutó la acción.","title":"Actor Id"},"description":"Filtrar por el usuario que ejecutó la acción."},{"name":"target_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por el recurso afectado (target).","title":"Target Id"},"description":"Filtrar por el recurso afectado (target)."},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"created_at >= (ISO 8601, inclusive).","title":"Date From"},"description":"created_at >= (ISO 8601, inclusive)."},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"created_at <= (ISO 8601, inclusive).","title":"Date To"},"description":"created_at <= (ISO 8601, inclusive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AuditLogEntryOut"},"title":"Response Platform Platform List Audit"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/promos":{"get":{"tags":["platform"],"summary":"Platform List Promos","description":"Códigos promocionales con su beneficio, límites y canjes consumidos.","operationId":"platform_platform_list_promos","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PromoCodeAdminOut"},"title":"Response Platform Platform List Promos"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Promo","description":"Crea un código promocional (409 `promo_code_taken` si el código ya existe).","operationId":"platform_platform_create_promo","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoCodeCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoCodeAdminOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/platform/promos/{promo_id}/redemptions":{"get":{"tags":["platform"],"summary":"Platform List Promo Redemptions","description":"Historial de canjes de un código (404 si el código no existe).","operationId":"platform_platform_list_promo_redemptions","parameters":[{"name":"promo_id","in":"path","required":true,"schema":{"type":"string","title":"Promo Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PromoRedemptionOut"},"title":"Response Platform Platform List Promo Redemptions"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/promos/{promo_id}":{"patch":{"tags":["platform"],"summary":"Platform Update Promo","description":"Desactiva un código, recorta su caducidad o cambia su límite de usos.\n\nLo que concede NO se edita: con el enlace repartido, cambiarlo por debajo es\npeor que crear otro código.","operationId":"platform_platform_update_promo","parameters":[{"name":"promo_id","in":"path","required":true,"schema":{"type":"string","title":"Promo Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoCodeUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoCodeAdminOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/admins":{"get":{"tags":["platform"],"summary":"Platform List Admins","description":"Allowlist actual de platform admins.","operationId":"platform_platform_list_admins","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformAdminOut"},"type":"array","title":"Response Platform Platform List Admins"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Admin","description":"Añade un usuario existente (dominio permitido) a la allowlist.","operationId":"platform_platform_create_admin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAdminCreateIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAdminOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/admins/sessions":{"get":{"tags":["platform"],"summary":"Platform List Admin Sessions","description":"Sesiones VIVAS de todos los platform admins (PJKT-2044).\n\nEl refresh de la cookie de ESTA petición se pasa al service solo para marcar\n`is_current` (por hash, nunca se persiste ni se devuelve). Revocar una fila\n= DELETE /platform/users/{user_id}/sessions/{session_id} (ya existente).","operationId":"platform_platform_list_admin_sessions","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformAdminSessionOut"},"type":"array","title":"Response Platform Platform List Admin Sessions"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/admins/{user_id}":{"delete":{"tags":["platform"],"summary":"Platform Delete Admin","description":"Quita a un usuario de la allowlist (nunca a uno mismo).","operationId":"platform_platform_delete_admin","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/feature-flags":{"get":{"tags":["platform"],"summary":"Platform List Feature Flags","description":"Estado GLOBAL de cada feature gateable (kill-switch de plataforma).","operationId":"platform_platform_list_feature_flags","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformFeatureFlagOut"},"type":"array","title":"Response Platform Platform List Feature Flags"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"tags":["platform"],"summary":"Platform Set Feature Flag","description":"Fija (upsert) el kill-switch GLOBAL de una feature; auditado before→after.","operationId":"platform_platform_set_feature_flag","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformFeatureFlagSetIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformFeatureFlagOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/organizations/{org_id}/feature-flags":{"get":{"tags":["platform"],"summary":"Platform List Org Feature Flags","description":"Estado EFECTIVO por feature de una org (override > global > plan).","operationId":"platform_platform_list_org_feature_flags","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformOrgFeatureFlagOut"},"title":"Response Platform Platform List Org Feature Flags"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["platform"],"summary":"Platform Set Org Feature Flag","description":"Fija (upsert) el override de UNA org para una feature; auditado before→after.","operationId":"platform_platform_set_org_feature_flag","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformFeatureFlagSetIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrgFeatureFlagOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/{org_id}/exports":{"get":{"tags":["platform"],"summary":"Platform List Org Exports","description":"Historial de exports de datos de una organización (más recientes primero).","operationId":"platform_platform_list_org_exports","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformExportOut"},"title":"Response Platform Platform List Org Exports"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Org Export","description":"Encola un export asíncrono de TODOS los datos de la org (job Dramatiq).","operationId":"platform_platform_create_org_export","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformExportOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/exports/{export_id}":{"get":{"tags":["platform"],"summary":"Platform Get Export","description":"Estado de un export; cuando `done` incluye `download_url` (endpoint autenticado).","operationId":"platform_platform_get_export","parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","title":"Export Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformExportOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/exports/{export_id}/download":{"get":{"tags":["platform"],"summary":"Platform Download Export","description":"Descarga AUTENTICADA (PlatformDep) del ZIP: hace stream del blob del\nStorageService. NUNCA una ruta pública — exige la sesión de platform admin.","operationId":"platform_platform_download_export","parameters":[{"name":"export_id","in":"path","required":true,"schema":{"type":"string","title":"Export Id"}}],"responses":{"200":{"description":"ZIP del export (un JSONL por entidad de la org).","content":{"application/json":{"schema":{}},"application/zip":{}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/announcements":{"get":{"tags":["platform"],"summary":"Platform List Announcements","description":"Historial de anuncios de plataforma, más recientes primero (status + alcance).","operationId":"platform_platform_list_announcements","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AnnouncementOut"},"title":"Response Platform Platform List Announcements"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Announcement","description":"Crea el anuncio (`queued`) y encola el fan-out sobre notifications.\n\nValida coherencia de audiencia (plan exige `audience_plan`; org exige\n`audience_org_id`) → 422. El envío por destinatario respeta preferencias/bajas\nde canal. Auditado `platform.announcement_sent` al terminar el envío.","operationId":"platform_platform_create_announcement","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnnouncementCreateIn"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnnouncementOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/announcements/recipient-count":{"get":{"tags":["platform"],"summary":"Platform Announcement Recipient Count","description":"PREVIEW del recuento de destinatarios ANTES de disparar (sin enviar nada).\n\nResuelve la audiencia → cuenta usuarios elegibles. El dueño ve a cuántos va\nantes de confirmar. Valida la coherencia de la audiencia (→ 422).","operationId":"platform_platform_announcement_recipient_count","parameters":[{"name":"audience_scope","in":"query","required":true,"schema":{"enum":["global","plan","org"],"type":"string","description":"Segmentación de la audiencia.","title":"Audience Scope"},"description":"Segmentación de la audiencia."},{"name":"audience_plan","in":"query","required":false,"schema":{"anyOf":[{"enum":["free","pro"],"type":"string"},{"type":"null"}],"description":"Plan objetivo (obligatorio si audience_scope=plan).","title":"Audience Plan"},"description":"Plan objetivo (obligatorio si audience_scope=plan)."},{"name":"audience_org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Organización objetivo (obligatoria si audience_scope=org).","title":"Audience Org Id"},"description":"Organización objetivo (obligatoria si audience_scope=org)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnnouncementRecipientCountOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/{org_id}/purge":{"post":{"tags":["platform"],"summary":"Platform Purge Organization","description":"Pide la PURGA (borrado físico IRREVERSIBLE) de una org, validando LAS 5\nSALVAGUARDAS: soft-deleted + grace cumplido (→422), export `done` (→409),\nguards self/last-admin (→403) y `confirm_name` exacto (→422). Si pasan: crea la\nfila, audita `platform.org_purge_requested` y encola el job (que RE-verifica\nantes de borrar). 202 con la purga en `queued`.","operationId":"platform_platform_purge_organization","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformPurgeRequestIn"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformPurgeOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}}},"/api/v1/platform/purges":{"get":{"tags":["platform"],"summary":"Platform List Purges","description":"Historial de purgas de plataforma, más recientes primero (status incluido).","operationId":"platform_platform_list_purges","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformPurgeOut"},"title":"Response Platform Platform List Purges"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/global-suppliers":{"get":{"tags":["platform"],"summary":"Platform List Global Suppliers","description":"Catálogo global paginado con contador de proveedores org vinculados.","operationId":"platform_platform_list_global_suppliers","parameters":[{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por nombre o tax_id (parcial, case-insensitive).","title":"Search"},"description":"Búsqueda por nombre o tax_id (parcial, case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GlobalSupplierOut"},"title":"Response Platform Platform List Global Suppliers"}}},"headers":{"X-Total-Count":{"description":"Total de entradas que cumplen los filtros.","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["platform"],"summary":"Platform Create Global Supplier","description":"Alta manual en el catálogo. Dedupe por tax_id (409 `duplicate_tax_id`).","operationId":"platform_platform_create_global_supplier","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierWriteIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/global-suppliers/import":{"post":{"tags":["platform"],"summary":"Platform Import Global Suppliers","description":"Import masivo CSV (UTF-8, cabecera con `name`, máx. 2MB y 5000 filas).","operationId":"platform_platform_import_global_suppliers","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_platform_platform_import_global_suppliers"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierImportResultOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Entity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/global-suppliers/{global_supplier_id}":{"patch":{"tags":["platform"],"summary":"Platform Update Global Supplier","description":"Edita una entrada del catálogo. 409 si el tax_id colisiona con OTRA.","operationId":"platform_platform_update_global_supplier","parameters":[{"name":"global_supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Global Supplier Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierWriteIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["platform"],"summary":"Platform Delete Global Supplier","description":"Borra la entrada; los suppliers org vinculados quedan con el vínculo a null.","operationId":"platform_platform_delete_global_supplier","parameters":[{"name":"global_supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Global Supplier Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/suppliers/{supplier_id}/promote":{"post":{"tags":["platform"],"summary":"Platform Promote Supplier","description":"Promueve un proveedor org al catálogo global y lo vincula (idempotente).","operationId":"platform_platform_promote_supplier","parameters":[{"name":"supplier_id","in":"path","required":true,"schema":{"type":"string","title":"Supplier Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GlobalSupplierOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/organizations/{org_id}/global-suppliers":{"get":{"tags":["suppliers"],"summary":"List Global Suppliers For Org","description":"Catálogo global (solo lectura) para autocompletar el alta de proveedores.","operationId":"suppliers_list_global_suppliers_for_org","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda por nombre o tax_id (parcial, case-insensitive).","title":"Search"},"description":"Búsqueda por nombre o tax_id (parcial, case-insensitive)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GlobalSupplierOut"},"title":"Response Suppliers List Global Suppliers For Org"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/subscriptions":{"get":{"tags":["platform"],"summary":"Platform List Subscriptions","description":"Flota paginada de licencias (todas las orgs), con filtros combinables.","operationId":"platform_platform_list_subscriptions","parameters":[{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"enum":["free","pro"],"type":"string"},{"type":"null"}],"description":"Filtrar por plan EFECTIVO.","title":"Plan"},"description":"Filtrar por plan EFECTIVO."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por estado del espejo de Stripe.","title":"Status"},"description":"Filtrar por estado del espejo de Stripe."},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"enum":["default","stripe","manual"],"type":"string"},{"type":"null"}],"description":"Filtrar por origen del plan efectivo.","title":"Source"},"description":"Filtrar por origen del plan efectivo."},{"name":"diverged","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtrar por divergencia.","title":"Diverged"},"description":"Filtrar por divergencia."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformSubscriptionRowOut"},"title":"Response Platform Platform List Subscriptions"}}},"headers":{"X-Total-Count":{"description":"Total de organizaciones que cumplen los filtros.","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/subscription":{"get":{"tags":["platform"],"summary":"Platform Get Organization Subscription","description":"Foto de suscripción/licencia de una organización.","operationId":"platform_platform_get_organization_subscription","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/subscription/grant":{"post":{"tags":["platform"],"summary":"Platform Grant Plan","description":"Otorga un plan a mano (override manual, sin Stripe). `reason` obligatorio.\n409 `managed_by_stripe` si la org sigue pagando en Stripe y no se manda `force`.","operationId":"platform_platform_grant_plan","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformPlanGrantIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"}}},"delete":{"tags":["platform"],"summary":"Platform Revoke Plan","description":"Revoca el override de plan y recomputa el efectivo desde el espejo de Stripe.","operationId":"platform_platform_revoke_plan","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/billing/at-risk":{"get":{"tags":["platform"],"summary":"Platform List Billing At Risk","description":"Suscripciones con el cobro en riesgo (past_due|unpaid|incomplete).","operationId":"platform_platform_list_billing_at_risk","responses":{"200":{"description":"Successful Response","headers":{"X-Total-Count":{"description":"Total de suscripciones en riesgo.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformBillingAtRiskRowOut"},"type":"array","title":"Response Platform Platform List Billing At Risk"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/billing/at-risk/{org_id}/notify":{"post":{"tags":["platform"],"summary":"Platform Notify Billing At Risk","description":"Reenvía al owner el aviso de pago pendiente (auditado, anti-spam 24 h).","operationId":"platform_platform_notify_billing_at_risk","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformBillingAtRiskNotifyOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"}}}},"/api/v1/platform/organizations/{org_id}/subscription/sync-from-stripe":{"post":{"tags":["platform"],"summary":"Platform Sync Subscription From Stripe","description":"Reconcilia el espejo de suscripción desde Stripe (respeta el override manual).","operationId":"platform_platform_sync_subscription_from_stripe","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/subscription/resync":{"post":{"tags":["platform"],"summary":"Platform Resync Subscription","description":"Concilia una divergencia releyendo Stripe (misma lógica que el webhook).","operationId":"platform_platform_resync_subscription","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionResyncOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/organizations/{org_id}/subscription/cancel":{"post":{"tags":["platform"],"summary":"Platform Cancel Subscription","description":"Cancela la suscripción real de Stripe (al fin de periodo o de inmediato).","operationId":"platform_platform_cancel_subscription","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionCancelIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSubscriptionDetailOut"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Entity"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/v1/platform/revenue/summary":{"get":{"tags":["platform"],"summary":"Platform Revenue Summary","description":"MRR/ARR/ARPA + suscripciones activas (en vivo OFF `subscriptions`) +\ntendencia (OFF los 2 snapshots más recientes) + basis.","operationId":"platform_platform_revenue_summary","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformRevenueSummaryOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/revenue/by-plan":{"get":{"tags":["platform"],"summary":"Platform Revenue By Plan","description":"MRR por plan (Σ mrr_minor == summary.mrr_minor).","operationId":"platform_platform_revenue_by_plan","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlatformRevenueByPlanRowOut"},"type":"array","title":"Response Platform Platform Revenue By Plan"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/revenue/metrics":{"get":{"tags":["platform"],"summary":"Platform Revenue Metrics","description":"Churn y MRR por mes (serie ≤12 meses, empieza donde hay evidencia real)\n+ desglose actual por plan (PJKT-2040). Lectura pura: sin auditoría.","operationId":"platform_platform_revenue_metrics","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformRevenueMetricsOut"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/platform/revenue/timeseries":{"get":{"tags":["platform"],"summary":"Platform Revenue Timeseries","description":"Serie temporal diaria de revenue (de `mrr_daily_snapshots`, huecos\nrellenos por carry-forward, más antiguos primero).","operationId":"platform_platform_revenue_timeseries","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":7,"description":"Ventana en días (7–365, defecto 30).","default":30,"title":"Days"},"description":"Ventana en días (7–365, defecto 30)."},{"name":"metric","in":"query","required":false,"schema":{"enum":["mrr","revenue"],"type":"string","description":"Métrica: `mrr` o `revenue` (ARR run-rate).","default":"mrr","title":"Metric"},"description":"Métrica: `mrr` o `revenue` (ARR run-rate)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformRevenuePointOut"},"title":"Response Platform Platform Revenue Timeseries"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/platform/revenue/subscriptions":{"get":{"tags":["platform"],"summary":"Platform Revenue Subscriptions","description":"Suscripciones (espejo local) con importe precomputado + `synced`,\npaginadas (X-Total-Count).","operationId":"platform_platform_revenue_subscriptions","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por estado del espejo de Stripe.","title":"Status"},"description":"Filtrar por estado del espejo de Stripe."},{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"enum":["free","pro"],"type":"string"},{"type":"null"}],"description":"Filtrar por plan comprado.","title":"Plan"},"description":"Filtrar por plan comprado."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Resultados por página (1–200).","default":50,"title":"Limit"},"description":"Resultados por página (1–200)."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Filas a saltar (paginación).","default":0,"title":"Offset"},"description":"Filas a saltar (paginación)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformRevenueSubscriptionRowOut"},"title":"Response Platform Platform Revenue Subscriptions"}}},"headers":{"X-Total-Count":{"description":"Total de suscripciones que cumplen los filtros.","schema":{"type":"integer"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AbsenceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"leave_type":{"type":"string","enum":["vacation","sick","unpaid","other"],"title":"Leave Type"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"days":{"type":"number","title":"Days"}},"type":"object","required":["id","organization_id","employee_id","leave_type","start_date","end_date","days"],"title":"Absence","description":"Una ausencia aprobada para el calendario de equipo."},"ActiveProjectItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"task_total":{"type":"integer","title":"Task Total"},"task_done":{"type":"integer","title":"Task Done"}},"type":"object","required":["id","name","task_total","task_done"],"title":"DashboardActiveProjectItem","description":"Un proyecto entre los más activos (por nº de tareas) de la organización."},"ActiveTimerOut":{"properties":{"is_running":{"type":"boolean","title":"Is Running"},"timer":{"anyOf":[{"$ref":"#/components/schemas/TimeEntryRunningOut"},{"type":"null"}]}},"type":"object","required":["is_running","timer"],"title":"ActiveTimerOut","description":"Respuesta de `GET .../time-entries/active`: SIEMPRE 200; `timer` es\n`null` cuando el usuario no tiene ningún timer corriendo."},"ActivityCreateIn":{"properties":{"type":{"type":"string","enum":["call","email","meeting","note","task"],"title":"Type"},"body":{"type":"string","title":"Body"},"deal_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Deal Id"},"lead_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Lead Id"}},"type":"object","required":["type","body"],"title":"ActivityCreateIn"},"AddMembersIn":{"properties":{"member_ids":{"items":{"type":"string","format":"uuid"},"type":"array","minItems":1,"title":"Member Ids"}},"type":"object","required":["member_ids"],"title":"ChatAddMembersIn","description":"Añadir miembros a un canal group/project."},"AgencyClientRow":{"properties":{"client_id":{"type":"string","format":"uuid","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"projects_count":{"type":"integer","title":"Projects Count"},"open_tasks":{"type":"integer","title":"Open Tasks"},"currency":{"type":"string","title":"Currency","default":"EUR"},"invoiced_total":{"type":"number","title":"Invoiced Total"},"outstanding_total":{"type":"number","title":"Outstanding Total"},"schema_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Schema Note"}},"type":"object","required":["client_id","client_name","projects_count","open_tasks","invoiced_total","outstanding_total"],"title":"AgencyClientRow","description":"Métricas de un cliente en el roll-up de agencia.\n\n- `projects_count`: proyectos de la org vinculados a este cliente.\n- `open_tasks`:     tareas abiertas (status != done) en esos proyectos.\n- `invoiced_total`: suma de importes de facturas EMITIDAS (sent/paid/overdue)\n  de este cliente, identificado por `invoices.client_id` y solo por nombre\n  cuando la factura no tiene ficha vinculada (PJKT-2305).\n- `outstanding_total`: suma de facturas sent/overdue con mismo criterio; es un\n  SUBCONJUNTO de `invoiced_total`, nunca mayor que él (PJKT-2283).\n- `currency`: divisa de los dos importes (la base de la organización).\n- `schema_note`: nota sobre cómo se atribuyen las facturas y sobre la\n  limitación del vínculo proyecto→cliente."},"AgencyOverviewOut":{"properties":{"clients":{"items":{"$ref":"#/components/schemas/AgencyClientRow"},"type":"array","title":"Clients"},"org_projects_total":{"type":"integer","title":"Org Projects Total"},"org_open_tasks_total":{"type":"integer","title":"Org Open Tasks Total"},"currency":{"type":"string","title":"Currency","default":"EUR"},"org_invoiced_total":{"type":"number","title":"Org Invoiced Total"},"org_outstanding_total":{"type":"number","title":"Org Outstanding Total"}},"type":"object","required":["clients","org_projects_total","org_open_tasks_total","org_invoiced_total","org_outstanding_total"],"title":"AgencyOverview","description":"Payload del overview de agencia.\n\n- `clients`: lista de filas por cliente (cap 200).\n- `org_projects_total`:  total de proyectos con client_id en la org.\n- `org_open_tasks_total`: total de tareas abiertas en esos proyectos.\n- `org_invoiced_total`:  suma de facturas EMITIDAS (sent/paid/overdue) de la\n  org (global).\n- `org_outstanding_total`: suma de facturas sent/overdue de la org (global);\n  subconjunto de `org_invoiced_total` (PJKT-2283).\n- `currency`: divisa de los dos totales (la base de la organización)."},"AgingBucketOut":{"properties":{"label":{"type":"string","enum":["0-30","31-60","61-90","90+"],"title":"Label"},"count":{"type":"integer","title":"Count"},"total":{"type":"number","title":"Total"}},"type":"object","required":["label","count","total"],"title":"AgingBucket","description":"Un tramo de antigüedad de la deuda pendiente."},"AgingDebtorOut":{"properties":{"client_name":{"type":"string","title":"Client Name"},"outstanding":{"type":"number","title":"Outstanding"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["client_name","outstanding","count"],"title":"AgingDebtor","description":"Cliente con facturación pendiente de cobro."},"AgingReportOut":{"properties":{"currency":{"type":"string","title":"Currency","default":"EUR"},"buckets":{"items":{"$ref":"#/components/schemas/AgingBucketOut"},"type":"array","title":"Buckets"},"top_debtors":{"items":{"$ref":"#/components/schemas/AgingDebtorOut"},"type":"array","title":"Top Debtors"},"total_outstanding":{"type":"number","title":"Total Outstanding"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["buckets","top_debtors","total_outstanding","criterio"],"title":"AgingReport","description":"Antigüedad de la deuda (AR aging) de facturas no cobradas."},"AmortizationMonthOut":{"properties":{"month":{"type":"string","title":"Month"},"depreciation":{"type":"number","title":"Depreciation"},"accumulated":{"type":"number","title":"Accumulated"},"book_value":{"type":"number","title":"Book Value"}},"type":"object","required":["month","depreciation","accumulated","book_value"],"title":"AmortizationMonth","description":"Un mes de la tabla de amortización."},"AnnouncementCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"body":{"type":"string","title":"Body"},"audience_scope":{"type":"string","enum":["global","plan","org"],"title":"Audience Scope"},"audience_plan":{"anyOf":[{"type":"string","enum":["free","pro"]},{"type":"null"}],"title":"Audience Plan"},"audience_org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Audience Org Id"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"}},"type":"object","required":["title","body","audience_scope"],"title":"AnnouncementCreate","description":"Cuerpo para crear (y encolar el envío de) un anuncio de plataforma.\n\nCoherencia de audiencia (validada aquí → 422 `validation_error`):\n`plan` exige `audience_plan`; `org` exige `audience_org_id`. `global` no\nnecesita ninguno (va a todos los usuarios activos)."},"AnnouncementOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"body":{"type":"string","title":"Body"},"audience_scope":{"type":"string","enum":["global","plan","org"],"title":"Audience Scope"},"audience_plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audience Plan"},"audience_org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Audience Org Id"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"status":{"type":"string","enum":["draft","queued","sending","sent","failed"],"title":"Status"},"recipient_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Recipient Count"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"}},"type":"object","required":["id","title","body","audience_scope","status","created_at"],"title":"Announcement"},"AnnouncementRecipientCountOut":{"properties":{"count":{"type":"integer","title":"Count"},"audience_scope":{"type":"string","enum":["global","plan","org"],"title":"Audience Scope"}},"type":"object","required":["count","audience_scope"],"title":"AnnouncementRecipientCount","description":"Preview del recuento de destinatarios elegibles (sin enviar nada)."},"ApAgingBucketOut":{"properties":{"label":{"type":"string","enum":["0-30","31-60","61-90","90+","sin-vencimiento"],"title":"Label"},"count":{"type":"integer","title":"Count"},"total":{"type":"number","title":"Total"}},"type":"object","required":["label","count","total"],"title":"ApAgingBucket"},"ApAgingReportOut":{"properties":{"currency":{"type":"string","title":"Currency","default":"EUR"},"buckets":{"items":{"$ref":"#/components/schemas/ApAgingBucketOut"},"type":"array","title":"Buckets"},"top_suppliers":{"items":{"$ref":"#/components/schemas/ApSupplierOut"},"type":"array","title":"Top Suppliers"},"total_outstanding":{"type":"number","title":"Total Outstanding"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["buckets","top_suppliers","total_outstanding","criterio"],"title":"ApAgingReport"},"ApSupplierOut":{"properties":{"supplier_name":{"type":"string","title":"Supplier Name"},"outstanding":{"type":"number","title":"Outstanding"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["supplier_name","outstanding","count"],"title":"ApSupplier"},"ApiKeyCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["name"],"title":"ApiKeyCreateIn","description":"Cuerpo de creación: {name, project_id?, expires_at?}.\n\n`name` obligatorio (1-120 chars). `project_id` opcional: si se omite/`null`\nla key es org-wide (requiere admin+); si se fija, la key queda acotada a ese\nproyecto (cualquier member+ del proyecto puede acuñarla). `expires_at`\nopcional: caducidad de la key; si se omite/`null` la key no caduca nunca."},"ApiKeyCreatedOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"token_prefix":{"type":"string","title":"Token Prefix"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"token":{"type":"string","title":"Token"}},"type":"object","required":["id","name","project_id","token_prefix","created_by","last_used_at","revoked_at","expires_at","created_at","token"],"title":"ApiKeyCreated","description":"Respuesta de creación: como `ApiKeyOut` + el token en claro `token`.\n\nEl token se muestra UNA sola vez (no se puede recuperar después); en la DB\nsolo queda su hash SHA-256."},"ApiKeyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"token_prefix":{"type":"string","title":"Token Prefix"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","project_id","token_prefix","created_by","last_used_at","revoked_at","expires_at","created_at"],"title":"ApiKey","description":"API key enmascarada: id, name, project_id, token_prefix, created_by,\nlast_used_at, revoked_at, expires_at, created_at. NUNCA expone el token ni su\nhash.\n\n`project_id` = `null` para keys org-wide; el UUID del proyecto si está acotada.\n`expires_at` = `null` si la key no caduca nunca."},"ApiKeyScopeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"}},"type":"object","required":["id","name","organization_id"],"title":"ApiKeyScope","description":"Scope de la API key que autenticó la petición (solo en auth via PAT).\n\nPresente en `UserOut.api_key` cuando la request usa `Authorization: Bearer\npjk_live_…`; `null`/ausente en sesiones por cookie."},"AssetAmortizationOut":{"properties":{"asset_id":{"type":"string","format":"uuid","title":"Asset Id"},"name":{"type":"string","title":"Name"},"acquisition_cost":{"type":"number","title":"Acquisition Cost"},"salvage_value":{"type":"number","title":"Salvage Value"},"useful_life_months":{"type":"integer","title":"Useful Life Months"},"monthly_depreciation":{"type":"number","title":"Monthly Depreciation"},"accumulated_depreciation":{"type":"number","title":"Accumulated Depreciation"},"book_value":{"type":"number","title":"Book Value"},"as_of":{"type":"string","format":"date","title":"As Of"},"schedule":{"items":{"$ref":"#/components/schemas/AmortizationMonthOut"},"type":"array","title":"Schedule"}},"type":"object","required":["asset_id","name","acquisition_cost","salvage_value","useful_life_months","monthly_depreciation","accumulated_depreciation","book_value","as_of","schedule"],"title":"AssetAmortization","description":"Plan de amortización completo de un activo."},"AssetPriceRefreshItemOut":{"properties":{"asset_id":{"type":"string","format":"uuid","title":"Asset Id"},"symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Symbol"},"asset_type":{"type":"string","enum":["fixed","crypto","etf"],"title":"Asset Type"},"status":{"type":"string","enum":["updated","unavailable","skipped"],"title":"Status"},"market_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Price"},"market_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Value"},"last_price_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Price At"}},"type":"object","required":["asset_id","symbol","asset_type","status","market_price","market_value","last_price_at"],"title":"AssetPriceRefreshItem","description":"Resultado de refrescar el precio de un activo de mercado."},"AssetPriceRefreshOut":{"properties":{"updated":{"type":"integer","title":"Updated"},"unavailable":{"type":"integer","title":"Unavailable"},"skipped":{"type":"integer","title":"Skipped"},"items":{"items":{"$ref":"#/components/schemas/AssetPriceRefreshItemOut"},"type":"array","title":"Items"}},"type":"object","required":["updated","unavailable","skipped","items"],"title":"AssetPriceRefresh","description":"Resumen de una actualización de precios en lote."},"AssetsRegisterSummaryOut":{"properties":{"total_assets":{"type":"integer","title":"Total Assets"},"active_assets":{"type":"integer","title":"Active Assets"},"disposed_assets":{"type":"integer","title":"Disposed Assets"},"total_acquisition_cost":{"type":"number","title":"Total Acquisition Cost"},"total_accumulated_depreciation":{"type":"number","title":"Total Accumulated Depreciation"},"total_net_book_value":{"type":"number","title":"Total Net Book Value"},"total_market_value":{"type":"number","title":"Total Market Value"}},"type":"object","required":["total_assets","active_assets","disposed_assets","total_acquisition_cost","total_accumulated_depreciation","total_net_book_value","total_market_value"],"title":"AssetsRegisterSummary","description":"Resumen del registro de activos fijos."},"AssignSprintTasksIn":{"properties":{"task_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Task Ids"}},"type":"object","required":["task_ids"],"title":"AssignSprintTasksIn","description":"Asigna un lote de tareas al sprint (todas deben ser del mismo proyecto)."},"AttachmentBase64In":{"properties":{"entity_type":{"type":"string","enum":["task","supplier_invoice"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"filename":{"type":"string","title":"Filename"},"content_base64":{"type":"string","title":"Content Base64"}},"type":"object","required":["entity_type","entity_id","filename","content_base64"],"title":"AttachmentBase64In","description":"Body de `POST .../projects/{project_id}/attachments` (adjuntar por base64,\napto para MCP / PAT scoped a proyecto). La entidad debe ser project-owned\n(`task` o `supplier_invoice`)."},"AttachmentUploadBase64":{"properties":{"entity_type":{"type":"string","enum":["invoice","expense","task","payslip","supplier_invoice","client","document"],"title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"filename":{"type":"string","title":"Filename"},"content_base64":{"type":"string","title":"Content Base64"}},"type":"object","required":["entity_type","entity_id","filename","content_base64"],"title":"AttachmentUploadBase64","description":"Body de `POST .../organizations/{org_id}/attachments/base64` (adjuntar por\nbase64, apto para MCP). La entidad debe ser org-scoped y con adjuntos:\n`invoice`, `expense`, `task`, `supplier_invoice`, `payslip` o `client`.\n\nA diferencia de `AttachmentBase64In` (project-scoped, solo task/supplier_invoice),\nadmite ingresos y gastos, que no cuelgan de un proyecto."},"AuditLogEntryOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"action":{"type":"string","title":"Action"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"target_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Target Id"},"target_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Type"},"actor_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Ip"},"actor_user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor User Agent"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","action","created_at"],"title":"AuditLogEntry"},"AutomationActionResult":{"properties":{"rule_id":{"type":"string","format":"uuid","title":"Rule Id"},"rule_name":{"type":"string","title":"Rule Name"},"conditions_matched":{"type":"boolean","title":"Conditions Matched"},"action":{"type":"string","title":"Action"},"action_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Action Params"},"would_apply":{"type":"boolean","title":"Would Apply"},"description":{"type":"string","title":"Description"}},"type":"object","required":["rule_id","rule_name","conditions_matched","action","action_params","would_apply","description"],"title":"AutomationActionResult","description":"Resultado de evaluar UNA acción en el dry-run."},"AutomationCondition":{"properties":{"field":{"type":"string","enum":["status","priority","type","project_id","assignee_id"],"title":"Field"},"op":{"type":"string","enum":["eq","ne","in"],"title":"Op"},"value":{"title":"Value"}},"type":"object","required":["field","op","value"],"title":"AutomationCondition","description":"Una condición individual de una regla.\n\nfield: uno de los campos whitelisteados de la tarea.\nop:    eq (igual) | ne (distinto) | in (cualquiera de una lista).\nvalue: valor literal; para \"in\" debe ser una lista."},"AutomationRuleCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"trigger_event":{"type":"string","enum":["task_created","task_status_changed","task_assigned"],"title":"Trigger Event"},"conditions":{"anyOf":[{"items":{"$ref":"#/components/schemas/AutomationCondition"},"type":"array"},{"type":"null"}],"title":"Conditions"},"action":{"type":"string","enum":["set_priority","set_status","assign","add_comment","notify"],"title":"Action"},"action_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Action Params"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"type":"object","required":["name","trigger_event","action"],"title":"AutomationRuleCreateIn"},"AutomationRuleOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"trigger_event":{"type":"string","title":"Trigger Event"},"conditions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Conditions"},"action":{"type":"string","title":"Action"},"action_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Action Params"},"is_active":{"type":"boolean","title":"Is Active"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","trigger_event","conditions","action","action_params","is_active","created_by","created_at","updated_at"],"title":"AutomationRule","description":"Regla de automatización (respuesta del contrato)."},"AutomationRuleUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"trigger_event":{"anyOf":[{"type":"string","enum":["task_created","task_status_changed","task_assigned"]},{"type":"null"}],"title":"Trigger Event"},"conditions":{"anyOf":[{"items":{"$ref":"#/components/schemas/AutomationCondition"},"type":"array"},{"type":"null"}],"title":"Conditions"},"action":{"anyOf":[{"type":"string","enum":["set_priority","set_status","assign","add_comment","notify"]},{"type":"null"}],"title":"Action"},"action_params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Action Params"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"AutomationRuleUpdateIn"},"AutomationTestIn":{"properties":{"task_id":{"type":"string","format":"uuid","title":"Task Id"}},"type":"object","required":["task_id"],"title":"AutomationTestIn","description":"Payload para dry-run de una regla contra una tarea concreta."},"BiAdHocQueryIn":{"properties":{"data_source":{"type":"string","enum":["tasks","invoices","expenses","projects","clients","deals"],"title":"Data Source"},"dimension":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Dimension"},"measure":{"type":"string","enum":["count","sum","avg"],"title":"Measure","default":"count"},"measure_field":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Measure Field"},"filters":{"anyOf":[{"items":{"$ref":"#/components/schemas/BiFilterSpec"},"type":"array"},{"type":"null"}],"title":"Filters"}},"type":"object","required":["data_source"],"title":"BiAdHocQueryIn"},"BiDashboardCreateIn":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"BiDashboardCreateIn"},"BiDashboardOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","created_by","created_at","updated_at"],"title":"BiDashboard"},"BiDashboardUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","title":"BiDashboardUpdateIn"},"BiFilterSpec":{"properties":{"column":{"type":"string","title":"Column"},"op":{"type":"string","enum":["eq","ne","gt","lt","in"],"title":"Op"},"value":{"title":"Value"}},"type":"object","required":["column","op","value"],"title":"BiFilterSpec","description":"One filter clause for a BI widget query."},"BiPositionSpec":{"properties":{"x":{"type":"integer","title":"X","default":0},"y":{"type":"integer","title":"Y","default":0},"w":{"type":"integer","title":"W","default":4},"h":{"type":"integer","title":"H","default":4}},"type":"object","title":"BiPositionSpec","description":"Grid position for a BI widget (x, y, w, h)."},"BiWidgetCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"widget_type":{"type":"string","enum":["kpi","bar","line","pie","table"],"title":"Widget Type"},"data_source":{"type":"string","enum":["tasks","invoices","expenses","projects","clients","deals"],"title":"Data Source"},"dimension":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Dimension"},"measure":{"type":"string","enum":["count","sum","avg"],"title":"Measure","default":"count"},"measure_field":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Measure Field"},"filters":{"anyOf":[{"items":{"$ref":"#/components/schemas/BiFilterSpec"},"type":"array"},{"type":"null"}],"title":"Filters"},"position":{"anyOf":[{"$ref":"#/components/schemas/BiPositionSpec"},{"type":"null"}]}},"type":"object","required":["title","widget_type","data_source"],"title":"BiWidgetCreateIn"},"BiWidgetDataOut":{"properties":{"widget_id":{"type":"string","format":"uuid","title":"Widget Id"},"data_source":{"type":"string","title":"Data Source"},"dimension":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dimension"},"measure":{"type":"string","title":"Measure"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"rows":{"items":{"$ref":"#/components/schemas/BiWidgetDataRow"},"type":"array","title":"Rows"}},"type":"object","required":["widget_id","data_source","dimension","measure","rows"],"title":"BiWidgetData"},"BiWidgetDataRow":{"properties":{"dimension_value":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dimension Value"},"value":{"title":"Value"}},"type":"object","required":["value"],"title":"BiWidgetDataRow"},"BiWidgetOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"dashboard_id":{"type":"string","format":"uuid","title":"Dashboard Id"},"title":{"type":"string","title":"Title"},"widget_type":{"type":"string","title":"Widget Type"},"data_source":{"type":"string","title":"Data Source"},"dimension":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dimension"},"measure":{"type":"string","title":"Measure"},"measure_field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Measure Field"},"filters":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Filters"},"position":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Position"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","dashboard_id","title","widget_type","data_source","dimension","measure","measure_field","filters","position","created_at","updated_at"],"title":"BiWidget"},"BiWidgetUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"widget_type":{"anyOf":[{"type":"string","enum":["kpi","bar","line","pie","table"]},{"type":"null"}],"title":"Widget Type"},"data_source":{"anyOf":[{"type":"string","enum":["tasks","invoices","expenses","projects","clients","deals"]},{"type":"null"}],"title":"Data Source"},"dimension":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Dimension"},"measure":{"anyOf":[{"type":"string","enum":["count","sum","avg"]},{"type":"null"}],"title":"Measure"},"measure_field":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Measure Field"},"filters":{"anyOf":[{"items":{"$ref":"#/components/schemas/BiFilterSpec"},"type":"array"},{"type":"null"}],"title":"Filters"},"position":{"anyOf":[{"$ref":"#/components/schemas/BiPositionSpec"},{"type":"null"}]}},"type":"object","title":"BiWidgetUpdateIn"},"BillingRedirect":{"properties":{"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"BillingRedirect","description":"URL de Stripe a la que redirigir el navegador (Checkout o Billing Portal)."},"BillingWebhookAck":{"properties":{"received":{"type":"boolean","title":"Received"}},"type":"object","required":["received"],"title":"BillingWebhookAck","description":"Acuse del webhook de Stripe (200) para que Stripe no reintente el evento."},"BoardColumnCreateIn":{"properties":{"name":{"type":"string","maxLength":100,"title":"Name"},"status_key":{"type":"string","title":"Status Key"},"category":{"anyOf":[{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},{"type":"null"}],"title":"Category"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"position":{"type":"integer","title":"Position","default":0},"wip_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Wip Limit"}},"type":"object","required":["name","status_key"],"title":"BoardColumnCreateIn"},"BoardColumnGroupOut":{"properties":{"column":{"$ref":"#/components/schemas/BoardColumnOut"},"task_count":{"type":"integer","title":"Task Count"},"over_wip":{"type":"boolean","title":"Over Wip"},"tasks":{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array","title":"Tasks"},"swimlanes":{"items":{"$ref":"#/components/schemas/BoardSwimlaneGroupOut"},"type":"array","title":"Swimlanes"}},"type":"object","required":["column","task_count","over_wip","tasks","swimlanes"],"title":"BoardColumnGroup","description":"Una columna del tablero con sus tareas agrupadas + señal de WIP."},"BoardColumnOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"name":{"type":"string","title":"Name"},"position":{"type":"integer","title":"Position"},"status_key":{"type":"string","title":"Status Key"},"category":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"],"title":"Category"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"wip_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Wip Limit"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","name","position","status_key","category","color","wip_limit","created_at","updated_at"],"title":"BoardColumn"},"BoardColumnReorderIn":{"properties":{"column_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Column Ids"}},"type":"object","required":["column_ids"],"title":"BoardColumnReorderIn","description":"Payload para reordenar columnas. El orden del array determina la posición\n(índice 0 = primera columna). Coincide con el contrato OpenAPI ``column_ids``."},"BoardColumnUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name"},"category":{"anyOf":[{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},{"type":"null"}],"title":"Category"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"},"wip_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Wip Limit"}},"type":"object","title":"BoardColumnUpdateIn"},"BoardOut":{"properties":{"swimlane":{"type":"string","enum":["none","assignee","priority","type"],"title":"Swimlane"},"columns":{"items":{"$ref":"#/components/schemas/BoardColumnGroupOut"},"type":"array","title":"Columns"}},"type":"object","required":["swimlane","columns"],"title":"Board","description":"Tablero completo del proyecto en una sola respuesta."},"BoardSwimlaneGroupOut":{"properties":{"key":{"type":"string","title":"Key"},"label":{"type":"string","title":"Label"},"task_count":{"type":"integer","title":"Task Count"},"tasks":{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array","title":"Tasks"}},"type":"object","required":["key","label","task_count","tasks"],"title":"BoardSwimlaneGroup","description":"Carril de una columna: tareas particionadas por assignee/priority/type."},"Body_oauthToken":{"properties":{"grant_type":{"type":"string","title":"Grant Type","default":""},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"},"redirect_uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Uri"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"code_verifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Verifier"},"refresh_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"}},"type":"object","title":"Body_oauthToken"},"Body_platform_platform_import_global_suppliers":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_platform_platform_import_global_suppliers"},"Body_uploadAttachment":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_uploadAttachment"},"Body_uploadChatAttachment":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_uploadChatAttachment"},"Body_uploadDocumentFile":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Título; por defecto el nombre del fichero."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Proyecto asociado."},"folder_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Id","description":"Carpeta donde archivarlo."},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"Documento padre en el árbol."}},"type":"object","required":["file"],"title":"Body_uploadDocumentFile"},"Body_uploadTaskAttachment":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_uploadTaskAttachment"},"BudgetCreateIn":{"properties":{"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"period":{"type":"string","title":"Period"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["category","period","amount"],"title":"BudgetCreateIn"},"BudgetOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"period":{"type":"string","title":"Period"},"amount":{"type":"number","title":"Amount"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","category","period","amount","notes","created_at","updated_at"],"title":"Budget"},"BudgetUpdateIn":{"properties":{"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"BudgetUpdateIn","description":"PATCH parcial."},"BudgetVsActualLineOut":{"properties":{"budget_id":{"type":"string","format":"uuid","title":"Budget Id"},"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"period":{"type":"string","title":"Period"},"budgeted":{"type":"number","title":"Budgeted"},"actual":{"type":"number","title":"Actual"},"remaining":{"type":"number","title":"Remaining"},"pct_used":{"type":"number","title":"Pct Used"}},"type":"object","required":["budget_id","category","project_id","period","budgeted","actual","remaining","pct_used"],"title":"BudgetVsActualLine","description":"Una línea del reporte presupuesto-vs-real."},"BudgetVsActualOut":{"properties":{"period":{"type":"string","title":"Period"},"currency":{"type":"string","title":"Currency","default":"EUR"},"lines":{"items":{"$ref":"#/components/schemas/BudgetVsActualLineOut"},"type":"array","title":"Lines"},"total_budgeted":{"type":"number","title":"Total Budgeted"},"total_actual":{"type":"number","title":"Total Actual"},"total_remaining":{"type":"number","title":"Total Remaining"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["period","lines","total_budgeted","total_actual","total_remaining","criterio"],"title":"BudgetVsActual","description":"Reporte completo presupuesto-vs-real para un período.\n\n`currency` (PJKT-2264) es la divisa BASE de la organización y TODOS los\nimportes del reporte —presupuestado y real, por línea y en total— están en\nella. El gasto real solo agrega documentos registrados en esa divisa: un\npresupuesto no tiene divisa propia (es dinero de la casa), así que comparar\ncontra una suma de euros y dólares daba un «excedido» inventado. No se\nconvierte nada; lo gastado en otra divisa queda fuera."},"BulkActionErrorOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["id","reason"],"title":"BulkActionError"},"BulkActionResultOut":{"properties":{"processed":{"type":"integer","title":"Processed"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"$ref":"#/components/schemas/BulkActionErrorOut"},"type":"array","title":"Errors"}},"type":"object","required":["processed","skipped","errors"],"title":"BulkActionResult","description":"Resultado honesto por lote: aplicados, omitidos y errores por id."},"BulkInviteIn":{"properties":{"emails":{"items":{"type":"string"},"type":"array","maxItems":200,"minItems":1,"title":"Emails"},"role":{"type":"string","enum":["member","manager","admin"],"title":"Role","default":"member"}},"type":"object","required":["emails"],"title":"BulkInviteIn","description":"POST .../members/invite-bulk — una lista de correos con UN rol común.\n\nNo se valida aquí la forma de cada correo a propósito: un 422 global por\nuna dirección mal escrita obligaría a corregir a ciegas y perdería el\nresto del lote. Cada correo se valida en el service y se reporta como\n`invalid_email` en su propia fila."},"BulkInviteItemOut":{"properties":{"email":{"type":"string","title":"Email"},"status":{"type":"string","enum":["invited","added","already_member","already_invited","duplicate","invalid_email","plan_limit_reached"],"title":"Status"},"invite_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Invite Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},"type":"object","required":["email","status"],"title":"BulkInviteItem","description":"Desenlace de UN correo del lote."},"BulkInviteResultOut":{"properties":{"results":{"items":{"$ref":"#/components/schemas/BulkInviteItemOut"},"type":"array","title":"Results"},"invited":{"type":"integer","title":"Invited"},"added":{"type":"integer","title":"Added"},"skipped":{"type":"integer","title":"Skipped"},"failed":{"type":"integer","title":"Failed"}},"type":"object","required":["results","invited","added","skipped","failed"],"title":"BulkInviteResult","description":"Resultado del alta en lote: una fila por correo + contadores."},"BurndownPointOut":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"ideal_remaining":{"type":"number","title":"Ideal Remaining"},"actual_remaining":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Actual Remaining"}},"type":"object","required":["date","ideal_remaining","actual_remaining"],"title":"SprintBurndownPoint","description":"Un día del burndown: puntos restantes ideal vs. real.\n\n`actual_remaining` es null para días futuros (más allá de hoy); el ideal se\nemite para todo el rango start_date..end_date."},"BusinessDueOut":{"properties":{"start":{"type":"string","format":"date-time","title":"Start"},"minutes":{"type":"integer","title":"Minutes"},"due_at":{"type":"string","format":"date-time","title":"Due At"},"timezone":{"type":"string","title":"Timezone"},"schedule_source":{"type":"string","enum":["organization","default"],"title":"Schedule Source"}},"type":"object","required":["start","minutes","due_at","timezone","schedule_source"],"title":"BusinessDue","description":"Instante en el que se cumplen N minutos laborables desde `start`.\n\nEs el cálculo de un vencimiento («cuatro horas laborables de respuesta»).\n`due_at` va en UTC, como todos los instantes de la API."},"BusinessMinutesOut":{"properties":{"start":{"type":"string","format":"date-time","title":"Start"},"end":{"type":"string","format":"date-time","title":"End"},"minutes":{"type":"integer","title":"Minutes"},"timezone":{"type":"string","title":"Timezone"},"schedule_source":{"type":"string","enum":["organization","default"],"title":"Schedule Source"}},"type":"object","required":["start","end","minutes","timezone","schedule_source"],"title":"BusinessMinutes","description":"Minutos laborables entre dos instantes, según la jornada de la org.\n\n`minutes` cuenta solo el tiempo dentro de la jornada laboral de la\norganización, descontando fines de semana, festivos (los recurrentes ya\nproyectados al año que toque) y las horas fuera de jornada. Se mide en su\nzona horaria, así que los cambios de hora de marzo y octubre no lo desplazan."},"CalendarEventOut":{"properties":{"kind":{"type":"string","enum":["holiday","absence"],"title":"Kind"},"title":{"type":"string","title":"Title"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"reference_id":{"type":"string","format":"uuid","title":"Reference Id"},"recurring":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Recurring"},"employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Employee Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Name"},"leave_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Leave Type"}},"type":"object","required":["kind","title","start_date","end_date","reference_id"],"title":"CalendarEvent","description":"Evento del calendario de la organización dentro del rango consultado.\n\n`kind=holiday` → festivo (recurring puede ser true/false); los recurrentes se\nexpanden a su fecha del rango. `kind=absence` → ausencia APROBADA que solapa\nel rango (con employee_id, employee_name, user_id y leave_type)."},"CalendarFeedOut":{"properties":{"token":{"type":"string","title":"Token"},"subscribe_url":{"type":"string","title":"Subscribe Url"}},"type":"object","required":["token","subscribe_url"],"title":"CalendarFeed","description":"Respuesta de GET /me/calendar-feed."},"CapacityOut":{"properties":{"week_start":{"type":"string","format":"date","title":"Week Start"},"week_end":{"type":"string","format":"date","title":"Week End"},"rows":{"items":{"$ref":"#/components/schemas/CapacityRowOut"},"type":"array","title":"Rows"}},"type":"object","required":["week_start","week_end","rows"],"title":"Capacity","description":"Respuesta del panel de capacidad: la semana + las filas por miembro."},"CapacityRowOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Employee Id"},"expected_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Expected Hours"},"expected_source":{"type":"string","enum":["override","schedule","department","org","none"],"title":"Expected Source"},"holiday_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Holiday Hours"},"absence_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Absence Hours"},"net_expected_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Net Expected Hours"},"logged_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Logged Hours"},"delta_hours":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Delta Hours"},"has_override":{"type":"boolean","title":"Has Override"},"conflict_days":{"items":{"type":"string","format":"date"},"type":"array","title":"Conflict Days"},"has_conflict":{"type":"boolean","title":"Has Conflict"}},"type":"object","required":["user_id","name","expected_hours","expected_source","holiday_hours","absence_hours","net_expected_hours","logged_hours","delta_hours","has_override","conflict_days","has_conflict"],"title":"CapacityRow","description":"Fila de conciliación de capacidad de un miembro para una semana.\n\nTodas las horas son `number` (horas decimales, no minutos). El significado:\n- `expected_hours`: horas esperadas brutas de la semana según el modelo\n  híbrido (precedencia: override manual → horario del empleado → horario del\n  departamento). `expected_source` indica cuál ganó.\n- `holiday_hours`: horas descontadas por festivos (del departamento y de la\n  organización) que caen en la semana (solo si el festivo cae en un día\n  laborable del horario).\n- `absence_hours`: horas descontadas por ausencias aprobadas que solapan la\n  semana, prorrateadas por día laborable.\n- `net_expected_hours`: capacidad NETA = max(0, esperadas − festivos −\n  ausencias). Es contra lo que se compara lo imputado.\n- `logged_hours`: horas imputadas (time_entries) del miembro esa semana.\n- `delta_hours`: `logged − net_expected` (negativo = faltan horas; positivo =\n  sobran / exceso).\n- `conflict_days`: días de la semana (ISO `YYYY-MM-DD`) en los que el miembro\n  tiene una ausencia APROBADA que solapa Y además ha imputado tiempo — una\n  incoherencia (no se debería fichar durante una ausencia aprobada).\n- `has_conflict`: `true` si `conflict_days` no está vacío."},"ChannelCreateIn":{"properties":{"type":{"type":"string","enum":["dm","group","project","channel"],"title":"Type"},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"is_public":{"type":"boolean","title":"Is Public","default":false},"voice_enabled":{"type":"boolean","title":"Voice Enabled","default":false},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"member_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Member Ids"}},"type":"object","required":["type"],"title":"ChatChannelCreateIn","description":"Crear canal. DM = idempotente entre el creador y UN member_id.\n\n- dm: exactamente 1 en member_ids (el otro usuario); sin name.\n- group: name + member_ids (opcional).\n- project: project_id + member_ids (opcional); ligado al proyecto.\n- channel (estilo Discord, PJKT-1957): name obligatorio + description e\n  is_public opcionales; member_ids = miembros iniciales.\nEl creador siempre queda como owner del canal."},"ChannelDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"type":"string","enum":["dm","group","project","channel"],"title":"Type"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"icon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon Url"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"is_public":{"type":"boolean","title":"Is Public","default":false},"voice_enabled":{"type":"boolean","title":"Voice Enabled","default":false},"is_cross_org":{"type":"boolean","title":"Is Cross Org","default":false},"peer_organization_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Organization Name"},"is_member":{"type":"boolean","title":"Is Member","default":true},"member_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Count"},"is_archived":{"type":"boolean","title":"Is Archived"},"unread":{"type":"integer","title":"Unread"},"is_muted":{"type":"boolean","title":"Is Muted"},"peer_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Peer Id"},"peer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Name"},"peer_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Avatar Url"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"members":{"items":{"$ref":"#/components/schemas/ChannelMemberOut"},"type":"array","title":"Members"}},"type":"object","required":["id","type","name","project_id","is_archived","unread","is_muted","created_at","updated_at","members"],"title":"ChatChannelDetail"},"ChannelMemberOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"type":"string","title":"Role"},"joined_at":{"type":"string","format":"date-time","title":"Joined At"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"}},"type":"object","required":["user_id","role","joined_at"],"title":"ChatChannelMember"},"ChannelOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"type":"string","enum":["dm","group","project","channel"],"title":"Type"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"icon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon Url"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"is_public":{"type":"boolean","title":"Is Public","default":false},"voice_enabled":{"type":"boolean","title":"Voice Enabled","default":false},"is_cross_org":{"type":"boolean","title":"Is Cross Org","default":false},"peer_organization_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Organization Name"},"is_member":{"type":"boolean","title":"Is Member","default":true},"member_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Count"},"is_archived":{"type":"boolean","title":"Is Archived"},"unread":{"type":"integer","title":"Unread"},"is_muted":{"type":"boolean","title":"Is Muted"},"peer_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Peer Id"},"peer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Name"},"peer_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer Avatar Url"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","type","name","project_id","is_archived","unread","is_muted","created_at","updated_at"],"title":"ChatChannel","description":"Resumen de canal para la lista. `unread` llega en F1.4 (de momento 0)."},"ChannelUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"icon_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Icon Url"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public"},"voice_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Voice Enabled"}},"type":"object","title":"ChatChannelUpdateIn","description":"Editar un canal: nombre, descripción, icono (PJKT-1871) y, en canales\nestilo Discord, la visibilidad público/privado (PJKT-1957).\n\nTodos los campos son opcionales; se aplican solo los presentes. Un DM no se\nedita (nombre/icono se derivan del interlocutor). `is_public` solo aplica a\ntype='channel' (422 en el resto). `icon_url` se normaliza como URL opcional\n(vacío → None para «quitar el icono»)."},"CheckoutSessionCreate":{"properties":{"plan":{"type":"string","const":"pro","title":"Plan"},"billing_period":{"type":"string","enum":["monthly","annual"],"title":"Billing Period","default":"monthly"},"promo_code":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Promo Code"},"success_path":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Success Path"}},"type":"object","required":["plan"],"title":"CheckoutSessionCreate","description":"Cuerpo para crear una sesión de Stripe Checkout: el plan de pago a contratar."},"ClientContactCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_primary":{"type":"boolean","title":"Is Primary","default":false}},"type":"object","required":["name"],"title":"ClientContactCreateIn"},"ClientContactOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"name":{"type":"string","title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_primary":{"type":"boolean","title":"Is Primary"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","client_id","name","email","phone","job_title","notes","is_primary","is_active","created_at","updated_at"],"title":"ClientContact"},"ClientContactUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"job_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Title"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_primary":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Primary"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"ClientContactUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes."},"ClientCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"client_type":{"type":"string","enum":["legal","individual"],"title":"Client Type","default":"legal"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["name"],"title":"ClientCreateIn"},"ClientLinkOrgIn":{"properties":{"target_org_id":{"type":"string","format":"uuid","title":"Target Org Id"}},"type":"object","required":["target_org_id"],"title":"ClientLinkOrgIn"},"ClientLinkOrgOut":{"properties":{"client_id":{"type":"string","format":"uuid","title":"Client Id"},"linked_org_id":{"type":"string","format":"uuid","title":"Linked Org Id"}},"type":"object","required":["client_id","linked_org_id"],"title":"ClientLinkOrg"},"ClientOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"client_type":{"type":"string","enum":["legal","individual"],"title":"Client Type"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_active":{"type":"boolean","title":"Is Active"},"linked_org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Linked Org Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","logo_url","client_type","email","phone","company","tax_id","address","city","country","postal_code","notes","is_active","linked_org_id","created_at","updated_at"],"title":"Client"},"ClientProfitabilityOut":{"properties":{"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"currency":{"type":"string","title":"Currency","default":"EUR"},"revenue":{"type":"number","title":"Revenue"},"cost":{"type":"number","title":"Cost"},"margin":{"type":"number","title":"Margin"},"project_count":{"type":"integer","title":"Project Count"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["client_id","client_name","revenue","cost","margin","project_count","criterio"],"title":"ClientProfitability","description":"Rentabilidad agregada por cliente: roll-up de `ProjectProfitability`.\n\nCada fila suma `revenue`/`cost` (= `cost_total` de `ProjectProfitability`,\nya con P-6 incluido) de todos los proyectos de un cliente; `margin` =\n`revenue` − `cost` y `project_count` cuenta los proyectos con actividad\nque aportaron a la fila. Un cliente sin proyectos con actividad en el\nrango simplemente no aparece (misma semántica que rentabilidad por\nproyecto: ausente, no en cero).\n\nLos proyectos sin cliente vinculado (`Project.client_id IS NULL`) se\nagrupan en una fila sintética con `client_id=null` y\n`client_name=\"Sin cliente\"` en vez de excluirse, para que su margen siga\nsiendo visible."},"ClientRegisterOut":{"properties":{"client_id":{"type":"string","format":"uuid","title":"Client Id"},"client_id_issued_at":{"type":"integer","title":"Client Id Issued At"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"token_endpoint_auth_method":{"type":"string","const":"none","title":"Token Endpoint Auth Method"},"grant_types":{"items":{"type":"string","enum":["authorization_code","refresh_token"]},"type":"array","title":"Grant Types"},"response_types":{"items":{"type":"string","const":"code"},"type":"array","title":"Response Types"},"scope":{"type":"string","title":"Scope"}},"type":"object","required":["client_id","client_id_issued_at","client_name","redirect_uris","token_endpoint_auth_method","grant_types","response_types","scope"],"title":"OauthClientRegistered","description":"Metadata REGISTRADA (RFC 7591 §3.2.1): es la que manda, no la pedida."},"ClientTimelineEntryOut":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","enum":["invoice","quote","expense","deal","crm_note","project","contract","contract_milestone","request","meeting"],"title":"Type"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"title":{"type":"string","title":"Title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"href":{"type":"string","title":"Href"}},"type":"object","required":["id","type","source_id","occurred_at","title","subtitle","status","amount","currency","href"],"title":"ClientTimelineEntry","description":"Un hecho de la vida del cliente, venga del módulo que venga.\n\nEl API decide el TIPO; el color y el icono los decide el front (tokens de\n`@projekt/design-tokens`). Aquí no viaja ni un hex ni un nombre de icono: eso\nobligaría a desplegar el API para cambiar un color."},"ClientUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"client_type":{"anyOf":[{"type":"string","enum":["legal","individual"]},{"type":"null"}],"title":"Client Type"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"ClientUpdateIn","description":"PATCH parcial: solo se aplican campos presentes."},"CommitSlim":{"properties":{"sha7":{"type":"string","title":"Sha7"},"message":{"type":"string","title":"Message"},"author_name":{"type":"string","title":"Author Name"},"author_login":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Login"},"date":{"type":"string","title":"Date"},"html_url":{"type":"string","title":"Html Url"},"task_refs":{"items":{"type":"string"},"type":"array","title":"Task Refs"}},"type":"object","required":["sha7","message","author_name","date","html_url","task_refs"],"title":"CommitSlim","description":"Commit slim (primera línea de mensaje, sha corto).\n\ntask_refs: referencias de tarea detectadas en el mensaje (ej. ['PJKT-12']).\nMáx. 10 refs por commit; deduplicadas y normalizadas a mayúsculas."},"ConsentDecisionIn":{"properties":{"approve":{"type":"boolean","title":"Approve"}},"type":"object","required":["approve"],"title":"ConsentDecisionIn"},"ConsentDecisionOut":{"properties":{"redirect_to":{"type":"string","title":"Redirect To"}},"type":"object","required":["redirect_to"],"title":"OauthConsentDecision"},"ConsentInfoOut":{"properties":{"request_id":{"type":"string","format":"uuid","title":"Request Id"},"client_name":{"type":"string","title":"Client Name"},"redirect_host":{"type":"string","title":"Redirect Host"},"scope":{"type":"string","title":"Scope"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["request_id","client_name","redirect_host","scope","expires_at"],"title":"OauthConsentInfo"},"ContractCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"contract_type":{"type":"string","enum":["service","employment","nda","framework","lease","other"],"title":"Contract Type"},"status":{"type":"string","enum":["draft","active","signed","expired","terminated","cancelled"],"title":"Status","default":"draft"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"employee_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Id"},"value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Value"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["title","contract_type"],"title":"ContractCreateIn"},"ContractOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"contract_number":{"type":"string","title":"Contract Number"},"title":{"type":"string","title":"Title"},"contract_type":{"type":"string","enum":["service","employment","nda","framework","lease","other"],"title":"Contract Type"},"status":{"type":"string","enum":["draft","active","signed","expired","terminated","cancelled"],"title":"Status"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Employee Id"},"value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Value"},"currency":{"type":"string","title":"Currency"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","contract_number","title","contract_type","status","client_id","employee_id","value","currency","start_date","end_date","body","notes","created_at","updated_at"],"title":"Contract"},"ContractTemplateCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"body":{"type":"string","title":"Body"}},"type":"object","required":["name","body"],"title":"ContractTemplateCreateIn"},"ContractTemplateOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"body":{"type":"string","title":"Body"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","body","created_at","updated_at"],"title":"ContractTemplate"},"ContractTemplateUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"}},"type":"object","title":"ContractTemplateUpdateIn"},"ContractUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"contract_type":{"anyOf":[{"type":"string","enum":["service","employment","nda","framework","lease","other"]},{"type":"null"}],"title":"Contract Type"},"status":{"anyOf":[{"type":"string","enum":["draft","active","signed","expired","terminated","cancelled"]},{"type":"null"}],"title":"Status"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"employee_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Id"},"value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Value"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"ContractUpdateIn"},"CopilotAttachmentIn":{"properties":{"media_type":{"type":"string","enum":["image/png","image/jpeg","image/webp","image/gif"],"title":"Media Type","description":"Tipo MIME de la imagen: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'."},"data":{"type":"string","minLength":1,"title":"Data","description":"Contenido de la imagen en base64, SIN el prefijo 'data:'."}},"type":"object","required":["media_type","data"],"title":"CopilotAttachment","description":"Una imagen adjunta a un turno del usuario (recibo, factura, captura…).\n\nKern la lee con la visión de Claude para extraer datos y PROPONER acciones.\n`data` es base64 SIN el prefijo `data:` (solo el payload). Se valida el tipo\n(debe ser una imagen soportada) y el tamaño decodificado (≤ ~7 MB)."},"CopilotChatIn":{"properties":{"messages":{"items":{"$ref":"#/components/schemas/CopilotMessageIn"},"type":"array","maxItems":50,"minItems":1,"title":"Messages","description":"Historial de la conversación (mínimo 1, máximo 50 turnos)."},"conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Conversation Id","description":"Conversación a continuar (del propio usuario en esta org). Si se omite, se crea una nueva."}},"type":"object","required":["messages"],"title":"CopilotChatIn","description":"Petición de chat: historial completo (el cliente reenvía todo el hilo).\n\n`conversation_id` continúa una conversación existente (debe ser del propio\nusuario en esta org); si se omite, el servidor crea una conversación nueva."},"CopilotChatOut":{"properties":{"reply":{"type":"string","title":"Reply"},"conversation_id":{"type":"string","format":"uuid","title":"Conversation Id"},"tool_calls":{"items":{"$ref":"#/components/schemas/CopilotToolCallOut"},"type":"array","title":"Tool Calls"},"proposed_actions":{"items":{"$ref":"#/components/schemas/CopilotProposedActionOut"},"type":"array","title":"Proposed Actions"}},"type":"object","required":["reply","conversation_id"],"title":"CopilotChat","description":"Respuesta del copiloto: texto final + tools ejecutadas + acciones propuestas.\n\n`conversation_id` es la conversación donde se persistió este turno (nueva si\nla petición no traía una); el cliente la reenvía para continuar el hilo."},"CopilotConversationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Resumen del primer mensaje del usuario, o None."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"message_count":{"type":"integer","title":"Message Count","description":"Número de mensajes (turnos) guardados en la conversación."}},"type":"object","required":["id","created_at","updated_at","message_count"],"title":"CopilotConversation","description":"Una conversación del histórico de Kern (para listar los hilos del usuario)."},"CopilotMessageIn":{"properties":{"role":{"type":"string","enum":["user","assistant"],"title":"Role"},"content":{"type":"string","minLength":1,"title":"Content","description":"Texto del turno (no vacío)."},"attachments":{"items":{"$ref":"#/components/schemas/CopilotAttachmentIn"},"type":"array","title":"Attachments","description":"Imágenes adjuntas (solo en turnos role=user)."}},"type":"object","required":["role","content"],"title":"CopilotMessage","description":"Un turno del historial de conversación.\n\nUn turno `role=user` puede incluir `attachments` (imágenes) que Kern lee con\nvisión. Los adjuntos solo se usan en mensajes del usuario."},"CopilotMessageOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"role":{"type":"string","enum":["user","assistant"],"title":"Role","description":"'user' | 'assistant'."},"content":{"type":"string","title":"Content"},"tool_calls":{"items":{"type":"string"},"type":"array","title":"Tool Calls","description":"Nombres de las tools ejecutadas en el turno (vacío si ninguna)."},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","role","content","created_at"],"title":"CopilotConvMessage","description":"Un mensaje persistido de una conversación (para reconstruir el hilo)."},"CopilotProposedActionOut":{"properties":{"type":{"type":"string","title":"Type","description":"Tipo de acción, p. ej. 'create_task' | 'create_quote' | 'create_expense'."},"summary":{"type":"string","title":"Summary","description":"Resumen legible en español de lo que se hará."},"endpoint":{"type":"string","title":"Endpoint","description":"Ruta REST informativa contra la que ejecutar."},"method":{"type":"string","title":"Method","description":"Método HTTP (siempre POST)."},"payload":{"additionalProperties":true,"type":"object","title":"Payload","description":"Cuerpo que se enviaría al endpoint real."}},"type":"object","required":["type","summary","endpoint","method","payload"],"title":"CopilotProposedAction","description":"Una acción de escritura PROPUESTA por la IA (no ejecutada).\n\nLa IA no muta nada: describe qué se haría y contra qué endpoint real; el\nusuario la confirma en la web y su cliente ejecuta la llamada (que sí aplica\npermisos). `payload` es el cuerpo listo para enviar a `endpoint`."},"CopilotToolCallOut":{"properties":{"name":{"type":"string","title":"Name"},"input":{"additionalProperties":true,"type":"object","title":"Input"}},"type":"object","required":["name","input"],"title":"CopilotToolCall","description":"Una tool de lectura ejecutada durante la respuesta (transparencia)."},"CostCenterCreateIn":{"properties":{"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["code","name"],"title":"CostCenterCreateIn"},"CostCenterOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","organization_id","code","name","parent_id","created_at","updated_at"],"title":"CostCenter"},"CostCenterRollupLineOut":{"properties":{"cost_center_id":{"type":"string","format":"uuid","title":"Cost Center Id"},"code":{"type":"string","title":"Code"},"name":{"type":"string","title":"Name"},"expenses_total":{"type":"number","title":"Expenses Total"},"supplier_invoices_total":{"type":"number","title":"Supplier Invoices Total"},"invoices_total":{"type":"number","title":"Invoices Total"},"grand_total":{"type":"number","title":"Grand Total"}},"type":"object","required":["cost_center_id","code","name","expenses_total","supplier_invoices_total","invoices_total","grand_total"],"title":"CostCenterRollupLine","description":"Totales por centro de coste para un período."},"CostCenterRollupOut":{"properties":{"period":{"type":"string","title":"Period"},"currency":{"type":"string","title":"Currency","default":"EUR"},"lines":{"items":{"$ref":"#/components/schemas/CostCenterRollupLineOut"},"type":"array","title":"Lines"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["period","lines","criterio"],"title":"CostCenterRollup","description":"Rollup completo por centros de coste para un período.\n\n`currency` (PJKT-2264) es la divisa BASE de la organización y TODOS los\nimportes del rollup están en ella: los tres agregados suman SOLO documentos\nemitidos en esa divisa. Antes eran `SUM(...)` ciegos y el `grand_total`\nmezclaba monedas en una cifra que se imputaba a un departamento. No se\nconvierte nada; lo emitido en otra divisa queda fuera."},"CostCenterUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"clear_parent":{"type":"boolean","title":"Clear Parent","default":false}},"type":"object","title":"CostCenterUpdateIn","description":"PATCH parcial."},"CriterioDeCalculoOut":{"properties":{"divisa":{"type":"string","title":"Divisa"},"desde":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Desde"},"hasta":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hasta"},"fuentes":{"items":{"$ref":"#/components/schemas/FuenteDelCriterioOut"},"type":"array","title":"Fuentes"},"descartes":{"items":{"$ref":"#/components/schemas/DescarteDelCriterioOut"},"type":"array","title":"Descartes"}},"type":"object","required":["divisa","desde","hasta","fuentes","descartes"],"title":"CriterioDeCalculo","description":"El criterio con el que se calculó la cifra a la que acompaña.\n\n`desde`/`hasta` son las cotas EFECTIVAS de esa cifra, no las que se pidieron:\nen una serie mensual, el primer y el último punto van recortados por el rango,\nque es lo que distingue un mes completo de medio mes leído como una caída.\n`null` = sin cota por ese extremo."},"CrmActivityOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"deal_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Deal Id"},"lead_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Lead Id"},"type":{"type":"string","enum":["call","email","meeting","note","task"],"title":"Type"},"body":{"type":"string","title":"Body"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","deal_id","lead_id","type","body","created_by","created_at"],"title":"CrmActivity"},"CrmSummaryOut":{"properties":{"currency":{"type":"string","title":"Currency","default":"EUR"},"open_deals":{"type":"integer","title":"Open Deals"},"open_value":{"type":"number","title":"Open Value"},"won_value":{"type":"number","title":"Won Value"},"won_count":{"type":"integer","title":"Won Count"},"lost_count":{"type":"integer","title":"Lost Count"},"win_rate":{"type":"number","title":"Win Rate"},"leads_by_status":{"items":{"$ref":"#/components/schemas/LeadStatusStatOut"},"type":"array","title":"Leads By Status"}},"type":"object","required":["open_deals","open_value","won_value","won_count","lost_count","win_rate","leads_by_status"],"title":"CrmSummary","description":"Resumen del CRM: pipeline abierto, ganado en el periodo, win rate y leads.\n\nLos recuentos de deals (y con ellos `win_rate`) se acotan a `currency` junto\ncon los importes: miden el mismo conjunto que las cifras que acompañan.\n`leads_by_status` no lleva dinero y cuenta todos los leads."},"CustomFieldDefCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"field_type":{"type":"string","enum":["text","number","date","select","url","email"],"title":"Field Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"position":{"type":"integer","title":"Position","default":0}},"type":"object","required":["name","field_type"],"title":"CustomFieldDefCreateIn","description":"Cuerpo del alta de una definición de campo personalizado.\n\n── `entity`: por qué NO está aquí (PJKT-2471) ────────────────────────────\n\nEl alta aceptaba `entity: str = \"task\"` y el listado un `?entity`, y ninguno\nde los dos estaba en el contrato: eran las 2 últimas posiciones de\n`CAMPOS_DE_PETICION_SOLO_EN_API`. La nota que las dejaba pendientes decía que\ndeclararlas sería «prometer una función que el modelo de datos no soporta»,\nporque `CustomFieldValue.task_id` es NOT NULL con FK a `tasks` y el único\nendpoint que escribe valores es el de tareas, así que una definición con\n`entity != \"task\"` no podría recibir un valor NUNCA.\n\nEse razonamiento es FALSO, y está reproducido en\n`tests/test_entity_solo_task.py`. `CustomFieldValueRepository.upsert` recibe\nel `task_id` de la RUTA —siempre una tarea, así que la FK se cumple— y\n`CustomFieldDefRepository.get_by_ids_in_org`, que es quien resuelve la\ndefinición en `set_task_custom_fields`, NO filtra por `entity`. Medido: una\ndefinición con `entity=\"deal\"` acepta un valor sobre una tarea con 200 y el\nendpoint de valores lo devuelve. Lo que no la devuelve es `TaskOut`, que\nresuelve las definiciones con `defs_map_for_org` → `list_for_org()`, y ése sí\nfiltra `entity == \"task\"`. O sea que el campo no era inofensivo: fabricaba\nvalores que existen en un endpoint y no existen en el otro.\n\nCon una sola entidad soportada de punta a punta, la salida honesta es la que\naconseja el propio check («si el campo no debe soportarse, lo que hay que\nquitar es la aceptación, no añadir la declaración»): el alta ya no lo\ndeclara, así que toda definición nace `entity=\"task\"` (el default de la\nCOLUMNA, que no cambia) y la mitad rota deja de ser alcanzable. No es una\nruptura de la regla 4: un cuerpo con `entity` sigue devolviendo 201 —Pydantic\ndescarta lo que no declara— y lo que cambia es que la definición resultante\nahora SÍ funciona. Mismo movimiento que `SharedTaskUpdate` (PJKT-2419): se\ndeja de anunciar lo que no se aplica.\n\nEl día que otra entidad se implemente de verdad, `entity` vuelve al cuerpo\nY al contrato a la vez, que es lo aditivo."},"CustomFieldDefOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"entity":{"type":"string","title":"Entity"},"name":{"type":"string","title":"Name"},"field_type":{"type":"string","enum":["text","number","date","select","url","email"],"title":"Field Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"position":{"type":"integer","title":"Position"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","entity","name","field_type","options","position","created_at"],"title":"CustomFieldDef","description":"Definición devuelta al cliente.\n\n`entity` SE QUEDA en la salida: la columna existe, el contrato la declara\n`required` desde el principio y su descripción —«actualmente siempre\n`task`»— pasa a ser verdad para todo lo que se cree desde PJKT-2471 (antes no\nlo era: el alta dejaba fabricar `entity=\"deal\"`). Lo que se fue es la\nACEPTACIÓN en la petición, no el dato."},"CustomFieldDefUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"}},"type":"object","title":"CustomFieldDefUpdateIn"},"CustomFieldValueIn":{"properties":{"def_id":{"type":"string","format":"uuid","title":"Def Id"},"value":{"type":"string","title":"Value"}},"type":"object","required":["def_id","value"],"title":"CustomFieldValueIn","description":"A single field value in the bulk upsert payload."},"CustomFieldValueOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"def_id":{"type":"string","format":"uuid","title":"Def Id"},"task_id":{"type":"string","format":"uuid","title":"Task Id"},"name":{"type":"string","title":"Name"},"field_type":{"type":"string","enum":["text","number","date","select","url","email"],"title":"Field Type"},"value":{"type":"string","title":"Value"}},"type":"object","required":["id","def_id","task_id","name","field_type","value"],"title":"CustomFieldValue","description":"A resolved custom field value on a task (includes def metadata).\n\n`id` y `task_id` (PJKT-2321) salen de las columnas NOT NULL del registro de\nvalor: el contrato los declaraba obligatorios desde el principio y el SDK los\ntipaba `string`, pero el API no los mandaba, así que quien los leía se\nencontraba un `undefined` en producción. Se mandan porque el dato existe\nsiempre en las dos rutas que construyen este schema (el endpoint de valores\nde una tarea y el `custom_fields` de `TaskOut`)."},"DashboardActivityItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"actor_name":{"type":"string","title":"Actor Name"},"actor_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Avatar Url"},"action":{"type":"string","title":"Action"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"summary":{"type":"string","title":"Summary"}},"type":"object","required":["id","actor_name","actor_avatar_url","action","entity_type","entity_id","created_at","summary"],"title":"DashboardActivityItem","description":"Entrada del feed de actividad (una fila de `audit_logs` enriquecida)."},"DashboardLayoutIn":{"properties":{"version":{"type":"integer","title":"Version"},"items":{"items":{"$ref":"#/components/schemas/DashboardWidgetItemIn"},"type":"array","title":"Items"}},"type":"object","required":["version","items"],"title":"DashboardLayoutIn","description":"PUT del layout del dashboard: reemplaza POR COMPLETO el guardado.\n\nEl frontend envía siempre el layout completo (sustitución, no merge). Los\n`id` de widget se normalizan y se DEDUPLICAN conservando el primero (el orden\ndel array es el orden de render), con tope de cantidad para no persistir\nlistas ilimitadas."},"DashboardLayoutOut":{"properties":{"version":{"type":"integer","title":"Version"},"items":{"items":{"$ref":"#/components/schemas/DashboardWidgetItemOut"},"type":"array","title":"Items"}},"type":"object","required":["version","items"],"title":"DashboardLayout","description":"DashboardLayout del contrato: layout del dashboard del usuario.\n\n`{version, items[]}`, persistido a nivel de USUARIO (columna\n`users.dashboard_layout`) como fuente de verdad cross-device. Un usuario sin\nlayout guardado recibe el vacío (`version=1`, `items=[]`), que el frontend\nreconcilia contra su catálogo de widgets para producir el layout por defecto."},"DashboardStatsOut":{"properties":{"issues":{"$ref":"#/components/schemas/IssueCountsOut"},"projects":{"$ref":"#/components/schemas/ProjectCountsOut"},"priority":{"items":{"$ref":"#/components/schemas/PriorityCountOut"},"type":"array","title":"Priority"},"workload":{"items":{"$ref":"#/components/schemas/WorkloadItemOut"},"type":"array","title":"Workload"},"finance":{"$ref":"#/components/schemas/app__modules__dashboard__schemas__FinanceSummaryOut"},"tasks_trend":{"items":{"$ref":"#/components/schemas/TasksTrendPointOut"},"type":"array","title":"Tasks Trend"},"my_tasks":{"items":{"$ref":"#/components/schemas/MyTaskItemOut"},"type":"array","title":"My Tasks"},"most_active_projects":{"items":{"$ref":"#/components/schemas/ActiveProjectItemOut"},"type":"array","title":"Most Active Projects"}},"type":"object","required":["issues","projects","priority","workload","finance","tasks_trend","my_tasks","most_active_projects"],"title":"DashboardStats","description":"Payload agregado del panel de inicio de una organización."},"DashboardWidgetItemIn":{"properties":{"id":{"type":"string","title":"Id"},"size":{"type":"string","enum":["sm","md","lg","xl"],"title":"Size"},"hidden":{"type":"boolean","title":"Hidden"},"size_by_breakpoint":{"anyOf":[{"additionalProperties":{"type":"string","enum":["sm","md","lg","xl"]},"type":"object"},{"type":"null"}],"title":"Size By Breakpoint"}},"type":"object","required":["id","size","hidden"],"title":"DashboardWidgetItemIn","description":"Item del layout en el PUT: `id` opaco y normalizado, tamaño válido."},"DashboardWidgetItemOut":{"properties":{"id":{"type":"string","title":"Id"},"size":{"type":"string","enum":["sm","md","lg","xl"],"title":"Size"},"hidden":{"type":"boolean","title":"Hidden"},"size_by_breakpoint":{"anyOf":[{"additionalProperties":{"type":"string","enum":["sm","md","lg","xl"]},"type":"object"},{"type":"null"}],"title":"Size By Breakpoint"}},"type":"object","required":["id","size","hidden"],"title":"DashboardWidgetItem","description":"Una instancia de widget en el layout: qué widget, tamaño y si está oculto."},"DealCloseIn":{"properties":{"result":{"type":"string","enum":["won","lost"],"title":"Result"},"lost_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lost Reason"}},"type":"object","required":["result"],"title":"DealCloseIn","description":"Cerrar un deal como ganado o perdido."},"DealCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"pipeline_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Pipeline Id"},"stage_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Stage Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Value","default":"0"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"probability":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Probability"},"expected_close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Close Date"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},"type":"object","required":["title"],"title":"DealCreateIn"},"DealMoveIn":{"properties":{"stage_id":{"type":"string","format":"uuid","title":"Stage Id"}},"type":"object","required":["stage_id"],"title":"DealMoveIn","description":"Mover el deal a otra etapa del tablero."},"DealOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"pipeline_id":{"type":"string","format":"uuid","title":"Pipeline Id"},"stage_id":{"type":"string","format":"uuid","title":"Stage Id"},"title":{"type":"string","title":"Title"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"value":{"type":"number","title":"Value"},"currency":{"type":"string","title":"Currency"},"probability":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Probability"},"expected_close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Close Date"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"status":{"type":"string","enum":["open","won","lost"],"title":"Status"},"lost_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lost Reason"},"converted_project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Converted Project Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","pipeline_id","stage_id","title","client_id","value","currency","probability","expected_close_date","owner_id","status","lost_reason","converted_project_id","created_at","updated_at"],"title":"Deal"},"DealUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Value"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"probability":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Probability"},"expected_close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Close Date"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"}},"type":"object","title":"DealUpdateIn","description":"PATCH parcial: solo se aplican campos presentes."},"DeliveryMetricsOut":{"properties":{"lead_time_avg_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lead Time Avg Hours"},"merged_count":{"type":"integer","title":"Merged Count"},"window_note":{"type":"string","title":"Window Note"}},"type":"object","required":["merged_count","window_note"],"title":"DeliveryMetrics","description":"Métricas de entrega DORA-lite de un proyecto, calculadas on-demand.\n\nSe computan a partir de los PRs mergeados que la API de GitHub devuelve para\nlos repos vinculados (ventana limitada, sin persistencia). Por eso NO es una\nDORA completa: `window_note` explica la limitación.\n\n- lead_time_avg_hours: media de (merged_at − created_at) en horas de los PRs\n  mergeados disponibles. None si no hay ninguno.\n- merged_count: nº de PRs mergeados en la ventana disponible.\n- window_note: nota sobre el alcance/limitación de la ventana."},"DepartmentCreateIn":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"acronym":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Acronym"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"},"annual_vacation_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Vacation Days"},"holidays":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Holidays"}},"type":"object","required":["name"],"title":"DepartmentCreateIn"},"DepartmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"acronym":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Acronym"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"},"annual_vacation_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Vacation Days"},"holidays":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Holidays"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","acronym","description","logo_url","color","weekly_schedule","annual_vacation_days","holidays","created_at","updated_at"],"title":"Department"},"DepartmentUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"acronym":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Acronym"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"},"annual_vacation_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Vacation Days"},"holidays":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Holidays"}},"type":"object","title":"DepartmentUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes."},"DepartmentVisibilityProjectIn":{"properties":{"project_id":{"type":"string","format":"uuid","title":"Project Id"},"access_level":{"type":"string","enum":["read","write"],"title":"Access Level","default":"write"}},"type":"object","required":["project_id"],"title":"DepartmentVisibilityProjectIn","description":"Un proyecto al que un departamento da acceso, con su nivel (PJKT-1955)."},"DepartmentVisibilityProjectOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"source_quote_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Quote Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"status":{"type":"string","enum":["active","archived"],"title":"Status"},"board_swimlane":{"anyOf":[{"type":"string","enum":["none","assignee","priority","type"]},{"type":"null"}],"title":"Board Swimlane"},"visibility":{"type":"string","enum":["organization","restricted"],"title":"Visibility"},"visibility_departments":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Visibility Departments"},"is_member":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Member"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"},"access_level":{"type":"string","enum":["read","write"],"title":"Access Level"}},"type":"object","required":["id","organization_id","key","name","description","status","visibility","created_at","updated_at","access_level"],"title":"DepartmentVisibilityProject","description":"Proyecto al que un departamento da acceso + el nivel que concede.\n\nSuperset de `ProjectOut` (schema `DepartmentVisibilityProject` del contrato)."},"DepartmentVisibilityProjectsUpdateIn":{"properties":{"projects":{"anyOf":[{"items":{"$ref":"#/components/schemas/DepartmentVisibilityProjectIn"},"type":"array"},{"type":"null"}],"title":"Projects"},"project_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Project Ids"}},"type":"object","title":"DepartmentVisibilityProjectsUpdateIn","description":"Body del PUT del set de proyectos a los que un departamento da acceso.\n\nReemplaza el set completo (semántica PUT; dirección inversa sobre el MISMO\nM:N). Cada project_id debe pertenecer a la MISMA org (422 si no). Lista vacía =\nel departamento deja de dar acceso a ningún proyecto.\n\nDos formas MUTUAMENTE EXCLUYENTES (422 si vienen las dos o ninguna):\n- `projects`: [{project_id, access_level}] — con nivel por proyecto.\n- `project_ids`: [uuid, …] — forma histórica, equivale a `write` en todos."},"DescarteDelCriterioOut":{"properties":{"motivo":{"type":"string","enum":["otra_divisa","estado_excluido","duplica_factura_ap","sin_linea_de_presupuesto","sin_centro_de_coste"],"title":"Motivo"},"documento":{"type":"string","enum":["factura","gasto","factura_de_proveedor","parte_de_horas"],"title":"Documento"},"estado":{"type":"string","title":"Estado"},"documentos":{"type":"integer","title":"Documentos"},"importe":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Importe"}},"type":"object","required":["motivo","documento","estado","documentos","importe"],"title":"DescarteDelCriterio","description":"Documentos que quedaron fuera de la cifra, agrupados por su motivo.\n\nUno por `(motivo, documento, estado)`. `importe` es `null` cuando sumarlo\nmentiría —hoy, exactamente el motivo `otra_divisa`, donde son monedas\ndistintas—; el recuento siempre vale."},"DesgloseDeLoFacturadoOut":{"properties":{"month":{"type":"string","title":"Month"},"currency":{"type":"string","title":"Currency"},"income":{"type":"number","title":"Income"},"total_invoices":{"type":"integer","title":"Total Invoices"},"invoices":{"items":{"$ref":"#/components/schemas/FacturaDelDesgloseOut"},"type":"array","title":"Invoices"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["month","currency","income","total_invoices","invoices","criterio"],"title":"DesgloseDeLoFacturado","description":"Las facturas que suman el `income` de un punto de la tendencia.\n\n`income` es el MISMO número que publica el punto para el mismo rango: no se\nrecalcula sumando `invoices`, sale de la consulta agregada que sirve la\ntendencia, así que paginar no lo mueve. `total_invoices` es el tamaño del\nconjunto completo, no el de la página.\n\n`criterio` es el del punto PROYECTADO a la familia `factura`: un punto tiene\ndos cifras (lo facturado y el coste) y esto abre la primera."},"DeviceTokenDeleteIn":{"properties":{"token":{"type":"string","maxLength":500,"title":"Token"}},"type":"object","required":["token"],"title":"DeviceTokenDeleteIn","description":"Baja de un token de dispositivo por su valor."},"DeviceTokenIn":{"properties":{"platform":{"type":"string","enum":["apns","fcm"],"title":"Platform"},"token":{"type":"string","maxLength":500,"title":"Token"}},"type":"object","required":["platform","token"],"title":"DeviceTokenIn","description":"Registro de un token de push nativo (APNs/FCM) de un dispositivo."},"DeviceTokenOut":{"properties":{"platform":{"type":"string","title":"Platform"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["platform","created_at"],"title":"DeviceToken","description":"Confirmación de un token registrado (sin exponer el token completo)."},"DocumentCommentAnchor":{"properties":{"block_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Block Id"},"quote":{"type":"string","title":"Quote"},"prefix":{"type":"string","title":"Prefix"},"suffix":{"type":"string","title":"Suffix"}},"type":"object","required":["block_id","quote","prefix","suffix"],"title":"DocumentCommentAnchor","description":"Fragmento del documento sobre el que se comenta.\n\nNO es una posición: un `start`/`end` en caracteres muere en la siguiente\nedición porque el contenido se guarda como un HTML entero, sin historia de\ncambios con la que rebasar nada. Esto es el `TextQuoteSelector` de la W3C\nWeb Annotation (cita + vecindad) más el id de bloque de TipTap.\n\nQuien lo RESUELVE contra el contenido de hoy es el cliente; el API solo\nguarda y devuelve."},"DocumentCommentCreateIn":{"properties":{"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"anchor":{"anyOf":[{"$ref":"#/components/schemas/DocumentCommentAnchor"},{"type":"null"}]}},"type":"object","required":["body"],"title":"DocumentCommentCreateIn"},"DocumentCommentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"document_id":{"type":"string","format":"uuid","title":"Document Id"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author_name":{"type":"string","title":"Author Name"},"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"anchor":{"anyOf":[{"$ref":"#/components/schemas/DocumentCommentAnchor"},{"type":"null"}]},"is_edited":{"type":"boolean","title":"Is Edited"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","document_id","author_id","author_name","body","parent_id","anchor","is_edited","created_at","updated_at"],"title":"DocumentComment","description":"Comentario del contrato: hilo plano (el cliente anida por `parent_id`).\n\n`author_id` es `null` si el autor fue borrado; `author_name` cae a\n\"Sistema\" en ese caso."},"DocumentCommentUpdateIn":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"DocumentCommentUpdateIn","description":"PATCH de un comentario: solo `body` (no admite null)."},"DocumentCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["title"],"title":"DocumentCreateIn"},"DocumentFromTemplateIn":{"properties":{"template_id":{"type":"string","format":"uuid","title":"Template Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"additionalProperties":false,"type":"object","required":["template_id"],"title":"DocumentFromTemplateIn","description":"Alta de un documento COPIANDO una plantilla de la organización.\n\nEl destino (carpeta, proyecto, padre) lo decide quien crea — «la carpeta\ndonde estás»—, no la plantilla: una plantilla que arrastrase su propia\ncarpeta crearía los documentos donde nadie los está mirando."},"DocumentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"source_quote_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Quote Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"position":{"type":"integer","title":"Position"},"title":{"type":"string","title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"kind":{"type":"string","enum":["page","file"],"title":"Kind"},"is_template":{"type":"boolean","title":"Is Template"},"file_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Name"},"file_mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Mime Type"},"file_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Size"},"file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Url"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","folder_id","parent_id","position","title","content","kind","is_template","file_name","file_mime_type","file_size","file_url","created_by","created_at","updated_at"],"title":"Document"},"DocumentPublicationOut":{"properties":{"document_id":{"type":"string","format":"uuid","title":"Document Id"},"published":{"type":"boolean","title":"Published"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"}},"type":"object","required":["document_id","published","token","public_url","published_at"],"title":"DocumentPublication","description":"Estado de publicación de un documento, con su enlace.\n\n`token`/`public_url` van en TODAS las respuestas, no solo en la de creación\ncomo en `QuoteShareOut`. No es una relajación: el token tampoco se guarda en\nclaro aquí — se DERIVA de `APP_SECRET`, así que el servidor puede\nrecalcularlo para quien ya tiene permiso de administrar el documento. El\nrazonamiento completo, en `publishing.py`."},"DocumentPublishIn":{"properties":{"rotate":{"type":"boolean","title":"Rotate","default":false}},"additionalProperties":false,"type":"object","title":"DocumentPublishIn","description":"Opciones de `POST …/publication`."},"DocumentSignIn":{"properties":{"code":{"type":"string","pattern":"^[0-9]{6}$","title":"Code"},"signer_name":{"type":"string","maxLength":255,"minLength":1,"title":"Signer Name"},"decision":{"type":"string","enum":["approved","rejected"],"title":"Decision"},"method":{"anyOf":[{"type":"string","enum":["handwritten","digital"]},{"type":"null"}],"title":"Method"},"signature_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature Image"},"reason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reason"}},"type":"object","required":["code","signer_name","decision"],"title":"DocumentSignIn","description":"Cuerpo de la firma: el código recibido + el veredicto."},"DocumentSignatureOut":{"properties":{"id":{"type":"string","title":"Id"},"organization_id":{"type":"string","title":"Organization Id"},"subject_type":{"type":"string","const":"quote","title":"Subject Type"},"subject_id":{"type":"string","title":"Subject Id"},"decision":{"type":"string","enum":["approved","rejected"],"title":"Decision"},"method":{"type":"string","enum":["handwritten","digital"],"title":"Method"},"signer_name":{"type":"string","title":"Signer Name"},"signer_email":{"type":"string","title":"Signer Email"},"signed_at":{"type":"string","format":"date-time","title":"Signed At"},"signer_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signer Ip"},"signer_user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signer User Agent"},"document_sha256":{"type":"string","title":"Document Sha256"},"document_bytes":{"type":"integer","title":"Document Bytes"},"evidence_hash":{"type":"string","title":"Evidence Hash"},"has_signature_image":{"type":"boolean","title":"Has Signature Image"},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","subject_type","subject_id","decision","method","signer_name","signer_email","signed_at","document_sha256","document_bytes","evidence_hash","has_signature_image","created_at"],"title":"DocumentSignature","description":"Expediente completo (vista interna, admin+)."},"DocumentUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"}},"type":"object","title":"DocumentUpdateIn","description":"PATCH parcial: solo se aplican campos presentes.\n\n`parent_id`/`position` permiten mover el documento dentro del árbol. Un\n`parent_id` que crearía un ciclo (el documento como su propio ancestro) lo\nrechaza el servicio con 422."},"DocumentVersionDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"document_id":{"type":"string","format":"uuid","title":"Document Id"},"title":{"type":"string","title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"author_name":{"type":"string","title":"Author Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","document_id","title","content","created_by","author_name","created_at"],"title":"DocumentVersionDetail","description":"Detalle del historial: incluye el contenido markdown completo."},"DocumentVersionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"document_id":{"type":"string","format":"uuid","title":"Document Id"},"title":{"type":"string","title":"Title"},"content_length":{"type":"integer","title":"Content Length"},"content_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Preview"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"author_name":{"type":"string","title":"Author Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","document_id","title","content_length","content_preview","created_by","author_name","created_at"],"title":"DocumentVersion","description":"Ítem del historial: SIN el contenido completo (solo longitud + preview)."},"EmployeeBulkActionErrorOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["id","reason"],"title":"EmployeeBulkActionError"},"EmployeeBulkActionIn":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":200,"title":"Ids"},"action":{"type":"string","enum":["activate","deactivate","delete"],"title":"Action"}},"type":"object","required":["ids","action"],"title":"EmployeeBulkActionIn"},"EmployeeBulkActionResultOut":{"properties":{"processed":{"type":"integer","title":"Processed"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"$ref":"#/components/schemas/EmployeeBulkActionErrorOut"},"type":"array","title":"Errors"}},"type":"object","required":["processed","skipped","errors"],"title":"BulkActionResult","description":"Resultado honesto por lote: aplicados, omitidos y errores por id."},"EmployeeCreateIn":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"invite":{"type":"boolean","title":"Invite","default":false},"position":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Position"},"department":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Department"},"employment_type":{"type":"string","enum":["interno","autonomo","externo","w2","1099"],"title":"Employment Type","default":"interno"},"base_salary":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Salary"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"tax_id_type":{"anyOf":[{"type":"string","enum":["DNI","NIE","CIF","SSN","EIN","ITIN"]},{"type":"null"}],"title":"Tax Id Type"},"tax_id":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Tax Id"},"social_security_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Social Security Number"},"birth_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Birth Date"},"nationality":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Nationality"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"City"},"province":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Province"},"postal_code":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Postal Code"},"country":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Country"},"tax_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Country"},"bank_iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Iban"},"bank_routing_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Routing Number"},"bank_account_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Account Number"},"gross_annual_salary":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Gross Annual Salary"},"irpf_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Irpf Rate"},"pagas":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pagas"}},"type":"object","required":["first_name","last_name"],"title":"EmployeeCreateIn"},"EmployeeDepartmentStatOut":{"properties":{"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["department","count"],"title":"EmployeeDepartmentStat","description":"Recuento de empleados por departamento (headcount)."},"EmployeeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"department_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department Logo Url"},"manager_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Manager Id"},"employment_type":{"type":"string","title":"Employment Type"},"base_salary":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Salary"},"currency":{"type":"string","title":"Currency"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"tax_id_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id Type"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"social_security_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Social Security Number"},"birth_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Birth Date"},"nationality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nationality"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"province":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Province"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"tax_country":{"type":"string","title":"Tax Country"},"bank_iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Iban"},"bank_routing_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Routing Number"},"bank_account_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Account Number"},"gross_annual_salary":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gross Annual Salary"},"irpf_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Irpf Rate"},"pagas":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pagas"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"invited":{"type":"boolean","title":"Invited","default":false}},"type":"object","required":["id","organization_id","first_name","last_name","email","position","department","employment_type","base_salary","currency","hire_date","tax_id_type","tax_id","social_security_number","birth_date","nationality","phone","address","city","province","postal_code","country","tax_country","bank_iban","bank_routing_number","bank_account_number","gross_annual_salary","irpf_rate","pagas","is_active","created_at","updated_at"],"title":"Employee"},"EmployeeUpdateIn":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"position":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Position"},"department":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Department"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"employment_type":{"anyOf":[{"type":"string","enum":["interno","autonomo","externo","w2","1099"]},{"type":"null"}],"title":"Employment Type"},"base_salary":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Base Salary"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"tax_id_type":{"anyOf":[{"type":"string","enum":["DNI","NIE","CIF","SSN","EIN","ITIN"]},{"type":"null"}],"title":"Tax Id Type"},"tax_id":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Tax Id"},"social_security_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Social Security Number"},"birth_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Birth Date"},"nationality":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Nationality"},"phone":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"city":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"City"},"province":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Province"},"postal_code":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Postal Code"},"country":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Country"},"tax_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Country"},"bank_iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Iban"},"bank_routing_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Routing Number"},"bank_account_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Account Number"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"manager_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Manager Id"},"gross_annual_salary":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Gross Annual Salary"},"irpf_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Irpf Rate"},"pagas":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pagas"}},"type":"object","title":"EmployeeUpdateIn","description":"PATCH parcial: solo se aplican campos presentes.\n\n- department_id (UUID|null): vincula al departamento estructurado.\n  Al vincular, sincroniza el string department con departments.name.\n  Para desvincular, enviar department_id: null (department string se conserva).\n- department (string): sigue siendo aceptado para compat; si se envía junto\n  con department_id, department_id tiene prioridad."},"EntitlementLimitsOut":{"properties":{"max_projects":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Projects"},"max_members":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Members"},"storage_gb":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Storage Gb"}},"type":"object","required":["max_projects","max_members","storage_gb"],"title":"EntitlementLimits","description":"Límites del plan. `null` = ilimitado."},"EntitlementUsageOut":{"properties":{"projects":{"type":"integer","title":"Projects"},"members":{"type":"integer","title":"Members"}},"type":"object","required":["projects","members"],"title":"EntitlementUsage","description":"Uso actual de la organización (para comparar con los límites)."},"EntitlementsOut":{"properties":{"plan":{"type":"string","enum":["free","pro"],"title":"Plan"},"features":{"items":{"type":"string"},"type":"array","title":"Features"},"disabled_modules":{"items":{"type":"string"},"type":"array","title":"Disabled Modules"},"limits":{"$ref":"#/components/schemas/EntitlementLimitsOut"},"usage":{"$ref":"#/components/schemas/EntitlementUsageOut"},"subscription":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionInfoOut"},{"type":"null"}]}},"type":"object","required":["plan","features","disabled_modules","limits","usage"],"title":"Entitlements","description":"Plan efectivo de la organización + features desbloqueadas + límites + uso.\n\nEl frontend lo usa para mostrar el plan, bloquear/atenuar features no incluidas\ny avisar al acercarse a un límite (CTA de upgrade)."},"ErasureReportOut":{"properties":{"anonymized_fields":{"items":{"type":"string"},"type":"array","title":"Anonymized Fields"},"sessions_revoked":{"type":"integer","title":"Sessions Revoked"},"passkeys_deleted":{"type":"integer","title":"Passkeys Deleted"},"push_subscriptions_deleted":{"type":"integer","title":"Push Subscriptions Deleted"},"memberships_left":{"type":"integer","title":"Memberships Left"}},"type":"object","required":["anonymized_fields","sessions_revoked","passkeys_deleted","push_subscriptions_deleted","memberships_left"],"title":"ErasureReport","description":"Informe del borrado GDPR: qué se anonimizó y cuánto se borró/revocó.\n\nSe devuelve al operador Y se guarda en el payload del audit\n`platform.user_erased` como evidencia de la supresión (art. 17 RGPD)."},"ErrorDetail":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"}},"type":"object","required":["code","message"],"title":"ErrorDetail"},"ErrorEnvelope":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"}},"type":"object","required":["error"],"title":"Error","description":"Cuerpo de TODA respuesta de error (contrato compartido del monorepo)."},"ErrorEventOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"},"method":{"type":"string","title":"Method"},"path":{"type":"string","title":"Path"},"status_code":{"type":"integer","title":"Status Code"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"},"message":{"type":"string","title":"Message"},"exception_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exception Type"},"traceback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Traceback"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"issue_task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Issue Task Id"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"},"veces":{"type":"integer","title":"Veces"},"ultima_vez":{"type":"string","format":"date-time","title":"Ultima Vez"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","method","path","status_code","message","veces","ultima_vez","created_at"],"title":"ErrorEvent"},"ErrorEventUpdateIn":{"properties":{"resolved":{"type":"boolean","title":"Resolved"}},"type":"object","required":["resolved"],"title":"ErrorEventUpdate"},"ErrorGroupOut":{"properties":{"exception_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exception Type"},"path":{"type":"string","title":"Path"},"count":{"type":"integer","title":"Count"},"open_count":{"type":"integer","title":"Open Count"},"first_seen":{"type":"string","format":"date-time","title":"First Seen"},"last_seen":{"type":"string","format":"date-time","title":"Last Seen"},"last_message":{"type":"string","title":"Last Message"},"sample_error_id":{"type":"string","format":"uuid","title":"Sample Error Id"}},"type":"object","required":["exception_type","path","count","open_count","first_seen","last_seen","last_message","sample_error_id"],"title":"ErrorGroup","description":"Fila de la vista agrupada: una firma (excepción + ruta) con contadores."},"ErrorGroupResolveIn":{"properties":{"exception_type":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Exception Type"},"path":{"type":"string","maxLength":512,"title":"Path"}},"type":"object","required":["exception_type","path"],"title":"ErrorGroupResolve","description":"Firma cuyas ocurrencias abiertas se marcan resueltas en bloque."},"ErrorGroupResolveResultOut":{"properties":{"updated":{"type":"integer","title":"Updated"}},"type":"object","required":["updated"],"title":"ErrorGroupResolveResult"},"EvmSummary":{"properties":{"as_of":{"type":"string","format":"date","title":"As Of"},"bac":{"type":"number","title":"Bac"},"ev":{"type":"number","title":"Ev"},"ac":{"type":"number","title":"Ac"},"pv":{"type":"number","title":"Pv"},"cpi":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cpi"},"spi":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Spi"},"eac":{"type":"number","title":"Eac"},"etc":{"type":"number","title":"Etc"},"vac":{"type":"number","title":"Vac"},"pv_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pv Note"}},"type":"object","required":["as_of","bac","ev","ac","pv","cpi","spi","eac","etc","vac","pv_note"],"title":"EvmSummary","description":"Earned Value Management summary in effort-hours."},"ExpenseBulkActionIn":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Ids"},"action":{"type":"string","enum":["approve","reject","set_supplier","set_tax_rate","set_category","mark_paid","void"],"title":"Action"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"category":{"anyOf":[{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"]},{"type":"null"}],"title":"Category"}},"type":"object","required":["ids","action"],"title":"ExpenseBulkActionIn"},"ExpenseCategoryStatOut":{"properties":{"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"currency":{"type":"string","title":"Currency","default":"EUR"},"total":{"type":"number","title":"Total"},"count":{"type":"integer","title":"Count"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["category","total","count","criterio"],"title":"ExpenseCategoryStat","description":"Total y recuento de gastos por categoría en el rango."},"ExpenseCreateIn":{"properties":{"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"description":{"type":"string","title":"Description"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount"},"date":{"type":"string","format":"date","title":"Date"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"tax_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Amount"},"supplier_invoice_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Invoice Number"},"kind":{"type":"string","enum":["expense","supplier_invoice"],"title":"Kind","default":"expense"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu","default":false},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","required":["category","description","amount","date"],"title":"ExpenseCreateIn"},"ExpenseOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Number"},"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"description":{"type":"string","title":"Description"},"amount":{"type":"number","title":"Amount"},"currency":{"type":"string","title":"Currency"},"date":{"type":"string","format":"date","title":"Date"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"status":{"type":"string","enum":["pending","approved","rejected","paid","void"],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tax Rate"},"tax_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tax Amount"},"tax_base":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tax Base"},"supplier_invoice_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Invoice Number"},"kind":{"type":"string","enum":["expense","supplier_invoice"],"title":"Kind"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu"},"source_invoice_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Invoice Id"},"source_org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Org Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"has_attachments":{"type":"boolean","title":"Has Attachments"}},"type":"object","required":["id","organization_id","number","category","description","amount","currency","date","vendor","status","notes","cost_center_id","supplier_id","tax_rate","tax_amount","kind","is_intra_eu","created_at","updated_at","has_attachments"],"title":"Expense","description":"Gasto tal y como sale por `/api/v1` (`_expense_to_out` es el único que lo\nconstruye, y pasa TODOS los campos).\n\nSiete campos perdieron su default en PJKT-2323 —`number`, `supplier_id`,\n`tax_rate`, `tax_amount`, `kind`, `is_intra_eu`, `has_attachments`—: el\ncontrato los exige (la clave llega siempre) y el default hacía que el schema\ndel API los declarase opcionales, 70 posiciones del tope\nREQUERIDOS_SOLO_EN_CONTRATO por defaults que nadie usaba. Un default en un\nschema de SALIDA solo sirve para que una construcción incompleta pase\ninadvertida."},"ExpenseUpdateIn":{"properties":{"status":{"anyOf":[{"type":"string","enum":["pending","approved","rejected","paid","void"]},{"type":"null"}],"title":"Status"},"category":{"anyOf":[{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"]},{"type":"null"}],"title":"Category"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount"},"date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"tax_amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Amount"},"supplier_invoice_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Invoice Number"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"is_intra_eu":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Intra Eu"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","title":"ExpenseUpdateIn","description":"PATCH parcial. `tax_rate`/`tax_amount` admiten null explícito (borrar\nel desglose de IVA); si llega `tax_rate` sin `tax_amount`, el service\nrecalcula la cuota sobre el amount efectivo.\n\n`supplier_id` no-null: vincula la ficha de proveedor (org-scoped; 404 si\nno existe) y sincroniza `vendor` = supplier.name a menos que `vendor` venga\nexplícito en este mismo PATCH. `supplier_id` null: desvincula sin tocar\n`vendor`."},"ExpensesByKindOut":{"properties":{"expense":{"type":"number","title":"Expense"},"supplier_invoice":{"type":"number","title":"Supplier Invoice"}},"type":"object","required":["expense","supplier_invoice"],"title":"DashboardExpensesByKind","description":"Las dos mitades del gasto, con el vocabulario de `expenses.kind`.\n\n`expense` = gastos normales aprobados; `supplier_invoice` = facturas de\nproveedor recibidas (AP) que no están `rejected`/`void`. Su suma es el\n`expenses` de arriba. Es el mismo par de valores que devuelve la dimensión\n`kind` del dataset `expenses` de BI, para que las dos pantallas puedan\nhablar de lo mismo con las mismas palabras."},"ExternalCollaboratorCreateIn":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"user_org_id":{"type":"string","format":"uuid","title":"User Org Id"},"role":{"type":"string","enum":["viewer","commenter","approver"],"title":"Role","default":"viewer"}},"type":"object","required":["user_id","user_org_id"],"title":"ExternalCollaboratorCreateIn"},"ExternalCollaboratorOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"user_org_id":{"type":"string","format":"uuid","title":"User Org Id"},"user_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Name"},"role":{"type":"string","enum":["viewer","commenter","approver"],"title":"Role"},"invited_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Invited By"},"accepted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Accepted At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","user_id","user_org_id","user_name","role","invited_by","accepted_at","revoked_at","created_at","updated_at"],"title":"ProjectExternalCollaborator"},"ExternalDmIn":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"}},"type":"object","required":["user_id","organization_id"],"title":"ChatExternalDmIn","description":"Abrir (o recuperar) un directo con alguien de otra organización.\n\n`organization_id` va explícito y no se deduce: quien está en varias\norganizaciones es alcanzable por varias puertas, y CUÁL se usó es lo que\ndecide después qué ajuste puede cerrar la conversación. Deducirla —«la\nprimera que encuentre»— habría atado el hilo a una organización al azar.\nSe valida contra su membresía real; no se confía en el id del cliente."},"ExternalPersonOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"organization_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization Slug"}},"type":"object","required":["user_id","name","organization_id","organization_name"],"title":"ChatExternalPerson","description":"Persona de otra organización con la que se puede abrir un directo.\n\nLleva lo justo para ELEGIRLA: nombre, avatar y de dónde viene. Ni email, ni\nheadline, ni bio — eso es su perfil social, que tiene su propia puerta\n(`profile_visibility`, PJKT-1990). Si el ajuste de la organización abriera\ntambién los perfiles de su gente, estaríamos decidiendo por ellos algo que\nellos ya decidieron."},"FacturaDelDesgloseOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"invoice_number":{"type":"string","title":"Invoice Number"},"client_name":{"type":"string","title":"Client Name"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"status":{"type":"string","enum":["sent","paid","overdue"],"title":"Status"},"total":{"type":"number","title":"Total"}},"type":"object","required":["id","invoice_number","client_name","issue_date","status","total"],"title":"FacturaDelDesglose","description":"Una factura que aporta a la cifra, con lo justo para comprobarla.\n\nSin líneas, sin acciones y sin divisa propia: la divisa es la del desglose (la\nbase de la organización) y una fila en otra no puede estar aquí — está en\n`criterio.descartes`. `id` es para abrir la factura de verdad; esto no es la\npantalla de facturas."},"FinanceCommentCreateIn":{"properties":{"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["body"],"title":"FinanceCommentCreateIn"},"FinanceCommentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author_name":{"type":"string","title":"Author Name"},"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"is_edited":{"type":"boolean","title":"Is Edited"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","entity_type","entity_id","author_id","author_name","body","parent_id","is_edited","created_at","updated_at"],"title":"FinanceComment","description":"Comentario financiero del contrato.\n\n`author_id` es `null` si el autor fue borrado; `author_name` cae a\n\"Sistema\" en ese caso. Hilo plano — el cliente anida por `parent_id`."},"FinanceCommentUpdateIn":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"FinanceCommentUpdateIn","description":"PATCH de un comentario: solo `body`."},"FinanceSettingsOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"payment_reminders_enabled":{"type":"boolean","title":"Payment Reminders Enabled"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["organization_id","payment_reminders_enabled","updated_at"],"title":"FinanceSettings","description":"Preferencias de finanzas de la organización."},"FinanceSettingsUpdateIn":{"properties":{"payment_reminders_enabled":{"type":"boolean","title":"Payment Reminders Enabled"}},"type":"object","required":["payment_reminders_enabled"],"title":"FinanceSettingsUpdate","description":"Cuerpo de PUT …/finance/settings."},"FinanceTrendPointOut":{"properties":{"month":{"type":"string","title":"Month"},"currency":{"type":"string","title":"Currency","default":"EUR"},"income":{"type":"number","title":"Income"},"expenses":{"type":"number","title":"Expenses"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["month","income","expenses","criterio"],"title":"FinanceTrendPoint","description":"Un punto de la serie mensual ingresos/gastos.\n\n`expenses` es el COSTE del mes con el criterio compartido de\n`finance/cost_rules.py` —gastos normales aprobados + facturas de proveedor\nrecibidas, sin doble conteo—, el mismo que `FinanceSummaryOut.expenses`\n(PJKT-2304). Antes contaba solo la mitad `kind='expense'`."},"FixedAssetCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"category":{"type":"string","title":"Category"},"asset_type":{"type":"string","enum":["fixed","crypto","etf"],"title":"Asset Type","default":"fixed"},"acquisition_date":{"type":"string","format":"date","title":"Acquisition Date"},"acquisition_cost":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Acquisition Cost"},"useful_life_months":{"type":"integer","title":"Useful Life Months"},"salvage_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Salvage Value","default":"0"},"method":{"type":"string","const":"straight_line","title":"Method","default":"straight_line"},"crypto_subtype":{"anyOf":[{"type":"string","enum":["native","token","stablecoin"]},{"type":"null"}],"title":"Crypto Subtype"},"symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Symbol"},"quantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quantity"}},"type":"object","required":["name","category","acquisition_date","acquisition_cost","useful_life_months"],"title":"FixedAssetCreateIn"},"FixedAssetOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"category":{"type":"string","title":"Category"},"asset_type":{"type":"string","enum":["fixed","crypto","etf"],"title":"Asset Type"},"acquisition_date":{"type":"string","format":"date","title":"Acquisition Date"},"acquisition_cost":{"type":"number","title":"Acquisition Cost"},"useful_life_months":{"type":"integer","title":"Useful Life Months"},"salvage_value":{"type":"number","title":"Salvage Value"},"method":{"type":"string","const":"straight_line","title":"Method"},"disposed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Disposed Date"},"crypto_subtype":{"anyOf":[{"type":"string","enum":["native","token","stablecoin"]},{"type":"null"}],"title":"Crypto Subtype"},"symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Symbol"},"quantity":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Quantity"},"market_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Value"},"market_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Price"},"last_price_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Price At"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["id","organization_id","name","category","asset_type","acquisition_date","acquisition_cost","useful_life_months","salvage_value","method","disposed_date","crypto_subtype","symbol","quantity","market_value","market_price","last_price_at","created_at","updated_at"],"title":"FixedAsset"},"FixedAssetUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"disposed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Disposed Date"},"symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Symbol"},"quantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Quantity"},"crypto_subtype":{"anyOf":[{"type":"string","enum":["native","token","stablecoin"]},{"type":"null"}],"title":"Crypto Subtype"}},"type":"object","title":"FixedAssetUpdateIn","description":"PATCH parcial."},"FuenteDelCriterioOut":{"properties":{"documento":{"type":"string","enum":["factura","gasto","factura_de_proveedor","parte_de_horas"],"title":"Documento"},"campo_de_fecha":{"type":"string","enum":["issue_date","due_date","date","entry_date"],"title":"Campo De Fecha"},"estados":{"items":{"type":"string"},"type":"array","title":"Estados"}},"type":"object","required":["documento","campo_de_fecha","estados"],"title":"FuenteDelCriterio","description":"De qué documentos sale una cifra, con SUS estados y SU columna de fecha."},"FunnelStageOut":{"properties":{"stage_id":{"type":"string","format":"uuid","title":"Stage Id"},"stage":{"type":"string","title":"Stage"},"position":{"type":"integer","title":"Position"},"currency":{"type":"string","title":"Currency","default":"EUR"},"count":{"type":"integer","title":"Count"},"value":{"type":"number","title":"Value"},"drop_off":{"type":"integer","title":"Drop Off"}},"type":"object","required":["stage_id","stage","position","count","value","drop_off"],"title":"FunnelStage","description":"Etapa ordenada del embudo con recuento y drop-off respecto a la anterior."},"GanttDependencyLink":{"properties":{"target_task_id":{"type":"string","format":"uuid","title":"Target Task Id"},"dep_type":{"type":"string","enum":["blocks","blocked_by","relates_to"],"title":"Dep Type"}},"type":"object","required":["target_task_id","dep_type"],"title":"GanttDependencyLink","description":"Compact dependency link embedded in each Gantt task bar."},"GanttResponse":{"properties":{"project_id":{"type":"string","format":"uuid","title":"Project Id"},"tasks":{"items":{"$ref":"#/components/schemas/GanttTask"},"type":"array","title":"Tasks"}},"type":"object","required":["project_id","tasks"],"title":"GanttResponse","description":"Full Gantt payload for a project."},"GanttTask":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"story_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Story Points"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"dependencies":{"items":{"$ref":"#/components/schemas/GanttDependencyLink"},"type":"array","title":"Dependencies"}},"type":"object","required":["id","title","status","assignee_id","story_points","start_date","due_date","dependencies"],"title":"GanttTask","description":"Single task bar for the Gantt chart.\n\nTasks without start_date or due_date are included with null dates;\nthe frontend can place them in an 'unscheduled' lane."},"GenericMessageOut":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"StepUpMessage","description":"Respuesta genérica (p. ej. 'código enviado'), sin filtrar estado."},"GithubConnectIn":{"properties":{"token":{"type":"string","minLength":10,"title":"Token","description":"Personal Access Token de GitHub."}},"type":"object","required":["token"],"title":"GithubConnectIn","description":"Cuerpo del PUT para conectar/actualizar el token GitHub de la org."},"GithubIntegrationOut":{"properties":{"connected":{"type":"boolean","title":"Connected"},"login":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Login"},"token_last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Last4"},"webhook_configured":{"type":"boolean","title":"Webhook Configured","default":false},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"}},"type":"object","required":["connected"],"title":"GithubIntegration","description":"Estado de la integración GitHub de una org."},"GithubOauthStartOut":{"properties":{"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"GithubOauthStartOut","description":"URL de autorización de GitHub para el flujo OAuth de la integración.\n\nscope='repo': acceso completo de lectura/escritura a repos públicos y privados.\nGitHub OAuth clásico no ofrece un scope de solo lectura para repos privados;\nel mínimo que permite clonar y listar repos privados es 'repo'."},"GithubPullReviewFile":{"properties":{"filename":{"type":"string","title":"Filename"},"previous_filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Previous Filename"},"status":{"type":"string","enum":["added","removed","modified","renamed","copied","changed","unchanged"],"title":"Status"},"additions":{"type":"integer","title":"Additions"},"deletions":{"type":"integer","title":"Deletions"},"patch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Patch"},"patch_truncated":{"type":"boolean","title":"Patch Truncated"},"blob_html_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blob Html Url"}},"type":"object","required":["filename","status","additions","deletions","patch","patch_truncated"],"title":"GithubPullReviewFile","description":"Un fichero cambiado por el PR, con su parche unificado tal cual lo da GitHub.\n\nNO se convierte el parche a una estructura de hunks en el servidor: ya es texto\ny meterlo en JSON solo lo engorda. Lo parsea el cliente.\n\n`patch` es None y `patch_truncated` True cuando GitHub no sirve el parche\n(binario, fichero enorme) o cuando se agotó el presupuesto de la respuesta."},"GithubPullReviewOut":{"properties":{"repo_full_name":{"type":"string","title":"Repo Full Name"},"number":{"type":"integer","title":"Number"},"title":{"type":"string","title":"Title"},"state":{"type":"string","enum":["open","closed"],"title":"State"},"merged":{"type":"boolean","title":"Merged"},"draft":{"type":"boolean","title":"Draft"},"html_url":{"type":"string","title":"Html Url"},"head_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Head Ref"},"base_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Ref"},"changed_files":{"type":"integer","title":"Changed Files"},"additions":{"type":"integer","title":"Additions"},"deletions":{"type":"integer","title":"Deletions"},"files":{"items":{"$ref":"#/components/schemas/GithubPullReviewFile"},"type":"array","title":"Files"},"files_truncated":{"type":"boolean","title":"Files Truncated"},"comments":{"items":{"$ref":"#/components/schemas/GithubReviewComment"},"type":"array","title":"Comments"},"comments_truncated":{"type":"boolean","title":"Comments Truncated"},"rate_limited":{"type":"boolean","title":"Rate Limited"},"fetch_failed":{"type":"boolean","title":"Fetch Failed"}},"type":"object","required":["repo_full_name","number","title","state","merged","draft","html_url","changed_files","additions","deletions","files","files_truncated","comments","comments_truncated","rate_limited","fetch_failed"],"title":"GithubPullReview","description":"Diff + comentarios de revisión de un PR. Solo lectura: no hay caja de responder.\n\nUn comentario escrito desde aquí lo firmaría el token de la integración de la\nORGANIZACIÓN, no la persona que revisa. Por eso este recurso no tiene POST.\n\nLos recortes se DICEN: `files_truncated` (más ficheros de los que caben),\n`patch_truncated` por fichero, `comments_truncated`. `rate_limited` es que\nGitHub agotó la cuota al pedir ficheros o comentarios (el PR sí se leyó); si\nla agota ya en la lectura del PR, sale un 429 y no este schema.\n\nY los fallos también se dicen: `fetch_failed` separa «esta lista está vacía»\nde «esta lista no se pudo leer» (502, timeout). Sin ese tercer estado el\ncliente pintaría «0 comentarios de revisión» como un hecho que nadie leyó."},"GithubRepoSlim":{"properties":{"full_name":{"type":"string","title":"Full Name"},"private":{"type":"boolean","title":"Private"},"default_branch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Branch"},"pushed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pushed At"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["full_name","private"],"title":"GithubRepoSlim","description":"Representación slim de un repositorio GitHub para el picker de la UI."},"GithubReviewComment":{"properties":{"id":{"type":"integer","title":"Id"},"author":{"type":"string","title":"Author"},"author_avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Avatar Url"},"created_at":{"type":"string","title":"Created At"},"body":{"type":"string","title":"Body"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions"},"path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path"},"line":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Line"},"in_reply_to_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"In Reply To Id"},"html_url":{"type":"string","title":"Html Url"}},"type":"object","required":["id","author","created_at","body","suggestions","path","line","html_url"],"title":"GithubReviewComment","description":"Comentario de revisión existente (anclado a una línea del diff).\n\n`suggestions` NO es un campo de GitHub: la sugerencia viaja dentro del cuerpo,\nen un bloque cercado ```suggestion. Se extrae de ahí y se quita del `body` para\nno pintarla dos veces. Nada inventado."},"GithubWebhookAck":{"properties":{"received":{"type":"boolean","title":"Received"},"event":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event"},"duplicate":{"type":"boolean","title":"Duplicate"},"processed":{"type":"boolean","title":"Processed"}},"type":"object","required":["received","event","duplicate","processed"],"title":"GithubWebhookAck","description":"Acuse del receptor de webhooks. Se devuelve SOLO tras verificar la firma HMAC.\n\n- received: la firma se verificó (siempre True cuando se llega a devolver esto).\n- event: tipo de evento (X-GitHub-Event); None si ausente.\n- duplicate: la entrega (X-GitHub-Delivery) ya se había procesado (reentrega).\n- processed: el evento se aplicó al estado (push/pull_request/issues). False para\n  reentregas o eventos no relevantes (p. ej. ping)."},"GithubWebhookConfig":{"properties":{"webhook_url":{"type":"string","title":"Webhook Url"},"secret":{"type":"string","title":"Secret"}},"type":"object","required":["webhook_url","secret"],"title":"GithubWebhookConfig","description":"Config del webhook devuelta al provisionar/rotar. El secreto se muestra 1 sola vez."},"GlobalSupplierImportErrorItem":{"properties":{"line":{"type":"integer","title":"Line"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["line","reason"],"title":"GlobalSupplierImportErrorItem","description":"Error de una fila del CSV: número de línea + motivo."},"GlobalSupplierImportResultOut":{"properties":{"created":{"type":"integer","title":"Created"},"updated":{"type":"integer","title":"Updated"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"$ref":"#/components/schemas/GlobalSupplierImportErrorItem"},"type":"array","title":"Errors"}},"type":"object","required":["created","updated","skipped","errors"],"title":"GlobalSupplierImportResult"},"GlobalSupplierOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"linked_count":{"type":"integer","title":"Linked Count","default":0},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","tax_id","email","phone","address","website","logo_url","notes","created_at","updated_at"],"title":"GlobalSupplier"},"GlobalSupplierWriteIn":{"properties":{"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["name"],"title":"GlobalSupplierWrite","description":"Alta/edición de una entrada del catálogo (solo platform admins)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthOut":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"HealthOut"},"HoldingCreateIn":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"parent_org_id":{"type":"string","format":"uuid","title":"Parent Org Id"}},"type":"object","required":["name","parent_org_id"],"title":"HoldingCreate","description":"Alta de un holding. La MATRIZ es una organización existente del caller."},"HoldingFinanceCurrencyOut":{"properties":{"currency":{"type":"string","title":"Currency"},"revenue":{"type":"number","title":"Revenue"},"expenses":{"type":"number","title":"Expenses"},"net":{"type":"number","title":"Net"},"gross_revenue":{"type":"number","title":"Gross Revenue"},"gross_expenses":{"type":"number","title":"Gross Expenses"},"intercompany_revenue_excluded":{"type":"number","title":"Intercompany Revenue Excluded"},"intercompany_expenses_excluded":{"type":"number","title":"Intercompany Expenses Excluded"}},"type":"object","required":["currency","revenue","expenses","net","gross_revenue","gross_expenses","intercompany_revenue_excluded","intercompany_expenses_excluded"],"title":"HoldingFinanceCurrency","description":"Ingresos/gastos consolidados de UNA divisa.\n\n`revenue`/`expenses` ya vienen NETEADOS de operaciones intercompañía;\n`gross_*` son los importes antes de netear y `intercompany_*_excluded` la\ndiferencia. Nunca se convierten divisas: cada divisa es una fila."},"HoldingFinanceOrgRowOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"is_parent":{"type":"boolean","title":"Is Parent"},"currencies":{"items":{"$ref":"#/components/schemas/HoldingFinanceCurrencyOut"},"type":"array","title":"Currencies"},"finance_visible":{"type":"boolean","title":"Finance Visible"}},"type":"object","required":["organization_id","name","is_parent","currencies","finance_visible"],"title":"HoldingFinanceOrgRow","description":"Desglose financiero de una organización del grupo, por divisa."},"HoldingFinanceOut":{"properties":{"holding_id":{"type":"string","format":"uuid","title":"Holding Id"},"from_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From Date"},"to_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To Date"},"currencies":{"items":{"$ref":"#/components/schemas/HoldingFinanceCurrencyOut"},"type":"array","title":"Currencies"},"organizations":{"items":{"$ref":"#/components/schemas/HoldingFinanceOrgRowOut"},"type":"array","title":"Organizations"},"intercompany_operations":{"type":"integer","title":"Intercompany Operations"},"intercompany_note":{"type":"string","title":"Intercompany Note"},"has_hidden_data":{"type":"boolean","title":"Has Hidden Data"}},"type":"object","required":["holding_id","currencies","organizations","intercompany_operations","intercompany_note","has_hidden_data"],"title":"HoldingFinance","description":"Finanzas consolidadas del holding, por divisa y con desglose por filial."},"HoldingMemberCreateIn":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"type":"string","enum":["owner","viewer"],"title":"Role","default":"viewer"}},"type":"object","required":["user_id"],"title":"HoldingMemberCreate"},"HoldingMemberOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"holding_id":{"type":"string","format":"uuid","title":"Holding Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"role":{"type":"string","enum":["owner","viewer"],"title":"Role"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","holding_id","user_id","role","created_at"],"title":"HoldingMember","description":"Miembro del holding. Solo id de usuario y rol: sin PII (decisión 3)."},"HoldingMemberUpdateIn":{"properties":{"role":{"type":"string","enum":["owner","viewer"],"title":"Role"}},"type":"object","required":["role"],"title":"HoldingMemberUpdate"},"HoldingOrgRowOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"plan":{"type":"string","title":"Plan"},"is_parent":{"type":"boolean","title":"Is Parent"},"projects_total":{"type":"integer","title":"Projects Total"},"projects_active":{"type":"integer","title":"Projects Active"},"tasks_total":{"type":"integer","title":"Tasks Total"},"tasks_open":{"type":"integer","title":"Tasks Open"},"people":{"type":"integer","title":"People"},"hidden_blocks":{"items":{"type":"string","enum":["finance","projects","people"]},"type":"array","title":"Hidden Blocks"}},"type":"object","required":["organization_id","name","slug","plan","is_parent","projects_total","projects_active","tasks_total","tasks_open","people","hidden_blocks"],"title":"HoldingOrgRow","description":"Una fila de la tabla de filiales del panel.\n\nSi un bloque está en `hidden_blocks`, sus contadores valen 0 y NO han sumado\nen los totales del consolidado. Un 0 con el bloque oculto significa «no lo\nmiras», no «no hay ninguno»; hay que leer `hidden_blocks` para distinguirlo.\nLos contadores no se vuelven `null` porque cambiar la nulabilidad de un campo\nya publicado sería breaking en v1 (`docs/api-contract.md`)."},"HoldingOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"parent_org_id":{"type":"string","format":"uuid","title":"Parent Org Id"},"parent_org_name":{"type":"string","title":"Parent Org Name"},"my_role":{"type":"string","enum":["owner","viewer"],"title":"My Role"},"can_manage":{"type":"boolean","title":"Can Manage"},"subsidiaries_count":{"type":"integer","title":"Subsidiaries Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","parent_org_id","parent_org_name","my_role","can_manage","subsidiaries_count","created_at"],"title":"Holding","description":"Holding visto por un miembro suyo."},"HoldingSubsidiaryAttachIn":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"}},"type":"object","required":["organization_id"],"title":"HoldingSubsidiaryAttach","description":"Adjuntar una organización al holding como filial."},"HoldingSummaryOut":{"properties":{"holding_id":{"type":"string","format":"uuid","title":"Holding Id"},"holding_name":{"type":"string","title":"Holding Name"},"parent_org_id":{"type":"string","format":"uuid","title":"Parent Org Id"},"subsidiaries_count":{"type":"integer","title":"Subsidiaries Count"},"organizations_count":{"type":"integer","title":"Organizations Count"},"projects_total":{"type":"integer","title":"Projects Total"},"projects_active":{"type":"integer","title":"Projects Active"},"tasks_total":{"type":"integer","title":"Tasks Total"},"tasks_open":{"type":"integer","title":"Tasks Open"},"tasks_by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"Tasks By Status"},"people_total":{"type":"integer","title":"People Total"},"organizations":{"items":{"$ref":"#/components/schemas/HoldingOrgRowOut"},"type":"array","title":"Organizations"},"has_hidden_data":{"type":"boolean","title":"Has Hidden Data"}},"type":"object","required":["holding_id","holding_name","parent_org_id","subsidiaries_count","organizations_count","projects_total","projects_active","tasks_total","tasks_open","tasks_by_status","people_total","organizations","has_hidden_data"],"title":"HoldingSummary","description":"Resumen consolidado de actividad del grupo (solo lectura, sin PII).\n\nLos totales suman SOLO las organizaciones cuyo bloque está visible. Es\ndeliberado: dejar sumar una filial oculta la delataría por diferencia (total\nmenos la suma de las filas visibles = el dato que se quería ocultar).\n`has_hidden_data` avisa de que los totales son parciales."},"HoldingUpdateIn":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"}},"type":"object","required":["name"],"title":"HoldingUpdate","description":"Renombrar el holding (único campo mutable)."},"HoldingVisibilityOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"finance":{"type":"boolean","title":"Finance"},"projects":{"type":"boolean","title":"Projects"},"people":{"type":"boolean","title":"People"},"hidden_blocks":{"items":{"type":"string","enum":["finance","projects","people"]},"type":"array","title":"Hidden Blocks"}},"type":"object","required":["organization_id","organization_name","finance","projects","people","hidden_blocks"],"title":"HoldingVisibility","description":"Configuración efectiva de UNA filial (con los defaults ya resueltos)."},"HoldingVisibilityUpdateIn":{"properties":{"finance":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Finance"},"projects":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Projects"},"people":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"People"}},"type":"object","title":"HoldingVisibilityUpdate","description":"PATCH parcial: lo que no se envía NO se toca.\n\nSolo booleanos. Deliberadamente no hay ningún campo de organización ni de\nholding en el body: el «de quién» sale del path y se verifica contra el\ncontexto de servidor, así que este payload no puede ensanchar el alcance."},"HolidayCreateIn":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"recurring":{"type":"boolean","title":"Recurring","default":false}},"type":"object","required":["date","name"],"title":"HolidayCreateIn","description":"Cuerpo de creación de un festivo de la organización."},"HolidayImportError":{"properties":{"fila":{"type":"integer","title":"Fila"},"motivo":{"type":"string","title":"Motivo"}},"type":"object","required":["fila","motivo"],"title":"HolidayImportError","description":"Motivo por el que una fila concreta no pudo importarse."},"HolidayImportIn":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/HolidayImportRowIn"},"type":"array","maxItems":1000,"minItems":1,"title":"Rows"}},"type":"object","required":["rows"],"title":"HolidayImportIn","description":"Cuerpo de importación: filas del CSV ya parseadas (cabecera excluida)."},"HolidayImportResult":{"properties":{"created":{"type":"integer","title":"Created"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"$ref":"#/components/schemas/HolidayImportError"},"type":"array","title":"Errors"}},"type":"object","required":["created","skipped","errors"],"title":"HolidayImportResult","description":"Resumen de la importación: creados, omitidos y errores por fila."},"HolidayImportRowIn":{"properties":{"fecha":{"type":"string","title":"Fecha","default":""},"nombre":{"type":"string","title":"Nombre","default":""},"ambito":{"type":"string","title":"Ambito","default":""}},"type":"object","title":"HolidayImportRowIn","description":"Una fila del CSV (texto plano); se valida FILA A FILA en el servicio.\n\nLos campos son opcionales con default \"\" para que una fila incompleta o\ninválida produzca un error de esa fila (no un 422 del lote entero)."},"HolidayOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"date":{"type":"string","format":"date","title":"Date"},"name":{"type":"string","title":"Name"},"recurring":{"type":"boolean","title":"Recurring"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","date","name","recurring","created_at","updated_at"],"title":"Holiday"},"HolidayUpdateIn":{"properties":{"date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date"},"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"},"recurring":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Recurring"}},"type":"object","title":"HolidayUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes.\n\nNinguno de los tres admite `null`: las tres columnas son NOT NULL, así que un\n`null` explícito no borraba nada —llegaba al UPDATE y lo reventaba con un 500—.\nOmitir el campo es lo que deja el festivo como está."},"ImpersonatingOut":{"properties":{"as_name":{"type":"string","title":"As Name"},"as_email":{"type":"string","title":"As Email"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"read_only":{"type":"boolean","title":"Read Only","default":true},"actor_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Name"}},"type":"object","required":["as_name","as_email","expires_at"],"title":"Impersonating","description":"Contexto de impersonation devuelto en `User.impersonating` (panel ops — W16).\n\nPresente (non-null) SOLO cuando la petición llega bajo una sesión de\nimpersonation: el `User` devuelto ES el usuario impersonado, y este objeto le\ndice al frontend que pinte el banner permanente \"Estás viendo como {as_name} —\nsolo lectura\". El enforcement es SIEMPRE server-side (middleware read-only +\npolicy); este flag es solo para pintar el banner, nunca para autorizar."},"ImpersonationCreateIn":{"properties":{"reason":{"type":"string","title":"Reason"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"}},"type":"object","required":["reason"],"title":"ImpersonationCreate","description":"Cuerpo para abrir una impersonation: motivo OBLIGATORIO + org opcional.\n\n`reason` (min 1, tras strip) es forense: queda en la fila y en la auditoría.\n`organization_id`, si viene, es el contexto que el soporte quería inspeccionar\ny se valida (el usuario impersonado debe ser miembro de esa org)."},"ImpersonationCreatedOut":{"properties":{"session_id":{"type":"string","format":"uuid","title":"Session Id"},"impersonated_user_id":{"type":"string","format":"uuid","title":"Impersonated User Id"},"token":{"type":"string","title":"Token"},"activation_url":{"type":"string","title":"Activation Url"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["session_id","impersonated_user_id","token","activation_url","expires_at"],"title":"ImpersonationCreated","description":"Respuesta de la creación: el token OPACO se devuelve UNA sola vez.\n\n`activation_url` es el enlace que el panel ops abre (navegación top-level) para\nque el API fije la cookie HttpOnly de impersonation en el dominio de la web y\nredirija al dashboard. `token` va aparte por si el panel prefiere construir el\nenlace; en DB solo vive su SHA-256, jamás el token en claro."},"ImpersonationSessionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"impersonator_id":{"type":"string","format":"uuid","title":"Impersonator Id"},"impersonator_email":{"type":"string","title":"Impersonator Email"},"impersonated_user_id":{"type":"string","format":"uuid","title":"Impersonated User Id"},"impersonated_email":{"type":"string","title":"Impersonated Email"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"reason":{"type":"string","title":"Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["id","impersonator_id","impersonator_email","impersonated_user_id","impersonated_email","reason","created_at","expires_at"],"title":"ImpersonationSession","description":"Sesión de impersonation VIVA (para el listado read-only de ops/health).\n\nNUNCA expone el token ni su hash: solo metadata forense (quién, a quién, por\nqué, cuándo caduca)."},"InboundEmailAck":{"properties":{"accepted":{"type":"boolean","title":"Accepted","default":true}},"type":"object","title":"InboundEmailAck","description":"Acuse del receptor público.\n\nSiempre el mismo cuerpo. No dice si la dirección existía, si el remitente era\nconocido, ni si se creó algo: cualquiera de esas tres cosas convertiría un\nendpoint público en un oráculo para enumerar buzones y clientes de otras\norganizaciones, y a este endpoint puede llegar cualquiera que sepa mandar un\ncorreo."},"InviteMemberIn":{"properties":{"identifier":{"type":"string","title":"Identifier"},"role":{"type":"string","enum":["member","manager","admin"],"title":"Role","default":"member"}},"type":"object","required":["identifier"],"title":"InviteMemberIn","description":"Payload de POST .../members/invite.\n\n`identifier` acepta un email o un nombre de usuario. Para emails\ndesconocidos se crea una invitación pendiente; para emails ya registrados\n(o usernames) se añade directamente como miembro.\n`role` solo admite `member` o `admin` (no `owner` — ese rol se otorga\nexplícitamente via PATCH .../members/{user_id} por un owner)."},"InviteOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"email":{"type":"string","title":"Email"},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"},"invited_by":{"type":"string","format":"uuid","title":"Invited By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"accepted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Accepted At"}},"type":"object","required":["id","organization_id","email","role","invited_by","created_at","expires_at"],"title":"OrgInvite","description":"Invitación pendiente (returned when the email is not yet registered)."},"InvitePreviewOut":{"properties":{"status":{"type":"string","enum":["pending","expired","accepted"],"title":"Status"},"organization_name":{"type":"string","title":"Organization Name"},"organization_slug":{"type":"string","title":"Organization Slug"},"organization_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization Logo Url"},"organization_brand_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization Brand Color"},"email":{"type":"string","title":"Email"},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"},"invited_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invited By Name"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["status","organization_name","organization_slug","email","role","expires_at"],"title":"OrgInvitePreview","description":"Ficha PÚBLICA de una invitación, resuelta por su token opaco.\n\nEs lo que ve quien abre el enlace del correo, normalmente sin sesión y a\nveces sin cuenta. Deliberadamente NO lleva agregados de la organización\n(miembros, proyectos): el correo invitado y el nombre de quien invita ya\nestaban en la bandeja de entrada de quien tiene el token, pero el tamaño de\nla organización sería información nueva y un token filtrado no debe\nconvertirse en un censo."},"InviteResultOut":{"properties":{"member":{"anyOf":[{"$ref":"#/components/schemas/MemberOut"},{"type":"null"}]},"invite":{"anyOf":[{"$ref":"#/components/schemas/InviteOut"},{"type":"null"}]}},"type":"object","title":"InviteResult","description":"Respuesta de POST .../members/invite.\n\nDiscrimina entre dos salidas mutuamente excluyentes:\n- `member` (not None) → usuario existente añadido directamente.\n- `invite` (not None) → invitación pendiente creada para email desconocido."},"InvoiceBulkActionIn":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Ids"},"action":{"type":"string","enum":["approve","reject","mark_paid","void","set_client"],"title":"Action"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"}},"type":"object","required":["ids","action"],"title":"InvoiceBulkActionIn"},"InvoiceCreateIn":{"properties":{"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"type":"string","format":"date","title":"Due Date"},"items":{"items":{"$ref":"#/components/schemas/InvoiceItemIn-Input"},"type":"array","title":"Items"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Tax Rate","default":"0"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu","default":false},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"payment_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","required":["client_name","issue_date","due_date","items"],"title":"InvoiceCreateIn"},"InvoiceFromTimeIn":{"properties":{"project_id":{"type":"string","format":"uuid","title":"Project Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"date_from":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date From"},"date_to":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date To"},"group_by":{"type":"string","enum":["user","task"],"title":"Group By","default":"user"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Tax Rate","default":"0"}},"type":"object","required":["project_id","client_id"],"title":"InvoiceFromTime","description":"Genera una factura DRAFT desde el tiempo facturable de un proyecto.\n\nAgrega las horas `is_billable` no facturadas (invoice_id IS NULL) del\nproyecto × la tarifa de facturación de cada persona (`hourly_bill_rate`)\nen líneas — una por usuario (`group_by=\"user\"`) o por tarea\n(`group_by=\"task\"`). `date_from`/`date_to` acotan por `entry_date` (ambos\nopcionales e inclusivos). Las entries consumidas quedan marcadas con el id\nde la factura para no facturarse dos veces."},"InvoiceItemIn-Input":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Quantity"},"unit_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Unit Price"}},"type":"object","required":["description","quantity","unit_price"],"title":"InvoiceItemIn"},"InvoiceItemIn-Output":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Quantity"},"unit_price":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Unit Price"}},"type":"object","required":["description","quantity","unit_price"],"title":"InvoiceItemIn"},"InvoiceItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"description":{"type":"string","title":"Description"},"quantity":{"type":"number","title":"Quantity"},"unit_price":{"type":"number","title":"Unit Price"},"amount":{"type":"number","title":"Amount"}},"type":"object","required":["id","description","quantity","unit_price","amount"],"title":"InvoiceItem"},"InvoiceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"invoice_number":{"type":"string","title":"Invoice Number"},"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"status":{"type":"string","enum":["draft","sent","paid","overdue","cancelled"],"title":"Status"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"type":"string","format":"date","title":"Due Date"},"subtotal":{"type":"number","title":"Subtotal"},"tax_rate":{"type":"number","title":"Tax Rate"},"tax_amount":{"type":"number","title":"Tax Amount"},"total":{"type":"number","title":"Total"},"currency":{"type":"string","title":"Currency"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"payment_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"linked_expense_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Linked Expense Id"},"rectifies_invoice_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Rectifies Invoice Id"},"rectification_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rectification Reason"},"items":{"items":{"$ref":"#/components/schemas/InvoiceItemOut"},"type":"array","title":"Items"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","invoice_number","client_name","client_email","status","issue_date","due_date","subtotal","tax_rate","tax_amount","total","currency","notes","cost_center_id","is_intra_eu","client_id","client_address","client_tax_id","payment_method","rectifies_invoice_id","rectification_reason","items","created_at","updated_at"],"title":"Invoice"},"InvoiceRefundIn":{"properties":{"amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*(?:\\d{0,10}|(?=[\\d.]{1,13}0*$)\\d{0,10}\\.\\d{0,2}0*$)"},{"type":"null"}],"title":"Amount"},"refund_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Refund Date"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"InvoiceRefundIn","description":"Reembolso —total o parcial— de una factura COBRADA.\n\n`amount` es el importe BRUTO que sale del banco (IVA incluido), en la divisa\nde la factura. Omitirlo o mandarlo `null` reembolsa todo lo que queda por\nreembolsar: el reembolso total es el caso particular del parcial, no un\ncamino aparte, y así no hay dos comportamientos que puedan divergir.\n\n`refund_date` es la fecha del DOCUMENTO rectificativo (su `issue_date`, la\nque decide en qué trimestre entra). Por defecto, hoy. No se deduce de la\nfecha de cobro porque **el producto no la guarda**: no existe `paid_date` en\nninguna de las 172 migraciones (PJKT-2496), así que inventarla sería poner en\nuna declaración una fecha que nadie escribió."},"InvoiceRefundsOut":{"properties":{"invoice_id":{"type":"string","format":"uuid","title":"Invoice Id"},"invoice_number":{"type":"string","title":"Invoice Number"},"currency":{"type":"string","title":"Currency"},"total":{"type":"number","title":"Total"},"refunded_amount":{"type":"number","title":"Refunded Amount"},"refundable_amount":{"type":"number","title":"Refundable Amount"},"refunds":{"items":{"$ref":"#/components/schemas/InvoiceOut"},"type":"array","title":"Refunds"}},"type":"object","required":["invoice_id","invoice_number","currency","total","refunded_amount","refundable_amount","refunds"],"title":"InvoiceRefunds","description":"Qué se ha reembolsado de una factura, cuánto y cuándo.\n\nLas tres cifras salen de la MISMA suma sobre las rectificativas, en la misma\nrespuesta, para que la pantalla no tenga que restar nada por su cuenta: una\nresta hecha en el cliente es otra fuente de verdad."},"InvoiceReminderOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"invoice_id":{"type":"string","format":"uuid","title":"Invoice Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"milestone_days":{"type":"integer","title":"Milestone Days"},"sent_to":{"type":"string","title":"Sent To"},"sent_at":{"type":"string","format":"date-time","title":"Sent At"}},"type":"object","required":["id","invoice_id","organization_id","milestone_days","sent_to","sent_at"],"title":"InvoiceReminder","description":"Un recordatorio de cobro ya enviado al cliente de la factura."},"InvoiceRenumberChangeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"old":{"type":"string","title":"Old"},"new":{"type":"string","title":"New"}},"type":"object","required":["id","old","new"],"title":"InvoiceRenumberChange"},"InvoiceRenumberIn":{"properties":{"prefix":{"type":"string","title":"Prefix","default":"INV"},"dry_run":{"type":"boolean","title":"Dry Run","default":true}},"type":"object","title":"InvoiceRenumberIn","description":"Renumeración correlativa de la serie de facturas (admin+).\n\n`prefix` delimita la serie (solo se tocan números `{prefix}-…`); las demás\nseries de la organización quedan intactas. `dry_run=True` (default)\ndevuelve el plan sin escribir nada."},"InvoiceRenumberResultOut":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run"},"total":{"type":"integer","title":"Total"},"changed":{"type":"integer","title":"Changed"},"mapping":{"items":{"$ref":"#/components/schemas/InvoiceRenumberChangeOut"},"type":"array","title":"Mapping"}},"type":"object","required":["dry_run","total","changed","mapping"],"title":"InvoiceRenumberResult"},"InvoiceShareCreateIn":{"properties":{"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days"}},"type":"object","title":"InvoiceShareCreateIn","description":"Cuerpo de POST …/share. `expires_in_days` = None → nunca expira."},"InvoiceShareOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"invoice_id":{"type":"string","format":"uuid","title":"Invoice Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url"}},"type":"object","required":["id","invoice_id","organization_id","created_by","expires_at","revoked_at","created_at"],"title":"InvoiceShareToken"},"InvoiceStatusStatOut":{"properties":{"status":{"type":"string","enum":["draft","sent","paid","overdue","cancelled"],"title":"Status"},"currency":{"type":"string","title":"Currency","default":"EUR"},"count":{"type":"integer","title":"Count"},"total":{"type":"number","title":"Total"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["status","count","total","criterio"],"title":"InvoiceStatusStat","description":"Recuento e importe de facturas por estado en el rango."},"InvoiceUpdateIn":{"properties":{"status":{"anyOf":[{"type":"string","enum":["draft","sent","paid","overdue","cancelled"]},{"type":"null"}],"title":"Status"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"client_email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Client Email"},"issue_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Issue Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"items":{"anyOf":[{"items":{"$ref":"#/components/schemas/InvoiceItemIn-Input"},"type":"array"},{"type":"null"}],"title":"Items"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Intra Eu"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"payment_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","title":"InvoiceUpdateIn","description":"PATCH parcial: solo se aplican campos presentes.\n\n`issue_date`, `items`, `tax_rate` y `currency` solo son editables en\nBORRADOR (el service devuelve 422 en cualquier otro estado): una factura\nemitida es un documento fiscal cerrado. Al cambiar `items`/`tax_rate` se\nrecalculan subtotal, cuota y total y se REEMPLAZAN las líneas."},"IssueCountsOut":{"properties":{"todo":{"type":"integer","title":"Todo"},"in_progress":{"type":"integer","title":"In Progress"},"done":{"type":"integer","title":"Done"},"cancelled":{"type":"integer","title":"Cancelled"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["todo","in_progress","done","cancelled","total"],"title":"DashboardIssueCounts","description":"Recuento de tareas de la org por estado (más el total).\n\n`cancelled` se contabiliza por separado; `total` incluye todos los estados\n(todo + in_progress + done + cancelled)."},"JoinTokenOut":{"properties":{"token":{"type":"string","title":"Token"},"url":{"type":"string","title":"Url"},"room":{"type":"string","title":"Room"},"identity":{"type":"string","title":"Identity"}},"type":"object","required":["token","url","room","identity"],"title":"MeetingJoinToken","description":"Token de acceso LiveKit + datos de conexión para unirse a la sala."},"KernAlertOut":{"properties":{"module":{"type":"string","enum":["finance","quotes","projects","tasks"],"title":"Module","description":"Módulo de origen: 'finance' | 'quotes' | 'projects' | 'tasks'."},"severity":{"type":"string","enum":["info","warn","urgent"],"title":"Severity","description":"Gravedad: 'info' | 'warn' | 'urgent'."},"title":{"type":"string","title":"Title","description":"Resumen corto del aviso, en español."},"detail":{"type":"string","title":"Detail","description":"Descripción algo más extensa del aviso."},"action_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Url","description":"Ruta web relativa para actuar (p. ej. /finance/invoices), o None."}},"type":"object","required":["module","severity","title","detail"],"title":"KernAlert","description":"Un aviso determinista de Kern (sin IA): algo que requiere atención.\n\nLo generan queries deterministas por módulo (facturas vencidas, presupuestos\nsin aprobar, proyectos en pérdidas, tareas vencidas), cada una respetando los\npermisos del módulo. `action_url` es la ruta web relativa donde actuar (p. ej.\n`/finance/invoices`), o None si no hay una acción directa."},"KeyResultCreateIn":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"target_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Target Value"},"current_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Current Value","default":"0"},"unit":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Unit"}},"type":"object","required":["title","target_value"],"title":"KeyResultCreateIn","description":"Crear un resultado clave. `current_value` por defecto 0."},"KeyResultOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"objective_id":{"type":"string","format":"uuid","title":"Objective Id"},"title":{"type":"string","title":"Title"},"target_value":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Target Value"},"current_value":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Current Value"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"progress":{"type":"number","title":"Progress"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","objective_id","title","target_value","current_value","unit","progress","created_at"],"title":"KeyResult"},"KeyResultSnapshotCreateIn":{"properties":{"value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Value"}},"type":"object","required":["value"],"title":"KeyResultSnapshotCreateIn","description":"Registra un snapshot de progreso: mueve `current_value` del KR a `value`\ny dejar rastro en el historial (a diferencia de `PATCH`, que también puede\ntocar título/target/unidad, esto es SOLO \"registrar el avance de hoy\")."},"KeyResultSnapshotOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"key_result_id":{"type":"string","format":"uuid","title":"Key Result Id"},"value":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Value"},"target_value":{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$","title":"Target Value"},"progress":{"type":"number","title":"Progress"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","key_result_id","value","target_value","progress","created_at"],"title":"KeyResultSnapshot","description":"Punto histórico del progreso de un KR (para pintar una gráfica de\nevolución). `value`/`target_value` son los vigentes EN ESE MOMENTO."},"KeyResultUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"target_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Target Value"},"current_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Current Value"},"unit":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Unit"}},"type":"object","title":"KeyResultUpdateIn","description":"Editar un resultado clave (PATCH parcial). Mover `current_value` es el\ncaso típico para reflejar avance manual."},"LeadCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"status":{"type":"string","enum":["new","contacted","qualified","converted","lost"],"title":"Status","default":"new"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["name"],"title":"LeadCreateIn"},"LeadOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"status":{"type":"string","enum":["new","contacted","qualified","converted","lost"],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"converted_client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Converted Client Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","email","phone","company","source","status","notes","converted_client_id","created_at","updated_at"],"title":"Lead"},"LeadStatusStatOut":{"properties":{"status":{"type":"string","enum":["new","contacted","qualified","converted","lost"],"title":"Status"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["status","count"],"title":"LeadStatusStat","description":"Recuento de leads por estado."},"LeadUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"status":{"anyOf":[{"type":"string","enum":["new","contacted","qualified","converted","lost"]},{"type":"null"}],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"LeadUpdateIn","description":"PATCH parcial."},"LeaveBalanceOut":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"year":{"type":"integer","title":"Year"},"allowance":{"type":"number","title":"Allowance"},"taken":{"type":"number","title":"Taken"},"pending":{"type":"number","title":"Pending"},"remaining":{"type":"number","title":"Remaining"}},"type":"object","required":["employee_id","year","allowance","taken","pending","remaining"],"title":"LeaveBalance"},"LeaveDecisionIn":{"properties":{"comment":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Comment"}},"type":"object","title":"LeaveDecisionIn","description":"Payload opcional para approve/reject (comentario interno)."},"LeaveRequestCreateIn":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"leave_type":{"type":"string","enum":["vacation","sick","unpaid","other"],"title":"Leave Type"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["employee_id","leave_type","start_date","end_date"],"title":"LeaveRequestCreateIn"},"LeaveRequestOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"leave_type":{"type":"string","enum":["vacation","sick","unpaid","other"],"title":"Leave Type"},"start_date":{"type":"string","format":"date","title":"Start Date"},"end_date":{"type":"string","format":"date","title":"End Date"},"days":{"type":"number","title":"Days"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"leave_status":{"type":"string","enum":["pending","approved","rejected"],"title":"Leave Status"},"approver_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Approver Id"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","employee_id","user_id","leave_type","start_date","end_date","days","reason","leave_status","approver_id","decided_at","created_at","updated_at"],"title":"LeaveRequest"},"LoginCodeRequestIn":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"LoginCodeRequest","description":"Cuerpo de solicitud de código: {email}."},"LoginCodeRequestOut":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"LoginCodeRequestResult","description":"Respuesta genérica — NUNCA revela si el email existe (anti-enumeración)."},"LoginCodeVerifyIn":{"properties":{"email":{"type":"string","title":"Email"},"code":{"type":"string","title":"Code"}},"type":"object","required":["email","code"],"title":"LoginCodeVerify","description":"Cuerpo de verificación: {email, code}.\n\nEl email viaja también aquí porque el código está LIGADO a él: el hash\nalmacenado es un HMAC del par (código, email), así que sin el email no se\npuede comprobar. Y de paso impide que un código emitido para una cuenta sirva\npara otra aunque el número coincida."},"LoginIn":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginIn"},"MeWorkOrgCountOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["organization_id","count"],"title":"MeWorkOrgCount","description":"Conteo de tareas abiertas asignadas al usuario en una organización.\n\nAgregado ligero para los badges del selector de organización en \"Mi\ntrabajo\": tareas del usuario NO cerradas (ni done ni cancelled), agrupadas\npor organización de la que es miembro."},"MeetingAdmissionDecideIn":{"properties":{"action":{"type":"string","enum":["admit","deny"],"title":"Action"}},"type":"object","required":["action"],"title":"MeetingAdmissionDecideIn","description":"Decisión del anfitrión sobre una solicitud de admisión."},"MeetingAdmissionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"meeting_id":{"type":"string","format":"uuid","title":"Meeting Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"status":{"type":"string","enum":["requested","admitted","denied"],"title":"Status"},"decided_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Decided By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"}},"type":"object","required":["id","meeting_id","organization_id","user_id","status","decided_by","created_at","decided_at"],"title":"MeetingAdmission"},"MeetingCapabilitiesOut":{"properties":{"video_enabled":{"type":"boolean","title":"Video Enabled"},"reason":{"anyOf":[{"type":"string","const":"livekit_unconfigured"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["video_enabled","reason"],"title":"MeetingCapabilities","description":"Capacidades de vídeo de ESTA instalación (no del plan ni del rol).\n\nSirve para que la interfaz pueda decir «la videollamada no está disponible en\nesta instalación» ANTES de crear una reunión, en vez de que el usuario choque\ncon el 503 `livekit_unconfigured` al pedir el token de sala."},"MeetingCreateIn":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Title"},"kind":{"type":"string","enum":["meeting","call"],"title":"Kind","default":"meeting"},"mode":{"type":"string","enum":["audio","video"],"title":"Mode","default":"video"},"channel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Channel Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"scheduled_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Start"},"scheduled_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled End"}},"type":"object","title":"MeetingCreateIn","description":"Crear una reunión. Sin `scheduled_start` = llamada instantánea (empieza ya).\n\n`channel_id` (opcional) liga la llamada a un canal de chat de la org.\n`client_id` (opcional) la liga a un cliente y la hace aparecer en el timeline\nde su ficha."},"MeetingInvitationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"status":{"type":"string","enum":["pending","accepted","declined"],"title":"Status"},"invited_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Invited By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"responded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Responded At"},"meeting":{"$ref":"#/components/schemas/MeetingOut"}},"type":"object","required":["id","status","invited_by","created_at","responded_at","meeting"],"title":"MeetingInvitation","description":"Una invitación mía con el resumen de la reunión embebido."},"MeetingInviteCreateIn":{"properties":{"user_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"User Ids"}},"type":"object","required":["user_ids"],"title":"MeetingInviteCreateIn","description":"Invitar a uno o más miembros de la org a una reunión."},"MeetingInviteOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"meeting_id":{"type":"string","format":"uuid","title":"Meeting Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"invited_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Invited By"},"status":{"type":"string","enum":["pending","accepted","declined"],"title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"responded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Responded At"}},"type":"object","required":["id","meeting_id","organization_id","user_id","invited_by","status","created_at","responded_at"],"title":"MeetingInvite"},"MeetingInviteRespondIn":{"properties":{"status":{"type":"string","enum":["accepted","declined"],"title":"Status"}},"type":"object","required":["status"],"title":"MeetingInviteRespondIn","description":"RSVP a mi propia invitación."},"MeetingOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"channel_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Channel Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"kind":{"type":"string","enum":["meeting","call"],"title":"Kind"},"mode":{"type":"string","enum":["audio","video"],"title":"Mode"},"slug":{"type":"string","title":"Slug"},"room_name":{"type":"string","title":"Room Name"},"status":{"type":"string","enum":["scheduled","live","ended"],"title":"Status"},"scheduled_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Start"},"scheduled_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled End"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"ended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ended At"},"calendar_event_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Calendar Event Id"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"join_path":{"type":"string","title":"Join Path"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","channel_id","client_id","title","kind","mode","slug","room_name","status","scheduled_start","scheduled_end","started_at","ended_at","calendar_event_id","created_by","join_path","created_at"],"title":"Meeting"},"MeetingUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Title"},"scheduled_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Start"},"scheduled_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled End"}},"type":"object","title":"MeetingUpdateIn","description":"Editar una reunión. Solo se toca lo que LLEGA.\n\nTodos los campos tienen default, así que omitir uno y mandarlo a `null` son\ndos peticiones distintas — y las dos son legítimas: «no cambies el título» y\n«déjalo sin título». El servicio las distingue con `model_fields_set`; con un\n`is not None` no habría forma de quitarle el título a una reunión y el\nusuario acabaría poniéndole un espacio en blanco.\n\nLo que NO se puede editar, y por qué: `kind` (una llamada no se convierte en\nreunión a posteriori: sería reescribir lo que pasó), `slug` y `room_name`\n(son el enlace que ya se ha repartido) y `status`, `started_at` y `ended_at`\n(los escribe el servidor a partir de hechos, no de opiniones)."},"MemberBulkErrorOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["user_id","reason"],"title":"MemberBulkError"},"MemberBulkResultOut":{"properties":{"ok":{"type":"integer","title":"Ok"},"errors":{"items":{"$ref":"#/components/schemas/MemberBulkErrorOut"},"type":"array","title":"Errors"}},"type":"object","required":["ok","errors"],"title":"MemberBulkResult","description":"Resultado honesto de una operación bulk sobre miembros."},"MemberBulkUpdateIn":{"properties":{"user_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":200,"minItems":1,"title":"User Ids"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"manager_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Manager Id"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","required":["user_ids"],"title":"MemberBulkUpdate","description":"POST /organizations/{org_id}/members/bulk — actualización masiva de RRHH.\n\nAplica el mismo UPSERT de ficha employee que el PATCH individual.\nAl menos un campo de actualización debe estar presente.\n- department_id: UUID del departamento (null = desvincular).\n- position: cargo/posición.\n- manager_id: employee_id del responsable a asignar a TODOS los\n  seleccionados (null = quitar responsable). Se valida por miembro que no\n  cree ciclos ni auto-referencia.\n- is_active: desactivar (False) o reactivar (True) a los seleccionados. No\n  elimina la membership ni la ficha; solo marca la ficha employee."},"MemberOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"},"joined_at":{"type":"string","format":"date-time","title":"Joined At"},"employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Employee Id"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"department_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department Logo Url"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"employment_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employment Type"},"is_active":{"type":"boolean","title":"Is Active"},"manager_employee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Manager Employee Id"},"manager_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Name"},"hourly_cost_rate":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Hourly Cost Rate"},"hourly_bill_rate":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Hourly Bill Rate"},"expected_hours_per_week":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Expected Hours Per Week"}},"type":"object","required":["user_id","name","email","role","joined_at","is_active"],"title":"Member","description":"Member del contrato: {user_id, name, email, avatar_url, role, joined_at, …RRHH}.\n\n`role` es el rol DE ESE MIEMBRO en la organización; `joined_at` es la\nfecha de alta de su membership; `avatar_url` es el avatar del usuario\n(null si no tiene) para pintar la lista de miembros con foto real.\nLos campos RRHH provienen de la ficha employee vinculada por email\n(null si no hay ficha creada aún). `is_active` es true por defecto cuando\nno hay ficha.\n\nU4 (sobre-exposición): `phone`, `hire_date`, `employment_type`,\n`hourly_cost_rate` (coste interno) y `hourly_bill_rate` son sensibles y\nsolo se rellenan para callers con rol manager+/admin+; para el resto\n(`member`) el mapper (`OrganizationService._member_to_out`) los devuelve\nen `null` — ya eran nullable en el contrato, no ha hecho falta cambiarlo."},"MemberRoleIn":{"properties":{"role":{"type":"string","enum":["moderator","member"],"title":"Role"}},"type":"object","required":["role"],"title":"ChatMemberRoleIn","description":"Cambiar el rol de un miembro de un canal type='channel' (PJKT-1957)."},"MemberUpdateIn":{"properties":{"role":{"anyOf":[{"type":"string","enum":["owner","admin","manager","member"]},{"type":"null"}],"title":"Role"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"hire_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Hire Date"},"employment_type":{"anyOf":[{"type":"string","enum":["interno","autonomo","externo","w2","1099"]},{"type":"null"}],"title":"Employment Type"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"manager_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Manager Id"},"hourly_cost_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Hourly Cost Rate"},"hourly_bill_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Hourly Bill Rate"},"expected_hours_per_week":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Expected Hours Per Week"}},"type":"object","title":"MemberUpdate","description":"PATCH ampliado de un miembro: rol + campos RRHH, todos opcionales.\n\n- `role`: solo owner/admin pueden cambiarlo; solo owner puede asignar/revocar owner.\n- Campos RRHH (department, position, phone, hire_date, employment_type,\n  is_active, department_id): editables por manager+. Se hace UPSERT de la\n  ficha employee vinculada por email del usuario.\n- `department_id`: al vincular sincroniza el string department con el nombre\n  del departamento."},"MessageCreateIn":{"properties":{"body":{"type":"string","minLength":1,"title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["body"],"title":"ChatMessageCreateIn"},"MessageEditIn":{"properties":{"body":{"type":"string","minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"ChatMessageEditIn"},"MessageOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"channel_id":{"type":"string","format":"uuid","title":"Channel Id"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"kind":{"type":"string","enum":["user","system"],"title":"Kind"},"body":{"type":"string","title":"Body"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"parent_preview":{"anyOf":[{"$ref":"#/components/schemas/ParentPreviewOut"},{"type":"null"}]},"is_edited":{"type":"boolean","title":"Is Edited"},"edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Edited At"},"pinned_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Pinned Until"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"reactions":{"items":{"$ref":"#/components/schemas/ReactionOut"},"type":"array","title":"Reactions"},"attachments":{"items":{"$ref":"#/components/schemas/app__modules__chat__schemas__AttachmentOut"},"type":"array","title":"Attachments"}},"type":"object","required":["id","channel_id","author_id","kind","body","entity_type","entity_id","parent_id","is_edited","edited_at","created_at","reactions","attachments"],"title":"ChatMessage"},"MilestoneCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"status":{"type":"string","enum":["pending","invoiced","paid"],"title":"Status","default":"pending"}},"type":"object","required":["title","amount"],"title":"MilestoneCreateIn"},"MilestoneOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"contract_id":{"type":"string","format":"uuid","title":"Contract Id"},"title":{"type":"string","title":"Title"},"amount":{"type":"number","title":"Amount"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"status":{"type":"string","enum":["pending","invoiced","paid"],"title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","contract_id","title","amount","due_date","status","created_at","updated_at"],"title":"ContractMilestone"},"MilestoneUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"status":{"anyOf":[{"type":"string","enum":["pending","invoiced","paid"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"MilestoneUpdateIn"},"Modelo303DuplicadoOut":{"properties":{"supplier_invoice_number":{"type":"string","title":"Supplier Invoice Number"},"motivo":{"type":"string","enum":["fuera_de_periodo","otra_divisa","sin_cuota_en_factura"],"title":"Motivo"},"cuota":{"type":"number","title":"Cuota"}},"type":"object","required":["supplier_invoice_number","motivo","cuota"],"title":"Modelo303Duplicado","description":"Gasto normal cuya cuota PUEDE estar contándose dos veces (PJKT-2280).\n\nSu IVA sí entra en `iva_soportado_expenses` —descartarlo lo dejaría fuera de\ntodas las declaraciones, porque la factura de proveedor que anota tampoco\nentra en esta—, pero la coincidencia de número está avisada aquí para que\nquien firme pueda comprobarla."},"Modelo303Out":{"properties":{"period":{"type":"string","title":"Period"},"currency":{"type":"string","title":"Currency","default":"EUR"},"iva_repercutido":{"type":"number","title":"Iva Repercutido"},"iva_soportado_ap":{"type":"number","title":"Iva Soportado Ap"},"iva_soportado_expenses":{"type":"number","title":"Iva Soportado Expenses"},"iva_soportado":{"type":"number","title":"Iva Soportado"},"resultado":{"type":"number","title":"Resultado"},"iva_soportado_expenses_descartado":{"type":"number","title":"Iva Soportado Expenses Descartado","default":0.0},"duplicados_posibles":{"items":{"$ref":"#/components/schemas/Modelo303DuplicadoOut"},"type":"array","title":"Duplicados Posibles"}},"type":"object","required":["period","iva_repercutido","iva_soportado_ap","iva_soportado_expenses","iva_soportado","resultado"],"title":"Modelo303","description":"Resumen fiscal IVA (Modelo 303 simplificado).\n\nrepercutido  = IVA de facturas emitidas (AR).\nsoportado    = IVA de facturas recibidas (AP) + gastos.\nresultado    = repercutido − soportado (positivo = a ingresar, negativo = a devolver)."},"Modelo347Out":{"properties":{"year":{"type":"integer","title":"Year"},"currency":{"type":"string","title":"Currency","default":"EUR"},"parties":{"items":{"$ref":"#/components/schemas/Modelo347PartyOut"},"type":"array","title":"Parties"}},"type":"object","required":["year","parties"],"title":"Modelo347","description":"Listado de terceros para el Modelo 347."},"Modelo347PartyOut":{"properties":{"party_name":{"type":"string","title":"Party Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"direction":{"type":"string","enum":["emitidas","recibidas"],"title":"Direction"},"total":{"type":"number","title":"Total"}},"type":"object","required":["party_name","tax_id","direction","total"],"title":"Modelo347Party","description":"Tercero con operaciones > 3005.06 € (Modelo 347 simplificado)."},"Modelo349Out":{"properties":{"year":{"type":"integer","title":"Year"},"currency":{"type":"string","title":"Currency","default":"EUR"},"parties":{"items":{"$ref":"#/components/schemas/Modelo349PartyOut"},"type":"array","title":"Parties"}},"type":"object","required":["year","parties"],"title":"Modelo349","description":"Listado de operaciones intracomunitarias para el Modelo 349."},"Modelo349PartyOut":{"properties":{"party_name":{"type":"string","title":"Party Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"direction":{"type":"string","enum":["emitidas","recibidas"],"title":"Direction"},"total":{"type":"number","title":"Total"}},"type":"object","required":["party_name","tax_id","direction","total"],"title":"Modelo349Party","description":"Operación intracomunitaria (Modelo 349)."},"MuteIn":{"properties":{"muted":{"type":"boolean","title":"Muted"}},"type":"object","required":["muted"],"title":"ChatMuteIn","description":"Silenciar/activar un canal para mí (F1.8)."},"MyProfileUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"weather_override":{"anyOf":[{"type":"string","enum":["clear","clouds","fog","rain","snow","thunder"]},{"type":"null"}],"title":"Weather Override"},"last_organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Last Organization Id"},"profile_visibility":{"anyOf":[{"type":"string","enum":["private","organization","public"]},{"type":"null"}],"title":"Profile Visibility"},"social_links":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Social Links"}},"type":"object","title":"MyProfileUpdateIn","description":"PATCH parcial del perfil propio: {name?, headline?, bio?, location?,\navatar_url?, language?, weather_override?, last_organization_id?}.\n\nSolo se aplican los campos presentes. `name` NO admite null explícito ni\nvacío (mismo criterio que el resto del sistema). `headline`/`bio`/`location`/\n`avatar_url`/`weather_override`/`last_organization_id` sí admiten null: borran\nel campo. `last_organization_id`, si es non-null, debe ser una org de la que\nel usuario es MIEMBRO (se valida en el service; org ajena → 404).\nNO expone email ni contraseña."},"MyStatsOut":{"properties":{"orgs":{"type":"integer","title":"Orgs"},"projects":{"type":"integer","title":"Projects"},"tasks_assigned":{"type":"integer","title":"Tasks Assigned"},"tasks_done":{"type":"integer","title":"Tasks Done"}},"type":"object","required":["orgs","projects","tasks_assigned","tasks_done"],"title":"MyStats","description":"MyStats del contrato: recuentos cruzados del usuario (todas sus orgs).\n\n{orgs, projects, tasks_assigned, tasks_done}. Solo lectura; alimenta la\ncabecera del perfil `/me`."},"MyTaskItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status","description":"Estado base o slug de columna personalizada."},"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"}},"type":"object","required":["id","title","status","priority","project_id","project_name"],"title":"DashboardMyTaskItem","description":"Una tarea abierta asignada al usuario actual (para el panel \"Mis tareas\")."},"MyWorkTaskOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority"},"type":{"type":"string","enum":["epic","story","task","bug","spike","chore"],"title":"Type"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","project_name","organization_id","organization_name","title","status","priority","type","due_date","parent_id","created_at","updated_at"],"title":"MyWorkTask","description":"Task de la vista My Work (cross-project, asignado al usuario actual)."},"NotificationChannelLimitOut":{"properties":{"event":{"type":"string","title":"Event"},"label":{"type":"string","title":"Label"},"channels":{"items":{"type":"string","enum":["in_app","email","web_push"]},"type":"array","title":"Channels"}},"type":"object","required":["event","label","channels"],"title":"NotificationChannelLimit","description":"Un evento cuyo reparto está acotado por código (techo de canales).\n\nSe publica para que la pantalla de preferencias pueda avisar: dentro de una\ncategoría con el email encendido puede haber avisos que nunca lo usen, y una\ncasilla que el servidor no puede cumplir es una casilla que miente."},"NotificationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"type":{"type":"string","enum":["task_assigned","task_due_soon","task_overdue","task.unassigned","task.unblocked","task.status_changed","comment","mention","project.announcement","project.member_added","project.status_changed","sprint.started","sprint.completed","sprint.ending","crossorg_link","billing.past_due","invoice_approved","supplier_invoice_approved","quote.accepted","quote.rejected","supplier_invoice.due_soon","budget.exceeded","invoice.reminder_sent","leave.requested","leave.approved","leave.rejected","timesheet.submitted","timesheet.approved","timesheet.rejected","payslip.published","timesheet.approval_pending","leave.balance_low","automation","org_member_added","org_invite_accepted","org_role_changed","chat_mention","chat_message","meeting_invite","meeting_invite_response","meeting_reminder","meeting_call","meeting_admission_request","meeting_admission_decided","deal.closed","deal.assigned","sla.at_risk","sla.breached","announcement","generic"],"title":"Type"},"category":{"type":"string","enum":["tasks","comments","projects","finance","hr","crossorg","org","automation","system","chat","crm","billing","sla"],"title":"Category"},"actor_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Actor Id"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Entity Id"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","user_id","organization_id","type","category","actor_id","entity_type","entity_id","title","body","link","read_at","created_at"],"title":"Notification","description":"Notificación in-app de un usuario dentro de una organización."},"NotificationPreferenceIn":{"properties":{"category":{"type":"string","enum":["tasks","comments","projects","finance","hr","crossorg","org","automation","system","chat","crm","billing","sla"],"title":"Category"},"in_app":{"type":"boolean","title":"In App","default":true},"email":{"type":"boolean","title":"Email","default":true},"web_push":{"type":"boolean","title":"Web Push","default":true},"digest":{"type":"string","enum":["instant","daily","weekly","off"],"title":"Digest","default":"instant"},"quiet_hours_start":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Quiet Hours Start"},"quiet_hours_end":{"anyOf":[{"type":"integer","maximum":23.0,"minimum":0.0},{"type":"null"}],"title":"Quiet Hours End"}},"type":"object","required":["category"],"title":"NotificationPreferenceIn","description":"Body del PUT de preferencias: ajusta UNA categoría (upsert)."},"NotificationPreferenceOut":{"properties":{"category":{"type":"string","enum":["tasks","comments","projects","finance","hr","crossorg","org","automation","system","chat","crm","billing","sla"],"title":"Category"},"in_app":{"type":"boolean","title":"In App"},"email":{"type":"boolean","title":"Email"},"web_push":{"type":"boolean","title":"Web Push"},"digest":{"type":"string","enum":["instant","daily","weekly","off"],"title":"Digest"},"quiet_hours_start":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quiet Hours Start"},"quiet_hours_end":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quiet Hours End"},"available_channels":{"items":{"type":"string","enum":["in_app","email","web_push"]},"type":"array","title":"Available Channels"},"channel_limits":{"items":{"$ref":"#/components/schemas/NotificationChannelLimitOut"},"type":"array","title":"Channel Limits"}},"type":"object","required":["category","in_app","email","web_push","digest","quiet_hours_start","quiet_hours_end","available_channels","channel_limits"],"title":"NotificationPreference","description":"Preferencia efectiva de una categoría (de la fila del usuario, o el default\nopt-out si no ha ajustado nada)."},"OAuthAuthorizationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"scope":{"type":"string","title":"Scope"},"authorized_at":{"type":"string","format":"date-time","title":"Authorized At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["id","client_id","client_name","scope","authorized_at","last_used_at"],"title":"OauthAuthorization","description":"Una app conectada: autorización OAuth ACTIVA del usuario (familia de tokens).\n\n`id` es el id del grant (familia); se pasa a DELETE para revocarla. Ningún\ncampo tiene default: los seis son obligatorios en la respuesta (los nullables\nsalen con valor `null`, no ausentes) — así el shape conforma el contrato."},"ObjectiveCreateIn":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"period":{"type":"string","maxLength":20,"pattern":"^[A-Za-z0-9-]{1,20}$","title":"Period"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"status":{"type":"string","enum":["active","done","archived"],"title":"Status","default":"active"},"key_results":{"items":{"$ref":"#/components/schemas/KeyResultCreateIn"},"type":"array","title":"Key Results"}},"type":"object","required":["title","period"],"title":"ObjectiveCreateIn","description":"Crear un objetivo. Opcionalmente con sus KRs iniciales en el mismo POST.\n\n`parent_id` opcional alinea el objetivo con uno PADRE ya existente de la\nMISMA organización (jerarquía OKR: objetivo de equipo → objetivo de\nempresa)."},"ObjectiveOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"period":{"type":"string","title":"Period"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"status":{"type":"string","enum":["active","done","archived"],"title":"Status"},"progress":{"type":"number","title":"Progress"},"key_results":{"items":{"$ref":"#/components/schemas/KeyResultOut"},"type":"array","title":"Key Results"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","title","description","period","owner_id","parent_id","status","progress","key_results","created_at"],"title":"Objective"},"ObjectiveUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"period":{"anyOf":[{"type":"string","maxLength":20,"pattern":"^[A-Za-z0-9-]{1,20}$"},{"type":"null"}],"title":"Period"},"owner_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Owner Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"status":{"anyOf":[{"type":"string","enum":["active","done","archived"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"ObjectiveUpdateIn","description":"Editar un objetivo (PATCH parcial). No toca los KRs (esos tienen sus\npropios endpoints).\n\n`parent_id` mueve el objetivo dentro de la jerarquía; un `parent_id` que\ncrearía un ciclo (el objetivo como su propio ancestro) lo rechaza el\nservicio con 422."},"OnboardingStateIn":{"properties":{"welcome_seen":{"type":"boolean","title":"Welcome Seen"},"tours_completed":{"items":{"type":"string"},"type":"array","title":"Tours Completed"},"version":{"type":"integer","title":"Version"},"setup":{"anyOf":[{"$ref":"#/components/schemas/SetupProgressIn"},{"type":"null"}]}},"type":"object","required":["welcome_seen","tours_completed","version"],"title":"OnboardingStateIn","description":"PUT del estado de onboarding: reemplaza POR COMPLETO el estado guardado.\n\nEl frontend envía SIEMPRE el estado completo (sustitución, no merge), así que\nreiniciar los tours = enviar el estado vacío. Las claves de `tours_completed`\nson opacas para el backend (la taxonomía la define el frontend); aquí solo se\nnormalizan: strip, se descartan vacías y duplicadas, tope de longitud y de\ncantidad para no persistir basura no acotada.\n\n`setup` es la única excepción a la sustitución total: OMITIRLO conserva el\nprogreso guardado del asistente. Sin esto, un cliente que no conoce el\nasistente (pestaña vieja, PWA cacheada) borraría el progreso con solo guardar\nun tour. `null` explícito sí lo borra; el objeto lo sustituye entero."},"OnboardingStateOut":{"properties":{"welcome_seen":{"type":"boolean","title":"Welcome Seen"},"tours_completed":{"items":{"type":"string"},"type":"array","title":"Tours Completed"},"version":{"type":"integer","title":"Version"},"setup":{"anyOf":[{"$ref":"#/components/schemas/SetupProgressOut"},{"type":"null"}]}},"type":"object","required":["welcome_seen","tours_completed","version"],"title":"OnboardingState","description":"OnboardingState del contrato: estado de onboarding del usuario.\n\n{welcome_seen, tours_completed, version, setup}. Persistido a nivel de USUARIO\ncomo fuente de verdad cross-device (columna `users.onboarding_state`). Un\nusuario sin estado guardado recibe el vacío (`welcome_seen=false`,\n`tours_completed=[]`, `version=0`, `setup=null`)."},"OrgAuditLogEntryOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email"},"action":{"type":"string","title":"Action"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"target_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Target Id"},"target_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Type"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","action","created_at"],"title":"OrgAuditLogEntry","description":"Entrada de auditoría de UNA organización, para el self-service admin+.\n\nShape deliberadamente más estrecho que el `AuditLogEntry` que lee el panel\nOPS (`platform.schemas.AuditLogEntryOut`):\n\n- Sin `actor_ip` / `actor_user_agent`: son forenses, pensados para\n  investigaciones de seguridad del operador de la plataforma, no para\n  autoservicio del cliente (podrían exponer IP/dispositivo de un\n  compañero de organización sin necesidad).\n- Sin `reason`: hoy solo lo rellenan acciones `platform.*` (impersonation,\n  purga…), que este endpoint excluye siempre — ver\n  `AuditRepository.list_for_organization`. Si en el futuro una acción\n  propia de la organización necesita `reason`, se añade aquí de forma\n  explícita (cambio de contrato).\n- Sin `organization_id`: el endpoint ya está scoped a una única\n  organización por el path; incluirlo sería redundante."},"OrgChartNode":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"name":{"type":"string","title":"Name"},"position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Position"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"is_active":{"type":"boolean","title":"Is Active"},"children":{"items":{"$ref":"#/components/schemas/OrgChartNode"},"type":"array","title":"Children"}},"type":"object","required":["employee_id","name","is_active","children"],"title":"OrgChartNode","description":"Nodo del organigrama; `children` contiene los reportes directos."},"OrgLastActivityOut":{"properties":{"summary":{"type":"string","title":"Summary"},"at":{"type":"string","format":"date-time","title":"At"}},"type":"object","required":["summary","at"],"title":"OrgLastActivity","description":"Última actividad registrada en audit_logs para la organización."},"OrgLinkOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"requester_org_id":{"type":"string","format":"uuid","title":"Requester Org Id"},"target_org_id":{"type":"string","format":"uuid","title":"Target Org Id"},"other_org_id":{"type":"string","format":"uuid","title":"Other Org Id"},"other_org_name":{"type":"string","title":"Other Org Name"},"other_org_slug":{"type":"string","title":"Other Org Slug"},"other_org_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Other Org Logo Url"},"direction":{"type":"string","enum":["incoming","outgoing"],"title":"Direction"},"status":{"type":"string","enum":["pending","accepted","rejected","revoked"],"title":"Status"},"requested_by":{"type":"string","format":"uuid","title":"Requested By"},"responded_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Responded By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","requester_org_id","target_org_id","other_org_id","other_org_name","other_org_slug","direction","status","requested_by","responded_by","created_at","updated_at"],"title":"OrgLink","description":"Enlace entre dos orgs desde el punto de vista de la org que consulta.\n\n`direction` indica si, para la org actual, el enlace es entrante (esta org\nes el target) o saliente (esta org es el requester). `other_org_*` son los\ndatos de la ORG AL OTRO LADO del enlace."},"OrgLinkRequestIn":{"properties":{"target_org_id":{"type":"string","format":"uuid","title":"Target Org Id"}},"type":"object","required":["target_org_id"],"title":"OrgLinkRequestIn"},"OrgScheduleOut":{"properties":{"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"}},"type":"object","required":["weekly_schedule"],"title":"OrgSchedule","description":"Horario laboral por defecto de la organización (nivel más general).\n\n`weekly_schedule`: {mon:{start,end}|null, tue…sun} o `null` si la\norganización no fija horario por defecto."},"OrgScheduleUpdateIn":{"properties":{"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"}},"type":"object","required":["weekly_schedule"],"title":"OrgScheduleUpdate","description":"Cuerpo del PUT del horario por defecto de la org (reemplazo total).\n\n`weekly_schedule: null` borra el horario por defecto. Se valida la forma\n(claves mon–sun; valor null o {start,end} en HH:MM)."},"OrgSummaryOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","required":["id","name","slug"],"title":"OrgSummary","description":"Org mínima para elegir con quién enlazar: {id, name, slug, logo_url}."},"OrganizationCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"default_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Currency"},"locale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locale"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"week_start_day":{"anyOf":[{"type":"integer","enum":[0,5,6]},{"type":"null"}],"title":"Week Start Day"}},"type":"object","required":["name","slug"],"title":"OrganizationCreateIn"},"OrganizationDomainClaimIn":{"properties":{"domain":{"type":"string","maxLength":253,"minLength":4,"title":"Domain"},"verification_email":{"type":"string","maxLength":255,"minLength":5,"title":"Verification Email"}},"type":"object","required":["domain","verification_email"],"title":"OrganizationDomainClaim","description":"Reclamar un dominio: el dominio y la dirección donde recibir el código.\n\nLa dirección tiene que ser DEL dominio que se reclama (se valida en el\nservice, con 422 `verification_email_mismatch`): es la prueba de control."},"OrganizationDomainEnforceIn":{"properties":{"enforced":{"type":"boolean","title":"Enforced"}},"type":"object","required":["enforced"],"title":"OrganizationDomainEnforce","description":"Activar o desactivar el modo obligatorio de un dominio ya verificado."},"OrganizationDomainOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"domain":{"type":"string","title":"Domain"},"verified":{"type":"boolean","title":"Verified"},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"enforced":{"type":"boolean","title":"Enforced"},"verification_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verification Email"},"verification_pending":{"type":"boolean","title":"Verification Pending"},"verification_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verification Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","domain","verified","enforced","verification_pending","created_at"],"title":"OrganizationDomain","description":"Dominio corporativo de la organización.\n\nNUNCA lleva el código de verificación ni su hash. Sí dice si hay una\nverificación pendiente y a qué dirección se envió, que es lo que necesita la\npantalla para decir «revisa admin@acme.com»."},"OrganizationDomainVerifyIn":{"properties":{"code":{"type":"string","maxLength":6,"minLength":6,"pattern":"^\\d{6}$","title":"Code"}},"type":"object","required":["code"],"title":"OrganizationDomainVerify","description":"Código recibido por correo. Seis dígitos, de un solo uso."},"OrganizationModuleOut":{"properties":{"key":{"type":"string","title":"Key"},"label":{"type":"string","title":"Label"},"description":{"type":"string","title":"Description"},"min_plan":{"type":"string","enum":["free","pro"],"title":"Min Plan"},"available":{"type":"boolean","title":"Available"},"enabled":{"type":"boolean","title":"Enabled"},"effective":{"type":"boolean","title":"Effective"},"degrades":{"additionalProperties":{"type":"string"},"type":"object","title":"Degrades"}},"type":"object","required":["key","label","description","min_plan","available","enabled","effective","degrades"],"title":"OrganizationModule","description":"Un módulo que la organización puede apagarse, con su estado actual.\n\nLos tres booleanos NO son redundantes: separan las tres decisiones que hoy\nviven fundidas en una sola lista de `features`.\n\n- `available`: lo que el PLAN (o 3XA) concede, IGNORANDO el interruptor de la\n  organización. Es lo que hace falta para poder volver a encenderlo.\n- `enabled`: el interruptor de la organización.\n- `effective`: lo que el servidor aplica de verdad = `available and enabled`.\n\nSin `available`, una organización que apaga un módulo no podría distinguir\n«lo apagué yo» de «me lo han quitado», y volver a encenderlo sería adivinar."},"OrganizationModuleSetIn":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"OrganizationModuleSet","description":"Encender o apagar un módulo para toda la organización.\n\nNo lleva `key`: la clave va en la ruta. `enabled=false` crea la fila de\napagado; `enabled=true` la borra — nunca concede nada que el plan no diera\n(ver `OrganizationDisabledModule`)."},"OrganizationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"brand_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Color"},"accent_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accent Color"},"legal_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Name"},"fiscal_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fiscal Address"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iban"},"bic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bic"},"bank_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Name"},"automations_enabled":{"type":"boolean","title":"Automations Enabled","default":true},"external_messaging_enabled":{"type":"boolean","title":"External Messaging Enabled","default":false},"timezone":{"type":"string","title":"Timezone","default":"Europe/Madrid"},"country":{"type":"string","title":"Country","default":"ES"},"default_currency":{"type":"string","title":"Default Currency","default":"EUR"},"locale":{"type":"string","title":"Locale","default":"es-ES"},"week_start_day":{"type":"integer","enum":[0,5,6],"title":"Week Start Day","default":0},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"},"member_count":{"type":"integer","title":"Member Count"},"project_count":{"type":"integer","title":"Project Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"last_activity":{"anyOf":[{"$ref":"#/components/schemas/OrgLastActivityOut"},{"type":"null"}]}},"type":"object","required":["id","name","slug","role","member_count","project_count","created_at","updated_at"],"title":"Organization","description":"Organization del contrato: {id, name, slug, logo_url, brand_color,\naccent_color, legal_name, fiscal_address, tax_id, iban, bic, bank_name,\nrole, member_count, project_count, created_at, updated_at, last_activity}.\n\n`role` es el rol del usuario autenticado en ESA organización (viene de la\nmembership, no de la tabla organizations). `member_count` y `project_count`\nson agregados calculados para que el frontend muestre la ficha de la org\n(nº de miembros + nº de proyectos asociados). `last_activity` es la entrada\nmás reciente de audit_logs para la org (null si no hay ninguna)."},"OrganizationUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"brand_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Color"},"accent_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accent Color"},"legal_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Name"},"fiscal_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fiscal Address"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"iban":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Iban"},"bic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bic"},"bank_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bank Name"},"automations_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Automations Enabled"},"external_messaging_enabled":{"type":"boolean","title":"External Messaging Enabled","default":false},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"default_currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Currency"},"locale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locale"},"week_start_day":{"anyOf":[{"type":"integer","enum":[0,5,6]},{"type":"null"}],"title":"Week Start Day"}},"type":"object","title":"OrganizationUpdateIn","description":"PATCH parcial del contrato: {name?, slug?, logo_url?, brand_color?, accent_color?,\nlegal_name?, fiscal_address?, tax_id?, iban?, bic?, bank_name?}.\n`name` y `slug` no admiten null explícito; el resto sí admiten null para borrar\nel valor. El `slug` es único en toda la plataforma: cambiarlo a uno ocupado\nresponde 409 `slug_taken` (lo resuelve el service, no el schema)."},"ParentPreviewOut":{"properties":{"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"body_excerpt":{"type":"string","title":"Body Excerpt"}},"type":"object","required":["author_id","author_name","body_excerpt"],"title":"ChatMessageParentPreview","description":"Extracto del mensaje citado (reply), para pintar la cita sin fetch extra."},"PasskeyLoginBeginIn":{"properties":{"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","title":"PasskeyLoginBeginIn","description":"Login begin: email opcional (acota allowCredentials a sus passkeys)."},"PasskeyLoginCompleteIn":{"properties":{"credential":{"additionalProperties":true,"type":"object","title":"Credential"}},"type":"object","required":["credential"],"title":"PasskeyLoginCompleteIn","description":"Respuesta de assertion del navegador."},"PasskeyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"transports":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transports"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","transports","last_used_at","created_at"],"title":"Passkey","description":"Passkey registrada, enmascarada (sin clave pública ni credential_id crudo)."},"PasskeyRegisterCompleteIn":{"properties":{"credential":{"additionalProperties":true,"type":"object","title":"Credential"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["credential"],"title":"PasskeyRegisterCompleteIn","description":"Respuesta de attestation del navegador + nombre opcional para la passkey."},"PayrollRunCreateIn":{"properties":{"period":{"type":"string","title":"Period","description":"Periodo en formato YYYY-MM (e.g. '2026-07')."}},"type":"object","required":["period"],"title":"PayrollRunCreateIn"},"PayrollRunOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"period":{"type":"string","title":"Period"},"status":{"type":"string","enum":["draft","processed"],"title":"Status"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","period","status","created_by","created_at"],"title":"PayrollRun"},"PayrollRunWithPayslipsOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"period":{"type":"string","title":"Period"},"status":{"type":"string","enum":["draft","processed"],"title":"Status"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"payslips":{"items":{"$ref":"#/components/schemas/PayslipOut"},"type":"array","title":"Payslips"}},"type":"object","required":["id","organization_id","period","status","created_by","created_at","payslips"],"title":"PayrollRunWithPayslips","description":"PayrollRun + sus payslips (respuesta del endpoint process/get-with-payslips)."},"PayslipOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"payroll_run_id":{"type":"string","format":"uuid","title":"Payroll Run Id"},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"gross":{"type":"number","title":"Gross"},"ss_worker":{"type":"number","title":"Ss Worker"},"irpf":{"type":"number","title":"Irpf"},"net":{"type":"number","title":"Net"},"irpf_rate_pct":{"type":"number","title":"Irpf Rate Pct"},"ss_rate_pct":{"type":"number","title":"Ss Rate Pct"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","payroll_run_id","employee_id","gross","ss_worker","irpf","net","irpf_rate_pct","ss_rate_pct","created_at"],"title":"Payslip"},"PinIn":{"properties":{"duration":{"type":"string","enum":["1w","15d","30d","forever"],"title":"Duration"}},"type":"object","required":["duration"],"title":"ChatMessagePinIn","description":"Pinear un mensaje con una duración fija (o «siempre»)."},"PipelineCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"is_default":{"type":"boolean","title":"Is Default","default":false}},"type":"object","required":["name"],"title":"PipelineCreateIn"},"PipelineMetricOut":{"properties":{"stage_id":{"type":"string","format":"uuid","title":"Stage Id"},"stage":{"type":"string","title":"Stage"},"currency":{"type":"string","title":"Currency","default":"EUR"},"count":{"type":"integer","title":"Count"},"value":{"type":"number","title":"Value"},"weighted_value":{"type":"number","title":"Weighted Value"}},"type":"object","required":["stage_id","stage","count","value","weighted_value"],"title":"PipelineMetric","description":"Recuento e importe (bruto y ponderado por probabilidad) por etapa."},"PipelineOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"is_default":{"type":"boolean","title":"Is Default"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"stages":{"items":{"$ref":"#/components/schemas/StageOut"},"type":"array","title":"Stages"}},"type":"object","required":["id","organization_id","name","is_default","created_at","stages"],"title":"Pipeline"},"PipelineUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default"}},"type":"object","title":"PipelineUpdateIn","description":"PATCH parcial."},"PlaceDetails":{"properties":{"formatted_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Formatted Address"},"street":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street"},"street_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Street Number"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"province":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Province"}},"type":"object","title":"PlaceDetails","description":"Detalles de dirección parseados de Google Places (New).\n\nTodos los campos son opcionales: Google no garantiza la presencia de cada\ncomponente para todas las direcciones del mundo."},"PlaceSuggestion":{"properties":{"place_id":{"type":"string","title":"Place Id"},"text":{"type":"string","title":"Text"},"main_text":{"type":"string","title":"Main Text"},"secondary_text":{"type":"string","title":"Secondary Text"}},"type":"object","required":["place_id","text","main_text","secondary_text"],"title":"PlaceSuggestion","description":"Sugerencia de autocompletar de Google Places (New).\n\n- `place_id`: identificador opaco de Google (stable, para /details).\n- `text`: texto completo de la sugerencia (e.g. \"Calle Gran Vía, 1, Madrid\").\n- `main_text`: parte principal (nombre de la vía o lugar).\n- `secondary_text`: parte secundaria (ciudad, país…)."},"PlatformAdminCreateIn":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"PlatformAdminCreate"},"PlatformAdminOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["user_id","email","name","created_at"],"title":"PlatformAdmin"},"PlatformAdminSessionOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"user_name":{"type":"string","title":"User Name"},"user_email":{"type":"string","title":"User Email"},"session_id":{"type":"string","format":"uuid","title":"Session Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent"},"is_current":{"type":"boolean","title":"Is Current"}},"type":"object","required":["user_id","user_name","user_email","session_id","created_at","is_current"],"title":"PlatformAdminSession","description":"Sesión VIVA de un platform admin (PJKT-2044, «accesos al panel»).\n\nFila plana admin × sesión para la tarjeta de la página Admins. NUNCA expone\nel token ni su hash: solo metadata de dispositivo. `is_current` marca la\nsesión del refresh con el que llega ESTA petición, para que el panel la\netiquete «esta sesión» y no ofrezca revocarla (auto-logout accidental)."},"PlatformBillingAtRiskNotifyOut":{"properties":{"sent":{"type":"boolean","title":"Sent"}},"type":"object","required":["sent"],"title":"PlatformBillingAtRiskNotifyResult","description":"Resultado de reenviar el aviso de pago pendiente al owner (PJKT-2037).\n\n`sent=false` = frenado por el anti-spam de 24 h (no es un error)."},"PlatformBillingAtRiskRowOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"owner_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Email"},"plan":{"type":"string","title":"Plan"},"stripe_status":{"type":"string","enum":["past_due","unpaid","incomplete"],"title":"Stripe Status"},"since":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"},"stripe_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Customer Id"}},"type":"object","required":["organization_id","organization_name","owner_email","plan","stripe_status","since","stripe_customer_id"],"title":"PlatformBillingAtRiskRow","description":"Fila de la lista de cobros en riesgo (PJKT-2037): suscripción del espejo\nde Stripe en `past_due`/`unpaid`/`incomplete`, con la org y el owner."},"PlatformEmailChangeRequestedOut":{"properties":{"sent":{"type":"boolean","title":"Sent"}},"type":"object","required":["sent"],"title":"PlatformEmailChangeRequested"},"PlatformErrorIssueOut":{"properties":{"task_id":{"type":"string","format":"uuid","title":"Task Id"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"created":{"type":"boolean","title":"Created"}},"type":"object","required":["task_id","project_id","organization_id","created"],"title":"PlatformErrorIssue"},"PlatformExportOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"status":{"type":"string","enum":["queued","running","done","failed","expired"],"title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"requested_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Requested By"},"download_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Download Url"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","organization_id","status","created_at"],"title":"PlatformExport","description":"Estado de un export de datos de una organización (job Dramatiq).\n\n`storage_key` NO se expone (es la ruta interna del blob en el StorageService):\nla descarga va SIEMPRE por el endpoint autenticado, nunca por una ruta\npública. `download_url` apunta a ese endpoint autenticado y solo viene\npresente cuando `status == 'done'`; el error (si `failed`) es un resumen\ncorto, jamás un stacktrace."},"PlatformFeatureFlagOut":{"properties":{"key":{"type":"string","title":"Key"},"min_plan":{"type":"string","enum":["free","pro"],"title":"Min Plan"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"updated_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Updated By"}},"type":"object","required":["key","min_plan","enabled"],"title":"PlatformFeatureFlag","description":"Estado GLOBAL de una feature en el panel ops: una fila por feature de\n`FEATURE_MIN_PLAN`. `enabled=null` = sin flag global (sigue el default del\nplan); `true`/`false` = kill-switch global explícito."},"PlatformFeatureFlagSetIn":{"properties":{"key":{"type":"string","title":"Key"},"enabled":{"type":"boolean","title":"Enabled"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["key","enabled"],"title":"PlatformFeatureFlagSet","description":"Upsert de un flag (global o de org): habilita/deshabilita una feature.\n`key` debe ser una feature conocida de `FEATURE_MIN_PLAN` (si no, 422)."},"PlatformHealthBackupsOut":{"properties":{"last_backup_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Backup At"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"},"stale":{"type":"boolean","title":"Stale"}},"type":"object","required":["stale"],"title":"PlatformHealthBackups","description":"Frescura de las copias de seguridad (PJKT-2028).\n\nUn backup que deja de correr no falla en ningún sitio: el cron muere en\nsilencio y el último fichero simplemente envejece. Este bloque lee el\ndirectorio de `infra/backups/backup.sh` (montado read-only en el api) y\ndelata ese envejecimiento sin entrar al servidor. `stale` es la señal de\nalerta: última copia con más de 26 h (cron diario + margen) o ninguna copia\n(incluido el caso «el directorio no existe», normal en dev local)."},"PlatformHealthDatabaseOut":{"properties":{"ok":{"type":"boolean","title":"Ok"},"latency_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency Ms"},"pool_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pool Size"},"pool_checked_in":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pool Checked In"},"pool_checked_out":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pool Checked Out"},"pool_overflow":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pool Overflow"}},"type":"object","required":["ok"],"title":"PlatformHealthDatabase"},"PlatformHealthErrorsOut":{"properties":{"last_hour":{"type":"integer","title":"Last Hour"},"last_24h":{"type":"integer","title":"Last 24H"}},"type":"object","required":["last_hour","last_24h"],"title":"PlatformHealthErrors"},"PlatformHealthOut":{"properties":{"status":{"type":"string","enum":["ok","degraded","down"],"title":"Status"},"generated_at":{"type":"string","format":"date-time","title":"Generated At"},"database":{"$ref":"#/components/schemas/PlatformHealthDatabaseOut"},"redis":{"$ref":"#/components/schemas/PlatformHealthRedisOut"},"scheduler":{"$ref":"#/components/schemas/PlatformHealthSchedulerOut"},"queues":{"items":{"$ref":"#/components/schemas/PlatformQueueDepthOut"},"type":"array","title":"Queues"},"errors":{"$ref":"#/components/schemas/PlatformHealthErrorsOut"},"web_push":{"$ref":"#/components/schemas/PlatformHealthWebPushOut"},"backups":{"$ref":"#/components/schemas/PlatformHealthBackupsOut"}},"type":"object","required":["status","generated_at","database","redis","scheduler","queues","errors","web_push","backups"],"title":"PlatformHealth"},"PlatformHealthRedisOut":{"properties":{"ok":{"type":"boolean","title":"Ok"},"latency_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency Ms"},"used_memory_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Used Memory Bytes"},"connected_clients":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Connected Clients"},"uptime_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Uptime Seconds"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"}},"type":"object","required":["ok"],"title":"PlatformHealthRedis"},"PlatformHealthSchedulerOut":{"properties":{"alive":{"type":"boolean","title":"Alive"},"last_beat_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Beat At"},"stale_after_seconds":{"type":"integer","title":"Stale After Seconds"}},"type":"object","required":["alive","stale_after_seconds"],"title":"PlatformHealthScheduler"},"PlatformHealthWebPushOut":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"subject_configured":{"type":"boolean","title":"Subject Configured"},"subscriptions":{"type":"integer","title":"Subscriptions"}},"type":"object","required":["enabled","subject_configured","subscriptions"],"title":"PlatformHealthWebPush","description":"Estado del canal Web Push (PJKT-1968).\n\nEl canal falla en SILENCIO si faltan las claves VAPID: `WebPushChannel` solo\nescribe un `logger.warning` la primera vez y no envía nada. Este sub-estado\npermite responder \"¿está el push activo en producción?\" sin leer logs.\n\n`subscriptions` cuenta las suscripciones vivas: `enabled=true` con 0\nsuscripciones significa que el canal está listo pero nadie ha dado permiso\ntodavía — un matiz que un booleano solo no distingue."},"PlatformJobStatusOut":{"properties":{"name":{"type":"string","title":"Name"},"last_run_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Run At"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"ok":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ok"},"schedule":{"type":"string","title":"Schedule"}},"type":"object","required":["name","schedule"],"title":"PlatformJobStatus","description":"Estado de un job programado (PJKT-2029): el registro declarativo aporta\n`name`/`schedule`; el latido de Redis (`jobs:heartbeat:{name}`) aporta el\nresto. Los tres campos de ejecución son null si el job nunca corrió (o su\nlatido caducó / Redis se vació)."},"PlatformLoginCodeResentOut":{"properties":{"sent":{"type":"boolean","title":"Sent"}},"type":"object","required":["sent"],"title":"PlatformLoginCodeResent"},"PlatformMembershipOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"}},"type":"object","required":["organization_id","organization_name","role"],"title":"PlatformMembership"},"PlatformOrgBulkAction":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"Ids"},"action":{"type":"string","enum":["suspend","unsuspend","soft_delete","set_plan"],"title":"Action"},"plan":{"anyOf":[{"type":"string","enum":["free","pro"]},{"type":"null"}],"title":"Plan"},"confirm":{"type":"boolean","title":"Confirm","default":false}},"type":"object","required":["ids","action"],"title":"PlatformOrgBulkAction"},"PlatformOrgFeatureFlagOut":{"properties":{"key":{"type":"string","title":"Key"},"min_plan":{"type":"string","enum":["free","pro"],"title":"Min Plan"},"plan_default":{"type":"boolean","title":"Plan Default"},"global_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Global Enabled"},"override_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Override Enabled"},"effective":{"type":"boolean","title":"Effective"},"source":{"type":"string","enum":["org","global","plan"],"title":"Source"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"updated_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Updated By"}},"type":"object","required":["key","min_plan","plan_default","global_enabled","override_enabled","effective","source"],"title":"PlatformOrgFeatureFlag","description":"Estado EFECTIVO de una feature para UNA org, con el desglose de la\nprecedencia (D6): override de org > flag global > default del plan."},"PlatformOrgMemberOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"role":{"type":"string","enum":["owner","admin","manager","member"],"title":"Role"}},"type":"object","required":["user_id","email","name","role"],"title":"PlatformOrgMember"},"PlatformOrgUsageOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"plan":{"type":"string","title":"Plan"},"storage_bytes":{"type":"integer","title":"Storage Bytes"},"tasks":{"type":"integer","title":"Tasks"},"documents":{"type":"integer","title":"Documents"},"attachments":{"type":"integer","title":"Attachments"},"members":{"type":"integer","title":"Members"},"projects_active":{"type":"integer","title":"Projects Active"},"projects_trashed":{"type":"integer","title":"Projects Trashed"},"projects_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Projects Limit"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["organization_id","name","plan","storage_bytes","tasks","documents","attachments","members","projects_active","projects_trashed","created_at"],"title":"PlatformOrgUsage","description":"Consumo agregado de una organización (PJKT-2048).\n\n`storage_bytes` = suma de los adjuntos registrados en `attachments`\n(file_size) y `task_attachments` (size_bytes). Los avatares/logos legacy\nque viven fuera de esas tablas NO cuentan: no hay fila que los mida."},"PlatformOrganizationDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"brand_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Color"},"status":{"type":"string","enum":["active","suspended","deleted"],"title":"Status"},"suspended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Suspended At"},"suspend_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suspend Reason"},"plan_usage":{"$ref":"#/components/schemas/PlatformPlanUsageOut"},"legal_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"fiscal_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fiscal Address"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"members":{"items":{"$ref":"#/components/schemas/PlatformOrgMemberOut"},"type":"array","title":"Members"},"project_count":{"type":"integer","title":"Project Count"},"trashed_project_count":{"type":"integer","title":"Trashed Project Count"},"task_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Task Counts"},"invoice_count":{"type":"integer","title":"Invoice Count"},"document_count":{"type":"integer","title":"Document Count"},"supplier_count":{"type":"integer","title":"Supplier Count"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"purge_eligible_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Purge Eligible At"}},"type":"object","required":["id","name","slug","status","plan_usage","created_at","members","project_count","trashed_project_count","task_counts","invoice_count","document_count","supplier_count"],"title":"PlatformOrganizationDetail"},"PlatformOrganizationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"status":{"type":"string","enum":["active","suspended","deleted"],"title":"Status"},"suspended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Suspended At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"member_count":{"type":"integer","title":"Member Count"},"project_count":{"type":"integer","title":"Project Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","slug","status","member_count","project_count","created_at"],"title":"PlatformOrganization"},"PlatformOrganizationUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"legal_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Legal Name"},"tax_id":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Tax Id"},"fiscal_address":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Fiscal Address"}},"type":"object","title":"PlatformOrganizationUpdate","description":"PATCH parcial de la ficha de una organización (PJKT-2032). `None` en un\ncampo anulable lo limpia; un campo ausente no se toca (exclude_unset en\nservice). El slug NO es editable: es el identificador público de la org."},"PlatformOverviewOut":{"properties":{"total_organizations":{"type":"integer","title":"Total Organizations"},"total_users":{"type":"integer","title":"Total Users"},"total_suppliers":{"type":"integer","title":"Total Suppliers"},"open_errors":{"type":"integer","title":"Open Errors"},"platform_admins":{"type":"integer","title":"Platform Admins"},"total_projects":{"type":"integer","title":"Total Projects"},"trashed_projects":{"type":"integer","title":"Trashed Projects"},"total_tasks":{"type":"integer","title":"Total Tasks"},"total_invoices":{"type":"integer","title":"Total Invoices"},"active_sessions":{"type":"integer","title":"Active Sessions"}},"type":"object","required":["total_organizations","total_users","total_suppliers","open_errors","platform_admins","total_projects","trashed_projects","total_tasks","total_invoices","active_sessions"],"title":"PlatformOverview","description":"Contadores globales del panel. `total_projects`/`total_tasks` cuentan lo\nVIVO —lo que el cliente ve y lo que enseñan las listas—; la papelera va en su\npropia cifra para que un KPI de negocio no suba cuando se borran cosas."},"PlatformPasskeyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","created_at"],"title":"PlatformPasskey"},"PlatformPlanGrantIn":{"properties":{"plan":{"type":"string","enum":["free","pro"],"title":"Plan"},"reason":{"type":"string","maxLength":255,"minLength":1,"title":"Reason"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"force":{"type":"boolean","title":"Force","default":false}},"type":"object","required":["plan","reason"],"title":"PlatformPlanGrant","description":"Otorgar un plan a mano (override manual). `reason` obligatorio (min 1)."},"PlatformPlanUsageOut":{"properties":{"plan":{"type":"string","title":"Plan"},"projects_active":{"type":"integer","title":"Projects Active"},"projects_trashed":{"type":"integer","title":"Projects Trashed"},"projects_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Projects Limit"},"at_limit":{"type":"boolean","title":"At Limit"}},"type":"object","required":["plan","projects_active","projects_trashed","at_limit"],"title":"PlatformPlanUsage","description":"Proyectos usados frente al cupo del plan.\n\nCierra el ticket más repetido («no puedo crear proyectos»): `projects_active`\nes el MISMO contador que el producto compara con el límite al crear\n(`OrganizationRepository.count_projects`), no una cuenta paralela del panel;\nsi divergieran, el operador vería un número en ops y el cliente otro en la\napp. La papelera va aparte porque NO ocupa cupo — y esa es justo la\nconfusión que trae el ticket."},"PlatformProjectDeleteIn":{"properties":{"confirm_name":{"type":"string","title":"Confirm Name"},"reason":{"type":"string","title":"Reason"}},"additionalProperties":false,"type":"object","required":["confirm_name","reason"],"title":"PlatformProjectDelete","description":"Confirmación tipada del borrado de un proyecto AJENO desde ops (fase 3).\n\nLas dos barreras van juntas a propósito. `confirm_name` es la humana (la\nmisma que la purga de org: `service.request_purge`); `reason` es la forense\ny aquí NO es opcional —a diferencia de la purga de org, donde el diálogo lo\nllama «opcional»—: quien borra no es el dueño del dato sino un tercero que\nactúa sobre lo que entendió de un ticket, y «¿por qué desapareció mi\nproyecto?» tiene que tener respuesta en `audit_logs`."},"PlatformProjectDeletionOut":{"properties":{"project_id":{"type":"string","format":"uuid","title":"Project Id"},"name":{"type":"string","title":"Name"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"skipped":{"type":"boolean","title":"Skipped"},"client_can_restore":{"type":"boolean","title":"Client Can Restore"},"plan_usage":{"$ref":"#/components/schemas/PlatformPlanUsageOut"}},"type":"object","required":["project_id","name","skipped","client_can_restore","plan_usage"],"title":"PlatformProjectDeletion","description":"Qué pasó al borrar y qué le pasa AL CLIENTE a continuación.\n\n`plan_usage` y `client_can_restore` viajan aquí porque restaurar sí consume\ncupo (`projects/service.restore` → `check_project_limit`): el operador tiene\nque ver en el mismo gesto si acaba de dejar al cliente sin vuelta atrás. No\nes un aviso genérico: es el estado real del cupo tras ESTE borrado."},"PlatformProjectDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"visibility":{"type":"string","title":"Visibility"},"task_count":{"type":"integer","title":"Task Count"},"task_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Task Counts"},"member_count":{"type":"integer","title":"Member Count"},"members":{"items":{"$ref":"#/components/schemas/PlatformProjectMemberOut"},"type":"array","title":"Members"},"department_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department Name"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"folder_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Name"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","status","visibility","task_count","task_counts","member_count","members","created_at","updated_at"],"title":"PlatformProjectDetail"},"PlatformProjectMemberOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["user_id","name","email"],"title":"PlatformProjectMember","description":"Miembro EXPLÍCITO del proyecto: el conjunto que da acceso cuando es\n`restricted`, y por tanto la respuesta al ticket «a Fulano no le aparece»."},"PlatformProjectOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"visibility":{"type":"string","title":"Visibility"},"task_count":{"type":"integer","title":"Task Count"},"member_count":{"type":"integer","title":"Member Count"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","status","visibility","task_count","member_count","created_at"],"title":"PlatformProject","description":"Proyecto visto desde ops (N1). Sin `description`: el alcance comercial y\nlas condiciones que suele llevar son contenido del cliente."},"PlatformPurgeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"org_name":{"type":"string","title":"Org Name"},"status":{"type":"string","enum":["queued","running","done","failed"],"title":"Status"},"scheduled_at":{"type":"string","format":"date-time","title":"Scheduled At"},"requested_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Requested By"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"executed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Executed At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","org_name","status","scheduled_at","created_at"],"title":"PlatformPurge","description":"Estado de una purga de organización (job Dramatiq).\n\n`organization_id` es null tras ejecutarse (la org ya no existe: su FK cayó a\nNULL); `org_name` es el snapshot que sobrevive al borrado. `error` (solo si\n`failed`) es un resumen corto — la salvaguarda que dejó de cumplirse o el\nfallo —, jamás un stacktrace."},"PlatformPurgeRequestIn":{"properties":{"confirm_name":{"type":"string","title":"Confirm Name"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["confirm_name"],"title":"PlatformPurgeRequest","description":"Cuerpo para PEDIR la purga de una org: type-to-confirm + motivo opcional.\n\n`confirm_name` DEBE coincidir EXACTO con el nombre de la org (la comparación\nla hace el service → 422 `confirm_name_mismatch` si no). `reason` es forense\n(queda en la auditoría, no en la fila de la purga)."},"PlatformPushDeviceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["web","native"],"title":"Kind"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","kind","created_at"],"title":"PlatformPushDevice"},"PlatformQueueDepthOut":{"properties":{"name":{"type":"string","title":"Name"},"pending":{"type":"integer","title":"Pending"},"dead_letter":{"type":"integer","title":"Dead Letter"}},"type":"object","required":["name","pending","dead_letter"],"title":"PlatformQueueDepth"},"PlatformRevenueByPlanRowOut":{"properties":{"plan":{"type":"string","enum":["free","pro"],"title":"Plan"},"mrr_minor":{"type":"integer","title":"Mrr Minor"},"active_subscriptions":{"type":"integer","title":"Active Subscriptions"}},"type":"object","required":["plan","mrr_minor","active_subscriptions"],"title":"PlatformRevenueByPlanRow","description":"Fila de MRR por plan (invariante: Σ mrr_minor == summary.mrr_minor)."},"PlatformRevenueMetricsByPlanRowOut":{"properties":{"plan":{"type":"string","enum":["free","pro"],"title":"Plan"},"active_orgs":{"type":"integer","title":"Active Orgs"},"mrr_minor":{"type":"integer","title":"Mrr Minor"}},"type":"object","required":["plan","active_orgs","mrr_minor"],"title":"PlatformRevenueMetricsByPlanRow","description":"Estado actual por plan: orgs con suscripción que cuenta + su MRR. Como hay\nuna suscripción por org, `active_orgs` == suscripciones que cuentan."},"PlatformRevenueMetricsOut":{"properties":{"months":{"items":{"$ref":"#/components/schemas/PlatformRevenueMonthPointOut"},"type":"array","title":"Months"},"by_plan":{"items":{"$ref":"#/components/schemas/PlatformRevenueMetricsByPlanRowOut"},"type":"array","title":"By Plan"}},"type":"object","required":["months","by_plan"],"title":"PlatformRevenueMetrics","description":"Métricas de ingresos: serie mensual (≤12 meses, más antiguos primero) +\ndesglose actual por plan (PJKT-2040)."},"PlatformRevenueMonthPointOut":{"properties":{"month":{"type":"string","title":"Month"},"mrr_minor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mrr Minor"},"new_paid_orgs":{"type":"integer","title":"New Paid Orgs"},"churned_orgs":{"type":"integer","title":"Churned Orgs"}},"type":"object","required":["month","mrr_minor","new_paid_orgs","churned_orgs"],"title":"PlatformRevenueMonthPoint","description":"Punto MENSUAL de churn y MRR (PJKT-2040). Limitaciones documentadas en el\ncontrato: altas/churn derivan del estado ACTUAL del espejo `subscriptions`\n(sin histórico de eventos); `mrr_minor` es None en meses anteriores al primer\nsnapshot (no se inventa un 0) y EN VIVO para el mes en curso."},"PlatformRevenuePointOut":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"value_minor":{"type":"integer","title":"Value Minor"}},"type":"object","required":["date","value_minor"],"title":"PlatformRevenuePoint","description":"Punto (día natural UTC) de la serie temporal de revenue."},"PlatformRevenueSubscriptionRowOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"plan":{"type":"string","enum":["free","pro"],"title":"Plan"},"status":{"type":"string","title":"Status"},"mrr_minor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mrr Minor"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"synced":{"type":"boolean","title":"Synced"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"canceled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Canceled At"}},"type":"object","required":["organization_id","organization_name","plan","status","mrr_minor","currency","synced","current_period_end","canceled_at"],"title":"PlatformRevenueSubscriptionRow","description":"Suscripción de Stripe (espejo local) con su MRR precomputado."},"PlatformRevenueSummaryOut":{"properties":{"mrr_minor":{"type":"integer","title":"Mrr Minor"},"arr_minor":{"type":"integer","title":"Arr Minor"},"currency":{"type":"string","title":"Currency"},"active_subscriptions":{"type":"integer","title":"Active Subscriptions"},"synced_subscriptions":{"type":"integer","title":"Synced Subscriptions"},"last_synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Synced At"},"arpa_minor":{"type":"integer","title":"Arpa Minor"},"mrr_change_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mrr Change Pct"},"new_mrr_minor":{"type":"integer","title":"New Mrr Minor"},"churned_mrr_minor":{"type":"integer","title":"Churned Mrr Minor"},"as_of":{"type":"string","format":"date-time","title":"As Of"},"basis":{"type":"string","enum":["stripe_synced","partial","list_price_estimate"],"title":"Basis"},"stripe_configured":{"type":"boolean","title":"Stripe Configured"}},"type":"object","required":["mrr_minor","arr_minor","currency","active_subscriptions","synced_subscriptions","last_synced_at","arpa_minor","mrr_change_pct","new_mrr_minor","churned_mrr_minor","as_of","basis","stripe_configured"],"title":"PlatformRevenueSummary","description":"Agregado de revenue de la plataforma (MRR/ARR/ARPA + tendencia)."},"PlatformSearchOrganizationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","required":["id","name","slug"],"title":"PlatformSearchOrganization"},"PlatformSearchResultOut":{"properties":{"organizations":{"items":{"$ref":"#/components/schemas/PlatformSearchOrganizationOut"},"type":"array","title":"Organizations"},"users":{"items":{"$ref":"#/components/schemas/PlatformSearchUserOut"},"type":"array","title":"Users"},"suppliers":{"items":{"$ref":"#/components/schemas/PlatformSearchSupplierOut"},"type":"array","title":"Suppliers"}},"type":"object","required":["organizations","users","suppliers"],"title":"PlatformSearchResult"},"PlatformSearchSupplierOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"}},"type":"object","required":["id","name","organization_id","organization_name"],"title":"PlatformSearchSupplier"},"PlatformSearchUserOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"}},"type":"object","required":["id","name","email"],"title":"PlatformSearchUser"},"PlatformSessionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["id","created_at","expires_at"],"title":"PlatformSession"},"PlatformSubscriptionCancelIn":{"properties":{"at_period_end":{"type":"boolean","title":"At Period End","default":true}},"type":"object","title":"PlatformSubscriptionCancel","description":"Cuerpo para cancelar la suscripción de Stripe de una org (W8).\n\n`at_period_end=True` (defecto) programa la cancelación al final del periodo\npagado; `False` cancela de inmediato."},"PlatformSubscriptionDetailOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"effective_plan":{"type":"string","enum":["free","pro"],"title":"Effective Plan"},"plan_source":{"type":"string","enum":["default","stripe","manual"],"title":"Plan Source"},"override":{"anyOf":[{"$ref":"#/components/schemas/PlatformSubscriptionOverrideOut"},{"type":"null"}]},"stripe":{"anyOf":[{"$ref":"#/components/schemas/PlatformSubscriptionStripeOut"},{"type":"null"}]},"diverged":{"type":"boolean","title":"Diverged"}},"type":"object","required":["organization_id","organization_name","effective_plan","plan_source","override","stripe","diverged"],"title":"PlatformSubscriptionDetail","description":"Foto de suscripción/licencia de una organización (vista ops)."},"PlatformSubscriptionOverrideOut":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"By"},"at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"At"}},"type":"object","required":["reason","expires_at","by","at"],"title":"PlatformSubscriptionOverride","description":"Metadatos del override manual (plan otorgado a mano por ops)."},"PlatformSubscriptionResyncOut":{"properties":{"before_plan":{"type":"string","enum":["free","pro"],"title":"Before Plan"},"after_plan":{"type":"string","enum":["free","pro"],"title":"After Plan"},"stripe_status":{"type":"string","title":"Stripe Status"},"changed":{"type":"boolean","title":"Changed"}},"type":"object","required":["before_plan","after_plan","stripe_status","changed"],"title":"PlatformSubscriptionResyncResult","description":"Resumen de la conciliación en un clic (PJKT-2038): antes → después.\n\n`after_plan` puede seguir divergiendo del espejo si hay un override manual\nactivo (el override manda, decisión D2); `changed` compara solo el plan\nEFECTIVO de la organización."},"PlatformSubscriptionRowOut":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"effective_plan":{"type":"string","enum":["free","pro"],"title":"Effective Plan"},"plan_source":{"type":"string","enum":["default","stripe","manual"],"title":"Plan Source"},"stripe_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Status"},"diverged":{"type":"boolean","title":"Diverged"}},"type":"object","required":["organization_id","organization_name","effective_plan","plan_source","stripe_status","diverged"],"title":"PlatformSubscriptionRow","description":"Fila de la flota de licencias (tabla paginada)."},"PlatformSubscriptionStripeOut":{"properties":{"status":{"type":"string","title":"Status"},"plan":{"type":"string","title":"Plan"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"customer_id":{"type":"string","title":"Customer Id"},"subscription_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subscription Id"}},"type":"object","required":["status","plan","current_period_end","customer_id","subscription_id"],"title":"PlatformSubscriptionStripe","description":"Espejo del estado de facturación en Stripe (tabla `subscriptions`)."},"PlatformSupplierBulkAction":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"Ids"},"action":{"type":"string","enum":["delete","merge","promote"],"title":"Action"},"target_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Target Id"},"confirm":{"type":"boolean","title":"Confirm","default":false}},"type":"object","required":["ids","action"],"title":"PlatformSupplierBulkAction"},"PlatformSupplierCreateIn":{"properties":{"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["organization_id","name"],"title":"PlatformSupplierCreate"},"PlatformSupplierOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"global_supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Global Supplier Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","organization_name","name","created_at","updated_at"],"title":"PlatformSupplier"},"PlatformSupplierUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"tax_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Address"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"additionalProperties":false,"type":"object","title":"PlatformSupplierUpdate","description":"PATCH parcial de un proveedor org-scoped (PJKT-2042). `None` en un campo\nanulable lo limpia; un campo ausente no se toca (exclude_unset en service)."},"PlatformTaskAttachmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"filename":{"type":"string","title":"Filename"},"content_type":{"type":"string","title":"Content Type"},"size_bytes":{"type":"integer","title":"Size Bytes"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","filename","content_type","size_bytes","created_at"],"title":"PlatformTaskAttachment","description":"Metadatos del adjunto. El NOMBRE del fichero es contenido (suele llevar el\nnombre de la persona o del caso), por eso viaja aquí y no en el detalle N1.\nLos BYTES no se sirven nunca desde ops: para eso está el export de la org."},"PlatformTaskCommentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"body":{"type":"string","title":"Body"},"is_edited":{"type":"boolean","title":"Is Edited"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","body","is_edited","created_at"],"title":"PlatformTaskComment"},"PlatformTaskContentOut":{"properties":{"task_id":{"type":"string","format":"uuid","title":"Task Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"comments":{"items":{"$ref":"#/components/schemas/PlatformTaskCommentOut"},"type":"array","title":"Comments"},"attachments":{"items":{"$ref":"#/components/schemas/PlatformTaskAttachmentOut"},"type":"array","title":"Attachments"}},"type":"object","required":["task_id","comments","attachments"],"title":"PlatformTaskContent"},"PlatformTaskDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"project_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Key"},"project_visibility":{"type":"string","title":"Project Visibility"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"type":{"type":"string","title":"Type"},"priority":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Priority"},"assignee_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee Name"},"creator_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Name"},"sprint_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sprint Name"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"parent_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Reference"},"story_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Story Points"},"estimated_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Hours"},"comment_count":{"type":"integer","title":"Comment Count"},"attachment_count":{"type":"integer","title":"Attachment Count"},"logged_minutes":{"type":"integer","title":"Logged Minutes"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"completed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Completed Date"},"status_history":{"items":{"$ref":"#/components/schemas/PlatformTaskStatusChangeOut"},"type":"array","title":"Status History"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","project_name","project_visibility","title","status","type","comment_count","attachment_count","logged_minutes","status_history","created_at","updated_at"],"title":"PlatformTaskDetail"},"PlatformTaskOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"project_visibility":{"type":"string","title":"Project Visibility"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"type":{"type":"string","title":"Type"},"priority":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Priority"},"assignee_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee Name"},"sprint_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sprint Name"},"parent_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Reference"},"comment_count":{"type":"integer","title":"Comment Count"},"attachment_count":{"type":"integer","title":"Attachment Count"},"logged_minutes":{"type":"integer","title":"Logged Minutes"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","project_name","project_visibility","title","status","type","comment_count","attachment_count","logged_minutes","created_at","updated_at"],"title":"PlatformTask","description":"Tarea vista desde ops (N1): metadatos y contadores, nunca texto del cliente."},"PlatformTaskStatusChangeOut":{"properties":{"status":{"type":"string","title":"Status"},"entered_at":{"type":"string","format":"date-time","title":"Entered At"},"left_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Left At"}},"type":"object","required":["status","entered_at"],"title":"PlatformTaskStatusChange","description":"Tramo de tiempo-en-estado: responde «¿desde cuándo está parada?» sin abrir\nel contenido."},"PlatformTimeseriesPointOut":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"new_users":{"type":"integer","title":"New Users"},"new_organizations":{"type":"integer","title":"New Organizations"},"logins":{"type":"integer","title":"Logins"},"errors":{"type":"integer","title":"Errors"}},"type":"object","required":["date","new_users","new_organizations","logins","errors"],"title":"PlatformTimeseriesPoint"},"PlatformTrashOrganizationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"slug":{"type":"string","title":"Slug"},"deleted_at":{"type":"string","format":"date-time","title":"Deleted At"},"purge_eligible_at":{"type":"string","format":"date-time","title":"Purge Eligible At"},"member_count":{"type":"integer","title":"Member Count"}},"type":"object","required":["id","name","slug","deleted_at","purge_eligible_at","member_count"],"title":"PlatformTrashOrganization","description":"Organización en la PAPELERA del panel ops (PJKT-2046): soft-deleted pero\naún restaurable. `purge_eligible_at` (deleted_at + gracia W19) le sirve al\npanel para pintar cuánta gracia queda antes de que la org sea purgable."},"PlatformTwoFactorDisabledOut":{"properties":{"disabled":{"type":"boolean","title":"Disabled"}},"type":"object","required":["disabled"],"title":"PlatformTwoFactorDisabled"},"PlatformUserBulkAction":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":100,"minItems":1,"title":"Ids"},"action":{"type":"string","enum":["suspend","unsuspend","soft_delete"],"title":"Action"},"confirm":{"type":"boolean","title":"Confirm","default":false}},"type":"object","required":["ids","action"],"title":"PlatformUserBulkAction"},"PlatformUserDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"status":{"type":"string","enum":["active","suspended","banned","deleted"],"title":"Status"},"suspended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Suspended At"},"status_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Reason"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"is_platform_admin":{"type":"boolean","title":"Is Platform Admin"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"memberships":{"items":{"$ref":"#/components/schemas/PlatformMembershipOut"},"type":"array","title":"Memberships"},"passkeys":{"items":{"$ref":"#/components/schemas/PlatformPasskeyOut"},"type":"array","title":"Passkeys"},"sessions":{"items":{"$ref":"#/components/schemas/PlatformSessionOut"},"type":"array","title":"Sessions"},"push_devices":{"items":{"$ref":"#/components/schemas/PlatformPushDeviceOut"},"type":"array","title":"Push Devices"}},"type":"object","required":["id","email","name","status","is_platform_admin","created_at","memberships","passkeys","sessions","push_devices"],"title":"PlatformUserDetail"},"PlatformUserEmailChangeIn":{"properties":{"new_email":{"type":"string","title":"New Email"}},"additionalProperties":false,"type":"object","required":["new_email"],"title":"PlatformUserEmailChange","description":"Petición de cambio de email: NO cambia nada, solo dispara el correo de\nverificación al buzón nuevo (PJKT-2033)."},"PlatformUserEraseIn":{"properties":{"confirm":{"type":"string","title":"Confirm"},"reason":{"type":"string","title":"Reason"}},"additionalProperties":false,"type":"object","required":["confirm","reason"],"title":"PlatformUserErase","description":"Confirmación tipada del borrado GDPR (PJKT-2047). El service valida que\n`confirm` sea exactamente «ELIMINAR»; aquí solo se tipa el shape.\n\n`reason` es OBLIGATORIO (min 1 tras strip): el borrado de una persona es\njusto el caso en el que alguien —el propio interesado, o un inspector—\npregunta «¿bajo qué solicitud se hizo esto?», y sin motivo el registro de\nauditoría no puede responderlo. La purga de un tenant entero, que es MENOS\npersonal, ya lo admitía; aquí ni siquiera existía el campo."},"PlatformUserOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"status":{"type":"string","enum":["active","suspended","banned","deleted"],"title":"Status"},"suspended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Suspended At"},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At"},"organization_count":{"type":"integer","title":"Organization Count"},"is_platform_admin":{"type":"boolean","title":"Is Platform Admin"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","email","name","status","organization_count","is_platform_admin","created_at"],"title":"PlatformUser"},"PlatformUserUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"additionalProperties":false,"type":"object","title":"PlatformUserUpdate","description":"Edición en caliente de campos inocuos (PJKT-2033). El email queda fuera a\npropósito: cambiarlo exige re-verificación (ver PlatformUserEmailChangeIn)."},"PortalApproveIn":{"properties":{"signer_name":{"type":"string","title":"Signer Name"},"signer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signer Email"}},"type":"object","required":["signer_name"],"title":"PortalApprove","description":"Cuerpo de la aprobación/firma pública: identidad del firmante.\n\nSe reutiliza tanto para aprobar una factura como para firmar un contrato\ndesde el link público del portal (sin autenticación)."},"PortalCodeSentOut":{"properties":{"expires_in_seconds":{"type":"integer","title":"Expires In Seconds"}},"type":"object","required":["expires_in_seconds"],"title":"PortalCodeSent","description":"Respuesta al pedir el código. DELIBERADAMENTE sin información.\n\nNo dice si esa dirección es contacto del cliente, ni la enmascara, ni\ndevuelve nombre alguno: quien abre el enlace ya sabe de qué empresa es el\nportal, así que una respuesta distinta para «esa persona no está» convertiría\nel enlace en un comprobador de quién trabaja allí. Sale siempre lo mismo, y\nel código solo viaja hacia el buzón si de verdad hay a quién mandárselo."},"PortalContractOut":{"properties":{"contract_number":{"type":"string","title":"Contract Number"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Value"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"signed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Signed At"},"signed_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signed By Name"}},"type":"object","required":["contract_number","title","status"],"title":"PortalContract","description":"Contrato visible en el portal (sin ids internos)."},"PortalIdentifyIn":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"PortalIdentify","description":"«Soy esta dirección»: el primer paso para entrar como persona."},"PortalIdentityOut":{"properties":{"contact_name":{"type":"string","title":"Contact Name"},"contact_email_hint":{"type":"string","title":"Contact Email Hint"},"client_name":{"type":"string","title":"Client Name"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["contact_name","contact_email_hint","client_name","expires_at"],"title":"PortalIdentity","description":"Quién está dentro ahora mismo.\n\nEl correo va ENMASCARADO aunque quien pregunta acabe de teclearlo: la\nrespuesta la puede leer cualquier script de la página, y el portal ya asume\nque la pantalla puede estar en un ordenador compartido."},"PortalInvoiceOut":{"properties":{"invoice_number":{"type":"string","title":"Invoice Number"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"type":"string","format":"date","title":"Due Date"},"status":{"type":"string","title":"Status"},"total":{"type":"number","title":"Total"},"currency":{"type":"string","title":"Currency"},"approved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Approved At"},"approved_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Approved By Name"}},"type":"object","required":["invoice_number","issue_date","due_date","status","total","currency"],"title":"PortalInvoice","description":"Factura visible en el portal (sin ids internos)."},"PortalLinkCreateIn":{"properties":{"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days"}},"type":"object","title":"PortalLinkCreate","description":"Cuerpo de POST …/portal-links.\n\n`expires_in_days`: días de validez del enlace. Si es None se aplica el\ndefault del service (30 días). Debe ser >= 1."},"PortalLinkOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","format":"uuid","title":"Client Id"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"portal_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal Url"}},"type":"object","required":["id","client_id","expires_at","revoked_at","created_at"],"title":"PortalLink","description":"Enlace del portal. `token`/`portal_url` SOLO se incluyen al crearlo."},"PortalOrgBrandOut":{"properties":{"name":{"type":"string","title":"Name"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"brand_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Color"},"accent_color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accent Color"},"legal_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Legal Name"}},"type":"object","required":["name"],"title":"PortalOrgBrand","description":"Marca de la organización que expone el portal (branding público)."},"PortalQuoteOut":{"properties":{"quote_number":{"type":"string","title":"Quote Number"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"expires_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expires At"},"status":{"type":"string","title":"Status"},"total":{"type":"number","title":"Total"},"currency":{"type":"string","title":"Currency"}},"type":"object","required":["quote_number","issue_date","status","total","currency"],"title":"PortalQuote","description":"Presupuesto visible en el portal (sin ids internos)."},"PortalRequestAttachmentIn":{"properties":{"filename":{"type":"string","maxLength":255,"title":"Filename"},"content_base64":{"type":"string","maxLength":13982037,"title":"Content Base64"}},"type":"object","required":["filename","content_base64"],"title":"PortalRequestAttachmentIn","description":"Un fichero que el cliente sube a SU petición, en base64.\n\nBase64 y no `multipart` porque el portal es una página que habla JSON con el\nAPI: una segunda forma de subir (formulario multiparte) sería una segunda\npuerta con sus propios límites, y la regla 18 no admite dos.\n\nLos límites son los COMUNES del producto (`app/shared/uploads.py`): 10 MiB,\nallow-list positiva de extensiones y verificación de los BYTES contra la\nextensión. Quien sube aquí es la persona MENOS verificada que toca el\nproducto, así que no hay ninguna excepción.\n\nEl techo del texto es el de los bytes ×4/3 (lo que crece base64) con margen:\nes lo único que corta el cuerpo ANTES de decodificarlo, y quien lo manda\nmerece un 422 que diga qué pasó en vez de un corte de conexión."},"PortalRequestAttachmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"filename":{"type":"string","title":"Filename"},"mime_type":{"type":"string","title":"Mime Type"},"file_size":{"type":"integer","title":"File Size"},"uploaded_by_client":{"type":"boolean","title":"Uploaded By Client"},"download_url":{"type":"string","title":"Download Url"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","filename","mime_type","file_size","uploaded_by_client","download_url","created_at"],"title":"PortalRequestAttachment","description":"Metadatos del adjunto. NUNCA los bytes, y NUNCA el `stored_name`.\n\n`download_url` apunta al endpoint del PORTAL, no al de la organización: ese\nexige un usuario del equipo y aquí no lo hay. Devolver el `file_url` interno\nsería enseñarle al cliente una puerta que no puede abrir."},"PortalRequestCommentCreateIn":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"PortalRequestCommentCreate","description":"Respuesta del cliente en su propia conversación."},"PortalRequestCommentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"author_kind":{"type":"string","enum":["client","team"],"title":"Author Kind"},"author_name":{"type":"string","title":"Author Name"},"body":{"type":"string","title":"Body"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","author_kind","author_name","body","created_at"],"title":"PortalRequestComment","description":"Un mensaje de la conversación. Solo llegan aquí los PÚBLICOS."},"PortalRequestCreateIn":{"properties":{"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"fields":{"items":{"$ref":"#/components/schemas/PortalRequestFieldValueIn"},"type":"array","title":"Fields","default":[]}},"type":"object","required":["request_type_id","title"],"title":"PortalRequestCreate","description":"Alta de una petición desde el portal.\n\nNO lleva solicitante (sale de la sesión verificada), ni prioridad, ni cola:\nel porqué de cada una está en `SupportService.create_request_from_portal`."},"PortalRequestDetailOut":{"properties":{"request":{"$ref":"#/components/schemas/PortalRequestOut"},"comments":{"items":{"$ref":"#/components/schemas/PortalRequestCommentOut"},"type":"array","title":"Comments"}},"type":"object","required":["request","comments"],"title":"PortalRequestDetail","description":"La petición más su conversación."},"PortalRequestFieldOut":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"name":{"type":"string","title":"Name"},"field_type":{"type":"string","enum":["text","number","date","select","url","email"],"title":"Field Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"required":{"type":"boolean","title":"Required"},"position":{"type":"integer","title":"Position"}},"type":"object","required":["field_def_id","name","field_type","options","required","position"],"title":"PortalRequestField","description":"Un campo del formulario, con lo justo para pintarlo y validarlo."},"PortalRequestFieldValueIn":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"value":{"type":"string","title":"Value"}},"type":"object","required":["field_def_id","value"],"title":"PortalRequestFieldValue","description":"Respuesta a uno de los campos del formulario."},"PortalRequestOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","enum":["open","in_progress","on_hold","resolved","cancelled"],"title":"Status"},"status_label":{"type":"string","title":"Status Label"},"request_type_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Type Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","title","description","status","status_label","request_type_name","created_at","updated_at"],"title":"PortalRequest","description":"Una petición TAL Y COMO la ve quien la abrió.\n\nLo que NO está aquí es la mitad del trabajo de esta etapa, y se enumera\nporque el riesgo es añadir un campo sin pensarlo: no hay `assignee_id` (a\nquién se lo hemos endosado por dentro), ni `project_id` (en qué bandeja\ncayó), ni el estado crudo del tablero, ni `due_at` ni nada del SLA (el\ncompromiso es un pacto comercial; enseñar el reloj invita a discutir el\nreloj), ni el identificador de ninguna otra entidad interna."},"PortalRequestTypeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"fields":{"items":{"$ref":"#/components/schemas/PortalRequestFieldOut"},"type":"array","title":"Fields"}},"type":"object","required":["id","name","description","fields"],"title":"PortalRequestType","description":"Tipo de petición ofrecible, SIN la cola.\n\n`project_id` no sale: a qué bandeja interna aterriza una petición —y por\ntanto qué equipo la ve— es organización de la casa, no información del\ncliente. Que el schema del portal sea otro y no el de `support` es lo que\nimpide que un campo nuevo allí aparezca aquí sin que nadie lo decida."},"PortalSummaryOut":{"properties":{"client_name":{"type":"string","title":"Client Name"},"org":{"$ref":"#/components/schemas/PortalOrgBrandOut"},"locale":{"type":"string","title":"Locale"},"currency":{"type":"string","title":"Currency"},"invoice_count":{"type":"integer","title":"Invoice Count"},"quote_count":{"type":"integer","title":"Quote Count"},"contract_count":{"type":"integer","title":"Contract Count"}},"type":"object","required":["client_name","org","locale","currency","invoice_count","quote_count","contract_count"],"title":"PortalSummary","description":"Resumen de la portada del portal: cliente, marca y recuentos."},"PortalVerifyIn":{"properties":{"email":{"type":"string","title":"Email"},"code":{"type":"string","title":"Code"}},"type":"object","required":["email","code"],"title":"PortalVerify","description":"Canje del código: la dirección otra vez (el código va ligado a ella) y los\nseis dígitos."},"PriorityCountOut":{"properties":{"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["priority","count"],"title":"DashboardPriorityCount","description":"Recuento de tareas abiertas (no `done`) para una prioridad."},"ProjectAnnouncementCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"pinned":{"type":"boolean","title":"Pinned","default":false}},"type":"object","required":["title"],"title":"ProjectAnnouncementCreateIn","description":"Body del POST de aviso: {title, body?, link?, pinned?}."},"ProjectAnnouncementOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"title":{"type":"string","title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"pinned":{"type":"boolean","title":"Pinned"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","organization_id","author_id","author_name","title","body","link","pinned","created_at","updated_at"],"title":"ProjectAnnouncement","description":"Aviso del proyecto tal y como lo devuelve el contrato (PJKT-1965)."},"ProjectCountsOut":{"properties":{"active":{"type":"integer","title":"Active"},"archived":{"type":"integer","title":"Archived"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["active","archived","total"],"title":"DashboardProjectCounts","description":"Recuento de proyectos de la org por estado (más el total)."},"ProjectCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"}},"type":"object","required":["name"],"title":"ProjectCreateIn"},"ProjectFolderCreateIn":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"position":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Position"},"visibility":{"type":"string","enum":["organization","restricted"],"title":"Visibility","default":"organization"}},"type":"object","required":["name"],"title":"ProjectFolderCreate","description":"Alta de carpeta. El nombre es único dentro de la organización."},"ProjectFolderDepartmentsIn":{"properties":{"department_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Department Ids"}},"type":"object","required":["department_ids"],"title":"ProjectFolderDepartmentsIn","description":"Set COMPLETO de departamentos con acceso a una carpeta (semántica PUT)."},"ProjectFolderOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"position":{"type":"integer","title":"Position"},"project_count":{"type":"integer","title":"Project Count"},"visibility":{"type":"string","enum":["organization","restricted"],"title":"Visibility"},"visibility_departments":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Visibility Departments"},"pinned":{"type":"boolean","title":"Pinned"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","position","project_count","visibility","visibility_departments","pinned","created_at"],"title":"ProjectFolder","description":"Carpeta de proyectos, con cuántos proyectos contiene y quién la ve."},"ProjectFolderUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120,"minLength":1},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Color"},"position":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Position"},"visibility":{"anyOf":[{"type":"string","enum":["organization","restricted"]},{"type":"null"}],"title":"Visibility"}},"type":"object","title":"ProjectFolderUpdate","description":"Cambios de una carpeta (PATCH parcial: solo lo enviado)."},"ProjectFollowStateIn":{"properties":{"following":{"type":"boolean","title":"Following"}},"type":"object","required":["following"],"title":"ProjectFollowStateIn","description":"Body del PUT de seguimiento: {following: bool}.\n\n`false` silencia (no borra el vínculo), para que el auto-follow no reactive lo\nque el usuario apagó a propósito."},"ProjectFollowStateOut":{"properties":{"following":{"type":"boolean","title":"Following"},"muted":{"type":"boolean","title":"Muted"},"source":{"anyOf":[{"type":"string","enum":["auto","manual"]},{"type":"null"}],"title":"Source"}},"type":"object","required":["following","muted","source"],"title":"ProjectFollowState","description":"Estado de seguimiento del caller sobre un proyecto (PJKT-1964).\n\n`following` = existe vínculo y NO está silenciado. `source` es None cuando\ntodavía no hay vínculo."},"ProjectFollowerOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"user_name":{"type":"string","title":"User Name"},"muted":{"type":"boolean","title":"Muted"},"source":{"type":"string","enum":["auto","manual"],"title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["user_id","user_name","muted","source","created_at"],"title":"ProjectFollower","description":"Seguidor de un proyecto para el listado de gestión (manager+)."},"ProjectGithubActivityOut":{"properties":{"repos":{"items":{"$ref":"#/components/schemas/RepoActivity"},"type":"array","title":"Repos"}},"type":"object","required":["repos"],"title":"ProjectGithubActivityOut","description":"Respuesta de /github/activity: actividad de todos los repos vinculados."},"ProjectMemberCreateIn":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"}},"type":"object","required":["user_id"],"title":"ProjectMemberCreateIn","description":"Body del POST de miembro: {user_id} — debe ser miembro de la MISMA org."},"ProjectMemberOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"user_name":{"type":"string","title":"User Name"},"user_email":{"type":"string","title":"User Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","project_id","user_id","user_name","user_email","created_at"],"title":"ProjectMember","description":"Miembro explícito de un proyecto restringido (shape del contrato)."},"ProjectOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"source_quote_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Quote Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"},"status":{"type":"string","enum":["active","archived"],"title":"Status"},"board_swimlane":{"anyOf":[{"type":"string","enum":["none","assignee","priority","type"]},{"type":"null"}],"title":"Board Swimlane"},"visibility":{"type":"string","enum":["organization","restricted"],"title":"Visibility"},"visibility_departments":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Visibility Departments"},"is_member":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Member"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"}},"type":"object","required":["id","organization_id","key","name","description","status","visibility","created_at","updated_at"],"title":"Project","description":"Project del contrato: {id, organization_id, key, name, description, logo_url,\nstatus, board_swimlane, created_at, updated_at, last_activity_at?}.\n\n`last_activity_at` = max(updated_at del proyecto, updated_at más reciente\nde sus tareas); solo lo calcula el listado (None en el resto de endpoints)."},"ProjectProfitabilityOut":{"properties":{"project_id":{"type":"string","format":"uuid","title":"Project Id"},"project_name":{"type":"string","title":"Project Name"},"deleted":{"type":"boolean","title":"Deleted"},"currency":{"type":"string","title":"Currency","default":"EUR"},"revenue":{"type":"number","title":"Revenue"},"cost_labor":{"type":"number","title":"Cost Labor"},"cost_expenses":{"type":"number","title":"Cost Expenses"},"cost_total":{"type":"number","title":"Cost Total"},"margin":{"type":"number","title":"Margin"},"margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Margin Pct"},"billable_hours":{"type":"number","title":"Billable Hours"},"total_hours":{"type":"number","title":"Total Hours"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["project_id","project_name","deleted","revenue","cost_labor","cost_expenses","cost_total","margin","margin_pct","billable_hours","total_hours","criterio"],"title":"ProjectProfitability","description":"Rentabilidad de un proyecto: ingreso vs coste laboral + gastos + AP.\n\n`margin_pct` = margin/revenue×100, o None cuando revenue == 0 (evita\ndividir por cero y distingue \"sin ingreso\" de \"0%\"). Los importes son\n`Money` (float en JSON); las horas van como float (minutos/60).\n\nNota de coste laboral: las horas de un miembro sin `hourly_cost_rate`\nconfigurada cuentan 0 € de coste (pero sí suman a las horas).\n\n`cost_expenses` (P-6) = gastos aprobados sin factura de proveedor\nasociada + facturas de proveedor (AP, `SupplierInvoice`) no void/rejected\nimputadas al proyecto. Política anti-doble-conteo documentada en\n`finance/analytics.py` (cabecera de la sección \"Rentabilidad por\nproyecto\").\n\n`deleted` avisa de que el proyecto de esta fila está en la PAPELERA. La fila\nexiste igualmente (PJKT-2306: borrar un proyecto no reescribe un periodo ya\ncerrado), y el flag es lo que permite a la pantalla explicar por qué enseña\nun proyecto que ya no sale en ninguna lista."},"ProjectRepositoryIn":{"properties":{"repo_full_name":{"type":"string","maxLength":255,"minLength":3,"title":"Repo Full Name","description":"Ej: 'acme/frontend'."}},"type":"object","required":["repo_full_name"],"title":"ProjectRepositoryIn","description":"Cuerpo del POST para vincular un repo a un proyecto."},"ProjectRepositoryOut":{"properties":{"id":{"type":"string","format":"uuid4","title":"Id"},"project_id":{"type":"string","format":"uuid4","title":"Project Id"},"organization_id":{"type":"string","format":"uuid4","title":"Organization Id"},"repo_full_name":{"type":"string","title":"Repo Full Name"},"default_branch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Branch"},"linked_by":{"anyOf":[{"type":"string","format":"uuid4"},{"type":"null"}],"title":"Linked By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","project_id","organization_id","repo_full_name","created_at"],"title":"ProjectRepositoryOut","description":"Vínculo persistido (fila de project_repositories)."},"ProjectShareCreateIn":{"properties":{"shared_with_org_id":{"type":"string","format":"uuid","title":"Shared With Org Id"},"permission":{"type":"string","enum":["read","read_comment","full"],"title":"Permission","default":"read"}},"type":"object","required":["shared_with_org_id"],"title":"ProjectShareCreateIn"},"ProjectShareOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"owner_org_id":{"type":"string","format":"uuid","title":"Owner Org Id"},"shared_with_org_id":{"type":"string","format":"uuid","title":"Shared With Org Id"},"shared_with_org_name":{"type":"string","title":"Shared With Org Name"},"permission":{"type":"string","enum":["read","read_comment","full"],"title":"Permission"},"status":{"type":"string","enum":["active","revoked","expired"],"title":"Status"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"accepted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Accepted At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"type":"object","required":["id","project_id","owner_org_id","shared_with_org_id","shared_with_org_name","permission","status","created_by","created_at","accepted_at","revoked_at"],"title":"ProjectShare","description":"Compartición de un proyecto con otra org (lado dueño)."},"ProjectUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"status":{"anyOf":[{"type":"string","enum":["active","archived"]},{"type":"null"}],"title":"Status"},"board_swimlane":{"anyOf":[{"type":"string","enum":["none","assignee","priority","type"]},{"type":"null"}],"title":"Board Swimlane"},"department_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Department Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"folder_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Folder Id"}},"type":"object","title":"ProjectUpdateIn","description":"PATCH parcial del contrato: {name?, description?, logo_url?, status?, board_swimlane?}.\n\n`name`/`status` no admiten null explícito (los validadores corren solo\npara valores enviados); `description` y `logo_url` sí: null los borra.\n`board_swimlane` acepta null para desactivar (equivale a \"none\")."},"ProjectVisibilityDepartmentIn":{"properties":{"department_id":{"type":"string","format":"uuid","title":"Department Id"},"access_level":{"type":"string","enum":["read","write"],"title":"Access Level","default":"write"}},"type":"object","required":["department_id"],"title":"ProjectVisibilityDepartmentIn","description":"Un departamento del set de visibilidad, con su nivel (PJKT-1955).\n\n`access_level` por defecto `write` = comportamiento histórico de la\nasociación (lectura y escritura)."},"ProjectVisibilityDepartmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"acronym":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Acronym"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"},"weekly_schedule":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Weekly Schedule"},"annual_vacation_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Annual Vacation Days"},"holidays":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Holidays"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"access_level":{"type":"string","enum":["read","write"],"title":"Access Level"}},"type":"object","required":["id","organization_id","name","acronym","description","logo_url","color","weekly_schedule","annual_vacation_days","holidays","created_at","updated_at","access_level"],"title":"ProjectVisibilityDepartment","description":"Departamento del set de visibilidad + el nivel que concede (PJKT-1955).\n\nSuperset de `DepartmentOut` (mismo shape que el schema\n`ProjectVisibilityDepartment` del contrato)."},"ProjectVisibilityDepartmentsUpdateIn":{"properties":{"departments":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProjectVisibilityDepartmentIn"},"type":"array"},{"type":"null"}],"title":"Departments"},"department_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Department Ids"}},"type":"object","title":"ProjectVisibilityDepartmentsUpdateIn","description":"Body del PUT del set de departamentos de visibilidad de un proyecto.\n\nReemplaza el set completo (semántica PUT). Cada department_id debe pertenecer\na la MISMA org (422 si no; nunca se confía en el id del cliente). Lista vacía =\nquitar todos los departamentos vinculados.\n\nDos formas MUTUAMENTE EXCLUYENTES (422 si vienen las dos o ninguna):\n- `departments`: [{department_id, access_level}] — con nivel por departamento.\n- `department_ids`: [uuid, …] — forma histórica, equivale a `write` en todos."},"ProjectVisibilityUpdateIn":{"properties":{"visibility":{"type":"string","enum":["organization","restricted"],"title":"Visibility"}},"type":"object","required":["visibility"],"title":"ProjectVisibilityUpdateIn","description":"Body del PUT de visibilidad: {visibility: 'organization'|'restricted'}."},"PromoCodeAdminOut":{"properties":{"code":{"type":"string","title":"Code"},"description":{"type":"string","title":"Description"},"kind":{"type":"string","enum":["free_period","discount"],"title":"Kind"},"plan":{"anyOf":[{"type":"string","const":"pro"},{"type":"null"}],"title":"Plan"},"free_months":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Free Months"},"percent_off":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Percent Off"},"amount_off_minor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Amount Off Minor"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"id":{"type":"string","title":"Id"},"stripe_coupon_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Coupon Id"},"max_redemptions":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Redemptions"},"redeemed_count":{"type":"integer","title":"Redeemed Count"},"starts_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Starts At"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["code","description","kind","id","redeemed_count","active","created_at"],"title":"PromoCodeAdmin","description":"Vista de plataforma: añade lo operativo (id, límites, canjes, estado)."},"PromoCodeCreate":{"properties":{"code":{"type":"string","maxLength":64,"minLength":3,"pattern":"^[A-Za-z0-9._-]+$","title":"Code"},"description":{"type":"string","maxLength":255,"minLength":1,"title":"Description"},"kind":{"type":"string","enum":["free_period","discount"],"title":"Kind"},"plan":{"anyOf":[{"type":"string","const":"pro"},{"type":"null"}],"title":"Plan"},"free_months":{"anyOf":[{"type":"integer","maximum":36.0,"minimum":1.0},{"type":"null"}],"title":"Free Months"},"percent_off":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Percent Off"},"amount_off_minor":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Amount Off Minor"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Currency"},"stripe_coupon_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Stripe Coupon Id"},"max_redemptions":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Redemptions"},"starts_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Starts At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["code","description","kind"],"title":"PromoCodeCreate","description":"Alta de un código promocional (solo platform admin).\n\nLa coherencia entre `kind` y los campos que lo cuantifican se valida en el\nservice (`promo_invalid`), no aquí: el mensaje tiene que explicar qué combina\nmal, y son reglas de negocio, no de forma."},"PromoCodeOut":{"properties":{"code":{"type":"string","title":"Code"},"description":{"type":"string","title":"Description"},"kind":{"type":"string","enum":["free_period","discount"],"title":"Kind"},"plan":{"anyOf":[{"type":"string","const":"pro"},{"type":"null"}],"title":"Plan"},"free_months":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Free Months"},"percent_off":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Percent Off"},"amount_off_minor":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Amount Off Minor"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["code","description","kind"],"title":"PromoCode","description":"Lo que un código promocional concede, para pintarlo antes de pagar.\n\nEs la vista PÚBLICA (la ve cualquier admin de organización con el enlace), así\nque no expone contadores de canjes ni el coupon de Stripe: solo qué da, a qué\nplan aplica y hasta cuándo."},"PromoCodeUpdate":{"properties":{"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"max_redemptions":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Redemptions"}},"type":"object","title":"PromoCodeUpdate","description":"Cambios permitidos tras el alta: desactivar, mover la caducidad o el límite.\n\nLo que un código CONCEDE no se edita: cambiarlo bajo los pies de quien ya\ntiene el enlace repartido es peor que crear otro código."},"PromoRedemptionOut":{"properties":{"organization_id":{"type":"string","title":"Organization Id"},"organization_name":{"type":"string","title":"Organization Name"},"redeemed_at":{"type":"string","format":"date-time","title":"Redeemed At"},"plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan"}},"type":"object","required":["organization_id","organization_name","redeemed_at","plan"],"title":"PromoRedemption","description":"Canje de un código, para el historial del panel de plataforma (PJKT-2039).\n\nLleva `organization_name` desnormalizado: el panel pinta el historial de un\nvistazo sin pedir la ficha de cada organización. `plan` es nullable en el\ncontrato por si algún día el canje deja de estar atado a un checkout con\nplan; hoy siempre viene informado."},"PublicDocumentOut":{"properties":{"title":{"type":"string","title":"Title"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["title","content","updated_at"],"title":"PublicDocument","description":"Vista pública de un documento publicado (sin sesión).\n\nLista BLANCA a propósito, igual que `PublicQuoteOut`: lo que no está escrito\naquí NO sale por el enlace. Quedan fuera, y no por olvido:\n\n- `id`, `organization_id`, `project_id`, `folder_id`, `parent_id` — ids\n  internos; con ellos el enlace sería un mapa de la organización.\n- `created_by` y cualquier nombre de autor — quién lo escribió es un dato de\n  la plantilla de la empresa, no del documento.\n- El nombre de la organización — publicar un documento no obliga a decir qué\n  empresa lo publica.\n- Comentarios e historial de versiones — cuelgan de otros endpoints, todos\n  autenticados, y ninguno de este schema. El historial es especialmente\n  sensible: contiene lo que el documento decía ANTES de corregirlo.\n\nAñadir columnas al modelo no puede colar nada aquí: habría que escribirlo."},"PublicDocumentSignatureOut":{"properties":{"decision":{"type":"string","enum":["approved","rejected"],"title":"Decision"},"method":{"type":"string","enum":["handwritten","digital"],"title":"Method"},"signer_name":{"type":"string","title":"Signer Name"},"signer_email":{"type":"string","title":"Signer Email"},"signed_at":{"type":"string","format":"date-time","title":"Signed At"},"document_sha256":{"type":"string","title":"Document Sha256"}},"type":"object","required":["decision","method","signer_name","signer_email","signed_at","document_sha256"],"title":"PublicDocumentSignature","description":"Comprobante que se le devuelve al firmante.\n\nSin IP, sin agente y sin el sello interno del expediente: el enlace público\nlo abre cualquiera a quien se lo reenvíen. Sí lleva la huella del documento,\nque es lo que permite al cliente comprobar por su cuenta que el PDF que se\ndescargue mañana es el mismo que firmó hoy."},"PublicInvoiceItemOut":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"type":"number","title":"Quantity"},"unit_price":{"type":"number","title":"Unit Price"},"amount":{"type":"number","title":"Amount"}},"type":"object","required":["description","quantity","unit_price","amount"],"title":"PublicInvoiceItem"},"PublicInvoiceOut":{"properties":{"invoice_number":{"type":"string","title":"Invoice Number"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"type":"string","format":"date","title":"Due Date"},"status":{"type":"string","enum":["draft","sent","paid","overdue","cancelled"],"title":"Status"},"currency":{"type":"string","title":"Currency"},"subtotal":{"type":"number","title":"Subtotal"},"tax_rate":{"type":"number","title":"Tax Rate"},"tax_amount":{"type":"number","title":"Tax Amount"},"total":{"type":"number","title":"Total"},"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"org_name":{"type":"string","title":"Org Name"},"org_logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Logo Url"},"locale":{"type":"string","title":"Locale"},"items":{"items":{"$ref":"#/components/schemas/PublicInvoiceItemOut"},"type":"array","title":"Items"}},"type":"object","required":["invoice_number","issue_date","due_date","status","currency","subtotal","tax_rate","tax_amount","total","client_name","client_email","org_name","org_logo_url","locale","items"],"title":"PublicInvoice","description":"Respuesta pública de una factura — no expone ids ni notas internas."},"PublicProfileOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"social_links":{"additionalProperties":{"type":"string"},"type":"object","title":"Social Links","default":{}},"tech_stack":{"items":{"type":"string"},"type":"array","title":"Tech Stack","default":[]},"profile_visibility":{"type":"string","enum":["private","organization","public"],"title":"Profile Visibility"},"followers_count":{"type":"integer","title":"Followers Count"},"following_count":{"type":"integer","title":"Following Count"},"viewer_is_following":{"type":"boolean","title":"Viewer Is Following"},"completed_tasks":{"type":"integer","title":"Completed Tasks"},"active_days":{"type":"integer","title":"Active Days"}},"type":"object","required":["id","name","profile_visibility","followers_count","following_count","viewer_is_following","completed_tasks","active_days"],"title":"PublicProfile","description":"Perfil de otra persona, tal y como lo ve quien lo visita.\n\nLo que NO lleva es tan importante como lo que lleva: **ni email, ni\norganizaciones, ni nombres de proyecto, cliente o tarea**. Los datos de\nrendimiento son números pelados — un desglose convertiría el perfil en una\nlista de nuestros clientes."},"PublicQuoteItemOut":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"type":"number","title":"Quantity"},"unit_price":{"type":"number","title":"Unit Price"},"amount":{"type":"number","title":"Amount"}},"type":"object","required":["description","quantity","unit_price","amount"],"title":"PublicQuoteItem"},"PublicQuoteOut":{"properties":{"quote_number":{"type":"string","title":"Quote Number"},"client_name":{"type":"string","title":"Client Name"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"expires_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expires At"},"status":{"type":"string","enum":["draft","sent","approved","rejected","converted","expired"],"title":"Status"},"items":{"items":{"$ref":"#/components/schemas/PublicQuoteItemOut"},"type":"array","title":"Items"},"subtotal":{"type":"number","title":"Subtotal"},"tax_rate":{"type":"number","title":"Tax Rate"},"tax_amount":{"type":"number","title":"Tax Amount"},"total":{"type":"number","title":"Total"},"currency":{"type":"string","title":"Currency"},"locale":{"type":"string","title":"Locale"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["quote_number","client_name","issue_date","expires_at","status","items","subtotal","tax_rate","tax_amount","total","currency","locale","notes"],"title":"PublicQuote","description":"Respuesta pública de un presupuesto — no expone ids internos ni IP.\n\nLista BLANCA a propósito (no un `exclude`): lo que no está escrito aquí no\nsale por el enlace público. En particular `internal_rejection_notes` —lo que\nel equipo anota sobre el cliente— no tiene campo en este schema, así que no\nhay forma de que se cuele añadiendo columnas al modelo."},"PullRequestSlim":{"properties":{"number":{"type":"integer","title":"Number"},"title":{"type":"string","title":"Title"},"user":{"type":"string","title":"User"},"draft":{"type":"boolean","title":"Draft"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"merged_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Merged At"},"html_url":{"type":"string","title":"Html Url"},"task_refs":{"items":{"type":"string"},"type":"array","title":"Task Refs"},"review_decision":{"anyOf":[{"type":"string","enum":["approved","changes_requested","commented","pending"]},{"type":"null"}],"title":"Review Decision"},"checks_state":{"anyOf":[{"type":"string","enum":["success","failure","pending","none"]},{"type":"null"}],"title":"Checks State"}},"type":"object","required":["number","title","user","draft","html_url","task_refs"],"title":"PullRequestSlim","description":"Pull request slim (open o merged).\n\ntask_refs: referencias de tarea detectadas en el título del PR (ej. ['PJKT-12']).\nMáx. 10 refs por PR; deduplicadas y normalizadas a mayúsculas.\n\nreview_decision / checks_state: solo se pueblan para PRs abiertos (on-demand,\nmisma cache corta). None si rate-limited o error parcial en la sub-consulta;\nel PR se sigue mostrando.\n\nreview_decision: \"approved\" | \"changes_requested\" | \"commented\" | \"pending\" | None.\nchecks_state: \"success\" | \"failure\" | \"pending\" | \"none\" | None."},"PushSubscriptionDeleteIn":{"properties":{"endpoint":{"type":"string","maxLength":500,"title":"Endpoint"}},"type":"object","required":["endpoint"],"title":"PushSubscriptionDeleteIn","description":"Baja de una suscripción por su endpoint."},"PushSubscriptionIn":{"properties":{"endpoint":{"type":"string","maxLength":500,"title":"Endpoint"},"keys":{"$ref":"#/components/schemas/PushSubscriptionKeysIn"},"user_agent":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"User Agent"}},"type":"object","required":["endpoint","keys"],"title":"PushSubscriptionIn","description":"Registro de una suscripción Web Push (shape de `PushSubscription.toJSON()`)."},"PushSubscriptionKeysIn":{"properties":{"p256dh":{"type":"string","maxLength":255,"title":"P256Dh"},"auth":{"type":"string","maxLength":255,"title":"Auth"}},"type":"object","required":["p256dh","auth"],"title":"PushSubscriptionKeysIn","description":"Claves de la suscripción Web Push del navegador (PushSubscription.keys)."},"PushSubscriptionOut":{"properties":{"endpoint":{"type":"string","title":"Endpoint"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["endpoint","created_at"],"title":"PushSubscription","description":"Confirmación de una suscripción registrada (sin claves)."},"QueueStatusOut":{"properties":{"name":{"type":"string","title":"Name"},"depth":{"type":"integer","title":"Depth"},"delayed":{"type":"integer","title":"Delayed"},"dead":{"type":"integer","title":"Dead"}},"type":"object","required":["name","depth","delayed","dead"],"title":"QueueStatus","description":"Estado completo de una cola Dramatiq (PJKT-2027).\n\nSustituye a la vista PlatformQueueDepth en la página de colas: añade los\nmensajes diferidos (`.DQ`) que aquella no cuenta. PlatformQueueDepth se\nconserva porque su shape es estable dentro del agregado de /platform/health."},"QuoteApproveIn":{"properties":{"signer_name":{"type":"string","title":"Signer Name"},"signer_email":{"type":"string","title":"Signer Email"}},"type":"object","required":["signer_name","signer_email"],"title":"QuoteApproveIn","description":"Cuerpo de la aprobación pública: identidad del firmante.\n\n`signer_email` es OBLIGATORIO (antes opcional). Sin él no hay a dónde mandar\nel acuse de recibo de una firma que compromete un importe, y el expediente se\nqueda con un nombre escrito a mano y ningún dato de contacto verificable."},"QuoteConvertIn":{"properties":{"deposit_percent":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Deposit Percent"}},"type":"object","title":"QuoteConvertIn","description":"Cuerpo de POST …/convert. Si `deposit_percent` viene, la factura es solo\nese % del total (una sola línea 'Depósito X%'); si no, se copian todas las\nlíneas del presupuesto tal cual."},"QuoteCreateIn":{"properties":{"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"expires_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expires At"},"items":{"items":{"$ref":"#/components/schemas/QuoteItemIn"},"type":"array","title":"Items"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Tax Rate","default":"0"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"deposit_percent":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Deposit Percent"}},"type":"object","required":["client_name","issue_date","items"],"title":"QuoteCreateIn"},"QuoteItemIn":{"properties":{"description":{"type":"string","title":"Description"},"quantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Quantity"},"unit_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Unit Price"}},"type":"object","required":["description","quantity","unit_price"],"title":"QuoteItemIn"},"QuoteItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"description":{"type":"string","title":"Description"},"quantity":{"type":"number","title":"Quantity"},"unit_price":{"type":"number","title":"Unit Price"},"amount":{"type":"number","title":"Amount"},"position":{"type":"integer","title":"Position"}},"type":"object","required":["id","description","quantity","unit_price","amount","position"],"title":"QuoteItem"},"QuoteOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"quote_number":{"type":"string","title":"Quote Number"},"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"status":{"type":"string","enum":["draft","sent","approved","rejected","converted","expired"],"title":"Status"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"expires_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expires At"},"subtotal":{"type":"number","title":"Subtotal"},"tax_rate":{"type":"number","title":"Tax Rate"},"tax_amount":{"type":"number","title":"Tax Amount"},"total":{"type":"number","title":"Total"},"currency":{"type":"string","title":"Currency"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"deposit_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Deposit Percent"},"converted_invoice_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Converted Invoice Id"},"approved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Approved At"},"approved_signer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Approved Signer Name"},"approved_signer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Approved Signer Email"},"approval_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Approval Ip"},"signature_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature Hash"},"rejected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rejected At"},"rejected_signer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejected Signer Name"},"rejected_signer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejected Signer Email"},"rejection_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Ip"},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"},"internal_rejection_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Internal Rejection Notes"},"items":{"items":{"$ref":"#/components/schemas/QuoteItemOut"},"type":"array","title":"Items"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","quote_number","client_name","client_email","status","issue_date","expires_at","subtotal","tax_rate","tax_amount","total","currency","notes","items","created_at","updated_at"],"title":"Quote"},"QuoteProjectCreateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"key":{"anyOf":[{"type":"string","maxLength":6,"minLength":2},{"type":"null"}],"title":"Key"}},"type":"object","title":"QuoteProjectCreateIn","description":"Body (opcional) al abrir el proyecto de un presupuesto aceptado.\n\nTodo es opcional porque el caso normal es no decidir nada: el servidor deriva\nel nombre del presupuesto y el cliente del propio presupuesto. `key` NO se\nvalida aquí a propósito —su regla (2–6 alfanuméricos ASCII, empieza por letra)\nvive en `ProjectCreateIn`, que es el alta real; copiarla aquí sería tener dos\ndefiniciones de la misma regla que acabarían divergiendo—."},"QuoteRejectIn":{"properties":{"signer_name":{"type":"string","title":"Signer Name"},"signer_email":{"type":"string","title":"Signer Email"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["signer_name","signer_email"],"title":"QuoteRejectIn","description":"Cuerpo del rechazo público: hermano exacto de `QuoteApproveIn` + motivo.\n\n`reason` es lo que escribe EL CLIENTE. No confundir con\n`internal_rejection_notes`, que la escribe el equipo y jamás sale de dentro."},"QuoteShareCreateIn":{"properties":{"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days"}},"type":"object","title":"QuoteShareCreateIn","description":"Cuerpo de POST …/share. `expires_in_days` = None → nunca expira."},"QuoteShareOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"quote_id":{"type":"string","format":"uuid","title":"Quote Id"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url"}},"type":"object","required":["id","quote_id","expires_at","created_at"],"title":"QuoteShareToken"},"QuoteUpdateIn":{"properties":{"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Address"},"client_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Tax Id"},"issue_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Issue Date"},"expires_at":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expires At"},"items":{"anyOf":[{"items":{"$ref":"#/components/schemas/QuoteItemIn"},"type":"array"},{"type":"null"}],"title":"Items"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"deposit_percent":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Deposit Percent"},"internal_rejection_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Internal Rejection Notes"}},"type":"object","title":"QuoteUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes.\n\n`issue_date`, `expires_at`, `items`, `tax_rate` y `deposit_percent` solo\nson editables mientras el presupuesto está en BORRADOR (el service devuelve\n422 en cualquier otro estado). Al cambiar `items`/`tax_rate` se recalculan\nsubtotal, cuota y total y se REEMPLAZAN las líneas."},"ReactionIn":{"properties":{"emoji":{"type":"string","maxLength":32,"minLength":1,"title":"Emoji"}},"type":"object","required":["emoji"],"title":"ChatReactionIn"},"ReactionOut":{"properties":{"emoji":{"type":"string","title":"Emoji"},"count":{"type":"integer","title":"Count"},"mine":{"type":"boolean","title":"Mine"}},"type":"object","required":["emoji","count","mine"],"title":"ChatReaction","description":"Reacción agregada por emoji: cuántos + si yo reaccioné."},"RecoveryCodesOut":{"properties":{"recovery_codes":{"items":{"type":"string"},"type":"array","title":"Recovery Codes"}},"type":"object","required":["recovery_codes"],"title":"RecoveryCodes","description":"Los códigos de recuperación EN CLARO. Solo se devuelven al emitirlos."},"RecurringExpenseCreateIn":{"properties":{"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"description":{"type":"string","title":"Description"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Amount"},"frequency":{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"],"title":"Frequency"},"next_run_date":{"type":"string","format":"date","title":"Next Run Date"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","required":["category","description","amount","frequency","next_run_date"],"title":"RecurringExpenseCreateIn"},"RecurringExpenseOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"category":{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"],"title":"Category"},"description":{"type":"string","title":"Description"},"amount":{"type":"number","title":"Amount"},"currency":{"type":"string","title":"Currency"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tax Rate"},"frequency":{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"],"title":"Frequency"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"next_run_date":{"type":"string","format":"date","title":"Next Run Date"},"last_run_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Last Run Date"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","category","description","amount","currency","vendor","tax_rate","frequency","day_of_month","next_run_date","last_run_date","is_active","created_at","updated_at"],"title":"RecurringExpense"},"RecurringExpenseUpdateIn":{"properties":{"category":{"anyOf":[{"type":"string","enum":["office","travel","software","marketing","payroll","other","hardware","hosting","telecom","subscriptions","professional_services","taxes","insurance","banking","supplies","training","meals","rent","utilities","shipping","legal","advertising","maintenance","fees"]},{"type":"null"}],"title":"Category"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Amount"},"frequency":{"anyOf":[{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"]},{"type":"null"}],"title":"Frequency"},"next_run_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Next Run Date"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"}},"type":"object","title":"RecurringExpenseUpdateIn","description":"PATCH parcial."},"RecurringInvoiceCreateIn":{"properties":{"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"items":{"items":{"$ref":"#/components/schemas/InvoiceItemIn-Input"},"type":"array","title":"Items"},"frequency":{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"],"title":"Frequency"},"next_run_date":{"type":"string","format":"date","title":"Next Run Date"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Tax Rate","default":"21"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","required":["client_name","items","frequency","next_run_date"],"title":"RecurringInvoiceCreateIn"},"RecurringInvoiceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"client_name":{"type":"string","title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"tax_rate":{"type":"number","title":"Tax Rate"},"currency":{"type":"string","title":"Currency"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"items":{"items":{"$ref":"#/components/schemas/InvoiceItemIn-Output"},"type":"array","title":"Items"},"frequency":{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"],"title":"Frequency"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"next_run_date":{"type":"string","format":"date","title":"Next Run Date"},"last_run_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Last Run Date"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","client_name","client_email","tax_rate","currency","notes","items","frequency","day_of_month","next_run_date","last_run_date","is_active","created_at","updated_at"],"title":"RecurringInvoice"},"RecurringInvoiceUpdateIn":{"properties":{"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"client_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Email"},"items":{"anyOf":[{"items":{"$ref":"#/components/schemas/InvoiceItemIn-Input"},"type":"array"},{"type":"null"}],"title":"Items"},"frequency":{"anyOf":[{"type":"string","enum":["weekly","biweekly","monthly","quarterly","yearly"]},{"type":"null"}],"title":"Frequency"},"next_run_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Next Run Date"},"tax_rate":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax Rate"},"day_of_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day Of Month"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"}},"type":"object","title":"RecurringInvoiceUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes."},"RegisterIn":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"name":{"type":"string","title":"Name"}},"type":"object","required":["email","password","name"],"title":"RegisterIn"},"ReminderCreateIn":{"properties":{"text":{"type":"string","maxLength":500,"minLength":1,"title":"Text"},"remind_at":{"type":"string","format":"date-time","title":"Remind At"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"}},"additionalProperties":false,"type":"object","required":["text","remind_at"],"title":"ReminderCreate","description":"Cuerpo de creación. Sin `user_id`: el dueño sale de la sesión."},"ReminderOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"text":{"type":"string","title":"Text"},"remind_at":{"type":"string","format":"date-time","title":"Remind At"},"done":{"type":"boolean","title":"Done"},"done_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Done At"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","user_id","text","remind_at","done","done_at","task_id","client_id","created_at","updated_at"],"title":"Reminder"},"ReminderUpdateIn":{"properties":{"text":{"anyOf":[{"type":"string","maxLength":500,"minLength":1},{"type":"null"}],"title":"Text"},"remind_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Remind At"},"done":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Done"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"}},"additionalProperties":false,"type":"object","title":"ReminderUpdate","description":"Cuerpo de actualización parcial: lo que no viene, no se toca.\n\nSemántica por campo, y la asimetría es a propósito (mismo criterio que\n`VaultItemUpdateIn`):\n\n- `text`/`remind_at`/`done`: omitido **o `null`** = sin cambio. Sus columnas\n  son NOT NULL, así que un `null` explícito no puede significar «bórralo» —\n  significaría una violación de integridad, o sea un 500 donde el usuario\n  esperaba un 422. Lo descarta el service.\n- `task_id`/`client_id`: omitido = sin cambio; `null` EXPLÍCITO desengancha.\n  Aquí sí son dos cosas distintas y las dos hacen falta, y `model_fields_set`\n  (vía `exclude_unset`) es lo que las separa.\n\n`done_at` no está aquí a propósito (lo sella el service) y `user_id` tampoco:\nun recordatorio no cambia de dueño, se borra y se hace otro."},"RepoActivity":{"properties":{"repo_full_name":{"type":"string","title":"Repo Full Name"},"stats":{"$ref":"#/components/schemas/RepoStats"},"commits":{"items":{"$ref":"#/components/schemas/CommitSlim"},"type":"array","title":"Commits"},"open_prs":{"items":{"$ref":"#/components/schemas/PullRequestSlim"},"type":"array","title":"Open Prs"},"merged_prs":{"items":{"$ref":"#/components/schemas/PullRequestSlim"},"type":"array","title":"Merged Prs"}},"type":"object","required":["repo_full_name","stats","commits","open_prs","merged_prs"],"title":"RepoActivity","description":"Actividad de un repo vinculado: commits + PRs open + PRs merged + stats."},"RepoStats":{"properties":{"stars":{"type":"integer","title":"Stars"},"open_issues":{"type":"integer","title":"Open Issues"},"default_branch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Branch"},"pushed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pushed At"}},"type":"object","required":["stars","open_issues"],"title":"RepoStats","description":"Métricas rápidas de un repo."},"RequestTypeCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_type":{"type":"string","enum":["task","bug","chore"],"title":"Task Type","default":"task"},"default_priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Default Priority","default":"medium"},"active":{"type":"boolean","title":"Active","default":true},"position":{"type":"integer","title":"Position","default":0},"fields":{"items":{"$ref":"#/components/schemas/RequestTypeFieldIn"},"type":"array","title":"Fields","default":[]}},"type":"object","required":["name"],"title":"RequestTypeCreateIn"},"RequestTypeFieldIn":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"required":{"type":"boolean","title":"Required","default":false},"position":{"type":"integer","title":"Position","default":0}},"type":"object","required":["field_def_id"],"title":"RequestTypeFieldIn","description":"Un campo del formulario: se REFERENCIA un campo personalizado que ya\nexiste en la organización (`custom_field_defs`), no se define uno nuevo."},"RequestTypeFieldOut":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"name":{"type":"string","title":"Name"},"field_type":{"type":"string","enum":["text","number","date","select","url","email"],"title":"Field Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"required":{"type":"boolean","title":"Required"},"position":{"type":"integer","title":"Position"}},"type":"object","required":["field_def_id","name","field_type","options","required","position"],"title":"RequestTypeField"},"RequestTypeOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"task_type":{"type":"string","enum":["task","bug","chore"],"title":"Task Type"},"default_priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Default Priority"},"active":{"type":"boolean","title":"Active"},"position":{"type":"integer","title":"Position"},"fields":{"items":{"$ref":"#/components/schemas/RequestTypeFieldOut"},"type":"array","title":"Fields"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","description","project_id","task_type","default_priority","active","position","fields","created_at","updated_at"],"title":"RequestType"},"RequestTypeUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_type":{"anyOf":[{"type":"string","enum":["task","bug","chore"]},{"type":"null"}],"title":"Task Type"},"default_priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Default Priority"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"},"fields":{"anyOf":[{"items":{"$ref":"#/components/schemas/RequestTypeFieldIn"},"type":"array"},{"type":"null"}],"title":"Fields"}},"type":"object","title":"RequestTypeUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes.\n\n`fields` omitido deja el formulario como está; `fields: []` lo vacía."},"RevenueByClientRowOut":{"properties":{"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"currency":{"type":"string","title":"Currency","default":"EUR"},"invoice_count":{"type":"integer","title":"Invoice Count"},"total":{"type":"number","title":"Total"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["client_id","client_name","invoice_count","total","criterio"],"title":"RevenueByClientRow","description":"Fila del agregado de ingresos por cliente."},"RoadmapItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"start":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start"},"end":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End"},"task_count":{"type":"integer","title":"Task Count"},"done_count":{"type":"integer","title":"Done Count"},"closed_count":{"type":"integer","title":"Closed Count"},"completion_pct":{"type":"number","title":"Completion Pct"}},"type":"object","required":["id","name","start","end","task_count","done_count","closed_count","completion_pct"],"title":"RoadmapItem","description":"One project row in the org-level roadmap timeline."},"SavedFilterCreateIn":{"properties":{"name":{"type":"string","maxLength":255,"title":"Name"},"entity":{"type":"string","maxLength":50,"title":"Entity"},"query":{"additionalProperties":true,"type":"object","title":"Query"}},"type":"object","required":["name","entity","query"],"title":"SavedFilterCreateIn"},"SavedFilterOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"entity":{"type":"string","title":"Entity"},"query":{"additionalProperties":true,"type":"object","title":"Query"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","user_id","name","entity","query","created_at","updated_at"],"title":"SavedFilter"},"SavedFilterUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"query":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Query"}},"type":"object","title":"SavedFilterUpdateIn"},"SchedulesOverviewItemOut":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"entries":{"items":{"$ref":"#/components/schemas/WorkScheduleOut"},"type":"array","title":"Entries"},"weekly_hours":{"type":"number","title":"Weekly Hours"}},"type":"object","required":["employee_id","entries","weekly_hours"],"title":"SchedulesOverviewItem","description":"Un empleado en la vista de resumen org-wide."},"SearchHit":{"properties":{"type":{"type":"string","enum":["task","project","client","invoice","document","employee","expense","supplier","member"],"title":"Type"},"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle"},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reference"},"url_hint":{"type":"string","title":"Url Hint"}},"type":"object","required":["type","id","title","url_hint"],"title":"SearchHit","description":"Un resultado de búsqueda global.\n\n`type` discrimina la entidad: task, project, client, invoice, document,\nemployee, expense, supplier, member.\n`reference` es la clave legible \"KEY-N\" (solo tasks; None para el resto).\n`url_hint` es una ruta relativa sugerida que el frontend puede usar\ndirectamente (e.g. `/projects/{pid}/tasks/{tid}`)."},"SessionOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"current":{"type":"boolean","title":"Current"}},"type":"object","required":["id","ip_address","user_agent","last_used_at","created_at","expires_at","current"],"title":"Session","description":"Sesión/dispositivo conectado del usuario autenticado (GET /me/sessions).\n\nUn refresh token vivo (no revocado ni caducado), enmascarado: NUNCA expone\nel token ni su hash, solo metadata de dispositivo. `current` marca la sesión\nde ESTA petición (el refresh de la cookie con la que llega la llamada), para\nque el frontend la etiquete como «este dispositivo» y evite auto-revocarla\npor accidente. `ip_address`/`user_agent` son `null` en sesiones legacy o\ncuando la request original no los aportó."},"SetTaskCustomFieldsIn":{"properties":{"fields":{"items":{"$ref":"#/components/schemas/CustomFieldValueIn"},"type":"array","title":"Fields"}},"type":"object","required":["fields"],"title":"SetTaskCustomFieldsIn","description":"Bulk upsert: {fields: [{def_id, value}, ...]}."},"SetTaskTagsIn":{"properties":{"tag_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Tag Ids"}},"type":"object","required":["tag_ids"],"title":"SetTaskTagsIn","description":"Body de PUT .../tags: fija el conjunto exacto de tags de la tarea."},"SetupProgressIn":{"properties":{"version":{"type":"integer","title":"Version"},"status":{"type":"string","enum":["in_progress","dismissed","completed"],"title":"Status"},"plan":{"anyOf":[{"type":"string","enum":["free","pro"]},{"type":"null"}],"title":"Plan"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step"},"completed_steps":{"items":{"type":"string"},"type":"array","title":"Completed Steps"},"skipped_steps":{"items":{"type":"string"},"type":"array","title":"Skipped Steps"},"answers":{"additionalProperties":{"type":"string"},"type":"object","title":"Answers"}},"type":"object","required":["version","status","plan","organization_id","current_step","completed_steps","skipped_steps","answers"],"title":"SetupProgressIn","description":"Progreso del asistente tal y como lo envía el frontend (sustitución entera).\n\nClaves de paso y respuestas son OPACAS para el backend; aquí solo se acotan\n(strip, sin duplicados, topes de longitud/cantidad) para no persistir basura."},"SetupProgressOut":{"properties":{"version":{"type":"integer","title":"Version"},"status":{"type":"string","enum":["in_progress","dismissed","completed"],"title":"Status"},"plan":{"anyOf":[{"type":"string","enum":["free","pro"]},{"type":"null"}],"title":"Plan"},"organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Organization Id"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step"},"completed_steps":{"items":{"type":"string"},"type":"array","title":"Completed Steps"},"skipped_steps":{"items":{"type":"string"},"type":"array","title":"Skipped Steps"},"answers":{"additionalProperties":{"type":"string"},"type":"object","title":"Answers"}},"type":"object","required":["version","status","plan","organization_id","current_step","completed_steps","skipped_steps","answers"],"title":"SetupProgress","description":"SetupProgress del contrato: progreso del asistente de puesta en marcha.\n\nSolo INTENCIÓN del usuario (dónde se quedó, qué saltó, qué respondió y con\nqué plan se generó el guion). Lo que de verdad existe —organización, logo,\nproyecto, cliente…— NO se guarda aquí: se deriva de los datos reales, que es\nlo único que no miente si la org se montó por API o MCP."},"SharedProjectDetailOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","enum":["active","archived"],"title":"Status"},"owner_org_id":{"type":"string","format":"uuid","title":"Owner Org Id"},"permission":{"type":"string","enum":["read","read_comment","full"],"title":"Permission"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","description","status","owner_org_id","permission","created_at"],"title":"SharedProjectDetail","description":"Proyecto compartido (lectura) enriquecido con dueño + permiso efectivo."},"SharedProjectOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"owner_org_id":{"type":"string","format":"uuid","title":"Owner Org Id"},"owner_org_name":{"type":"string","title":"Owner Org Name"},"permission":{"type":"string","enum":["read","read_comment","full"],"title":"Permission"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"status":{"type":"string","enum":["active","archived"],"title":"Status"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["id","name","owner_org_id","owner_org_name","permission","status"],"title":"SharedProject","description":"Proyecto de OTRA org compartido con la org actual (lado destinatario).\n\nEnriquecido con logo/estado/descripción para pintar la card igual que las\npropias (mismo icono/avatar)."},"SharedTaskCommentCreateIn":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"SharedTaskCommentCreateIn","description":"Comentario en una tarea de un proyecto COMPARTIDO: solo `body`.\n\nLa escritura cross-tenant no acepta hilo ni visibilidad: el service crea el\ncomentario con `parent_id=None` e `internal` por defecto, y el contrato\ndeclara un cuerpo de una sola propiedad.\n\nEl endpoint recibía `TaskCommentCreateIn`, así que ADMITÍA `parent_id` y\n`visibility` y los descartaba en silencio: responder «en el hilo de» desde la\norganización invitada devolvía 201 con un comentario en la raíz, y pedir\n`visibility: public` devolvía 201 con un comentario interno. Declarar solo lo\nque se aplica es lo único que no miente (mismo criterio que\n`SharedTaskUpdateIn`)."},"SharedTaskUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"}},"type":"object","title":"SharedTaskUpdate","description":"PATCH de una tarea de un proyecto COMPARTIDO: los CINCO campos del contrato.\n\n── Por qué no se reusa `TaskUpdateIn` ────────────────────────────────────\n\nLa escritura cross-tenant es acotada a propósito: el service solo aplica\n`status`, `priority`, `assignee_id`, `title` y `description`\n(`crossorg/shared_write.py::_SHARED_WRITE_UPDATE_FIELDS`), y el contrato lo\ndeclara así desde el principio (`SharedTaskUpdate`, cinco propiedades).\n\nEl endpoint, en cambio, recibía `TaskUpdateIn` —catorce campos—, así que\naceptaba con 200 nueve que el service tira acto seguido: `sprint_id`,\n`parent_id`, `type`, `story_points`, `estimated_hours` y las cuatro fechas.\nMover una tarea compartida de sprint por esta vía «funcionaba» y no hacía\nnada. Es el mismo agujero que el `cost_center_id` de la wave 10 —un campo que\nno funciona por una vía y nadie lo nota— con el final silencioso en vez del\n500: nadie lo ejercitaba porque el SDK, generado del contrato, nunca pudo\nenviarlo.\n\nCon este schema el API declara lo que de verdad aplica. Los campos siguen\nignorándose si llegan (Pydantic descarta lo no declarado, como dice el propio\ncontrato), pero ya no se anuncian.\n\n── `parent_id`: por qué falta y qué costaría añadirlo (PJKT-2421) ────────\n\nEl ALTA de tarea compartida SÍ acepta padre, así que la asimetría —se puede\ncrear colgando de una épica y no se puede mover nunca— parece un olvido. No\nlo es: `crossorg/shared_write.py::_validate_parent` valida «mismo proyecto\nhost» pero NO valida ciclos, porque en un alta no puede haberlos. Reutilizarlo\npara un re-parent dejaría a la org INVITADA cerrar un ciclo en la jerarquía\nde la org dueña. Abrirlo es portar `TaskService._ensure_no_parent_cycle` a la\nruta cross-tenant, con su test de aislamiento entre organizaciones; el motivo\nentero está al lado de `_SHARED_WRITE_UPDATE_FIELDS`."},"SignatureCodeSentOut":{"properties":{"email_hint":{"type":"string","title":"Email Hint"},"expires_in_seconds":{"type":"integer","title":"Expires In Seconds"}},"type":"object","required":["email_hint","expires_in_seconds"],"title":"SignatureCodeSent","description":"Confirmación del envío del código (con el correo enmascarado)."},"SlaPauseSpan":{"properties":{"category":{"type":"string","title":"Category"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"ended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ended At"}},"type":"object","required":["category","started_at","ended_at"],"title":"SlaPauseSpan","description":"Un tramo en el que el reloj estuvo parado (la defensa del incumplimiento)."},"SlaPolicyCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"target_kind":{"type":"string","enum":["first_response","resolution"],"title":"Target Kind"},"target_minutes":{"type":"integer","maximum":100000.0,"minimum":1.0,"title":"Target Minutes"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"task_type":{"anyOf":[{"type":"string","enum":["epic","story","task","bug","spike","chore"]},{"type":"null"}],"title":"Task Type"},"start_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array"},{"type":"null"}],"title":"Start Categories"},"stop_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array"},{"type":"null"}],"title":"Stop Categories"},"extra_paused_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress"]},"type":"array"},{"type":"null"}],"title":"Extra Paused Categories"},"warn_at_percent":{"anyOf":[{"type":"integer","maximum":99.0,"minimum":1.0},{"type":"null"}],"title":"Warn At Percent"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["name","target_kind","target_minutes"],"title":"SlaPolicyCreateIn","description":"Alta de una política. Los criterios omitidos significan «cualquiera»."},"SlaPolicyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"target_kind":{"type":"string","enum":["first_response","resolution"],"title":"Target Kind"},"target_minutes":{"type":"integer","title":"Target Minutes"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"task_type":{"anyOf":[{"type":"string","enum":["epic","story","task","bug","spike","chore"]},{"type":"null"}],"title":"Task Type"},"start_categories":{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array","title":"Start Categories"},"stop_categories":{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array","title":"Stop Categories"},"extra_paused_categories":{"items":{"type":"string","enum":["todo","in_progress"]},"type":"array","title":"Extra Paused Categories"},"warn_at_percent":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Warn At Percent"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","target_kind","target_minutes","project_id","priority","task_type","start_categories","stop_categories","extra_paused_categories","warn_at_percent","active","created_at","updated_at"],"title":"SlaPolicy","description":"Política tal y como la devuelve el API (condiciones ya resueltas).\n\n`start_categories`/`stop_categories` salen SIEMPRE con las categorías\nefectivas, no con el `null` que significa «las de por defecto»: quien lee la\npolítica tiene que poder ver cuándo arranca y cuándo para su reloj sin\nconocerse los defaults del motor."},"SlaPolicyUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"target_minutes":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":1.0},{"type":"null"}],"title":"Target Minutes"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"task_type":{"anyOf":[{"type":"string","enum":["epic","story","task","bug","spike","chore"]},{"type":"null"}],"title":"Task Type"},"start_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array"},{"type":"null"}],"title":"Start Categories"},"stop_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"]},"type":"array"},{"type":"null"}],"title":"Stop Categories"},"extra_paused_categories":{"anyOf":[{"items":{"type":"string","enum":["todo","in_progress"]},"type":"array"},{"type":"null"}],"title":"Extra Paused Categories"},"warn_at_percent":{"anyOf":[{"type":"integer","maximum":99.0,"minimum":1.0},{"type":"null"}],"title":"Warn At Percent"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"SlaPolicyUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes en el body.\n\nLos criterios de alcance (`project_id`, `priority`, `task_type`) y las\ncondiciones SÍ admiten `null` explícito: es como se ensancha una política a\n«cualquier proyecto» o se vuelve a las condiciones por defecto del objetivo."},"SprintBurndownOut":{"properties":{"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"total_points":{"type":"integer","title":"Total Points"},"days":{"items":{"$ref":"#/components/schemas/BurndownPointOut"},"type":"array","title":"Days"}},"type":"object","required":["start_date","end_date","total_points","days"],"title":"SprintBurndown","description":"Burndown calculado del sprint (sin tabla de snapshots).\n\n`total_points` = Σ story_points de las tareas del sprint (null cuenta como 0).\n`days` va vacío si al sprint le faltan start_date o end_date."},"SprintCompleteOut":{"properties":{"sprint":{"$ref":"#/components/schemas/SprintOut"},"stats":{"$ref":"#/components/schemas/SprintStatsOut"},"rolled_over_task_count":{"type":"integer","title":"Rolled Over Task Count"},"rollover_target_sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Rollover Target Sprint Id"}},"type":"object","required":["sprint","stats","rolled_over_task_count","rollover_target_sprint_id"],"title":"SprintComplete","description":"Resultado de cerrar un sprint (W8 — G11).\n\n`stats` son las del sprint YA cerrado: solo quedan tareas `done`/\n`cancelled` (las incompletas se movieron), así que están congeladas hasta\nque alguien reasigne tareas a mano. `rollover_target_sprint_id` es `null`\ncuando el destino fue el backlog (sin sprint) por no haber otro sprint\n`active` en el proyecto."},"SprintCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Goal"},"status":{"type":"string","enum":["planning","active","completed"],"title":"Status","default":"planning"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"}},"type":"object","required":["name"],"title":"SprintCreateIn"},"SprintOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Goal"},"status":{"type":"string","enum":["planning","active","completed"],"title":"Status"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","organization_id","name","goal","status","start_date","end_date","created_at","updated_at"],"title":"Sprint","description":"Sprint del contrato: todos los campos siempre presentes (null cuando no hay)."},"SprintStatsOut":{"properties":{"total_tasks":{"type":"integer","title":"Total Tasks"},"done_tasks":{"type":"integer","title":"Done Tasks"},"total_points":{"type":"integer","title":"Total Points"},"done_points":{"type":"integer","title":"Done Points"},"by_status":{"items":{"$ref":"#/components/schemas/StatusCountOut"},"type":"array","title":"By Status"}},"type":"object","required":["total_tasks","done_tasks","total_points","done_points","by_status"],"title":"SprintStats","description":"Agregados de un sprint para tarjetas de resumen."},"SprintUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"goal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Goal"},"status":{"anyOf":[{"type":"string","enum":["planning","active","completed"]},{"type":"null"}],"title":"Status"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"}},"type":"object","title":"SprintUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes en el body.\n\n`name`/`status` no admiten null explícito. `goal`, `start_date` y `end_date`\nsí: null los limpia. `exclude_unset` en el service distingue \"omitido\" (no\ntoca) de \"null\" (borra)."},"SprintVelocityOut":{"properties":{"sprints":{"items":{"$ref":"#/components/schemas/VelocitySprintOut"},"type":"array","title":"Sprints"},"average_points":{"type":"number","title":"Average Points"},"average_tasks":{"type":"number","title":"Average Tasks"}},"type":"object","required":["sprints","average_points","average_tasks"],"title":"SprintVelocity","description":"Histórico de velocidad del equipo (W8 — G11).\n\nUn punto por sprint `completed` del proyecto, en orden cronológico\nascendente, más el promedio simple (media móvil si se pidió `limit`).\nLos sprints en `planning`/`active` no cuentan: aún no han cerrado."},"StageCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"},"probability":{"type":"integer","title":"Probability","default":0},"is_won":{"type":"boolean","title":"Is Won","default":false},"is_lost":{"type":"boolean","title":"Is Lost","default":false}},"type":"object","required":["name"],"title":"StageCreateIn"},"StageOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"pipeline_id":{"type":"string","format":"uuid","title":"Pipeline Id"},"name":{"type":"string","title":"Name"},"position":{"type":"integer","title":"Position"},"probability":{"type":"integer","title":"Probability"},"is_won":{"type":"boolean","title":"Is Won"},"is_lost":{"type":"boolean","title":"Is Lost"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","pipeline_id","name","position","probability","is_won","is_lost","created_at"],"title":"Stage"},"StageUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position"},"probability":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Probability"},"is_won":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Won"},"is_lost":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Lost"}},"type":"object","title":"StageUpdateIn","description":"PATCH parcial: solo se aplican campos presentes."},"StatusCountOut":{"properties":{"status":{"type":"string","enum":["todo","in_progress","done","cancelled"],"title":"Status"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["status","count"],"title":"SprintStatusCount","description":"Recuento de tareas del sprint en UNO de los cuatro estados base.\n\n`status` NO es el `status` abierto de una tarea (que admite estados\npersonalizados de columnas de tablero): `_compute_stats` emite exactamente\n`_STATUS_ORDER` —los cuatro de `TaskStatus`, en orden— y nada más, así que\naquí el conjunto sí es cerrado y se tipa. Mientras era `str`, el enum que el\ncontrato declaraba para esta posición se quedaba sin respaldo… y declaraba\nTRES valores cuando el API manda los cuatro en cada respuesta."},"StatusIn":{"properties":{"status":{"type":"string","enum":["online","away","busy","offline"],"title":"Status"}},"type":"object","required":["status"],"title":"ChatStatusIn","description":"Estado que el usuario fija para sí mismo (P4)."},"StatusTimeEntry":{"properties":{"status":{"type":"string","title":"Status"},"seconds":{"type":"integer","title":"Seconds"},"category":{"type":"string","enum":["todo","in_progress","in_review","blocked","done","cancelled"],"title":"Category"},"accountable":{"type":"boolean","title":"Accountable"}},"type":"object","required":["status","seconds","category","accountable"],"title":"StatusTimeEntry","description":"Tiempo acumulado en un estado concreto (segundos)."},"StatusTimeOut":{"properties":{"task_id":{"type":"string","format":"uuid","title":"Task Id"},"entries":{"items":{"$ref":"#/components/schemas/StatusTimeEntry"},"type":"array","title":"Entries"},"total_seconds":{"type":"integer","title":"Total Seconds"},"accountable_seconds":{"type":"integer","title":"Accountable Seconds"}},"type":"object","required":["task_id","entries","total_seconds","accountable_seconds"],"title":"StatusTime","description":"Desglose de tiempo-en-estado de una tarea (PJKT-1765).\n\n`entries` incluye estados personalizados; el estado actual cuenta hasta ahora.\n`total_seconds` es la suma de todos los tramos.\n\nEl reloj se PARA en los estados terminales: sus tramos aportan 0 segundos, de\nmodo que el total de una tarea cerrada no crece indefinidamente (y si se\nreabre, el rato que estuvo cerrada tampoco cuenta). El porqué, en\n`tasks/status_time.py`.\n\n`accountable_seconds` es el subconjunto imputable: excluye los tramos en\nrevisión y bloqueado, donde la tarea estaba esperando a otro. Es el número\nque debe usar cualquier medición de duración o de SLA (regla y porqué en\n`app/shared/task_categories.py`)."},"StepUpCodeIn":{"properties":{"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"StepUpCodeIn","description":"Cuerpo de confirmación por código (TOTP/recovery o código de email)."},"StepUpMethodsOut":{"properties":{"passkey":{"type":"boolean","title":"Passkey"},"totp":{"type":"boolean","title":"Totp"},"email":{"type":"boolean","title":"Email"}},"type":"object","required":["passkey","totp","email"],"title":"StepUpMethods","description":"Factores que el usuario puede usar para confirmar. `email` siempre true\n(fallback universal); `passkey`/`totp` según lo que tenga configurado."},"StepUpPasskeyCompleteIn":{"properties":{"credential":{"additionalProperties":true,"type":"object","title":"Credential"}},"type":"object","required":["credential"],"title":"StepUpPasskeyCompleteIn","description":"Respuesta de la assertion WebAuthn (`PublicKeyCredential.toJSON()`)."},"StepUpResultOut":{"properties":{"confirmed":{"type":"boolean","title":"Confirmed"},"method":{"type":"string","enum":["passkey","totp","email"],"title":"Method"},"expires_in":{"type":"integer","title":"Expires In"}},"type":"object","required":["confirmed","method","expires_in"],"title":"StepUpResult","description":"Confirmación realizada: qué factor y cuántos segundos dura la ventana."},"StoreAppCapabilityOut":{"properties":{"capacidad":{"type":"string","title":"Capacidad"},"tramo":{"type":"string","enum":["gratis","premium"],"title":"Tramo"},"descripcion":{"type":"string","title":"Descripcion"}},"type":"object","required":["capacidad","tramo","descripcion"],"title":"StoreAppCapability","description":"Una capacidad con su tramo: lo que pinta la ficha y lo que mira el gate."},"StoreAppForOrganizationOut":{"properties":{"key":{"type":"string","title":"Key"},"nombre":{"type":"string","title":"Nombre"},"descripcion":{"type":"string","title":"Descripcion"},"icono":{"type":"string","title":"Icono"},"modelo":{"type":"string","enum":["free","freemium","paid","addon"],"title":"Modelo"},"addon_de":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Addon De"},"precio_mensual_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Precio Mensual Cents"},"precio_anual_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Precio Anual Cents"},"moneda":{"type":"string","title":"Moneda"},"trial_dias":{"type":"integer","title":"Trial Dias"},"incluida_en_pro":{"type":"boolean","title":"Incluida En Pro"},"solo_pro":{"type":"boolean","title":"Solo Pro"},"capacidades":{"items":{"$ref":"#/components/schemas/StoreAppCapabilityOut"},"type":"array","title":"Capacidades"},"instalada":{"type":"boolean","title":"Instalada"},"instalada_en":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Instalada En"},"suscripcion":{"anyOf":[{"$ref":"#/components/schemas/StoreAppSubscriptionOut"},{"type":"null"}]},"puede_instalar":{"type":"boolean","title":"Puede Instalar"},"dependencias":{"additionalProperties":{"type":"integer"},"type":"object","title":"Dependencias"}},"type":"object","required":["key","nombre","descripcion","icono","modelo","addon_de","precio_mensual_cents","precio_anual_cents","moneda","trial_dias","incluida_en_pro","solo_pro","capacidades","instalada","instalada_en","suscripcion","puede_instalar","dependencias"],"title":"StoreAppForOrganization","description":"El catálogo con el estado de UNA organización encima."},"StoreAppOut":{"properties":{"key":{"type":"string","title":"Key"},"nombre":{"type":"string","title":"Nombre"},"descripcion":{"type":"string","title":"Descripcion"},"icono":{"type":"string","title":"Icono"},"modelo":{"type":"string","enum":["free","freemium","paid","addon"],"title":"Modelo"},"addon_de":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Addon De"},"precio_mensual_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Precio Mensual Cents"},"precio_anual_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Precio Anual Cents"},"moneda":{"type":"string","title":"Moneda"},"trial_dias":{"type":"integer","title":"Trial Dias"},"incluida_en_pro":{"type":"boolean","title":"Incluida En Pro"},"solo_pro":{"type":"boolean","title":"Solo Pro"},"capacidades":{"items":{"$ref":"#/components/schemas/StoreAppCapabilityOut"},"type":"array","title":"Capacidades"}},"type":"object","required":["key","nombre","descripcion","icono","modelo","addon_de","precio_mensual_cents","precio_anual_cents","moneda","trial_dias","incluida_en_pro","solo_pro","capacidades"],"title":"StoreApp","description":"Una app del catálogo público, sin nada de ninguna organización."},"StoreAppSubscriptionOut":{"properties":{"app_key":{"type":"string","title":"App Key"},"estado":{"type":"string","enum":["trial","activa","en_gracia","cancelada"],"title":"Estado"},"periodo":{"type":"string","enum":["mensual","anual"],"title":"Periodo"},"viva":{"type":"boolean","title":"Viva"},"trial_hasta":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trial Hasta"},"renueva_en":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Renueva En"},"cancelada_en":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cancelada En"},"importe_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Importe Cents"},"moneda":{"type":"string","title":"Moneda"}},"type":"object","required":["app_key","estado","periodo","viva","trial_hasta","renueva_en","cancelada_en","importe_cents","moneda"],"title":"StoreAppSubscription","description":"Suscripción de una organización a una app.\n\nEs el MISMO schema en los dos sitios donde aparece —el campo `suscripcion`\ndel catálogo por organización y la lista de «Mis apps»— y por eso lleva\n`app_key`: en el catálogo es redundante (ya lo dice la app que lo contiene),\npero tenerlo permite que la lista sea de este tipo en vez de un segundo\nschema casi igual que un día divergiría.\n\n`viva` no es derivable desde fuera sin repetir la regla: la calcula\n`OrganizationAppSubscription.viva_en`, que es la única definición del\nsistema (una `cancelada` con periodo pagado por delante SIGUE concediendo).\nSi el front la dedujera del estado y las fechas, habría dos reglas y una se\nquedaría atrás."},"StoreSubscribeIn":{"properties":{"periodo":{"type":"string","enum":["mensual","anual"],"title":"Periodo","description":"Periodicidad del cobro. Por defecto, mensual.","default":"mensual"}},"type":"object","title":"StoreSubscribeRequest","description":"Cuerpo de «Suscribirse»: con qué periodicidad."},"StoreSubscribeOut":{"properties":{"checkout_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checkout Url"},"suscripcion":{"anyOf":[{"$ref":"#/components/schemas/StoreAppSubscriptionOut"},{"type":"null"}]}},"type":"object","required":["checkout_url","suscripcion"],"title":"StoreSubscribeResult","description":"Resultado de «Suscribirse»: o una suscripción, o un sitio al que ir.\n\nExactamente UNO de los dos campos viene relleno, y cuál depende de algo que\nel cliente no puede saber de antemano: si la organización ya tiene una\nsuscripción de Stripe contenedora (§5), la app se añade como un item más y\nla suscripción queda hecha en el acto; si es su primera compra, hay que\npasar por Checkout para dar una tarjeta.\n\nSe devuelven en la misma respuesta en vez de tener dos endpoints porque el\nfront hace lo mismo en los dos casos —enseñar el resultado o redirigir— y\npartirlo obligaría a preguntar antes «¿tengo suscripción?», que es una\ncarrera: entre la pregunta y la compra puede haber cambiado."},"SubscriptionInfoOut":{"properties":{"status":{"type":"string","title":"Status"},"renews_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Renews At"}},"type":"object","required":["status"],"title":"SubscriptionInfo","description":"Datos de la suscripción de Stripe (plan de pago con cliente en Stripe)."},"SubtaskListOut":{"properties":{"subtasks":{"items":{"$ref":"#/components/schemas/TaskOut"},"type":"array","title":"Subtasks"},"progress":{"$ref":"#/components/schemas/SubtaskProgressOut"}},"type":"object","required":["subtasks","progress"],"title":"SubtaskList","description":"Respuesta de GET .../tasks/{task_id}/subtasks: hijos + rollup de progreso."},"SubtaskProgressOut":{"properties":{"total":{"type":"integer","title":"Total"},"done":{"type":"integer","title":"Done"}},"type":"object","required":["total","done"],"title":"SubtaskProgressOut","description":"Rollup de progreso de subtareas (hijo de TaskOut cuando se solicita)."},"SupplierCleanupResultOut":{"properties":{"deleted":{"type":"integer","title":"Deleted"},"kept":{"type":"integer","title":"Kept"}},"type":"object","required":["deleted","kept"],"title":"SupplierCleanupResult","description":"Resultado de POST .../suppliers/cleanup (purga de proveedores fantasma).\n\n- deleted: proveedores eliminados (sin gastos ni facturas AP).\n- kept: proveedores conservados (tienen al menos un gasto o factura AP)."},"SupplierCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"tax_id":{"type":"string","title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"global_supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Global Supplier Id"}},"type":"object","required":["name","tax_id"],"title":"SupplierCreateIn"},"SupplierInvoiceBulkActionIn":{"properties":{"ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":200,"minItems":1,"title":"Ids"},"action":{"type":"string","enum":["approve","reject","mark_paid","void","delete"],"title":"Action"},"confirm":{"type":"boolean","title":"Confirm","default":false}},"type":"object","required":["ids","action"],"title":"SupplierInvoiceBulkActionIn","description":"Body de POST /supplier-invoices/bulk.\n\nLas acciones de estado (approve/reject/mark_paid/void) reutilizan la misma\nmáquina de estados que el single-item; `delete` es destructiva y exige\n`confirm=True` (el service devuelve 422 si falta). El resultado honesto se\ndevuelve como BulkActionResult (reutilizado de finance.schemas)."},"SupplierInvoiceCreateIn":{"properties":{"supplier_name":{"type":"string","title":"Supplier Name"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"number":{"type":"string","title":"Number"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"subtotal":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Subtotal"},"tax":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Tax","default":"0"},"total":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Total"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu","default":false}},"type":"object","required":["supplier_name","number","issue_date","subtotal","total"],"title":"SupplierInvoiceCreateIn"},"SupplierInvoiceOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"supplier_name":{"type":"string","title":"Supplier Name"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"number":{"type":"string","title":"Number"},"issue_date":{"type":"string","format":"date","title":"Issue Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"subtotal":{"type":"number","title":"Subtotal"},"tax":{"type":"number","title":"Tax"},"total":{"type":"number","title":"Total"},"status":{"type":"string","enum":["received","approved","rejected","paid","void"],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"type":"boolean","title":"Is Intra Eu"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","supplier_name","supplier_tax_id","supplier_id","number","issue_date","due_date","project_id","subtotal","tax","total","status","notes","cost_center_id","is_intra_eu","created_at","updated_at"],"title":"SupplierInvoice"},"SupplierInvoiceUpdateIn":{"properties":{"supplier_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Name"},"supplier_tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier Tax Id"},"supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supplier Id"},"number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Number"},"issue_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Issue Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"subtotal":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Subtotal"},"tax":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Tax"},"total":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Total"},"status":{"anyOf":[{"type":"string","enum":["received","approved","rejected","paid","void"]},{"type":"null"}],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"cost_center_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Cost Center Id"},"is_intra_eu":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Intra Eu"}},"type":"object","title":"SupplierInvoiceUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes."},"SupplierOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"global_supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Global Supplier Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","tax_id","email","phone","address","logo_url","notes","global_supplier_id","created_at","updated_at"],"title":"Supplier"},"SupplierUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"SupplierUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes.\n\nLos obligatorios (name, tax_id) siguen siendo opcionales *como clave*: si no\nvienen, no se tocan. Lo que se rechaza es enviarlos vacíos o a `null`."},"SupplierWithStatsOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"logo_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"global_supplier_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Global Supplier Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"currency":{"type":"string","title":"Currency","default":"EUR"},"invoice_count":{"type":"integer","title":"Invoice Count"},"total_spend":{"type":"number","title":"Total Spend"},"expense_count":{"type":"integer","title":"Expense Count"},"expense_total":{"type":"number","title":"Expense Total"},"spend_total":{"type":"number","title":"Spend Total"}},"type":"object","required":["id","organization_id","name","tax_id","email","phone","address","logo_url","notes","global_supplier_id","created_at","updated_at","invoice_count","total_spend","expense_count","expense_total","spend_total"],"title":"SupplierWithStats","description":"Supplier + agregados combinados: facturas AP + gastos aprobados.\n\n- invoice_count / total_spend: solo facturas de proveedor (supplier_invoices).\n- expense_count / expense_total: gastos aprobados cuyo vendor coincide (case-insensitive).\n- spend_total: suma combinada = total_spend + expense_total; usado para ordenar el top.\n- currency: divisa de los tres importes (la base de la organización).\n\n`currency` (PJKT-2264): los agregados suman SOLO documentos registrados en\nla divisa base y los recuentos se acotan con ellos. Antes eran `SUM(...)`\nciegos, así que «lo gastado con este proveedor» mezclaba monedas — y como\n`spend_total` ordena el top de proveedores, esa mezcla decidía además quién\nsalía primero. No se convierte nada; lo registrado en otra divisa queda fuera."},"SupportEmailChannelCreateIn":{"properties":{"address":{"type":"string","title":"Address"},"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["address","request_type_id"],"title":"SupportEmailChannelCreateIn"},"SupportEmailChannelOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"address":{"type":"string","title":"Address"},"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","address","request_type_id","active","created_at","updated_at"],"title":"SupportEmailChannel"},"SupportEmailChannelUpdateIn":{"properties":{"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"request_type_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Request Type Id"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"SupportEmailChannelUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes. Ninguno admite null."},"SupportIngestAcceptedOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"number":{"type":"integer","title":"Number"},"created":{"type":"boolean","title":"Created"},"attachments_stored":{"type":"integer","title":"Attachments Stored"},"attachments_rejected":{"items":{"$ref":"#/components/schemas/SupportIngestRejectedAttachmentOut"},"type":"array","title":"Attachments Rejected"}},"type":"object","required":["id","number","created","attachments_stored","attachments_rejected"],"title":"SupportIngestAccepted","description":"Acuse de la incidencia. Lo mínimo para que la web externa pueda enlazarla.\n\nNO devuelve la cola, ni el proyecto, ni el estado, ni nada del resto de la\nbandeja: la credencial sirve para ESCRIBIR, y un acuse generoso es una vía de\nlectura disfrazada."},"SupportIngestAttachmentIn":{"properties":{"filename":{"type":"string","maxLength":255,"title":"Filename"},"content_base64":{"type":"string","maxLength":13982037,"title":"Content Base64"}},"type":"object","required":["filename","content_base64"],"title":"SupportIngestAttachmentIn","description":"Un fichero, en base64. Una captura es la mitad de un parte de incidencia.\n\nLos límites son los COMUNES del producto (`app/shared/uploads.py`): 10 MiB,\nallow-list positiva de extensiones y verificación de los BYTES contra la\nextensión. No hay una segunda puerta de subida con reglas propias (regla 18)\n— el fichero pasa por `AttachmentService`, igual que el de un correo."},"SupportIngestFieldValueIn":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"value":{"type":"string","maxLength":2000,"title":"Value"}},"type":"object","required":["field_def_id","value"],"title":"SupportIngestFieldValueIn","description":"Respuesta a uno de los campos del formulario del tipo de petición."},"SupportIngestFormFieldOut":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"name":{"type":"string","title":"Name"},"field_type":{"type":"string","title":"Field Type"},"options":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Options"},"required":{"type":"boolean","title":"Required"},"position":{"type":"integer","title":"Position"}},"type":"object","required":["field_def_id","name","field_type","options","required","position"],"title":"SupportIngestFormField","description":"Un campo del formulario, resuelto, para que la web externa lo pinte."},"SupportIngestFormOut":{"properties":{"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"fields":{"items":{"$ref":"#/components/schemas/SupportIngestFormFieldOut"},"type":"array","title":"Fields"}},"type":"object","required":["request_type_id","name","description","fields"],"title":"SupportIngestForm","description":"El formulario que ESTA credencial puede rellenar, y nada más.\n\nExiste para que configurar la integración no sea copiar UUIDs a mano de la\npantalla de ajustes al código de otra web. Es la única lectura que la\ncredencial permite y está acotada a su PROPIO tipo de petición: no ve la cola,\nni las peticiones, ni los demás tipos de la organización."},"SupportIngestKeyCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"hourly_limit":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Hourly Limit"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["name","request_type_id"],"title":"SupportIngestKeyCreateIn","description":"Alta de una credencial de ingesta.\n\n`request_type_id` es OBLIGATORIO y no opcional: de él salen la organización,\nla cola y la prioridad de todo lo que entre por esta credencial. Una\ncredencial sin tipo tendría que elegir cola en tiempo de ingesta, y elegirla\nla web externa es exactamente lo que este diseño no permite."},"SupportIngestKeyCreatedOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"name":{"type":"string","title":"Name"},"token_prefix":{"type":"string","title":"Token Prefix"},"hourly_limit":{"type":"integer","title":"Hourly Limit"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"token":{"type":"string","title":"Token"}},"type":"object","required":["id","organization_id","request_type_id","name","token_prefix","hourly_limit","created_by","last_used_at","revoked_at","expires_at","created_at","token"],"title":"SupportIngestKeyCreated","description":"Como `SupportIngestKey` + el token en claro, que sale UNA sola vez.\n\nNo se puede recuperar después: en la base solo queda su SHA-256. Si se\npierde, se revoca y se acuña otra."},"SupportIngestKeyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"name":{"type":"string","title":"Name"},"token_prefix":{"type":"string","title":"Token Prefix"},"hourly_limit":{"type":"integer","title":"Hourly Limit"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","request_type_id","name","token_prefix","hourly_limit","created_by","last_used_at","revoked_at","expires_at","created_at"],"title":"SupportIngestKey","description":"Credencial ENMASCARADA: nunca lleva el token ni su hash."},"SupportIngestRejectedAttachmentOut":{"properties":{"filename":{"type":"string","title":"Filename"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["filename","reason"],"title":"SupportIngestRejectedAttachment","description":"Un fichero que NO se guardó, y por qué.\n\nSe contesta en vez de callarse porque quien llama es un programa y puede\narreglarlo: la ingesta de correo se calla porque al otro lado hay una persona\na la que no se le puede devolver un error de validación."},"SupportIngestRequestIn":{"properties":{"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":20000},{"type":"null"}],"title":"Description"},"external_ref":{"anyOf":[{"type":"string","maxLength":191},{"type":"null"}],"title":"External Ref"},"idempotency_key":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Idempotency Key"},"fields":{"items":{"$ref":"#/components/schemas/SupportIngestFieldValueIn"},"type":"array","maxItems":50,"title":"Fields"},"attachments":{"items":{"$ref":"#/components/schemas/SupportIngestAttachmentIn"},"type":"array","maxItems":5,"title":"Attachments"}},"type":"object","required":["title"],"title":"SupportIngestRequestIn","description":"La incidencia que manda la web externa.\n\nLo que NO se acepta aquí, y ninguna ausencia es casual:\n\n- **`organization_id`**: sale de la credencial. Si viniera en el cuerpo habría\n  que elegir entre creerlo o ignorarlo, y creerlo es la fuga de tenant.\n- **`project_id` / cola**: sale del tipo de petición de la credencial. Una web\n  externa no elige en qué bandeja del equipo aterriza su incidencia, ni hace\n  nacer proyectos.\n- **`priority`**: la fija el tipo. Si la eligiera quien escribe, todas serían\n  urgentes y la palabra dejaría de significar algo (mismo argumento que el\n  portal y el correo).\n- **el solicitante**: no se vincula a ningún contacto. El remitente CLASIFICA,\n  nunca DA ACCESO: aceptar «esto lo manda ana@cliente.com» colgaría la\n  petición del portal de esa persona a cambio de una línea de JSON."},"SupportRequestCreateIn":{"properties":{"request_type_id":{"type":"string","format":"uuid","title":"Request Type Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"channel":{"type":"string","enum":["portal","email","phone","internal"],"title":"Channel","default":"internal"},"contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Contact Id"},"client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Client Id"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"fields":{"items":{"$ref":"#/components/schemas/SupportRequestFieldValueIn"},"type":"array","title":"Fields","default":[]}},"type":"object","required":["request_type_id","title"],"title":"SupportRequestCreateIn","description":"Alta de una petición desde DENTRO (una llamada, un aviso presencial).\n\nLas vías externas (portal y correo) llegan en las etapas siguientes y\nreutilizan este mismo caso de uso: lo único que cambia entre ellas es el\n`channel` y cómo se identifica al solicitante."},"SupportRequestFieldValueIn":{"properties":{"field_def_id":{"type":"string","format":"uuid","title":"Field Def Id"},"value":{"type":"string","title":"Value"}},"type":"object","required":["field_def_id","value"],"title":"SupportRequestFieldValueIn","description":"Respuesta a uno de los campos del formulario."},"SupportRequestOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"number":{"type":"integer","title":"Number"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority"},"type":{"type":"string","title":"Type"},"channel":{"type":"string","title":"Channel"},"request_type_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Request Type Id"},"requester_contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Requester Contact Id"},"requester_client_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Requester Client Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","number","title","description","status","priority","type","channel","request_type_id","requester_contact_id","requester_client_id","created_at","updated_at"],"title":"SupportRequest","description":"Una petición: la tarea que la representa MÁS de quién y por dónde vino."},"SupportSavedReplyCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"body":{"type":"string","title":"Body"},"shortcut":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shortcut"}},"type":"object","required":["title","body"],"title":"SupportSavedReplyCreateIn","description":"Alta de una respuesta guardada."},"SupportSavedReplyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"title":{"type":"string","title":"Title"},"body":{"type":"string","title":"Body"},"shortcut":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shortcut"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","title","body","shortcut","created_by","created_at","updated_at"],"title":"SupportSavedReply","description":"Una plantilla, con el cuerpo LITERAL.\n\nLos marcadores viajan sin sustituir a propósito: quien la pide todavía no ha\nelegido la petición en la que la va a insertar. La sustitución vive en el\ncliente (`apps/web/src/features/support/plantillas.ts`), que es el único que\nsabe las dos cosas a la vez y además enseña el resultado antes de enviarlo."},"SupportSavedReplyUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"},"shortcut":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shortcut"}},"type":"object","title":"SupportSavedReplyUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes.\n\n`shortcut` es el ÚNICO que admite `null`, y no por descuido: es la única\nforma de QUITARLE el atajo a una respuesta que lo tenía. `title` y `body`\nrechazan el null como el resto del módulo — ahí un null es un error de quien\nllama, no una intención."},"TagCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color","default":"slate"}},"type":"object","required":["name"],"title":"TagCreateIn"},"TagOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"used_count":{"type":"integer","title":"Used Count"}},"type":"object","required":["id","organization_id","name","color","created_at","used_count"],"title":"Tag","description":"Tag del contrato: {id, organization_id, name, color, created_at, used_count}.\n\n`used_count` es el nº de tareas (de la misma organización) que tienen este\ntag asignado; 0 cuando no se usa en ninguna. Se calcula, no se almacena."},"TagRef":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"color":{"type":"string","title":"Color"}},"type":"object","required":["id","name","color"],"title":"TagRef","description":"Referencia compacta a un tag embebida en `Task.tags` ({id, name, color})."},"TagUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"color":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Color"}},"type":"object","title":"TagUpdateIn","description":"PATCH parcial: solo se aplican campos presentes; ninguno admite null."},"TaskActivityEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"action":{"type":"string","title":"Action"},"actor_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Actor Id"},"actor_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"}},"type":"object","required":["id","action","actor_id","actor_name","created_at","payload"],"title":"TaskActivityEntry","description":"Entrada del feed de actividad (bitácora) de una tarea.\n\nMaterializa un registro de audit_logs filtrado por task_id en el payload.\n`actor_name` resuelto por JOIN a users; null cuando el usuario se borró."},"TaskAttachmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"task_id":{"type":"string","format":"uuid","title":"Task Id"},"filename":{"type":"string","title":"Filename"},"content_type":{"type":"string","title":"Content Type"},"size_bytes":{"type":"integer","title":"Size Bytes"},"uploaded_by":{"type":"string","format":"uuid","title":"Uploaded By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","project_id","task_id","filename","content_type","size_bytes","uploaded_by","created_at"],"title":"TaskAttachment","description":"Metadatos de un adjunto (NUNCA incluye los bytes — usar /download)."},"TaskBulkErrorOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["id","reason"],"title":"TaskBulkError"},"TaskBulkIn":{"properties":{"task_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Task Ids"},"op":{"type":"string","enum":["set_status","set_assignee","set_sprint","set_parent","set_priority","add_tags","remove_tags","delete"],"title":"Op"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Sprint Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"tag_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Tag Ids"}},"type":"object","required":["task_ids","op"],"title":"TaskBulkIn","description":"Body de `POST .../tasks/bulk`: una operación sobre un lote de tareas.\n\nEl valor de la operación viaja en el campo correspondiente a `op`. Máximo\n200 ids (se deduplican en el service). `assignee_id`/`sprint_id`/`parent_id`\nadmiten `null` (desasignar / sacar de sprint / desvincular del padre); por\neso solo son \"requeridos\" en el sentido de que su ausencia es un valor\nválido (None), no un error."},"TaskBulkResultOut":{"properties":{"updated":{"type":"integer","title":"Updated"},"skipped":{"type":"integer","title":"Skipped"},"errors":{"items":{"$ref":"#/components/schemas/TaskBulkErrorOut"},"type":"array","title":"Errors"}},"type":"object","required":["updated","skipped","errors"],"title":"TaskBulkResult","description":"Resultado honesto del bulk: aplicadas, omitidas y errores por id."},"TaskCommentCreateIn":{"properties":{"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"visibility":{"type":"string","enum":["internal","public"],"title":"Visibility","default":"internal"}},"type":"object","required":["body"],"title":"TaskCommentCreateIn"},"TaskCommentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"task_id":{"type":"string","format":"uuid","title":"Task Id"},"author_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Id"},"author_name":{"type":"string","title":"Author Name"},"body":{"type":"string","title":"Body"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"is_edited":{"type":"boolean","title":"Is Edited"},"visibility":{"type":"string","enum":["internal","public"],"title":"Visibility"},"author_contact_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Author Contact Id"},"channel":{"anyOf":[{"type":"string","enum":["app","portal","email"]},{"type":"null"}],"title":"Channel"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","task_id","author_id","author_name","body","parent_id","is_edited","visibility","channel","created_at","updated_at"],"title":"TaskComment","description":"Comentario del contrato: hilo plano (el cliente anida por `parent_id`).\n\n`author_id` es `null` si el autor fue borrado; `author_name` cae a\n\"Sistema\" en ese caso."},"TaskCommentUpdateIn":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"TaskCommentUpdateIn","description":"PATCH de un comentario: solo `body` (no admite null)."},"TaskCreateIn":{"properties":{"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority","default":"medium"},"type":{"type":"string","enum":["epic","story","task","bug","spike","chore"],"title":"Type","default":"task"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"story_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Story Points"},"estimated_hours":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Estimated Hours"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"completed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Completed Date"},"sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Sprint Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["title"],"title":"TaskCreateIn"},"TaskDependencyCreateIn":{"properties":{"target_task_id":{"type":"string","format":"uuid","title":"Target Task Id"},"dep_type":{"type":"string","enum":["blocks","blocked_by","relates_to"],"title":"Dep Type"}},"type":"object","required":["target_task_id","dep_type"],"title":"TaskDependencyCreateIn"},"TaskDependencyOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"source_task_id":{"type":"string","format":"uuid","title":"Source Task Id"},"target_task_id":{"type":"string","format":"uuid","title":"Target Task Id"},"dep_type":{"type":"string","enum":["blocks","blocked_by","relates_to"],"title":"Dep Type"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","project_id","source_task_id","target_task_id","dep_type","created_at","updated_at"],"title":"TaskDependency"},"TaskFilterQuery":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"type":{"anyOf":[{"type":"string","enum":["epic","story","task","bug","spike","chore"]},{"type":"null"}],"title":"Type"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Sprint Id"},"tag_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Tag Ids"},"q":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"},"due_before":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Before"},"due_after":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due After"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"root_only":{"type":"boolean","title":"Root Only","default":false},"sort":{"type":"string","enum":["created","updated","due","priority","number","title"],"title":"Sort","default":"created"},"order":{"type":"string","enum":["asc","desc"],"title":"Order","default":"asc"},"limit":{"type":"integer","title":"Limit","default":200},"offset":{"type":"integer","title":"Offset","default":0}},"type":"object","title":"TaskFilterQuery","description":"Filtros + orden + paginación server-side del listado de tareas.\n\nConvergen aquí los query params de `listTasks` y el blob JSON que persisten\nlos `saved_filters` (entidad \"tasks\"); por eso `extra=\"ignore\"` tolera claves\nde más en un blob guardado. Todos los campos son opcionales; sin ninguno el\nresultado es el listado completo del proyecto ordenado por antigüedad."},"TaskGithubCommitSlim":{"properties":{"sha7":{"type":"string","title":"Sha7"},"message":{"type":"string","title":"Message"},"author_name":{"type":"string","title":"Author Name"},"date":{"type":"string","title":"Date"},"html_url":{"type":"string","title":"Html Url"}},"type":"object","required":["sha7","message","author_name","date","html_url"],"title":"TaskGithubCommitSlim","description":"Commit encontrado via GitHub Search API para una referencia de tarea."},"TaskGithubLinksOut":{"properties":{"reference":{"type":"string","title":"Reference"},"repos":{"items":{"$ref":"#/components/schemas/TaskGithubRepoLinks"},"type":"array","title":"Repos"},"rate_limited":{"type":"boolean","title":"Rate Limited"}},"type":"object","required":["reference","repos","rate_limited"],"title":"TaskGithubLinksOut","description":"Respuesta de /tasks/{task_id}/github-links.\n\nrate_limited=True indica que GitHub devolvió 403 rate-limit para al menos\nun repo; la tarjeta no debe romper — muestra lo que hay y una advertencia."},"TaskGithubPRSlim":{"properties":{"number":{"type":"integer","title":"Number"},"title":{"type":"string","title":"Title"},"state":{"type":"string","enum":["open","closed"],"title":"State"},"merged":{"type":"boolean","title":"Merged"},"html_url":{"type":"string","title":"Html Url"}},"type":"object","required":["number","title","state","merged","html_url"],"title":"TaskGithubPRSlim","description":"PR encontrado via GitHub Search API para una referencia de tarea."},"TaskGithubRepoLinks":{"properties":{"repo_full_name":{"type":"string","title":"Repo Full Name"},"commits":{"items":{"$ref":"#/components/schemas/TaskGithubCommitSlim"},"type":"array","title":"Commits"},"prs":{"items":{"$ref":"#/components/schemas/TaskGithubPRSlim"},"type":"array","title":"Prs"}},"type":"object","required":["repo_full_name","commits","prs"],"title":"TaskGithubRepoLinks","description":"Commits y PRs de un repo concreto que referencian la tarea."},"TaskOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"project_id":{"type":"string","format":"uuid","title":"Project Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"number":{"type":"integer","title":"Number"},"reference":{"type":"string","title":"Reference"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"type":"string","title":"Status"},"priority":{"type":"string","enum":["low","medium","high","urgent"],"title":"Priority"},"type":{"type":"string","enum":["epic","story","task","bug","spike","chore"],"title":"Type"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"assignee_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Assignee Name"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Name"},"sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Sprint Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"subtask_count":{"type":"integer","title":"Subtask Count"},"subtasks_done":{"type":"integer","title":"Subtasks Done"},"attachment_count":{"type":"integer","title":"Attachment Count"},"story_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Story Points"},"estimated_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Hours"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"completed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Completed Date"},"tags":{"items":{"$ref":"#/components/schemas/TagRef"},"type":"array","title":"Tags"},"custom_fields":{"items":{"$ref":"#/components/schemas/CustomFieldValueOut"},"type":"array","title":"Custom Fields"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","project_id","organization_id","number","reference","title","description","status","priority","type","assignee_id","assignee_name","created_by","created_by_name","sprint_id","parent_id","subtask_count","subtasks_done","attachment_count","story_points","estimated_hours","start_date","due_date","due_at","completed_date","tags","custom_fields","created_at","updated_at"],"title":"Task","description":"Task del contrato: todos los campos siempre presentes (null cuando no hay).\n\n`tags` es siempre una lista (vacía si la tarea no tiene etiquetas).\n`parent_id` es null cuando no es subtarea.\n`subtask_count` y `subtasks_done` son 0 cuando no tiene hijos.\n`attachment_count` es 0 cuando no tiene adjuntos.\n`custom_fields` es una lista vacía cuando no tiene campos personalizados.\n`number` es el número secuencial por proyecto; `reference` = \"{key}-{number}\"."},"TaskSearchIn":{"properties":{"filter_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Filter Id"},"query":{"anyOf":[{"$ref":"#/components/schemas/TaskFilterQuery"},{"type":"null"}]}},"type":"object","title":"TaskSearchIn","description":"Body de `POST .../tasks/search`: ejecuta un filtro guardado o uno inline.\n\nExactamente uno de `filter_id` (un `saved_filter` propio, entidad \"tasks\") o\n`query` (blob inline con el shape de `TaskFilterQuery`). El validador exige\nexclusividad (ni ambos ni ninguno → 422).\n\n`query` está TIPADO como `TaskFilterQuery`, no como `dict[str, Any]`: el\ncontrato promete `query.{priority,type,sort,order}` con sus enums y con el\nblob opaco el schema que servía FastAPI no declaraba ni la posición, así que\nel barrido de `make api-openapi` no podía desmentir esa promesa (que resultó\nser cierta — los campos se validaban aguas abajo, en el service). Tipado, el\ncontrato y el API dicen lo mismo en el mismo sitio, y el SDK ya no promete\nnada que nadie compruebe. `extra=\"ignore\"` de `TaskFilterQuery` mantiene la\ntolerancia a claves desconocidas que el contrato documenta."},"TaskSlaClockOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"task_id":{"type":"string","format":"uuid","title":"Task Id"},"policy_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Policy Id"},"policy_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Policy Name"},"target_kind":{"type":"string","enum":["first_response","resolution"],"title":"Target Kind"},"target_minutes":{"type":"integer","title":"Target Minutes"},"state":{"type":"string","enum":["pending","running","paused","met","breached"],"title":"State"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"stopped_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Stopped At"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"consumed_minutes":{"type":"integer","title":"Consumed Minutes"},"remaining_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining Minutes"},"elapsed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Elapsed At"},"breached":{"type":"boolean","title":"Breached"},"breached_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Breached At"},"closed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Closed At"},"based_on_reconstructed":{"type":"boolean","title":"Based On Reconstructed"},"pauses":{"items":{"$ref":"#/components/schemas/SlaPauseSpan"},"type":"array","title":"Pauses"},"calendar_snapshot":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Calendar Snapshot"}},"type":"object","required":["id","task_id","policy_id","policy_name","target_kind","target_minutes","state","started_at","stopped_at","due_at","consumed_minutes","remaining_minutes","elapsed_at","breached","breached_at","closed_at","based_on_reconstructed","pauses","calendar_snapshot"],"title":"TaskSlaClock","description":"Reloj de una tarea contra un objetivo.\n\n`due_at` es el vencimiento. Mientras el reloj vive es una PROYECCIÓN\ncalculada como si el trabajo se reanudara ahora (por eso se mueve cuando la\ntarea está bloqueada); cuando el reloj para queda congelado junto con el\nresto de la fila y ya no cambia, ni siquiera si después cambia el calendario\nde la organización."},"TaskUpdateIn":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"priority":{"anyOf":[{"type":"string","enum":["low","medium","high","urgent"]},{"type":"null"}],"title":"Priority"},"type":{"anyOf":[{"type":"string","enum":["epic","story","task","bug","spike","chore"]},{"type":"null"}],"title":"Type"},"assignee_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Assignee Id"},"sprint_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Sprint Id"},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Parent Id"},"story_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Story Points"},"estimated_hours":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Estimated Hours"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Due Date"},"due_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Due At"},"completed_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Completed Date"}},"type":"object","title":"TaskUpdateIn","description":"PATCH parcial: solo se aplican los campos presentes en el body.\n\n`title`/`status`/`priority`/`type` no admiten null explícito (los\nvalidadores corren solo para valores enviados, no para defaults).\n`description`, `assignee_id`, `sprint_id`, `story_points`,\n`estimated_hours`, `start_date` y `due_date` sí: null los limpia (borra\ndescripción / desasigna / saca del sprint / quita puntos-horas-fechas).\n`exclude_unset` en el service distingue \"omitido\" (no toca) de \"null\"\n(borra)."},"TasksTrendPointOut":{"properties":{"week":{"type":"string","title":"Week"},"created":{"type":"integer","title":"Created"},"done":{"type":"integer","title":"Done"}},"type":"object","required":["week","created","done"],"title":"DashboardTasksTrendPoint","description":"Un punto de la serie semanal: tareas creadas y completadas esa semana."},"TeamUtilizationOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"billable_hours":{"type":"number","title":"Billable Hours"},"total_hours":{"type":"number","title":"Total Hours"},"utilization_pct":{"type":"number","title":"Utilization Pct"}},"type":"object","required":["user_id","name","billable_hours","total_hours","utilization_pct"],"title":"TeamUtilization","description":"Utilización de un miembro: horas facturables sobre horas totales.\n\n`utilization_pct` = billable/total×100, o 0 cuando total == 0."},"TimeEntryCreateIn":{"properties":{"minutes":{"type":"integer","title":"Minutes"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_billable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Billable"}},"type":"object","required":["minutes","entry_date"],"title":"TimeEntryCreateIn"},"TimeEntryOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"task_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Reference"},"task_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"minutes":{"type":"integer","title":"Minutes"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"is_billable":{"type":"boolean","title":"Is Billable"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","user_id","project_id","task_id","task_reference","task_title","description","minutes","entry_date","is_billable","created_at","updated_at"],"title":"TimeEntry"},"TimeEntryProjectCreateIn":{"properties":{"minutes":{"type":"integer","title":"Minutes"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_billable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Billable"}},"type":"object","required":["minutes","entry_date"],"title":"TimeEntryProjectCreateIn","description":"Body de `POST .../projects/{project_id}/time-entries`.\n\nEl proyecto va en la RUTA (no en el body), lo que permite un PAT scoped a\nese proyecto. Resto igual que `TimeEntryCreateIn`."},"TimeEntryRunningOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"task_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Reference"},"task_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"is_billable":{"type":"boolean","title":"Is Billable"},"elapsed_seconds":{"type":"integer","title":"Elapsed Seconds"}},"type":"object","required":["id","organization_id","user_id","project_id","task_id","task_reference","task_title","description","entry_date","started_at","is_billable","elapsed_seconds"],"title":"TimeEntryRunning","description":"Un timer EN CURSO (`minutes IS NULL`): shape distinto de `TimeEntryOut`\na propósito — expone `started_at`/`elapsed_seconds` en vez de `minutes`."},"TimeEntryStartIn":{"properties":{"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_billable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Billable"}},"type":"object","title":"TimeEntryStartIn","description":"Body de `POST .../time-entries/start`.\n\nSin `minutes`/`entry_date`: el servidor fija `entry_date` a la fecha UTC\nactual y `started_at` al instante de arranque; `minutes` queda NULL hasta\nparar el timer."},"TimeEntrySummaryGroupOut":{"properties":{"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"total_minutes":{"type":"integer","title":"Total Minutes"},"billable_minutes":{"type":"integer","title":"Billable Minutes"},"non_billable_minutes":{"type":"integer","title":"Non Billable Minutes"},"entries_count":{"type":"integer","title":"Entries Count"}},"type":"object","required":["project_id","user_id","total_minutes","billable_minutes","non_billable_minutes","entries_count"],"title":"TimeEntrySummaryGroup","description":"Una fila del desglose de `TimeEntrySummaryOut.groups`.\n\n`project_id`/`user_id`: solo uno de los dos es no-nulo, según `group_by`\n(el otro siempre `null`). `project_id=null` con `group_by=project` agrupa\nlas horas SIN proyecto asociado."},"TimeEntrySummaryOut":{"properties":{"group_by":{"type":"string","enum":["project","user","none"],"title":"Group By"},"from_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From Date"},"to_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To Date"},"total_minutes":{"type":"integer","title":"Total Minutes"},"billable_minutes":{"type":"integer","title":"Billable Minutes"},"non_billable_minutes":{"type":"integer","title":"Non Billable Minutes"},"entries_count":{"type":"integer","title":"Entries Count"},"groups":{"items":{"$ref":"#/components/schemas/TimeEntrySummaryGroupOut"},"type":"array","title":"Groups"}},"type":"object","required":["group_by","from_date","to_date","total_minutes","billable_minutes","non_billable_minutes","entries_count","groups"],"title":"TimeEntrySummary","description":"Respuesta de `GET .../time-entries/summary`.\n\n`total_minutes`/`billable_minutes`/`non_billable_minutes`/`entries_count`\nson el GRAN TOTAL de las entradas que cumplen los filtros (siempre\npresentes, sea cual sea `group_by`); `groups` es el desglose por proyecto/\nusuario (vacío si `group_by=none`). Los timers EN CURSO (`minutes IS\nNULL`) nunca se cuentan: no tienen duración todavía."},"TimeEntryUpdateIn":{"properties":{"minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minutes"},"entry_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Entry Date"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_billable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Billable"}},"type":"object","title":"TimeEntryUpdateIn","description":"PATCH parcial: solo se aplican campos presentes."},"TimesheetDayOut":{"properties":{"entry_date":{"type":"string","format":"date","title":"Entry Date"},"total_minutes":{"type":"integer","title":"Total Minutes"},"entries":{"items":{"$ref":"#/components/schemas/TimesheetEntryRef"},"type":"array","title":"Entries"}},"type":"object","required":["entry_date","total_minutes","entries"],"title":"TimesheetDay","description":"Agregado de tiempo imputado en un día."},"TimesheetEntryRef":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"minutes":{"type":"integer","title":"Minutes"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"project_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Project Id"},"task_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Task Id"}},"type":"object","required":["id","minutes","description","project_id","task_id"],"title":"TimesheetEntryRef","description":"Referencia ligera a un time entry (sin duplicar todos los campos)."},"TimesheetOverviewRowOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"week_start":{"type":"string","format":"date","title":"Week Start"},"total_minutes":{"type":"integer","title":"Total Minutes"},"approval_status":{"type":"string","enum":["not_submitted","draft","submitted","approved","rejected"],"title":"Approval Status"},"period_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Period Id"},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At"},"approved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Approved At"},"approver_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Approver Id"}},"type":"object","required":["user_id","week_start","total_minutes","approval_status","period_id","submitted_at","approved_at","approver_id"],"title":"TimesheetOverviewRow","description":"Fila de la vista de aprobaciones: una (usuario, semana) con horas imputadas.\n\nIncluye semanas con horas que NUNCA se enviaron (`approval_status ==\n'not_submitted'`, sin periodo), para que admin/owner (o manager en su ámbito)\nvea TODAS las horas del equipo y no solo las solicitadas. `period_id` solo es\nno-nulo cuando existe TimesheetPeriod (habilita aprobar/rechazar esa fila)."},"TimesheetPeriodOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"week_start":{"type":"string","format":"date","title":"Week Start"},"period_status":{"type":"string","enum":["draft","submitted","approved","rejected"],"title":"Period Status"},"submitted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Submitted At"},"approved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Approved At"},"approver_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Approver Id"},"total_minutes":{"type":"integer","title":"Total Minutes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","user_id","week_start","period_status","submitted_at","approved_at","approver_id","total_minutes","created_at","updated_at"],"title":"TimesheetPeriod"},"TimesheetSubmitIn":{"properties":{"week_start":{"type":"string","format":"date","title":"Week Start"}},"type":"object","required":["week_start"],"title":"TimesheetSubmitIn","description":"Envío del parte de la semana que EMPIEZA en `week_start`.\n\nLa comprobación de que `week_start` es de verdad el primer día de la semana\nya NO vive aquí (PJKT-2270): el día en que empieza la semana es un dato de la\norganización (`organizations.week_start_day`, 0 lunes · 5 sábado · 6 domingo)\ny un schema no tiene acceso al tenant. Un lunes fijo convertía el parte de una\norganización estadounidense —cuya semana va de domingo a sábado— en horas\nimputadas al periodo de nómina de al lado. La valida el service, que sí sabe\nde qué organización se trata, y sigue siendo un 422 `validation_error`."},"TimesheetWeekOut":{"properties":{"period":{"anyOf":[{"$ref":"#/components/schemas/TimesheetPeriodOut"},{"type":"null"}]},"week_start":{"type":"string","format":"date","title":"Week Start"},"week_end":{"type":"string","format":"date","title":"Week End"},"days":{"items":{"$ref":"#/components/schemas/TimesheetDayOut"},"type":"array","title":"Days"},"total_minutes":{"type":"integer","title":"Total Minutes"}},"type":"object","required":["period","week_start","week_end","days","total_minutes"],"title":"TimesheetWeek","description":"Semana de timesheet: estado del periodo + días agregados."},"TokenResponseOut":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","const":"Bearer","title":"Token Type","default":"Bearer"},"expires_in":{"type":"integer","title":"Expires In"},"refresh_token":{"type":"string","title":"Refresh Token"},"scope":{"type":"string","title":"Scope"}},"type":"object","required":["access_token","expires_in","refresh_token","scope"],"title":"OauthTokenResponse"},"TwoFactorCodeIn":{"properties":{"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"TwoFactorCode","description":"Cuerpo con un código de verificación (TOTP o de recuperación)."},"TwoFactorSetupOut":{"properties":{"secret":{"type":"string","title":"Secret"},"otpauth_uri":{"type":"string","title":"Otpauth Uri"}},"type":"object","required":["secret","otpauth_uri"],"title":"TwoFactorSetup","description":"Datos del alta: el secreto y el URI del QR.\n\nEl secreto viaja UNA sola vez, en esta respuesta. No hay ningún endpoint que\nlo vuelva a mostrar: quien no lo guardó, reconfigura desde cero."},"TwoFactorStatusOut":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"pending_setup":{"type":"boolean","title":"Pending Setup"},"confirmed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Confirmed At"},"recovery_codes_remaining":{"type":"integer","title":"Recovery Codes Remaining"}},"type":"object","required":["enabled","pending_setup","recovery_codes_remaining"],"title":"TwoFactorStatus","description":"Estado del segundo factor de la cuenta."},"UiPreferencesUpdateIn":{"properties":{"ambient_background":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ambient Background"},"card_style":{"anyOf":[{"type":"string","enum":["solid","translucent","glass"]},{"type":"null"}],"title":"Card Style"},"os_preferences":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Os Preferences"},"call_ringtone":{"type":"boolean","title":"Call Ringtone","default":false}},"type":"object","title":"UiPreferencesUpdate","description":"PUT parcial de las preferencias de UI: {ambient_background?, card_style?,\ncall_ringtone?}.\n\nSolo se aplican los campos presentes (mismo criterio que `MyProfileUpdateIn`):\nun cuerpo `{}` no cambia nada. `ambient_background` admite null explícito (lo\nborra → sin fondo de ambiente) y es una clave OPACA (la taxonomía de shaders\nla define el frontend); se acota a `_AMBIENT_BACKGROUND_MAX` chars. `card_style`\ny `call_ringtone` NO admiten null (sus columnas son NOT NULL con default):\nenviarlos null es 422, igual que `name` en el perfil."},"UsageBreakdownOut":{"properties":{"period":{"type":"string","title":"Period"},"resource":{"type":"string","enum":["ia_tokens","video_minutos"],"title":"Resource"},"scope":{"type":"string","enum":["persona","organizacion"],"title":"Scope"},"included_per_person":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Included Per Person"},"total_used":{"type":"integer","title":"Total Used"},"rows":{"items":{"$ref":"#/components/schemas/UsageRowOut"},"type":"array","title":"Rows"}},"type":"object","required":["period","resource","scope","included_per_person","total_used","rows"],"title":"UsageBreakdown","description":"Quién ha gastado qué, de mayor a menor.\n\nSolo aparece quien tiene consumo. Un listado con ochenta personas a cero para\nencontrar a las tres que gastan es un listado que nadie lee, y el que falta\nse deduce: si no está, no gastó."},"UsageHistoryOut":{"properties":{"resource":{"type":"string","enum":["ia_tokens","video_minutos"],"title":"Resource"},"periods":{"items":{"$ref":"#/components/schemas/UsagePeriodOut"},"type":"array","title":"Periods"}},"type":"object","required":["resource","periods"],"title":"UsageHistory","description":"El total de la organización, mes a mes, del más reciente al más antiguo.\n\nLos meses SIN consumo NO salen: no hay fila que sumar. Quien pinte la serie\ntiene que rellenar los huecos con cero — una gráfica que se salta un mes\nvacío miente sobre la pendiente."},"UsageItemOut":{"properties":{"resource":{"type":"string","enum":["ia_tokens","video_minutos"],"title":"Resource"},"scope":{"type":"string","enum":["persona","organizacion"],"title":"Scope"},"included":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Included"},"used":{"type":"integer","title":"Used"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"}},"type":"object","required":["resource","scope","included","used","remaining"],"title":"UsageItem","description":"Lo mío de UN recurso, en el periodo en curso."},"UsageMeOut":{"properties":{"period":{"type":"string","title":"Period"},"items":{"items":{"$ref":"#/components/schemas/UsageItemOut"},"type":"array","title":"Items"}},"type":"object","required":["period","items"],"title":"UsageMe","description":"Mi consumo del periodo, un item por recurso."},"UsagePeriodOut":{"properties":{"period":{"type":"string","title":"Period"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["period","total"],"title":"UsagePeriod"},"UsageRowOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"used":{"type":"integer","title":"Used"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Remaining"}},"type":"object","required":["user_id","name","email","used","remaining"],"title":"UsageRow","description":"Lo que ha gastado UNA persona."},"UserOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"weather_override":{"anyOf":[{"type":"string","enum":["clear","clouds","fog","rain","snow","thunder"]},{"type":"null"}],"title":"Weather Override"},"last_organization_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Last Organization Id"},"profile_visibility":{"type":"string","enum":["private","organization","public"],"title":"Profile Visibility","default":"private"},"social_links":{"additionalProperties":{"type":"string"},"type":"object","title":"Social Links","default":{}},"ambient_background":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ambient Background"},"card_style":{"type":"string","enum":["solid","translucent","glass"],"title":"Card Style","default":"solid"},"os_preferences":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Os Preferences"},"call_ringtone":{"type":"boolean","title":"Call Ringtone","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"},"api_key":{"anyOf":[{"$ref":"#/components/schemas/ApiKeyScopeOut"},{"type":"null"}]},"impersonating":{"anyOf":[{"$ref":"#/components/schemas/ImpersonatingOut"},{"type":"null"}]}},"type":"object","required":["id","email","name","created_at"],"title":"User","description":"User del contrato: {id, email, name, avatar_url, headline, bio,\nlocation, weather_override, last_organization_id, created_at, api_key?,\nimpersonating?}.\n\n`avatar_url`, `headline`, `bio`, `location` y `weather_override` son\nopcionales (null cuando el usuario no los ha rellenado). Los editables\nvía `PATCH /me/profile`. `last_organization_id` es la última organización\nusada (null si aún no se ha fijado); el frontend la restaura al abrir.\n`api_key` está presente (non-null) solo cuando la petición se autenticó\nmediante un PAT Bearer; null en sesiones por cookie. `impersonating` está\npresente (non-null) solo bajo una sesión de impersonation del panel ops."},"UserPostCreateIn":{"properties":{"body":{"type":"string","maxLength":2000,"minLength":1,"title":"Body"}},"type":"object","required":["body"],"title":"UserPostCreate","description":"Nueva publicación en el perfil propio."},"UserPostOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"body":{"type":"string","title":"Body"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","user_id","body","created_at","updated_at"],"title":"UserPost","description":"Publicación de un perfil."},"UserPresenceOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"online":{"type":"boolean","title":"Online"},"last_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen"},"status":{"type":"string","enum":["online","away","busy","offline"],"title":"Status"}},"type":"object","required":["user_id","online","last_seen","status"],"title":"ChatUserPresence","description":"Presencia de un usuario: conectado, última conexión y estado elegido (P4)."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VapidPublicKeyOut":{"properties":{"public_key":{"type":"string","title":"Public Key"}},"type":"object","required":["public_key"],"title":"VapidPublicKey","description":"Clave pública VAPID (applicationServerKey) para suscribirse a Web Push.\n\nPública por diseño. `\"\"` cuando el servidor no tiene Web Push configurado\n(el front lo interpreta como \"push deshabilitado\")."},"VaultItemCreateIn":{"properties":{"name":{"type":"string","title":"Name"},"secret":{"type":"string","title":"Secret"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"shared_with":{"type":"string","enum":["org","admins","usuarios"],"title":"Shared With","default":"org"},"shared_user_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":200,"title":"Shared User Ids"}},"type":"object","required":["name","secret"],"title":"VaultItemCreate","description":"Cuerpo de creación: {name, secret, username?, url?, notes?, shared_with?,\nshared_user_ids?}.\n\nEl `secret` viaja en claro SOLO aquí (HTTPS); se persiste cifrado y la\nrespuesta ya no lo incluye. `shared_with` por defecto 'org'.\n\n`shared_user_ids` solo otorga acceso con `shared_with='usuarios'`, pero se\nvalida SIEMPRE (regla 8: los ids los manda el cliente): el service comprueba\nque todos son miembros vivos de la organización antes de guardar nada."},"VaultItemOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"name":{"type":"string","title":"Name"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"shared_with":{"type":"string","enum":["org","admins","usuarios"],"title":"Shared With"},"shared_user_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Shared User Ids"},"created_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","organization_id","name","username","url","notes","shared_with","shared_user_ids","created_by","created_at","updated_at"],"title":"VaultItem","description":"Credencial SIN secreto: id, organization_id, name, username, url, notes,\nshared_with, shared_user_ids, created_by, created_at, updated_at. Ni\n`secret` ni `secret_encrypted` existen en este shape, así que no pueden\nfiltrarse."},"VaultItemUpdateIn":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"shared_with":{"anyOf":[{"type":"string","enum":["org","admins","usuarios"]},{"type":"null"}],"title":"Shared With"},"shared_user_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":200},{"type":"null"}],"title":"Shared User Ids"}},"type":"object","title":"VaultItemUpdate","description":"Edición parcial. Semántica por campo (ver contrato):\n\n- `name`/`secret`/`shared_with`: omitido o `null` = sin cambio (sus columnas\n  no admiten NULL; un `null` explícito aquí no puede significar «borra»).\n- `username`/`url`/`notes`: `null` EXPLÍCITO borra el valor; omitido lo\n  conserva. El service distingue ambos casos con `model_fields_set`.\n- `shared_user_ids`: `null` = sin cambio; una lista REEMPLAZA la anterior\n  entera (no suma), así que `[]` deja la credencial sin destinatarios. La\n  asimetría con los tres de arriba es a propósito: aquí «lista vacía» y «no\n  lo toques» son cosas distintas y las dos hacen falta."},"VaultSecretOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"secret":{"type":"string","title":"Secret"}},"type":"object","required":["id","name","username","secret"],"title":"VaultSecretOut","description":"Revelado explícito: el secreto DESCIFRADO + lo mínimo para identificarlo.\n\nCada respuesta de este shape tiene su entrada de auditoría\n(`vault_item.revealed`) escrita ANTES de responder."},"VelocitySprintOut":{"properties":{"sprint_id":{"type":"string","format":"uuid","title":"Sprint Id"},"name":{"type":"string","title":"Name"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date"},"completed_tasks":{"type":"integer","title":"Completed Tasks"},"completed_points":{"type":"integer","title":"Completed Points"}},"type":"object","required":["sprint_id","name","start_date","end_date","completed_tasks","completed_points"],"title":"SprintVelocityEntry","description":"Un sprint cerrado dentro del histórico de velocidad."},"VersionOut":{"properties":{"app_version":{"type":"string","title":"App Version"},"git_sha":{"type":"string","title":"Git Sha"},"build_time":{"type":"string","title":"Build Time"},"app_env":{"type":"string","title":"App Env"}},"type":"object","required":["app_version","git_sha","build_time","app_env"],"title":"VersionOut"},"VoiceChannelOut":{"properties":{"channel_id":{"type":"string","format":"uuid","title":"Channel Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_public":{"type":"boolean","title":"Is Public"},"is_member":{"type":"boolean","title":"Is Member"},"is_archived":{"type":"boolean","title":"Is Archived"},"room":{"type":"string","title":"Room"},"participants":{"items":{"$ref":"#/components/schemas/VoiceParticipantOut"},"type":"array","title":"Participants"},"participant_count":{"type":"integer","title":"Participant Count"}},"type":"object","required":["channel_id","name","is_public","is_member","is_archived","room","participants","participant_count"],"title":"ChatVoiceChannel","description":"Fila de la barra lateral: el canal de audio + su ocupación."},"VoiceJoinOut":{"properties":{"token":{"type":"string","title":"Token"},"url":{"type":"string","title":"Url"},"room":{"type":"string","title":"Room"},"identity":{"type":"string","format":"uuid","title":"Identity"},"participants":{"items":{"$ref":"#/components/schemas/VoiceParticipantOut"},"type":"array","title":"Participants"},"participant_count":{"type":"integer","title":"Participant Count"}},"type":"object","required":["token","url","room","identity","participants","participant_count"],"title":"ChatVoiceJoin","description":"Credenciales LiveKit para entrar + quién había ya dentro.\n\nLos cuatro campos de conexión son los mismos que `MeetingJoinToken`: el token\nlo firma el MISMO `mint_access_token`, así que no hay dos formatos de token\nque puedan divergir."},"VoiceParticipantOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"joined_at":{"type":"string","format":"date-time","title":"Joined At"}},"type":"object","required":["user_id","joined_at"],"title":"ChatVoiceParticipant","description":"Alguien que está DENTRO de la sala ahora mismo."},"VoiceRoomOut":{"properties":{"channel_id":{"type":"string","format":"uuid","title":"Channel Id"},"room":{"type":"string","title":"Room"},"participants":{"items":{"$ref":"#/components/schemas/VoiceParticipantOut"},"type":"array","title":"Participants"},"participant_count":{"type":"integer","title":"Participant Count"}},"type":"object","required":["channel_id","room","participants","participant_count"],"title":"ChatVoiceRoom","description":"Estado de la sala de audio de un canal."},"WeatherForecastDayOut":{"properties":{"date":{"type":"string","title":"Date"},"condition":{"anyOf":[{"type":"string","enum":["clear","clouds","fog","rain","snow","thunder"]},{"type":"null"}],"title":"Condition"},"temp_max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temp Max"},"temp_min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temp Min"}},"type":"object","required":["date"],"title":"WeatherForecastDay","description":"Un día de la previsión meteorológica (PJKT-1877)."},"WeatherOut":{"properties":{"condition":{"anyOf":[{"type":"string","enum":["clear","clouds","fog","rain","snow","thunder"]},{"type":"null"}],"title":"Condition"},"temperature":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temperature"},"temp_min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temp Min"},"temp_max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temp Max"},"is_override":{"type":"boolean","title":"Is Override"},"unavailable":{"type":"boolean","title":"Unavailable"},"forecast":{"items":{"$ref":"#/components/schemas/WeatherForecastDayOut"},"type":"array","title":"Forecast","default":[]}},"type":"object","required":["is_override","unavailable"],"title":"WeatherCondition","description":"Respuesta del endpoint GET /me/weather.\n\n{condition, temperature, temp_min, temp_max, is_override, unavailable}.\n`condition` es null cuando el proveedor no está disponible y no hay override.\n`unavailable` es true cuando la llamada a Open-Meteo falló (pero HTTP 200).\n`is_override` es true cuando se devuelve `users.weather_override` en lugar\ndel valor en vivo."},"WebAuthnOptionsOut":{"properties":{"options":{"additionalProperties":true,"type":"object","title":"Options","description":"Objeto de opciones PublicKeyCredentialCreation/RequestOptions (JSON WebAuthn)."}},"type":"object","required":["options"],"title":"WebAuthnOptions","description":"Opciones WebAuthn (registro o autenticación) serializadas para el navegador."},"WorkScheduleBulkSetIn":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/WorkScheduleEntryIn"},"type":"array","maxItems":50,"title":"Entries","description":"Lista de turnos; una lista vacía borra el horario."}},"type":"object","title":"WorkScheduleBulkSetIn","description":"Reemplaza el horario completo de un empleado."},"WorkScheduleEmployeeSummaryOut":{"properties":{"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"entries":{"items":{"$ref":"#/components/schemas/WorkScheduleOut"},"type":"array","title":"Entries"},"weekly_hours":{"type":"number","title":"Weekly Hours"}},"type":"object","required":["employee_id","entries","weekly_hours"],"title":"WorkScheduleEmployeeSummary","description":"Horario de un empleado + total de horas semanales."},"WorkScheduleEntryIn":{"properties":{"weekday":{"type":"integer","maximum":6.0,"minimum":0.0,"title":"Weekday","description":"0=Lunes … 6=Domingo"},"start_time":{"type":"string","title":"Start Time","description":"HH:MM (24 h)"},"end_time":{"type":"string","title":"End Time","description":"HH:MM (24 h) — debe ser > start_time"}},"type":"object","required":["weekday","start_time","end_time"],"title":"WorkScheduleEntryIn"},"WorkScheduleOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"employee_id":{"type":"string","format":"uuid","title":"Employee Id"},"weekday":{"type":"integer","title":"Weekday"},"start_time":{"type":"string","title":"Start Time"},"end_time":{"type":"string","title":"End Time"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","employee_id","weekday","start_time","end_time","created_at"],"title":"WorkSchedule"},"WorkloadItemOut":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"name":{"type":"string","title":"Name"},"assigned":{"type":"integer","title":"Assigned"},"done":{"type":"integer","title":"Done"}},"type":"object","required":["user_id","name","assigned","done"],"title":"DashboardWorkloadItem","description":"Carga de trabajo de un miembro: tareas asignadas abiertas y completadas."},"app__modules__attachments__schemas__AttachmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"organization_id":{"type":"string","format":"uuid","title":"Organization Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","format":"uuid","title":"Entity Id"},"uploaded_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Uploaded By"},"original_name":{"type":"string","title":"Original Name"},"mime_type":{"type":"string","title":"Mime Type"},"file_size":{"type":"integer","title":"File Size"},"file_url":{"type":"string","title":"File Url"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","organization_id","entity_type","entity_id","uploaded_by","original_name","mime_type","file_size","file_url","created_at"],"title":"Attachment","description":"Metadatos de un adjunto genérico (NUNCA incluye los bytes del fichero).\n\nEl fichero se sirve directamente por nginx en `file_url`\n(/uploads/attachments/<stored_name>)."},"app__modules__chat__schemas__AttachmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"filename":{"type":"string","title":"Filename"},"mime":{"type":"string","title":"Mime"},"size":{"type":"integer","title":"Size"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","filename","mime","size","created_at"],"title":"ChatAttachment"},"app__modules__chat__schemas__UnreadCountOut":{"properties":{"count":{"type":"integer","title":"Count"}},"type":"object","required":["count"],"title":"ChatUnreadCount"},"app__modules__dashboard__schemas__FinanceSummaryOut":{"properties":{"currency":{"type":"string","title":"Currency","default":"EUR"},"revenue":{"type":"number","title":"Revenue"},"invoiced":{"type":"number","title":"Invoiced"},"outstanding":{"type":"number","title":"Outstanding"},"expenses":{"type":"number","title":"Expenses"},"expenses_by_kind":{"$ref":"#/components/schemas/ExpensesByKindOut"},"net":{"type":"number","title":"Net"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["revenue","invoiced","outstanding","expenses","expenses_by_kind","net","criterio"],"title":"DashboardFinance","description":"Ingresos, gastos y neto de la org en el rango consultado.\n\n`revenue` = cobrado (facturas paid); `invoiced` = facturado (sent + paid +\noverdue, el `INCOME_INVOICE_STATUSES` de `finance/analytics.py`);\n`outstanding` = pendiente de cobro (sent + overdue); `expenses` = COSTE del\nrango; `net` = revenue − expenses.\n\nLas tres primeras cumplen `invoiced == revenue + outstanding`: lo pendiente\nde cobro es lo facturado que aún no ha entrado. Cuando `invoiced` metía el\nBORRADOR esa igualdad no se cumplía y el panel publicaba una cifra mayor que\nla de agencia, la tendencia, el ingreso por cliente, la rentabilidad y BI\nsobre los mismos datos.\n\n`expenses` (PJKT-2281) es el mismo número que `expenses` de\n`/finance/summary`: gastos normales aprobados + facturas de proveedor no\nanuladas, con el anti-doble-conteo de `finance/cost_rules.py`. Antes era\n`status='approved'` sin mirar `kind`, así que metía las facturas de\nproveedor aprobadas y dejaba fuera las `pending`/`paid` — un número que no\ncuadraba con ninguna otra pantalla. `expenses_by_kind` desglosa las dos\nmitades para que la tarjeta pueda decir explícitamente qué está sumando.\n\n`currency` (PJKT-2264) es la divisa BASE de la organización y todos los\nimportes están en ella: los agregados suman SOLO documentos emitidos en esa\ndivisa. Antes eran `SUM(...)` ciegos —euros y dólares en un mismo número— y\nel panel los pintaba sin decir de qué moneda hablaba. No se convierte nada."},"app__modules__finance__schemas__FinanceSummaryOut":{"properties":{"currency":{"type":"string","title":"Currency","default":"EUR"},"revenue":{"type":"number","title":"Revenue"},"expenses":{"type":"number","title":"Expenses"},"net":{"type":"number","title":"Net"},"outstanding":{"type":"number","title":"Outstanding"},"invoice_count":{"type":"integer","title":"Invoice Count"},"expense_count":{"type":"integer","title":"Expense Count"},"invoices_by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"Invoices By Status"},"criterio":{"$ref":"#/components/schemas/CriterioDeCalculoOut"}},"type":"object","required":["revenue","expenses","net","outstanding","invoice_count","expense_count","invoices_by_status","criterio"],"title":"FinanceSummary","description":"Resumen financiero de la organización en el rango.\n\n`expenses` (y por tanto `net`) incluye AP — P-6: gastos aprobados sin\nfactura de proveedor asociada + facturas de proveedor (`SupplierInvoice`)\nno void/rejected. Política anti-doble-conteo documentada en\n`finance/analytics.py` (sección \"Rentabilidad por proyecto\").\n\nDesde PJKT-2404 la respuesta lleva además el `criterio` con el que se\ncalcularon esos importes, incluidos los documentos que quedaron fuera y por\nqué: antes, un gasto en otra divisa desaparecía del total y el total no lo\nmencionaba."},"app__modules__notifications__schemas__UnreadCountOut":{"properties":{"count":{"type":"integer","title":"Count"}},"type":"object","required":["count"],"title":"UnreadCount","description":"Número de notificaciones sin leer del usuario en la organización actual."}},"securitySchemes":{"PAT":{"type":"http","scheme":"bearer","bearerFormat":"pjk_live_","description":"Personal Access Token minted in Settings → API keys or a project. Send as `Authorization: Bearer pjk_live_…`."}}},"security":[{"PAT":[]}]}