{"openapi":"3.1.0","info":{"title":"nexus-mcp · Grants API","description":"Agregador de oportunidades de financiamiento para LATAM. Bilingüe ES/EN. Multi-fuente normalizado.","version":"0.1.0"},"paths":{"/admin/plans":{"get":{"tags":["admin"],"summary":"List Plans","description":"Lista la config de todos los planes (para /admin/plans y pricing).","operationId":"list_plans_admin_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Plans Admin Plans Get"}}}}}}},"/admin/plans/{plan}":{"patch":{"tags":["admin"],"summary":"Update Plan","description":"Edita la config de un plan (estados visibles, features, límites).","operationId":"update_plan_admin_plans__plan__patch","parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string","title":"Plan"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Plan Admin Plans  Plan  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/auth-config":{"get":{"tags":["admin"],"summary":"Get Auth Config","description":"Estado de métodos de acceso + wiring de Supabase (solo lectura).","operationId":"get_auth_config_admin_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Auth Config Admin Auth Config Get"}}}}}},"put":{"tags":["admin"],"summary":"Update Auth Config","description":"Guarda qué métodos se muestran en /login. magic_link no es editable\n(siempre activo). Devuelve warnings si se prende un método con wiring ⚠.","operationId":"update_auth_config_admin_auth_config_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthMethodsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Update Auth Config Admin Auth Config Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/auth-config/test":{"post":{"tags":["admin"],"summary":"Test Auth Method","description":"Prueba en vivo de un método. magic_link envía un enlace real; password y\ngoogle validan el wiring de Supabase.","operationId":"test_auth_method_admin_auth_config_test_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Test Auth Method Admin Auth Config Test Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users":{"get":{"tags":["admin"],"summary":"List Users","description":"Lista profiles + flags derivados (tier/vence/estado de suscripción, disabled).\n\nSin `subscription`: paginación a nivel DB (camino común, eficiente).\nCon `subscription`: el bucket se deriva de las subs embebidas, así que se\ntrae una ventana acotada, se filtra/pagina en Python para que el total sea\ncorrecto.","operationId":"list_users_admin_users_get","parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Substring match (ILIKE) sobre email","title":"Email"},"description":"Substring match (ILIKE) sobre email"},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"enum":["subscriber","admin","superadmin"],"type":"string"},{"type":"null"}],"title":"Role"}},{"name":"subscription","in":"query","required":false,"schema":{"anyOf":[{"enum":["active","expiring_30d","inactive","none"],"type":"string"},{"type":"null"}],"description":"Filtra por estado de suscripción derivado: active|expiring_30d|inactive|none","title":"Subscription"},"description":"Filtra por estado de suscripción derivado: active|expiring_30d|inactive|none"},{"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":"object","additionalProperties":true,"title":"Response List Users Admin Users Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}":{"patch":{"tags":["admin"],"summary":"Update User","description":"Updates parciales a profiles: `role` (promover/degradar) y/o `disabled`\n(habilitar/inhabilitar la cuenta).\n\nGuards:\n- 400 si no se mandan campos.\n- 409 si el admin intenta auto-degradarse siendo el único admin.\n- 409 si el admin intenta inhabilitar su propia cuenta.\n- 404 si el user_id no existe.","operationId":"update_user_admin_users__user_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update User Admin Users  User Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"Get User","description":"Profile completo + historial de subscriptions + pagos Hotmart (reconciliación)\n+ resumen de actividad. Todo lo que el detalle admin necesita en una call.","operationId":"get_user_admin_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get User Admin Users  User Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/email":{"patch":{"tags":["admin"],"summary":"Change User Email","description":"Migra el correo de acceso (login) de un pagante, preservando su suscripción.\n\nCaso de uso: el cliente compró con el correo de otra persona (p. ej. su hija)\ny quiere entrar con el suyo. Cambia auth.users.email + profiles.email, conserva\nel correo viejo como `payer_alias` (para que las renovaciones de Hotmart no\ndupliquen la cuenta) y cierra las sesiones activas. La suscripción cuelga del\nuser_id, así que no se toca.\n\nGuards (vía EmailChangeError):\n- 422 si el correo nuevo es inválido.\n- 404 si el user_id no existe.\n- 409 si el correo nuevo es igual al actual, o ya pertenece a otra cuenta.","operationId":"change_user_email_admin_users__user_id__email_patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailChange"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Change User Email Admin Users  User Id  Email Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/bulk":{"post":{"tags":["admin"],"summary":"Bulk User Action","description":"Aplica una acción a varios usuarios. Fail-soft: un usuario que falle no\naborta el lote — devuelve el resultado por usuario.","operationId":"bulk_user_action_admin_users_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUserAction"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Bulk User Action Admin Users Bulk Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/access-link":{"post":{"tags":["admin"],"summary":"Create Access Link","description":"Enlace de acceso (magic link) de un solo uso para soporte.\n\nCaso de uso: el magic link cae en cuarentena del buzón del usuario (típico\ncon Microsoft 365). Soporte genera el enlace aquí y se lo entrega por otro\ncanal (WhatsApp). Expira ~1h, un solo uso. No expone contraseña ni cambia la\nUX pública (solo-enlace-mágico).","operationId":"create_access_link_admin_users__user_id__access_link_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Access Link Admin Users  User Id  Access Link Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/subscriptions":{"post":{"tags":["admin"],"summary":"Activate Subscription","description":"Activa (o reactiva) una subscription para `user_id`.\n\nSi ya existe una `active`, la pausa y crea una nueva. Devuelve la fila\ncreada vía la RPC `activate_subscription` (security definer).","operationId":"activate_subscription_admin_users__user_id__subscriptions_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionActivate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Activate Subscription Admin Users  User Id  Subscriptions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/subscriptions/{sub_id}":{"patch":{"tags":["admin"],"summary":"Update Subscription","description":"Pausa / reanuda / revoca / extiende. PATCH parcial.","operationId":"update_subscription_admin_users__user_id__subscriptions__sub_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"sub_id","in":"path","required":true,"schema":{"type":"string","title":"Sub Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Subscription Admin Users  User Id  Subscriptions  Sub Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources":{"get":{"tags":["admin"],"summary":"List Sources Admin","description":"Sources + últimos 5 sync_runs cada uno + open_count. Útil para health UI.","operationId":"list_sources_admin_admin_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Sources Admin Admin Sources Get"}}}}}},"post":{"tags":["admin"],"summary":"Create Source","description":"Crea una nueva source row con su config. Valida el config; conflict si\nel slug ya existe.","operationId":"create_source_admin_sources_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Source Admin Sources Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources/metrics":{"get":{"tags":["admin"],"summary":"Sources Metrics","description":"Indicadores de ingesta para /admin/sources: nuevas convocatorias cargadas\n(24h/7d/30d), tendencia diaria, temáticas (ranking global + % de cobertura) y\ndesglose por fuente. `days` controla la ventana de tendencia/temáticas.","operationId":"sources_metrics_admin_sources_metrics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sources Metrics Admin Sources Metrics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/notifications/metrics":{"get":{"tags":["admin"],"summary":"Notifications Metrics","description":"Indicadores de notificaciones por email para /admin/notifications (Fase 1):\nenvíos (24h/7d/30d), por canal, por estado, tendencia diaria y campañas de\nnewsletter por día. Aperturas/clics llegan en Fase 2 (ingesta de eventos Resend).","operationId":"notifications_metrics_admin_notifications_metrics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Notifications Metrics Admin Notifications Metrics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources/{slug}":{"patch":{"tags":["admin"],"summary":"Update Source","description":"Toggle enabled, editar metadata. NO cambia `slug` ni `id`.","operationId":"update_source_admin_sources__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Source Admin Sources  Slug  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources/{slug}/trigger-sync":{"post":{"tags":["admin"],"summary":"Trigger Sync","description":"Dispara una sync ad-hoc del source. Bloqueante — devuelve cuando termina.\n\nAcepta sources hardcoded (REGISTRY) y configurables (lookup vía DB en\nget_source()). 404 si el slug no existe en ningún lado.","operationId":"trigger_sync_admin_sources__slug__trigger_sync_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Trigger Sync Admin Sources  Slug  Trigger Sync Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources/preview":{"post":{"tags":["admin"],"summary":"Preview Source","description":"Corre fetch + normalize en vivo contra el upstream SIN persistir.\n\nÚtil para que la UI muestre \"10 opps de muestra\" antes de que el admin\nhaga submit del config. Cap a `limit` items.","operationId":"preview_source_admin_sources_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourcePreviewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Preview Source Admin Sources Preview Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/sources/infer-config":{"post":{"tags":["admin"],"summary":"Infer Config","description":"LLM (Sonnet 4.6) infiere un ConfigurableSourceConfig desde la URL y\nauto-corre el preview. El admin decide si persistir tal cual, editar o\ndescartar.\n\nTres outcomes posibles:\n- LLM devuelve config inválido (no parsea con pydantic) → 200 con\n  `validation_error` poblado y `preview=null`.\n- LLM devuelve config válido pero preview falla en vivo (selectores\n  malos) → 200 con `verdict='broken'` o 'partial' en preview.\n- Todo OK → 200 con `verdict='good'`, samples poblados, listo para\n  submit a POST /admin/sources.","operationId":"infer_config_admin_sources_infer_config_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferConfigRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Infer Config Admin Sources Infer Config Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/coverage":{"get":{"tags":["admin"],"summary":"Coverage","description":"Métricas de cobertura geográfica LATAM. Devuelve total opps open,\nsources habilitadas, conteo por país, gaps (países sin opps) y\ncoverage_rate (% de países LATAM cubiertos).","operationId":"coverage_admin_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Coverage Admin Coverage Get"}}}}}}},"/admin/opportunities/audit":{"get":{"tags":["admin"],"summary":"Opportunities Audit","description":"Auditoría temporal de oportunidades + radar de completitud. Filtra por\nventana (presets o rango custom) sobre created_at y devuelve la lista con\ndrill-through, agregados por fuente/monto/temática/país, el funnel del\nperíodo, completeness_score por oportunidad + promedio, y KPIs comerciales\ncopiables. Solo lectura.","operationId":"opportunities_audit_admin_opportunities_audit_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"Ventana: 24h | 7d | 30d | all | custom","default":"7d","title":"Period"},"description":"Ventana: 24h | 7d | 30d | all | custom"},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO inicio (solo period=custom)","title":"From"},"description":"ISO inicio (solo period=custom)"},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO fin (solo period=custom)","title":"To"},"description":"ISO fin (solo period=custom)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Opportunities Audit Admin Opportunities Audit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/analytics":{"get":{"tags":["admin"],"summary":"Analytics","description":"Analítica de intención: qué buscan (keywords + semánticas), qué búsquedas\nquedan SIN resultados (brechas de catálogo), qué temas/países declaran los\nperfiles y qué oportunidades enganchan más. Insumo para decidir qué cargar.","operationId":"analytics_admin_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Ventana en días","default":30,"title":"Days"},"description":"Ventana en días"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Admin Analytics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/analytics/gaps":{"get":{"tags":["admin"],"summary":"Analytics Gaps","description":"Brecha demanda-vs-oferta (temática, monto, país): cruza qué piden los\nusuarios (perfiles, búsquedas, guardadas) contra qué tiene el catálogo, y\nemite una lista priorizada de objetivos de ingesta. Insumo directo para el\nequipo de carga: hacia dónde buscar nuevas oportunidades.","operationId":"analytics_gaps_admin_analytics_gaps_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Ventana en días","default":30,"title":"Days"},"description":"Ventana en días"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Gaps Admin Analytics Gaps Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/analytics/demand-scan":{"get":{"tags":["admin"],"summary":"Analytics Demand Scan","description":"Objetivos de búsqueda candidatos (demanda del cliente → metas de ingesta),\ncon DETALLE completo (incluye conteo de pagantes por celda). Espejo interno del\ntool MCP `scan_client_demand`, pero sin recortar la evidencia. Insumo del panel\ny del circuito curated-ingest.","operationId":"analytics_demand_scan_admin_analytics_demand_scan_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Ventana de demanda en días","default":30,"title":"Days"},"description":"Ventana de demanda en días"},{"name":"min_gap","in":"query","required":false,"schema":{"type":"number","minimum":0,"description":"Umbral mínimo de gap_score","default":0.0,"title":"Min Gap"},"description":"Umbral mínimo de gap_score"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Máximo de objetivos","default":50,"title":"Limit"},"description":"Máximo de objetivos"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Demand Scan Admin Analytics Demand Scan Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/search-goals":{"get":{"tags":["admin"],"summary":"List Search Goals","description":"Objetivos de búsqueda persistidos (cola de ingesta), por estado y prioridad.","operationId":"list_search_goals_admin_search_goals_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por estado","title":"Status"},"description":"Filtrar por estado"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Search Goals Admin Search Goals Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Create Search Goal","description":"Captura un objetivo de búsqueda (idempotente por celda tema×país×banda).","operationId":"create_search_goal_admin_search_goals_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchGoalCapture"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Search Goal Admin Search Goals Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/search-goals/{goal_id}":{"patch":{"tags":["admin"],"summary":"Update Search Goal","description":"Cambia el estado de un objetivo (open → in_ingest → satisfied/dismissed).","operationId":"update_search_goal_admin_search_goals__goal_id__patch","parameters":[{"name":"goal_id","in":"path","required":true,"schema":{"type":"string","title":"Goal Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchGoalStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Search Goal Admin Search Goals  Goal Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/commercial":{"get":{"tags":["admin"],"summary":"Commercial","description":"Analítica comercial: ingreso REAL (neto productor USD) + bruto + leakage,\nMRR/ARR normalizado, ventas por ciclo (mensual/anual) y alta/renovación,\nseries de tiempo, suscripciones, perfil del comprador, activación y bloque de\npotencial (ARR run-rate, pipeline de renovación, neto vs precio lista, palanca\nde conversión). Con `reconcile=true` cruza contra la Hotmart Sales API para\ndetectar ventas no capturadas (más lento).","operationId":"commercial_admin_commercial_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Ventana en días","default":30,"title":"Days"},"description":"Ventana en días"},{"name":"reconcile","in":"query","required":false,"schema":{"type":"boolean","description":"Reconciliar vs Hotmart Sales API","default":false,"title":"Reconcile"},"description":"Reconciliar vs Hotmart Sales API"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Commercial Admin Commercial Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/customer-success":{"get":{"tags":["admin"],"summary":"Customer Success","description":"Panel de Customer Success: una fila por pagante activo con cs_score, bucket\n(ok|gap_catalogo|dato_incompleto|inactivo|sin_perfil), diagnóstico y accionables.\n\nLEE el snapshot precalculado por el cron /api/cron/cs-snapshot (el barrido en\nvivo tarda ~95s y no cabe en los 60s de la función web). `snapshot_at` indica\nla frescura; `pending=true` si el cron aún no corrió por primera vez.","operationId":"customer_success_admin_customer_success_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Customer Success Admin Customer Success Get"}}}}}}},"/admin/customer-success/country-coverage":{"get":{"tags":["admin"],"summary":"Customer Success Country Coverage","description":"Semáforo de cobertura por país-cliente (garantía de oferta local).\n\nPor cada país LATAM con cliente pagante activo: nº de clientes, opps abiertas\npaís-específico (excluye '*'), perfiles por debajo del umbral de matches, y un\nsemáforo verde/amarillo/rojo. LEE el snapshot precalculado (mismo cron que la\nlista); `snapshot_at` indica la frescura.","operationId":"customer_success_country_coverage_admin_customer_success_country_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Customer Success Country Coverage Admin Customer Success Country Coverage Get"}}}}}}},"/admin/customer-success/{user_id}":{"get":{"tags":["admin"],"summary":"Customer Success Detail","description":"Detalle de un pagante: diagnóstico + actividad COMPLETA (historial de búsquedas,\nperfiles con issues por campo, línea de tiempo) + cola de accionables + última\nrecomendación inteligente persistida.\n\nEn vivo para UN usuario (rápido); la actividad completa solo se arma aquí, no en\nel barrido/snapshot.","operationId":"customer_success_detail_admin_customer_success__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Customer Success Detail Admin Customer Success  User Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/customer-success/{user_id}/recommendation":{"post":{"tags":["admin"],"summary":"Customer Success Recommendation","description":"Genera (o devuelve cache) la recomendación inteligente del cliente: diagnóstico +\nqué ajustar en el perfil + borrador de mensaje + oportunidades vigentes que encajan.\n\nLLM BAJO DEMANDA (solo al pulsar el botón), enforce_budget=False. `force=true`\nregenera. Las oportunidades se eligen de candidatas vigentes reales (no se inventan).","operationId":"customer_success_recommendation_admin_customer_success__user_id__recommendation_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"Regenerar aunque haya una en cache","default":false,"title":"Force"},"description":"Regenerar aunque haya una en cache"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Customer Success Recommendation Admin Customer Success  User Id  Recommendation Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/customer-success/actionables/{actionable_id}":{"patch":{"tags":["admin"],"summary":"Update Actionable","description":"Marca un accionable como atendido (done), descartado (dismissed) o reabierto.","operationId":"update_actionable_admin_customer_success_actionables__actionable_id__patch","parameters":[{"name":"actionable_id","in":"path","required":true,"schema":{"type":"string","title":"Actionable Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionableStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Actionable Admin Customer Success Actionables  Actionable Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap":{"get":{"tags":["admin"],"summary":"Roadmap","description":"Tablero 4DX: anclas (North Star + WIGs + lead measures) con su progreso,\ntarjetas por columna del ciclo de vida, presión por ancla, huérfanas (trabajo\nsin meta) y el buzón. Lectura para cualquier admin.","operationId":"roadmap_admin_roadmap_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Roadmap Admin Roadmap Get"}}}}}}},"/admin/roadmap/anchors":{"post":{"tags":["admin"],"summary":"Create Anchor","description":"Crea un ancla 4DX. Solo superadmin (definir el destino es su trabajo).","operationId":"create_anchor_admin_roadmap_anchors_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnchorCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Anchor Admin Roadmap Anchors Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap/anchors/{slug}":{"patch":{"tags":["admin"],"summary":"Update Anchor","description":"Edita un ancla (típicamente metric_current = mover el marcador).","operationId":"update_anchor_admin_roadmap_anchors__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnchorUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Anchor Admin Roadmap Anchors  Slug  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap/cards":{"post":{"tags":["admin"],"summary":"Create Card","description":"Crea una tarjeta de feature enlazada a sus anclas. Solo superadmin.","operationId":"create_card_admin_roadmap_cards_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Card Admin Roadmap Cards Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap/cards/{slug}":{"patch":{"tags":["admin"],"summary":"Update Card","description":"Edita una tarjeta a mano (override del protocolo): mover estado o ajustar\nhilos. El camino normal es scripts/roadmap.py; esto es el escape humano.","operationId":"update_card_admin_roadmap_cards__slug__patch","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Card Admin Roadmap Cards  Slug  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap/suggestions":{"post":{"tags":["admin"],"summary":"Create Suggestion","description":"Captura una idea/bug/feature al buzón. Cualquier admin (es el insumo del\nplan que el equipo aporta).","operationId":"create_suggestion_admin_roadmap_suggestions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Suggestion Admin Roadmap Suggestions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/roadmap/suggestions/{suggestion_id}":{"patch":{"tags":["admin"],"summary":"Update Suggestion","description":"Tría una sugerencia. status='promoted' crea una tarjeta a partir de ella.","operationId":"update_suggestion_admin_roadmap_suggestions__suggestion_id__patch","parameters":[{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Suggestion Admin Roadmap Suggestions  Suggestion Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/subscriptions/audit":{"get":{"tags":["admin"],"summary":"Subscriptions Audit","description":"Inconsistencias pago↔suscripción para revisión manual. Categorías:\n\n- active_no_payment : sub activa provista por sistema (activated_by NULL) sin\n                      PURCHASE_APPROVED en payment_events → pago no verificable.\n- payment_no_active : PURCHASE_APPROVED en payment_events cuyo email no tiene\n                      ninguna sub activa → pago sin acceso provisto.\n- refund_still_active: REFUNDED/CHARGEBACK pero el usuario sigue activo.\n- expiring_30d      : subs activas que vencen en ≤30 días.\n\nReutiliza `_sub_is_active()` como única fuente de verdad de validez.","operationId":"subscriptions_audit_admin_subscriptions_audit_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Subscriptions Audit Admin Subscriptions Audit Get"}}}}}}},"/admin/dashboard":{"get":{"tags":["admin"],"summary":"Dashboard","description":"Snapshot consolidado para la UI admin home. Junta en una sola call:\n- catalog: opps por status + top sources por open_count.\n- sources_health: cada source con stale flag (last_success_at > 48h).\n- coverage: gaps por país (reusa coverage_metrics).\n- users: counts (total, active subscribers, admins, pending proposals).\n- recent_activity: top 15 audit_events recientes.","operationId":"dashboard_admin_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Dashboard Admin Dashboard Get"}}}}}}},"/admin/audit-events":{"get":{"tags":["admin"],"summary":"List Audit Events","description":"Auditoría: acciones privilegiadas con timestamp y metadata. Solo admin.","operationId":"list_audit_events_admin_audit_events_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtrar por actor","title":"User Id"},"description":"Filtrar por actor"},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"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":"object","additionalProperties":true,"title":"Response List Audit Events Admin Audit Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities":{"post":{"tags":["admin"],"summary":"Create Manual Opportunity","description":"Inserta/actualiza una oportunidad one-off reusando ManualSource.run().\n\nReusa upsert (dedup por canonical_hash), cálculo USD, sync_run y enrichment.","operationId":"create_manual_opportunity_admin_opportunities_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualOpportunityCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Manual Opportunity Admin Opportunities Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/check-duplicate":{"post":{"tags":["admin"],"summary":"Check Duplicate Opportunity","description":"Devuelve oportunidades ya existentes que podrían duplicar este draft.\n\nCruce por url_source, canonical_hash y similitud de título (pg_trgm). Solo lee;\nla extensión Chrome lo llama al capturar y al insertar para avisar al admin.","operationId":"check_duplicate_opportunity_admin_opportunities_check_duplicate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckDuplicateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Check Duplicate Opportunity Admin Opportunities Check Duplicate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/by-url":{"get":{"tags":["admin"],"summary":"Get Opportunity By Source Url","description":"Busca la opp cuya `url_source` == `url` (la página OFICIAL) y la devuelve\nlista para editar. Permite editar entrando directo a la página oficial —\ndonde está la info— en vez de a la ficha de Nexus. 404 si no existe.","operationId":"get_opportunity_by_source_url_admin_opportunities_by_url_get","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":2048,"title":"Url"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Opportunity By Source Url Admin Opportunities By Url Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/{opportunity_id}":{"get":{"tags":["admin"],"summary":"Get Opportunity For Edit","description":"Devuelve una opp con todos sus campos editables + detalle + completitud.\n\nEs el GET que usa la extensión para CARGAR una opp ya existente antes de\neditarla (por UUID). Solo lee.","operationId":"get_opportunity_for_edit_admin_opportunities__opportunity_id__get","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Opportunity For Edit Admin Opportunities  Opportunity Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin"],"summary":"Update Opportunity","description":"Actualiza los campos editables de una opp existente, por UUID.\n\nConserva la identidad (`source_id`, `source_native_id`) — solo edita el\ncontenido. Recalcula `canonical_hash`, los montos USD y `canonical_tags` con\nlos mismos helpers que el upsert (`compute_canonical_hash`, `to_usd`,\n`normalize_tags`), y persiste `detail_sections` (`build_detail_update`). No\ncrea filas nuevas. El cron `populate-embeddings` re-embebe al cambiar el\ncontenido (idempotente por content_hash).","operationId":"update_opportunity_admin_opportunities__opportunity_id__patch","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOpportunityRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Opportunity Admin Opportunities  Opportunity Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/extract-from-url":{"post":{"tags":["admin"],"summary":"Extract Opportunity","description":"Extrae con LLM los campos de UNA oportunidad desde su URL. NO persiste.\n\nDevuelve un borrador estilo `OpportunityIn` (con `source_native_id` y\n`url_source` ya inyectados) para que el admin lo revise/edite y luego lo\npublique vía `POST /admin/opportunities`. Si el draft no valida contra el\nschema, devolvemos `validation_error` en vez de fallar — el front lo corrige.","operationId":"extract_opportunity_admin_opportunities_extract_from_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractFromUrlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Extract Opportunity Admin Opportunities Extract From Url Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/merge-official":{"post":{"tags":["admin"],"summary":"Merge Official","description":"Pasada B: extrae el RFP oficial y lo mergea sobre el draft del agregador.\n\nEl resultado tiene `url_source`/`source_native_id` del sitio oficial (dedup\ncanónico) + la clasificación del agregador. Si el oficial no se lee bien\n(SPA, 404), devolvemos `official_warning` pero igual el draft mergeado con\nel link oficial correcto — nunca se pierde info.","operationId":"merge_official_admin_opportunities_merge_official_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeOfficialRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Merge Official Admin Opportunities Merge Official Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/read-pdf":{"post":{"tags":["admin"],"summary":"Read Pdf Opportunity","description":"Descarga el PDF de bases, extrae sus campos y los FUSIONA con el draft.\n\nOn-demand y consciente del costo: solo corre cuando el admin lo pide. Reusa el\nlector `pdf_to_text` y el merge `_merge_aggregator_official` (los valores\npresentes del draft ganan; el PDF rellena los vacíos). NO persiste.","operationId":"read_pdf_opportunity_admin_opportunities_read_pdf_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadPdfRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Read Pdf Opportunity Admin Opportunities Read Pdf Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/{opportunity_id}/documents/ingest":{"post":{"tags":["admin"],"summary":"Ingest Document","description":"Descarga, guarda, parsea y extrae los hechos de UN documento (PDF).\n\nProcesa un documento a la vez (la extensión itera client-side y muestra\nprogreso por-doc). Idempotente por (opp, url, sha): re-ingesta del mismo\nbinario no re-descarga ni re-llama al LLM. Nunca lanza 5xx por errores de\ndescarga/parseo/budget — devuelve la fila con `status`/`error` y HTTP 200,\npara no tumbar el batch de la extensión.\n\nWrapper delgado sobre `core.document_ingest.ingest_one_document` (la lógica\nde descarga/guardado/parseo/análisis vive ahí, reutilizada por\n`core.fit_dossier.ensure_documents`); esta capa solo valida la request y\nadapta la respuesta (signed_url/warning).","operationId":"ingest_document_admin_opportunities__opportunity_id__documents_ingest_post","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestDocumentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Ingest Document Admin Opportunities  Opportunity Id  Documents Ingest Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/opportunities/{opportunity_id}/deep-research":{"post":{"tags":["admin"],"summary":"Deep Research","description":"Sintetiza el brief de investigación + mergea los hechos de los documentos\nanalizados en detail_sections, recalcula completitud y (si persist) persiste.\n\nLos hechos por-doc ya están extraídos (gratis aquí); el único costo LLM es la\nsíntesis del brief. Si el brief falla por budget, devolvemos los hechos\nmergeados + completitud SIN brief y un warning (HTTP 200), y si persist igual\nse guardan los hechos (ya estaban calculados). Nunca se pierde info.","operationId":"deep_research_admin_opportunities__opportunity_id__deep_research_post","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeepResearchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Deep Research Admin Opportunities  Opportunity Id  Deep Research Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/featured":{"get":{"tags":["admin"],"summary":"List Featured","description":"Lista las destacadas (incluye programadas/expiradas — vista admin).","operationId":"list_featured_admin_featured_get","parameters":[{"name":"rail","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtra por rail (default todos)","title":"Rail"},"description":"Filtra por rail (default todos)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Featured Admin Featured Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Create Featured","description":"Destaca una oportunidad en un rail editorial del feed.","operationId":"create_featured_admin_featured_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeaturedCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Featured Admin Featured Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/featured/{featured_id}":{"patch":{"tags":["admin"],"summary":"Update Featured","description":"Edita una destacada (copy/orden/ventana de fechas).","operationId":"update_featured_admin_featured__featured_id__patch","parameters":[{"name":"featured_id","in":"path","required":true,"schema":{"type":"string","title":"Featured Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeaturedUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Featured Admin Featured  Featured Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Featured","description":"Quita una oportunidad de la curaduría editorial.","operationId":"delete_featured_admin_featured__featured_id__delete","parameters":[{"name":"featured_id","in":"path","required":true,"schema":{"type":"string","title":"Featured Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Featured Admin Featured  Featured Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/llm-budget":{"get":{"tags":["admin"],"summary":"Llm Budget Status","operationId":"llm_budget_status_admin_llm_budget_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Llm Budget Status Admin Llm Budget Get"}}}}}}},"/admin/llm-budget/settings":{"post":{"tags":["admin"],"summary":"Update Llm Budget Settings","operationId":"update_llm_budget_settings_admin_llm_budget_settings_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LlmBudgetSettingsIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Update Llm Budget Settings Admin Llm Budget Settings Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/encaje-access":{"get":{"tags":["admin"],"summary":"Get Encaje Access","description":"Allowlist actual de planes con acceso a ENCAJE (admins siempre pasan;\nver `core.encaje_access`).","operationId":"get_encaje_access_admin_encaje_access_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Encaje Access Admin Encaje Access Get"}}}}}},"put":{"tags":["admin"],"summary":"Update Encaje Access","description":"Reemplaza la allowlist de planes con acceso a ENCAJE (beta). No afecta\na admins/superadmins, que siempre tienen acceso.","operationId":"update_encaje_access_admin_encaje_access_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncajeAccessIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Update Encaje Access Admin Encaje Access Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/saved_searches":{"get":{"tags":["saved_searches"],"summary":"List My Saved Searches","description":"Lista las saved_searches del usuario actual. 401 si no hay JWT válido.","operationId":"list_my_saved_searches_me_saved_searches_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List My Saved Searches Me Saved Searches Get"}}}}}},"post":{"tags":["saved_searches"],"summary":"Create Saved Search","description":"Crea una saved_search. **Requiere plan con `can_saved_searches`** (Básico+).\n\nEl cron `send-alerts` (Fase 3c) la procesará en su próxima corrida.","operationId":"create_saved_search_me_saved_searches_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSearchCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Saved Search Me Saved Searches Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/saved_searches/{search_id}":{"patch":{"tags":["saved_searches"],"summary":"Update Saved Search","description":"Update parcial sobre name/filters/notify_*. Solo el dueño puede editar.","operationId":"update_saved_search_me_saved_searches__search_id__patch","parameters":[{"name":"search_id","in":"path","required":true,"schema":{"type":"string","title":"Search Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedSearchUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Saved Search Me Saved Searches  Search Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["saved_searches"],"summary":"Delete Saved Search","operationId":"delete_saved_search_me_saved_searches__search_id__delete","parameters":[{"name":"search_id","in":"path","required":true,"schema":{"type":"string","title":"Search Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/saved_opportunities":{"get":{"tags":["saved_opportunities"],"summary":"List My Saved Opportunities","description":"Lista las favoritas del usuario (enriquecidas con la opp). 401 sin JWT.\n\n`profile_id` opcional → filtra por el perfil de origen.","operationId":"list_my_saved_opportunities_me_saved_opportunities_get","parameters":[{"name":"profile_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List My Saved Opportunities Me Saved Opportunities Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["saved_opportunities"],"summary":"Save Opportunity","description":"Guarda (favoritea) una opp. Upsert idempotente: reguardar no falla.","operationId":"save_opportunity_me_saved_opportunities_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedOppCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Save Opportunity Me Saved Opportunities Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/saved_opportunities/{opportunity_id}":{"delete":{"tags":["saved_opportunities"],"summary":"Unsave Opportunity","description":"Quita una opp de favoritas. Idempotente: 204 aunque no existiera.","operationId":"unsave_opportunity_me_saved_opportunities__opportunity_id__delete","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/notification-preferences":{"get":{"tags":["notifications"],"summary":"Get Notification Preferences","description":"Preferencias del usuario actual. Sin fila → defaults (suscrito).","operationId":"get_notification_preferences_me_notification_preferences_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferences"}}}}}},"put":{"tags":["notifications"],"summary":"Update Notification Preferences","description":"Upsert de las preferencias del usuario actual.","operationId":"update_notification_preferences_me_notification_preferences_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferences"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferences"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/api-tokens":{"get":{"tags":["api_tokens"],"summary":"List My Tokens","description":"Lista mis tokens (sin plaintext — solo metadata).","operationId":"list_my_tokens_me_api_tokens_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List My Tokens Me Api Tokens Get"}}}}}},"post":{"tags":["api_tokens"],"summary":"Create Token","description":"Genera un nuevo token. **Requiere subscription activa** (o admin).\n\nEl plaintext se devuelve UNA VEZ; el server solo persiste el hash.","operationId":"create_token_me_api_tokens_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Token Me Api Tokens Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/api-tokens/{token_id}/revoke":{"post":{"tags":["api_tokens"],"summary":"Revoke Token","description":"Revocación soft: marca revoked_at. El token deja de validar.","operationId":"revoke_token_me_api_tokens__token_id__revoke_post","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Token Me Api Tokens  Token Id  Revoke Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/me/suggest-source":{"get":{"tags":["source_proposals"],"summary":"List My Suggestions","description":"Lista las propuestas hechas por el caller. 401 sin JWT.","operationId":"list_my_suggestions_me_suggest_source_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List My Suggestions Me Suggest Source Get"}}}}}},"post":{"tags":["source_proposals"],"summary":"Suggest Source","description":"Crea una source_proposal y, si auto_infer=True, infiere el config con\nel LLM y CORRE EL PREVIEW en vivo para clasificarla con un `verdict`.\n\nEl colaborador no-técnico solo pega una URL: este endpoint le devuelve en\nlenguaje plano si funciona (✅), funciona parcialmente (⚠️) o no se pudo\nleer (❌, escalada a DEV Engine). Todo es fail-soft: ante cualquier error\nla propuesta igual se guarda marcada para revisión técnica.","operationId":"suggest_source_me_suggest_source_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceSuggestRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Suggest Source Me Suggest Source Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/source-proposals":{"get":{"tags":["source_proposals_admin"],"summary":"List Proposals","operationId":"list_proposals_admin_source_proposals_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","approved","rejected","duplicate"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"needs_engineering","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Needs Engineering"}},{"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":"object","additionalProperties":true,"title":"Response List Proposals Admin Source Proposals Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["source_proposals_admin"],"summary":"Create Proposal","description":"Guarda una propuesta desde el modo 'Proponer' del wizard admin.\n\nCorre el preview server-side (autoritativo) para fijar verdict y\nneeds_engineering, y la deja pending para revisión/aprobación posterior.","operationId":"create_proposal_admin_source_proposals_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalCreateFromWizard"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Proposal Admin Source Proposals Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/source-proposals/{proposal_id}/approve":{"post":{"tags":["source_proposals_admin"],"summary":"Approve Proposal","operationId":"approve_proposal_admin_source_proposals__proposal_id__approve_post","parameters":[{"name":"proposal_id","in":"path","required":true,"schema":{"type":"string","title":"Proposal Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalApprove"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Approve Proposal Admin Source Proposals  Proposal Id  Approve Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/source-proposals/{proposal_id}/reject":{"post":{"tags":["source_proposals_admin"],"summary":"Reject Proposal","operationId":"reject_proposal_admin_source_proposals__proposal_id__reject_post","parameters":[{"name":"proposal_id","in":"path","required":true,"schema":{"type":"string","title":"Proposal Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalReject"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Reject Proposal Admin Source Proposals  Proposal Id  Reject Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/curated/opportunities":{"post":{"tags":["curated"],"summary":"Ingest Opportunities","description":"Inyecta un lote de convocatorias curadas (idempotente por source_native_id).\n\nServidor autoritativo: deriva `status` desde `deadline_at` y rechaza ítems\nsin tags canónicos. Devuelve resultado por-ítem (created|updated|rejected).\nSin rate-limit por-usuario (es un token de servicio); el cap de lote (50) y\nla cadencia diaria son el throttle.","operationId":"ingest_opportunities_v1_curated_opportunities_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CuratedIngestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CuratedIngestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/curated/schema":{"get":{"tags":["curated"],"summary":"Curated Schema","description":"Contrato (JSON Schema de OpportunityIn) + el vocabulario canónico de tags.\n\nEl skill valida cada candidata contra `schema` y elige `thematic_tags` de\n`canonical_tags` ANTES de enviar — así la data llega lista y el servidor no\nla rechaza. Público (no expone datos).","operationId":"curated_schema_v1_curated_schema_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Curated Schema V1 Curated Schema Get"}}}}}}},"/v1/profiles":{"get":{"tags":["profiles"],"summary":"List My Profiles","description":"Lista los match_profiles del usuario actual (activos y archivados).","operationId":"list_my_profiles_v1_profiles_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List My Profiles V1 Profiles Get"}}}}}},"post":{"tags":["profiles"],"summary":"Create Profile","description":"Crea un match_profile. Requiere plan con `can_profile_matching` (Pro).\n\nAplica cap de 3 perfiles activos (admin bypass) y encola el embedding —\nla response retorna `embedding_status='pending'`. Delega en el servicio\ncompartido `matching.create_match_profile` (fuente única REST + MCP).","operationId":"create_profile_v1_profiles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchProfileIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Profile V1 Profiles Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/profiles/{profile_id}":{"get":{"tags":["profiles"],"summary":"Get Profile","description":"Devuelve un perfil específico. 404 si no es del user (RLS via filtro explícito).","operationId":"get_profile_v1_profiles__profile_id__get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Profile V1 Profiles  Profile Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["profiles"],"summary":"Update Profile","description":"Update parcial. Si cambia narrative/tags/extras, re-encola embedding.","operationId":"update_profile_v1_profiles__profile_id__patch","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Profile V1 Profiles  Profile Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["profiles"],"summary":"Archive Or Delete Profile","description":"Eliminar perfil. Default soft (status='archived'); ?hard=true elimina la fila.\n\n- Soft (default): libera cupo de 3 activos, conserva historial.\n- Hard (?hard=true): borra match_profiles + cascade a embeddings, jobs, feedback,\n  runs. No reversible. Pensado para perfiles de prueba.","operationId":"archive_or_delete_profile_v1_profiles__profile_id__delete","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}},{"name":"hard","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Hard"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/profiles/{profile_id}/refresh-embedding":{"post":{"tags":["profiles"],"summary":"Refresh Embedding","description":"Genera el embedding del perfil sincrónicamente (sin esperar al cron).\n\nÚtil después de crear/editar un perfil para que el usuario vea matches\nsemánticos inmediatamente, en vez de esperar 15 min.\n\nIdempotente: si content_hash no cambió, retorna `status='unchanged'` sin\nre-embeddear.","operationId":"refresh_embedding_v1_profiles__profile_id__refresh_embedding_post","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Refresh Embedding V1 Profiles  Profile Id  Refresh Embedding Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/profiles/{profile_id}/quality":{"get":{"tags":["profiles"],"summary":"Profile Quality","description":"Audit de calidad del perfil — heurísticas + cobertura esperada.\n\nDevuelve issues (severity warn/info), score 0-100, y conteos de cuántas\nopportunities pasan cada hard filter por separado y juntos.\n\nÚtil para mostrar en la UI por qué el perfil podría dar pocos matches.","operationId":"profile_quality_v1_profiles__profile_id__quality_get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Profile Quality V1 Profiles  Profile Id  Quality Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/profiles/extract":{"post":{"tags":["profiles"],"summary":"Extract Profile","description":"Extrae un draft de match_profile desde PDF, URL, texto o Anexo SGR.\n\nDiscriminación de path:\n  - Content-Type multipart/form-data → file + source_type_form\n  - application/json → body con {source_type, url|text}","operationId":"extract_profile_v1_profiles_extract_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_extract_profile_v1_profiles_extract_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Extract Profile V1 Profiles Extract Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/match":{"post":{"tags":["match"],"summary":"Match","description":"Ejecuta matching. Discrimina por payload + auth state.","operationId":"match_v1_match_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Match V1 Match Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/match/feedback":{"post":{"tags":["match"],"summary":"Submit Feedback","description":"Up/down/neutralizar un rating sobre un (profile, opportunity).\n\nrating=1 (up) | rating=-1 (down) | rating=0 (delete existing).\nValidamos ownership del profile (defense-in-depth, RLS también).","operationId":"submit_feedback_v1_match_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Submit Feedback V1 Match Feedback Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events":{"post":{"tags":["events"],"summary":"Log Events","description":"Registra un lote de eventos de interacción. Best-effort (siempre 200).","operationId":"log_events_v1_events_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventsBatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Log Events V1 Events Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/feed":{"get":{"tags":["feed"],"summary":"Get Feed","operationId":"get_feed_v1_feed_get","parameters":[{"name":"tab","in":"query","required":false,"schema":{"anyOf":[{"enum":["for_you","todas","guardadas"],"type":"string"},{"type":"null"}],"description":"for_you | todas | guardadas","title":"Tab"},"description":"for_you | todas | guardadas"},{"name":"lang","in":"query","required":false,"schema":{"type":"string","description":"es|en — presentación de títulos/resúmenes","default":"es","title":"Lang"},"description":"es|en — presentación de títulos/resúmenes"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":24,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Búsqueda de texto libre","title":"Q"},"description":"Búsqueda de texto libre"},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"title":"Country"}},{"name":"country_exact","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Country Exact"}},{"name":"source_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Source Slug"}},{"name":"org_type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Org Type"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV: open,forecast,closed","title":"Status"},"description":"CSV: open,forecast,closed"},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"enum":["latam_first","relevance_first","deadline_asc","newest"],"type":"string"},{"type":"null"}],"title":"Sort"}},{"name":"max_amount_usd","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"title":"Max Amount Usd"}},{"name":"deadline_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deadline After"}},{"name":"deadline_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deadline Before"}},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Active Only"}},{"name":"anon_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UUID de cliente anónimo (localStorage)","title":"Anon Id"},"description":"UUID de cliente anónimo (localStorage)"},{"name":"limit_per_rail","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"default":12,"title":"Limit Per Rail"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Feed V1 Feed Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/feed/counts":{"get":{"tags":["feed"],"summary":"Get Feed Counts","description":"Badges de las tres pestañas. Boundary propio: no bloquea la lista.\n\nToma los mismos filtros que la lista — un badge que ignora los filtros\ncontradice lo que el usuario tiene delante.","operationId":"get_feed_counts_v1_feed_counts_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"title":"Country"}},{"name":"country_exact","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Country Exact"}},{"name":"source_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Source Slug"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV: open,forecast,closed","title":"Status"},"description":"CSV: open,forecast,closed"},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Active Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"anyOf":[{"type":"integer"},{"type":"null"}]},"title":"Response Get Feed Counts V1 Feed Counts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/fit-dossiers/access":{"get":{"tags":["fit_dossiers"],"summary":"Encaje Access","description":"Capability check para la UI: ¿puede este caller usar ENCAJE?\n\nNo es un gate duro — anon/no-access devuelve `False`, no 401/403 — para\nque el front decida si muestra el botón/CTA de ENCAJE sin tener que\nmanejar errores.","operationId":"encaje_access_v1_fit_dossiers_access_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Response Encaje Access V1 Fit Dossiers Access Get"}}}}}}},"/v1/fit-dossiers":{"post":{"tags":["fit_dossiers"],"summary":"Create Dossier","description":"Genera (o regenera, idempotente por profile_id+opportunity_id) un fit dossier.","operationId":"create_dossier_v1_fit_dossiers_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDossierRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Dossier V1 Fit Dossiers Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["fit_dossiers"],"summary":"List Dossiers","description":"Lista los dossiers del subscriber, opcionalmente filtrados por oportunidad.","operationId":"list_dossiers_v1_fit_dossiers_get","parameters":[{"name":"opportunity_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opportunity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response List Dossiers V1 Fit Dossiers Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/fit-dossiers/{dossier_id}":{"get":{"tags":["fit_dossiers"],"summary":"Get Dossier","operationId":"get_dossier_v1_fit_dossiers__dossier_id__get","parameters":[{"name":"dossier_id","in":"path","required":true,"schema":{"type":"string","title":"Dossier Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Dossier V1 Fit Dossiers  Dossier Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/fit-dossiers/{dossier_id}/export":{"get":{"tags":["fit_dossiers"],"summary":"Export Dossier","description":"Descarga la ficha en Markdown crudo o PDF (fpdf2).","operationId":"export_dossier_v1_fit_dossiers__dossier_id__export_get","parameters":[{"name":"dossier_id","in":"path","required":true,"schema":{"type":"string","title":"Dossier Id"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","pattern":"^(md|pdf)$","default":"md","title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/fit-dossiers/{dossier_id}/handoff":{"get":{"tags":["fit_dossiers"],"summary":"Handoff","description":"Payload de \"entrega\" del dossier: encaje + brief de oportunidad, pensado\npara consumo aguas abajo (p. ej. un asistente de redacción de propuesta).\nReservado a usuarios con handoff_access.enabled=true (migración 0086).","operationId":"handoff_v1_fit_dossiers__dossier_id__handoff_get","parameters":[{"name":"dossier_id","in":"path","required":true,"schema":{"type":"string","title":"Dossier Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Handoff V1 Fit Dossiers  Dossier Id  Handoff Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/hotmart":{"post":{"tags":["webhooks"],"summary":"Hotmart Webhook","operationId":"hotmart_webhook_webhooks_hotmart_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Hotmart Webhook Webhooks Hotmart Post"}}}}}}},"/webhooks/resend":{"post":{"tags":["webhooks"],"summary":"Resend Webhook","description":"Ingesta de eventos de Resend (delivered/opened/clicked/bounced...).\n\nVerifica la firma Svix con RESEND_WEBHOOK_SECRET y registra el evento en\nemail_events (idempotente por svix-id). Público: la autenticidad la da la\nfirma, no `require_admin`. Fail-soft: nunca 500 por un evento suelto.","operationId":"resend_webhook_webhooks_resend_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Resend Webhook Webhooks Resend Post"}}}}}}},"/":{"get":{"tags":["meta"],"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Root  Get"}}}}}}},"/health":{"get":{"tags":["meta"],"summary":"Health","description":"Health check con verificación de dependencias.\n\nDevuelve 200 si la base de datos responde, 503 si no — así el monitoreo\nexterno detecta una caída real (no solo que el proceso está vivo). Incluye\nseñales operativas best-effort (headroom de presupuesto LLM y frescura de\nsync) para anticipar degradación antes de que llegue como ticket.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}},"/stats":{"get":{"tags":["meta"],"summary":"Stats","description":"Counts globales para la home: total_open_opps, total_sources,\ntotal_countries, last_sync_at. Fuente: queries.home_stats().","operationId":"stats_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stats Stats Get"}}}}}}},"/plans":{"get":{"tags":["meta"],"summary":"Public Plans","description":"Planes públicos para la página de precios (/planes).\n\nSolo los `is_public`, ordenados por `sort_order`, exponiendo lo necesario\npara renderizar tarjetas + botón de compra Hotmart (`checkout_url`).","operationId":"public_plans_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Public Plans Plans Get"}}}}}}},"/public/auth-config":{"get":{"tags":["auth"],"summary":"Public Auth Config","description":"Métodos de acceso visibles en /login (anon). No expone wiring.","operationId":"public_auth_config_public_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Public Auth Config Public Auth Config Get"}}}}}}},"/me/close-account":{"post":{"tags":["auth"],"summary":"Close Account","description":"Cierre de cuenta self-service (soft): inhabilita el profile, revoca\nsuscripciones activas y banea la sesión de Supabase. Reversible por un\nadmin (no es un borrado destructivo). Las cuentas admin no pueden\ncerrarse desde aquí para evitar lock-outs accidentales.","operationId":"close_account_me_close_account_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Close Account Me Close Account Post"}}}}}}},"/auth/me":{"get":{"tags":["auth"],"summary":"Me","description":"Devuelve el profile del usuario autenticado + estado de subscription.\n\nUna sola call resuelve todo lo que el frontend necesita para decidir tier:\n  - role: 'subscriber' | 'admin' | 'superadmin'\n  - plan: 'free' | 'basic' | 'pro' (efectivo; admin/superadmin → 'pro')\n  - subscription_tier: tier de la subscription activa | null\n  - subscription_active: bool (compat) — True si hay sub `active` o es\n    admin/superadmin.\n  - subscription_expires_at: ISO string | null.","operationId":"me_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Me Auth Me Get"}}}}}}},"/opportunities":{"get":{"tags":["opportunities"],"summary":"List Opportunities","operationId":"list_opportunities_opportunities_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Texto libre (búsqueda ILIKE en title/summary)","title":"Q"},"description":"Texto libre (búsqueda ILIKE en title/summary)"},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"description":"ISO-3166 alpha-2 (ej. CO, MX)","title":"Country"},"description":"ISO-3166 alpha-2 (ej. CO, MX)"},{"name":"country_exact","in":"query","required":false,"schema":{"type":"boolean","description":"Si true, `country` matchea SOLO el país explícito y excluye convocatorias globales ('*', ej. grants.gov US / Horizon UE). Default false = incluye globales. Sin efecto si no se envía `country`.","default":false,"title":"Country Exact"},"description":"Si true, `country` matchea SOLO el país explícito y excluye convocatorias globales ('*', ej. grants.gov US / Horizon UE). Default false = incluye globales. Sin efecto si no se envía `country`."},{"name":"max_amount_usd","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Filtra por amount_max_usd <= valor","title":"Max Amount Usd"},"description":"Filtra por amount_max_usd <= valor"},{"name":"deadline_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deadline After"}},{"name":"deadline_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deadline Before"}},{"name":"org_type","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"ngo|university|sme|gov|... Acepta repetido (?org_type=ngo&org_type=university) → OR (cualquiera matchea).","title":"Org Type"},"description":"ngo|university|sme|gov|... Acepta repetido (?org_type=ngo&org_type=university) → OR (cualquiera matchea)."},{"name":"source_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filtra por slug de fuente (grants_gov|minciencias|idb|corfo_cl|...). Acepta repetido → OR.","title":"Source Slug"},"description":"Filtra por slug de fuente (grants_gov|minciencias|idb|corfo_cl|...). Acepta repetido → OR."},{"name":"source_lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"description":"Filtra por idioma default de la fuente (es|en). Distinto de `lang` (que solo afecta presentación).","title":"Source Lang"},"description":"Filtra por idioma default de la fuente (es|en). Distinto de `lang` (que solo afecta presentación)."},{"name":"status","in":"query","required":false,"schema":{"type":"string","description":"open|forecast|closed|withdrawn|all. CSV ('open,forecast'). Anon: cap a open+forecast aunque pida más. Subscriber: ve todos.","default":"open,forecast","title":"Status"},"description":"open|forecast|closed|withdrawn|all. CSV ('open,forecast'). Anon: cap a open+forecast aunque pida más. Subscriber: ve todos."},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Si true, descarta convocatorias ya vencidas (deadline_at < ahora) aunque su status siga 'open'/'forecast' por desfase con la fuente. Conserva las de deadline sin fecha. Úsalo para vistas de 'vigentes'.","default":false,"title":"Active Only"},"description":"Si true, descarta convocatorias ya vencidas (deadline_at < ahora) aunque su status siga 'open'/'forecast' por desfase con la fuente. Conserva las de deadline sin fecha. Úsalo para vistas de 'vigentes'."},{"name":"lang","in":"query","required":false,"schema":{"type":"string","description":"es|en — selecciona títulos/resúmenes traducidos. NO filtra rows.","default":"es","title":"Lang"},"description":"es|en — selecciona títulos/resúmenes traducidos. NO filtra rows."},{"name":"sort","in":"query","required":false,"schema":{"type":"string","description":"latam_first (default) | relevance_first (LATAM score Fase 2) | deadline_asc | newest","default":"latam_first","title":"Sort"},"description":"latam_first (default) | relevance_first (LATAM score Fase 2) | deadline_asc | newest"},{"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":"object","additionalProperties":true,"title":"Response List Opportunities Opportunities Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/opportunities/{opportunity_id}":{"get":{"tags":["opportunities"],"summary":"Get Opportunity","operationId":"get_opportunity_opportunities__opportunity_id__get","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Opportunity Opportunities  Opportunity Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/opportunities/{opportunity_id}/enrich-detail":{"post":{"tags":["opportunities"],"summary":"Enrich Opportunity Detail","description":"Genera (on-demand) las secciones ricas de una oportunidad y las cachea.\n\nPremium: requiere un plan con `show_premium_fields` (igual que la\ndescripción larga). Idempotente: si `detail_sections` ya existe (incluido\n`{}` como cache negativo), se devuelve sin gastar LLM. Si no, se descarga\n`url_source`, se extrae con Claude y se persiste. Devuelve las secciones en\n`lang` (es|en) con fallback al original.","operationId":"enrich_opportunity_detail_opportunities__opportunity_id__enrich_detail_post","parameters":[{"name":"opportunity_id","in":"path","required":true,"schema":{"type":"string","title":"Opportunity Id"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Enrich Opportunity Detail Opportunities  Opportunity Id  Enrich Detail Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/opportunities/upcoming/deadlines":{"get":{"tags":["opportunities"],"summary":"Upcoming Deadlines","operationId":"upcoming_deadlines_opportunities_upcoming_deadlines_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}],"title":"Country"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Upcoming Deadlines Opportunities Upcoming Deadlines Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/opportunities/search/semantic":{"get":{"tags":["opportunities"],"summary":"Semantic Search","description":"Búsqueda semántica con embeddings (text-embedding-3-small + pgvector).\n\nEndpoint premium — requiere un plan con `can_semantic_search` (Pro).","operationId":"semantic_search_opportunities_search_semantic_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":3,"description":"Query en lenguaje natural","title":"Q"},"description":"Query en lenguaje natural"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"country","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"ISO-3166 alpha-2 repetible (?country=CO&country=MX) → OR/overlap. Una región se manda como sus países. Vacío = todos.","title":"Country"},"description":"ISO-3166 alpha-2 repetible (?country=CO&country=MX) → OR/overlap. Una región se manda como sus países. Vacío = todos."},{"name":"country_exact","in":"query","required":false,"schema":{"type":"boolean","description":"Si true, matchea SOLO elegibilidad explícita de los países pedidos y excluye globales ('*', ej. grants.gov US / Horizon UE). Sin efecto si no se envía `country`.","default":false,"title":"Country Exact"},"description":"Si true, matchea SOLO elegibilidad explícita de los países pedidos y excluye globales ('*', ej. grants.gov US / Horizon UE). Sin efecto si no se envía `country`."},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"open","title":"Status"}},{"name":"min_similarity","in":"query","required":false,"schema":{"type":"number","maximum":1.0,"minimum":0.0,"default":0.4,"title":"Min Similarity"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Semantic Search Opportunities Search Semantic Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sources":{"get":{"tags":["meta"],"summary":"Sources","operationId":"sources_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sources Sources Get"}}}}}}}},"components":{"schemas":{"ActionableStatusUpdate":{"properties":{"status":{"type":"string","enum":["pending","done","dismissed"],"title":"Status"}},"type":"object","required":["status"],"title":"ActionableStatusUpdate","description":"PATCH de un accionable: el equipo de CS lo marca atendido o descartado."},"AnchorCreate":{"properties":{"kind":{"type":"string","enum":["north_star","wig","lead_measure"],"title":"Kind"},"title":{"type":"string","maxLength":300,"minLength":1,"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"parent_slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Slug"},"metric_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Metric Name"},"metric_unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Metric Unit"},"metric_target":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Metric Target"},"metric_current":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Metric Current"}},"type":"object","required":["kind","title"],"title":"AnchorCreate"},"AnchorUpdate":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"metric_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Metric Name"},"metric_unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Metric Unit"},"metric_target":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Metric Target"},"metric_current":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Metric Current"},"display_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Display Order"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"AnchorUpdate"},"AuthMethodsUpdate":{"properties":{"password":{"type":"boolean","title":"Password"},"google":{"type":"boolean","title":"Google"}},"type":"object","required":["password","google"],"title":"AuthMethodsUpdate"},"AuthTestRequest":{"properties":{"method":{"type":"string","enum":["magic_link","password","google"],"title":"Method"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["method"],"title":"AuthTestRequest"},"Body_extract_profile_v1_profiles_extract_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"source_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Type"}},"type":"object","title":"Body_extract_profile_v1_profiles_extract_post"},"BulkUserAction":{"properties":{"user_ids":{"items":{"type":"string"},"type":"array","maxItems":500,"minItems":1,"title":"User Ids"},"action":{"type":"string","enum":["promote_admin","demote_subscriber","activate_subscription","disable","enable"],"title":"Action"},"tier":{"type":"string","enum":["free","basic","pro"],"title":"Tier","default":"basic"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"notify":{"type":"boolean","title":"Notify","default":true}},"type":"object","required":["user_ids","action"],"title":"BulkUserAction","description":"Acción en lote sobre un conjunto de user_ids ya registrados."},"CardCreate":{"properties":{"title":{"type":"string","maxLength":300,"minLength":1,"title":"Title"},"anchor_slugs":{"items":{"type":"string"},"type":"array","title":"Anchor Slugs"},"status":{"type":"string","enum":["idea","planned","in_progress","testing","production","archived"],"title":"Status","default":"idea"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"effort":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Effort"},"impact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Impact"}},"type":"object","required":["title"],"title":"CardCreate"},"CardUpdate":{"properties":{"status":{"anyOf":[{"type":"string","enum":["idea","planned","in_progress","testing","production","archived"]},{"type":"null"}],"title":"Status"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"git_branch":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Git Branch"},"pr_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pr Url"},"pr_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pr State"},"commit_sha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commit Sha"},"deploy_env":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deploy Env"}},"type":"object","title":"CardUpdate","description":"PATCH parcial de una tarjeta. `status` registra evento (source=manual)."},"CheckDuplicateRequest":{"properties":{"opportunity":{"$ref":"#/components/schemas/OpportunityIn"},"opportunity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opportunity Id","description":"Al EDITAR una opp existente, su UUID — se excluye del cruce para que no se marque como duplicada de sí misma."}},"type":"object","required":["opportunity"],"title":"CheckDuplicateRequest","description":"Draft a cruzar contra Nexus antes de insertarlo (sin persistir)."},"CreateDossierRequest":{"properties":{"profile_id":{"type":"string","minLength":1,"title":"Profile Id"},"opportunity_id":{"type":"string","minLength":1,"title":"Opportunity Id"}},"type":"object","required":["profile_id","opportunity_id"],"title":"CreateDossierRequest"},"CuratedIngestRequest":{"properties":{"source_slug":{"type":"string","title":"Source Slug","default":"curated_latam"},"items":{"items":{"additionalProperties":true,"type":"object"},"type":"array","maxItems":50,"minItems":1,"title":"Items"}},"type":"object","required":["items"],"title":"CuratedIngestRequest"},"CuratedIngestResponse":{"properties":{"source_slug":{"type":"string","title":"Source Slug"},"sync_run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sync Run Id"},"summary":{"additionalProperties":{"type":"integer"},"type":"object","title":"Summary"},"results":{"items":{"$ref":"#/components/schemas/CuratedItemResult"},"type":"array","title":"Results"}},"type":"object","required":["source_slug","summary","results"],"title":"CuratedIngestResponse"},"CuratedItemResult":{"properties":{"source_native_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Native Id"},"result":{"type":"string","title":"Result"},"opportunity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opportunity Id"},"canonical_tags":{"items":{"type":"string"},"type":"array","title":"Canonical Tags","default":[]},"amount_max_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount Max Usd"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings","default":[]},"errors":{"items":{"type":"string"},"type":"array","title":"Errors","default":[]}},"type":"object","required":["result"],"title":"CuratedItemResult"},"DeadlineType":{"type":"string","enum":["hard","rolling","multi_round","unknown"],"title":"DeadlineType"},"DeepResearchRequest":{"properties":{"persist":{"type":"boolean","title":"Persist","description":"Si False, preview sin escribir (como read-pdf).","default":true}},"type":"object","title":"DeepResearchRequest"},"EmailChange":{"properties":{"new_email":{"type":"string","maxLength":320,"minLength":3,"title":"New Email"}},"type":"object","required":["new_email"],"title":"EmailChange","description":"Cambio del correo de acceso (login) de un pagante."},"EncajeAccessIn":{"properties":{"plans":{"items":{"type":"string"},"type":"array","title":"Plans"}},"type":"object","title":"EncajeAccessIn"},"EventIn":{"properties":{"event_type":{"type":"string","enum":["impression","click","save","dismiss","open_external","feed_view","load_more"],"title":"Event Type"},"opportunity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Opportunity Id"},"surface":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Surface"},"rail":{"anyOf":[{"type":"string","maxLength":48},{"type":"null"}],"title":"Rail"},"position":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Position"},"profile_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"client_ts":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Client Ts"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"}},"type":"object","required":["event_type"],"title":"EventIn"},"EventsBatch":{"properties":{"events":{"items":{"$ref":"#/components/schemas/EventIn"},"type":"array","maxItems":50,"title":"Events"},"anon_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Anon Id"},"session_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Session Id"}},"type":"object","title":"EventsBatch"},"ExtractFromUrlRequest":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":8,"title":"Url"},"sample_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample Html","description":"HTML opcional ya renderizado (p.ej. el DOM que captura la extensión Chrome). Si se provee, el LLM lo usa en vez de hacer GET de la URL — clave para portales SPA que el backend no puede renderizar."},"structured_hints":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Structured Hints","description":"Señales densas que captura la extensión: {main_text, jsonld, meta, pdf_links}. main_text (contenido principal limpio) se usa como cuerpo preferente; jsonld/meta/pdf_links se anteponen al prompt sin truncar."}},"type":"object","required":["url"],"title":"ExtractFromUrlRequest","description":"Carga manual asistida: una URL de convocatoria a extraer con LLM."},"FeaturedCreate":{"properties":{"opportunity_id":{"type":"string","title":"Opportunity Id"},"rail":{"type":"string","title":"Rail","default":"editorial"},"headline":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Headline"},"blurb":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Blurb"},"rank":{"type":"integer","title":"Rank","default":0},"audience":{"type":"string","enum":["all","subscriber"],"title":"Audience","default":"all"},"starts_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Starts At"},"ends_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ends At"}},"type":"object","required":["opportunity_id"],"title":"FeaturedCreate"},"FeaturedUpdate":{"properties":{"headline":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Headline"},"blurb":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Blurb"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank"},"audience":{"anyOf":[{"type":"string","enum":["all","subscriber"]},{"type":"null"}],"title":"Audience"},"starts_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Starts At"},"ends_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ends At"}},"type":"object","title":"FeaturedUpdate"},"FeedbackRequest":{"properties":{"profile_id":{"type":"string","title":"Profile Id"},"opportunity_id":{"type":"string","title":"Opportunity Id"},"rating":{"type":"integer","enum":[-1,0,1],"title":"Rating"},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note"}},"type":"object","required":["profile_id","opportunity_id","rating"],"title":"FeedbackRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InferConfigRequest":{"properties":{"url":{"type":"string","title":"Url","description":"URL del listado a analizar"},"sample_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample Html","description":"HTML opcional. Si no se provee, el endpoint hace GET de la URL."},"strategy_hint":{"anyOf":[{"type":"string","enum":["html_list","html_two_step"]},{"type":"null"}],"title":"Strategy Hint"},"preview_limit":{"type":"integer","maximum":15.0,"minimum":1.0,"title":"Preview Limit","default":5}},"type":"object","required":["url"],"title":"InferConfigRequest"},"IngestDocumentRequest":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":8,"title":"Url"},"label":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Label"}},"type":"object","required":["url"],"title":"IngestDocumentRequest"},"LlmBudgetSettingsIn":{"properties":{"cap_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Cap Usd"},"batch_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Batch Enabled"}},"type":"object","title":"LlmBudgetSettingsIn"},"ManualOpportunityCreate":{"properties":{"source_slug":{"type":"string","title":"Source Slug","default":"curated_manual"},"opportunity":{"$ref":"#/components/schemas/OpportunityIn"},"detail_sections":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail Sections","description":"Secciones ricas (elegibilidad, cómo aplicar, fechas clave, documentos, criterios) capturadas al curar. Se persisten para que la opp nazca completa, sin esperar el backfill perezoso al primer view."}},"type":"object","required":["opportunity"],"title":"ManualOpportunityCreate","description":"Ingesta de UNA oportunidad curada a mano.\n\n`opportunity` es un OpportunityIn completo (Pydantic ya valida ISO-2/ISO-4217,\ntítulo no vacío, enums). `source_slug` debe ser una source type='manual'\nexistente (default: la semilla `curated_manual`).\n\nGuardarraíl: capturar SOLO hechos (título, funder, deadline, monto, URL). El\nsummary_es/en y el latam_relevance_score los genera NUESTRO enrichment — no se\ncopia redacción editorial de terceros (p. ej. Nodoká)."},"MatchProfileIn":{"properties":{"profile_kind":{"$ref":"#/components/schemas/ProfileKind"},"display_name":{"type":"string","maxLength":200,"minLength":1,"title":"Display Name"},"country_iso2":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"title":"Country Iso2"},"target_countries":{"items":{"type":"string"},"type":"array","title":"Target Countries"},"thematic_tags":{"items":{"type":"string"},"type":"array","title":"Thematic Tags"},"derived_org_type":{"$ref":"#/components/schemas/OrgType","default":"any"},"budget_needed_usd_min":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Min"},"budget_needed_usd_max":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Max"},"needed_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Needed By Date"},"language":{"type":"string","enum":["es","en"],"title":"Language","default":"es"},"narrative":{"type":"string","maxLength":2000,"minLength":300,"title":"Narrative"},"alerts_enabled":{"type":"boolean","title":"Alerts Enabled","default":true},"extras":{"additionalProperties":true,"type":"object","title":"Extras"}},"type":"object","required":["profile_kind","display_name","narrative"],"title":"MatchProfileIn","description":"Payload de creación/actualización. Validación de extras se hace en core/matching."},"MatchProfileInline":{"properties":{"target_countries":{"items":{"type":"string"},"type":"array","title":"Target Countries"},"thematic_tags":{"items":{"type":"string"},"type":"array","title":"Thematic Tags"},"derived_org_type":{"$ref":"#/components/schemas/OrgType","default":"any"},"country_iso2":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"title":"Country Iso2"},"budget_needed_usd_min":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Min"},"budget_needed_usd_max":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Max"},"needed_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Needed By Date"},"narrative":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Narrative"}},"type":"object","title":"MatchProfileInline","description":"Payload usado por anon en POST /v1/match sin persistencia.\n\nSubset de MatchProfileIn; narrativa es opcional (anon no paga embedding)."},"MatchRequest":{"properties":{"profile_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"},"profile_inline":{"anyOf":[{"$ref":"#/components/schemas/MatchProfileInline"},{"type":"null"}]},"top_n":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Top N","default":20},"min_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Min Score","default":0.35},"status_filter":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Status Filter"},"lang":{"type":"string","pattern":"^(es|en)$","title":"Lang","default":"es"}},"type":"object","title":"MatchRequest","description":"Request body. Exactamente uno de profile_id / profile_inline debe estar presente.\n\nEl cap efectivo de `top_n` depende del tier:\n  - anon:       5  (defense-in-depth, también enforced en handler)\n  - subscriber: 50\n  - admin:      200\nEl schema acepta hasta 200; el handler clamp-ea según tier."},"MergeOfficialRequest":{"properties":{"aggregator_draft":{"additionalProperties":true,"type":"object","title":"Aggregator Draft","description":"Draft estilo OpportunityIn extraído del agregador"},"official_url":{"type":"string","maxLength":2048,"minLength":8,"title":"Official Url"},"aggregator_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Aggregator Url"},"aggregator_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Aggregator Name"}},"type":"object","required":["aggregator_draft","official_url"],"title":"MergeOfficialRequest","description":"Combina el draft de un agregador con el RFP oficial al que enlaza."},"NotificationPreferences":{"properties":{"daily_newsletter":{"type":"boolean","title":"Daily Newsletter","default":true}},"type":"object","title":"NotificationPreferences","description":"Preferencias de notificación por email del usuario."},"OpportunityIn":{"properties":{"source_native_id":{"type":"string","maxLength":256,"minLength":1,"title":"Source Native Id"},"title_orig":{"type":"string","minLength":1,"title":"Title Orig"},"summary_orig":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Orig"},"description_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description Md"},"funder_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Funder Name"},"funder_country":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"title":"Funder Country"},"funder_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Funder Type"},"amount_currency_orig":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}$"},{"type":"null"}],"title":"Amount Currency Orig"},"amount_orig_min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount Orig Min"},"amount_orig_max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount Orig Max"},"deadline_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deadline At"},"deadline_type":{"$ref":"#/components/schemas/DeadlineType","default":"unknown"},"deadline_text_orig":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deadline Text Orig"},"posted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Posted At"},"eligible_countries":{"items":{"type":"string"},"type":"array","title":"Eligible Countries"},"eligible_org_types":{"items":{"$ref":"#/components/schemas/OrgType"},"type":"array","title":"Eligible Org Types"},"thematic_tags":{"items":{"type":"string"},"type":"array","title":"Thematic Tags"},"url_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url Source"},"url_pdf":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url Pdf"},"status":{"$ref":"#/components/schemas/OpportunityStatus","default":"unknown"},"raw":{"additionalProperties":true,"type":"object","title":"Raw"},"category":{"type":"string","enum":["grant","tender","capital"],"title":"Category","default":"grant"},"type_details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Type Details"}},"type":"object","required":["source_native_id","title_orig"],"title":"OpportunityIn","description":"Lo que un conector debe entregar tras `normalize()`. Validado antes del upsert."},"OpportunityStatus":{"type":"string","enum":["open","closed","forecast","withdrawn","unknown"],"title":"OpportunityStatus"},"OrgType":{"type":"string","enum":["ngo","university","sme","startup","gov","individual","research_inst","any"],"title":"OrgType"},"PlanUpdate":{"properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"visible_statuses":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Visible Statuses"},"show_premium_fields":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Show Premium Fields"},"can_semantic_search":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Can Semantic Search"},"can_saved_searches":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Can Saved Searches"},"max_saved_searches":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Saved Searches"},"can_email_alerts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Can Email Alerts"},"can_profile_matching":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Can Profile Matching"},"max_profiles":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Profiles"},"rate_limit_per_hour":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rate Limit Per Hour"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public"},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sort Order"},"price_display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Display"},"app_access":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"App Access"},"checkout_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checkout Url"},"hotmart_offer_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotmart Offer Code"},"access_months":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Access Months"},"checkout_url_annual":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checkout Url Annual"},"hotmart_offer_code_annual":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotmart Offer Code Annual"},"price_display_annual":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Display Annual"},"access_months_annual":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Access Months Annual"}},"type":"object","title":"PlanUpdate","description":"Edición parcial de la config de un plan (todos opcionales)."},"ProfileKind":{"type":"string","enum":["org","person","project"],"title":"ProfileKind"},"ProfileUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Display Name"},"country_iso2":{"anyOf":[{"type":"string","pattern":"^[A-Z]{2}$"},{"type":"null"}],"title":"Country Iso2"},"target_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Target Countries"},"thematic_tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Thematic Tags"},"derived_org_type":{"anyOf":[{"$ref":"#/components/schemas/OrgType"},{"type":"null"}]},"budget_needed_usd_min":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Min"},"budget_needed_usd_max":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Budget Needed Usd Max"},"needed_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Needed By Date"},"language":{"anyOf":[{"type":"string","pattern":"^(es|en)$"},{"type":"null"}],"title":"Language"},"narrative":{"anyOf":[{"type":"string","maxLength":2000,"minLength":300},{"type":"null"}],"title":"Narrative"},"alerts_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Alerts Enabled"},"extras":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extras"},"status":{"anyOf":[{"type":"string","pattern":"^(active|archived)$"},{"type":"null"}],"title":"Status"}},"type":"object","title":"ProfileUpdate","description":"Update parcial. Todos los campos opcionales."},"ProposalApprove":{"properties":{"create_source":{"type":"boolean","title":"Create Source","description":"Si True y inferred_config es válido, crea la source en sources (enabled=False, schedule_cron del slug propuesto).","default":false},"slug":{"anyOf":[{"type":"string","pattern":"^[a-z][a-z0-9_]{1,49}$"},{"type":"null"}],"title":"Slug","description":"Slug a usar si create_source=True. Requerido en ese caso."},"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"review_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Notes"},"config_override":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config Override","description":"Si se provee, sobreescribe el inferred_config (admin editó manualmente). Se valida contra el schema."},"force":{"type":"boolean","title":"Force","description":"Override del gate de calidad: crear la source aun cuando verdict≠good o el config tenga _warning. Default False.","default":false}},"type":"object","title":"ProposalApprove"},"ProposalCreateFromWizard":{"properties":{"url":{"type":"string","title":"Url","description":"URL del listado propuesto"},"config":{"additionalProperties":true,"type":"object","title":"Config","description":"ConfigurableSourceConfig ya inferido/editado"},"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason"}},"type":"object","required":["url","config"],"title":"ProposalCreateFromWizard"},"ProposalReject":{"properties":{"review_notes":{"type":"string","maxLength":1000,"minLength":1,"title":"Review Notes"},"duplicate_of":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Duplicate Of","description":"Si la rechaza por duplicada, el slug ya existente. Marca status='duplicate'."}},"type":"object","required":["review_notes"],"title":"ProposalReject"},"ReadPdfRequest":{"properties":{"pdf_url":{"type":"string","maxLength":2048,"minLength":8,"title":"Pdf Url"},"draft":{"additionalProperties":true,"type":"object","title":"Draft","description":"Draft actual estilo OpportunityIn. Sus valores presentes NO se pisan."},"detail_sections":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail Sections","description":"detail_sections actuales; el PDF llena las faltantes."}},"type":"object","required":["pdf_url"],"title":"ReadPdfRequest","description":"Lee el PDF de las bases y lo fusiona con el draft actual (on-demand)."},"SavedOppCreate":{"properties":{"opportunity_id":{"type":"string","minLength":1,"title":"Opportunity Id"},"profile_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"}},"type":"object","required":["opportunity_id"],"title":"SavedOppCreate"},"SavedSearchCreate":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"filters":{"additionalProperties":true,"type":"object","title":"Filters","description":"Mismos filtros que /opportunities (q, country, status, source_slug, max_amount_usd, deadline_after, deadline_before, org_type, ...)."},"notify_email":{"type":"boolean","title":"Notify Email","default":true},"notify_webhook":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Notify Webhook"}},"type":"object","required":["name","filters"],"title":"SavedSearchCreate"},"SavedSearchUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120,"minLength":1},{"type":"null"}],"title":"Name"},"filters":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Filters"},"notify_email":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Email"},"notify_webhook":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Notify Webhook"}},"type":"object","title":"SavedSearchUpdate"},"SearchGoalCapture":{"properties":{"canonical_tag":{"type":"string","maxLength":120,"minLength":1,"title":"Canonical Tag"},"country":{"anyOf":[{"type":"string","maxLength":2},{"type":"null"}],"title":"Country"},"amount_band":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount Band"},"demand_score":{"type":"number","title":"Demand Score","default":0.0},"source_signal":{"type":"string","enum":["gap","customer_success","search_query","profile"],"title":"Source Signal","default":"gap"},"evidence":{"additionalProperties":true,"type":"object","title":"Evidence"}},"type":"object","required":["canonical_tag"],"title":"SearchGoalCapture","description":"Captura manual de un objetivo de búsqueda desde el panel."},"SearchGoalStatusUpdate":{"properties":{"status":{"type":"string","enum":["open","in_ingest","satisfied","dismissed"],"title":"Status"}},"type":"object","required":["status"],"title":"SearchGoalStatusUpdate","description":"Transición de estado de un objetivo (cola de ingesta)."},"SourceCreate":{"properties":{"slug":{"type":"string","pattern":"^[a-z][a-z0-9_]{1,49}$","title":"Slug","description":"Lowercase + underscores. Ej 'ministerio_cti_pa'."},"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"tier":{"type":"string","enum":["free","apify","commercial","scrape_own"],"title":"Tier","default":"scrape_own"},"type":{"type":"string","enum":["api","scrape","apify","manual"],"title":"Type","default":"scrape"},"base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Url"},"language_default":{"type":"string","enum":["es","en","pt"],"title":"Language Default","default":"es"},"schedule_cron":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Schedule Cron"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"enabled":{"type":"boolean","title":"Enabled","default":false},"config":{"additionalProperties":true,"type":"object","title":"Config","description":"ConfigurableSourceConfig — validado con pydantic."},"force":{"type":"boolean","title":"Force","description":"Override del gate de calidad: crear aun cuando el config tenga _warning del LLM (página no apta). Default False.","default":false}},"type":"object","required":["slug","name","config"],"title":"SourceCreate"},"SourcePreviewRequest":{"properties":{"config":{"additionalProperties":true,"type":"object","title":"Config"},"limit":{"type":"integer","maximum":30.0,"minimum":1.0,"title":"Limit","default":10}},"type":"object","required":["config"],"title":"SourcePreviewRequest"},"SourceSuggestRequest":{"properties":{"url":{"type":"string","title":"Url","description":"URL del listado de convocatorias propuesta"},"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason"},"auto_infer":{"type":"boolean","title":"Auto Infer","description":"Si True, llama al LLM para inferir el config y lo guarda con la propuesta. Cuesta ~$0.10 por subido; admin puede deshabilitarlo más adelante si crece la cola.","default":true}},"type":"object","required":["url"],"title":"SourceSuggestRequest"},"SourceUpdate":{"properties":{"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"schedule_cron":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Schedule Cron"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"language_default":{"anyOf":[{"type":"string","enum":["es","en"]},{"type":"null"}],"title":"Language Default"}},"type":"object","title":"SourceUpdate"},"SubscriptionActivate":{"properties":{"tier":{"type":"string","enum":["free","basic","pro"],"title":"Tier","default":"basic"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"notify":{"type":"boolean","title":"Notify","default":true}},"type":"object","title":"SubscriptionActivate"},"SubscriptionUpdate":{"properties":{"status":{"anyOf":[{"type":"string","enum":["pending","active","paused","revoked"]},{"type":"null"}],"title":"Status"},"tier":{"anyOf":[{"type":"string","enum":["free","basic","pro"]},{"type":"null"}],"title":"Tier"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"SubscriptionUpdate"},"SuggestionCreate":{"properties":{"kind":{"type":"string","enum":["idea","bug","feature"],"title":"Kind","default":"idea"},"title":{"type":"string","maxLength":300,"minLength":1,"title":"Title"},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body"}},"type":"object","required":["title"],"title":"SuggestionCreate"},"SuggestionUpdate":{"properties":{"status":{"anyOf":[{"type":"string","enum":["new","triaged","promoted","rejected"]},{"type":"null"}],"title":"Status"},"promote_anchor_slugs":{"items":{"type":"string"},"type":"array","title":"Promote Anchor Slugs"}},"type":"object","title":"SuggestionUpdate"},"TokenCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","description":"Etiqueta para el token (e.g. 'Claude desktop')"}},"type":"object","required":["name"],"title":"TokenCreate"},"UpdateOpportunityRequest":{"properties":{"opportunity":{"$ref":"#/components/schemas/OpportunityIn"},"detail_sections":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Detail Sections"}},"type":"object","required":["opportunity"],"title":"UpdateOpportunityRequest","description":"Edición de una opp existente (por UUID). Conserva su identidad."},"UserUpdate":{"properties":{"role":{"anyOf":[{"type":"string","enum":["subscriber","admin","superadmin"]},{"type":"null"}],"title":"Role"},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Disabled"},"notify":{"type":"boolean","title":"Notify","default":true}},"type":"object","title":"UserUpdate","description":"Updates parciales al profile: `role` y/o `disabled` (habilitar/inhabilitar)."},"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"}}}}