{"openapi":"3.0.3","info":{"title":"Nexus Legal API","version":"1.7.0","description":"API de análisis legal multijurisdiccional con arquitectura multi-agente y Zero Retention.\n\n## Novedades v1.6 (2026-05-19)\n\n- **Mode=deep operativo**: cadena Nodo A (Claude Opus 4.7) + Nodo B (DeepSeek V4-Pro) audita adversarialmente. Cuesta 2 créditos. Devuelve objeto `audit` con hallazgos estructurados (alucinaciones, citas falsas, omisiones, errores jurisdiccionales).\n- **Modelos directos**: llamadas via APIs de los proveedores (Anthropic, DeepSeek) sin proxies — menor latencia, mejor rate-limit.\n- **Hardening tras auditoría externa**: rate limit sliding window real, idempotency hash recursivo, Zero Retention en jobs async, SSRF en webhooks, durabilidad de jobs con cron de recovery, validación de scopes consolidada.\n\n## Características destacadas\n\n- **Tiers**: standard (10 rpm) · premium (200 rpm) · enterprise (1000 rpm). Asignación al crear la API key en `/developers`.\n- **Idempotency-Key**: header HTTP estándar tipo Stripe en POST evita double-charge en retries. Cache 24h. Conflict si el body cambia.\n- **Sandbox**: `/api/v1/sandbox/analyze` devuelve respuestas pre-canned para integraciones, sin consumir créditos.\n- **Usage**: `/api/v1/usage` con agregaciones por día/semana/mes, filtros por key u organización, export CSV.\n- **Batch** (Tier 2): `/api/v1/analyze/batch` procesa hasta 50 documentos en paralelo en una sola llamada.\n- **Async jobs + webhooks** (Tier 2): `/api/v1/jobs` para análisis fire-and-forget + webhooks firmados HMAC-SHA256 con reintentos exponenciales 5×.\n- **Multi-tenancy** (Tier 2): organizaciones + sub-keys revocables con usage rollup. Ideal para distributors B2B2C.\n- **Zero Retention**: procesamos en RAM en suelo UE (Países Bajos), no persistimos documentos por defecto.\n\nSDKs oficiales: **TypeScript** (`@nexus-legal/sdk` en npm) · **Python** (`nexus-legal` en PyPI).\n\nSoporte: support@nexusquantum.legal · Documentación interactiva: https://legal.nexusquantum.legal/developers/api-explorer","contact":{"name":"Nexus Legal","email":"support@nexusquantum.legal","url":"https://legal.nexusquantum.legal/developers"},"license":{"name":"Commercial","url":"https://nexusquantum.legal/terms"}},"servers":[{"url":"https://legal.nexusquantum.legal","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"nlk_..."}},"schemas":{"AnalyzeRequest":{"type":"object","required":["text"],"properties":{"text":{"type":"string","minLength":50,"maxLength":50000,"description":"Texto del documento legal a analizar."},"jurisdiction":{"type":"string","default":"ES","example":"ES","description":"ISO country code (default: ES). 31 supported."},"legalBranch":{"type":"string","enum":["civil","penal","laboral","mercantil","contencioso","constitucional","familia","internacional"],"default":"civil","description":"Rama jurídica del análisis."},"language":{"type":"string","enum":["es","en"],"default":"es","description":"Idioma de la respuesta."},"mode":{"type":"string","enum":["standard","deep"],"default":"standard","description":"Modo de análisis.\n\n• `standard` (1 crédito): solo Nodo A (Claude Opus 4.7).\n• `deep` (2 créditos): Nodo A + Nodo B (DeepSeek V4-Pro) audita adversarialmente buscando alucinaciones, citas falsas, omisiones y errores jurisdiccionales. La respuesta incluye un objeto `audit` con hallazgos estructurados.\n\nAliases compat aceptados: `agil` ⇄ `standard`, `auditoria` ⇄ `deep`."}}},"AuditFinding":{"type":"object","required":["type","severity","description"],"properties":{"type":{"type":"string","enum":["hallucination","missing_clause","weak_reasoning","citation_error","other"],"description":"Categoría del hallazgo de auditoría."},"severity":{"type":"string","enum":["low","medium","high"]},"description":{"type":"string","description":"Descripción concreta del hallazgo. Max 1000 chars."}}},"BrandingConfig":{"type":"object","description":"Configuración white-label de una API key. Las sub-keys heredan del master de la org.","properties":{"branding":{"type":"string","enum":["nexus","partner","none"],"default":"nexus","description":"Modo de branding. `nexus` mantiene 'Nexus Legal'. `partner` sustituye por `partner_brand_name`. `none` elimina las menciones."},"partner_brand_name":{"type":"string","nullable":true,"maxLength":80,"description":"Nombre comercial del partner. Sustituye a 'Nexus Legal' en el body del análisis cuando `branding='partner'`. Requerido si modo `partner`.","example":"Lemontech AI"},"partner_legal_name":{"type":"string","nullable":true,"maxLength":200,"description":"Razón social legal. Usada en audit-trail PDF y DPA generation.","example":"Lemontech S.A."},"partner_logo_url":{"type":"string","format":"uri","nullable":true,"description":"URL https pública del logo. Anti-SSRF (no localhost, no IPs privadas).","example":"https://cdn.lemontech.com/logo.png"},"partner_primary_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","nullable":true,"description":"Color hex #RRGGBB para emails y dashboards.","example":"#1A5F8B"},"partner_support_email":{"type":"string","format":"email","nullable":true,"description":"Reply-to en notificaciones de webhook + system messages.","example":"soporte@lemontech.com"},"partner_website":{"type":"string","format":"uri","nullable":true,"description":"URL del sitio del partner. Footer de emails.","example":"https://lemontech.com"}}},"AuditResult":{"type":"object","required":["auditor","text","findings","overallSeverity","recommendations","processingMs"],"description":"Auditoría adversarial del Nodo B sobre el análisis del Nodo A. Presente solo cuando mode=deep.","properties":{"auditor":{"type":"string","example":"deepseek-v4-pro"},"text":{"type":"string","description":"Análisis textual del auditor, formato libre."},"findings":{"type":"array","items":{"$ref":"#/components/schemas/AuditFinding"}},"overallSeverity":{"type":"string","enum":["low","medium","high"]},"recommendations":{"type":"string","description":"Recomendaciones concretas, formato libre."},"processingMs":{"type":"number"}}},"AnalyzeResponse":{"type":"object","required":["analysis","jurisdiction","legalBranch","model","processingMs","mode"],"properties":{"analysis":{"type":"string","description":"Análisis legal generado por el Nodo A (Claude Opus 4.7)."},"jurisdiction":{"type":"string","description":"Código ISO de la jurisdicción procesada."},"legalBranch":{"type":"string","description":"Rama jurídica aplicada."},"model":{"type":"string","description":"Modelo(s) LLM utilizado(s). En mode=deep: 'claude-opus-4-7+deepseek-v4-pro'."},"processingMs":{"type":"number","description":"Latencia total del análisis en milisegundos."},"mode":{"type":"string","enum":["standard","deep"],"description":"Modo canónico aplicado (alias normalizados)."},"creditsCharged":{"type":"integer","description":"Créditos consumidos por este análisis (1 standard / 2 deep)."},"audit":{"$ref":"#/components/schemas/AuditResult","description":"Auditoría adversarial del Nodo B. SOLO presente cuando mode=deep."}}},"JurisdictionsResponse":{"type":"object","required":["jurisdictions","total","legalSystems","note"],"properties":{"jurisdictions":{"type":"array","items":{"type":"object"},"description":"Lista de jurisdicciones soportadas con metadatos."},"total":{"type":"number","description":"Número total de jurisdicciones disponibles."},"legalSystems":{"type":"array","items":{"type":"string"},"description":"Sistemas jurídicos cubiertos."},"note":{"type":"string","description":"Nota sobre jurisdicciones con verificación activa."}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"InsufficientCreditsError":{"type":"object","required":["error","balance","required"],"properties":{"error":{"type":"string","example":"INSUFFICIENT_CREDITS"},"balance":{"type":"number","description":"Saldo actual de créditos del usuario."},"required":{"type":"number","description":"Créditos requeridos para esta operación."}}},"Organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"sub_keys":{"type":"integer","description":"Sub-keys activas en esta org (solo presente en list endpoint)."}}},"OrgKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"key_prefix":{"type":"string","description":"Primeros 12 chars (nlk_xxxxxxxx). El resto NO se expone."},"role":{"type":"string","enum":["solo","master","child"]},"child_label":{"type":"string","nullable":true},"scopes":{"type":"array","items":{"type":"string"}},"tier":{"type":"string","enum":["standard","premium","enterprise"]},"rate_limit_rpm":{"type":"integer"},"active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time","nullable":true},"expires_at":{"type":"string","format":"date-time","nullable":true}}},"SubKeyCreated":{"type":"object","properties":{"key":{"type":"string","description":"Sub-key plaintext (`nlk_…`). SE MUESTRA UNA SOLA VEZ. Guárdalo de forma segura.","example":"nlk_4f7a8c92d6b1e3f5a8c2d4e6f1a3b5c7d9e2f4a6b8c1d3e5f7a9c2e4d6f8a1b3"},"api_key":{"$ref":"#/components/schemas/OrgKey"}}}}},"paths":{"/api/v1/analyze":{"post":{"summary":"Analiza un documento legal","description":"Envía el texto de un documento legal y recibe un análisis estructurado generado por el motor multi-agente de Nexus Legal. El crédito se debita únicamente tras un análisis exitoso.\n\nSoporta el header opcional `Idempotency-Key` (8-200 chars alfanuméricos + `-_`). Si se envía, repetir la misma petición con la misma key devuelve la respuesta cacheada SIN cobrar créditos adicionales (TTL 24h). Si el body cambia, devuelve 409 Conflict.","operationId":"analyzeDocument","security":[{"ApiKeyAuth":[]}],"tags":["Análisis"],"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\nconst result = await nexus.analyze({\n  text:         \"Por el presente contrato, el arrendador cede el uso…\",\n  jurisdiction: \"ES\",\n  legalBranch:  \"civil\",\n});\n\nconsole.log(result.analysis);\nconsole.log(\"Rate limit remaining:\", result.rateLimit.remaining);"},{"lang":"JavaScript","label":"fetch","source":"const res = await fetch(\"https://legal.nexusquantum.legal/api/v1/analyze\", {\n  method:  \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${process.env.NEXUS_API_KEY}`,\n    \"Content-Type\":  \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n    text:         \"Por el presente contrato, el arrendador cede el uso…\",\n    jurisdiction: \"ES\",\n    legalBranch:  \"civil\",\n  }),\n});\nif (!res.ok) throw new Error(`Nexus API ${res.status}`);\nconst data = await res.json();\nconsole.log(data.analysis);"},{"lang":"Python","label":"requests","source":"import os, uuid, requests\n\nresp = requests.post(\n    \"https://legal.nexusquantum.legal/api/v1/analyze\",\n    headers={\n        \"Authorization\":   f\"Bearer {os.environ['NEXUS_API_KEY']}\",\n        \"Idempotency-Key\": str(uuid.uuid4()),\n        \"Content-Type\":    \"application/json\",\n    },\n    json={\n        \"text\":         \"Por el presente contrato, el arrendador cede el uso…\",\n        \"jurisdiction\": \"ES\",\n        \"legalBranch\":  \"civil\",\n    },\n    timeout=120,\n)\nresp.raise_for_status()\nprint(resp.json()[\"analysis\"])"},{"lang":"Shell","label":"curl","source":"curl -X POST https://legal.nexusquantum.legal/api/v1/analyze \\\n  -H \"Authorization: Bearer $NEXUS_API_KEY\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"text\":         \"Por el presente contrato, el arrendador cede el uso…\",\n    \"jurisdiction\": \"ES\",\n    \"legalBranch\":  \"civil\"\n  }'"}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,200}$"},"description":"Idempotency key opcional. Repetir con misma key devuelve respuesta cacheada (24h TTL).","example":"550e8400-e29b-41d4-a716-446655440000"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeRequest"},"example":{"text":"Este contrato de arrendamiento tiene por objeto...","jurisdiction":"ES","legalBranch":"civil","language":"es","mode":"standard"}}}},"responses":{"200":{"description":"Análisis completado con éxito.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Número máximo de peticiones permitidas en la ventana."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones restantes en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Timestamp Unix en el que se reinicia el contador."},"X-Processing-Ms":{"schema":{"type":"integer"},"description":"Latencia total del análisis en milisegundos."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeResponse"}}}},"400":{"description":"Parámetros inválidos o JSON malformado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"API key inválida o sin permisos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"API key inválida o sin permisos."}}}},"402":{"description":"Créditos insuficientes para procesar la solicitud.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCreditsError"}}}},"422":{"description":"Entidad no procesable — texto fuera de los límites permitidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit excedido.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Segundos hasta que se puede reintentar la solicitud."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Error interno del servidor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/analyze/batch":{"post":{"summary":"Analiza hasta 50 documentos en paralelo","description":"Procesa un array de hasta 50 documentos legales en paralelo (concurrencia configurable 1-10). Devuelve resultados en el mismo orden que el array de entrada con `status: success` o `status: error` por documento.\n\nCréditos: pre-check del worst-case (`documents.length` créditos). Post-process se cobra sólo por `status: success`. Si no hay saldo suficiente para el batch completo, devuelve 402 sin procesar nada.\n\nRate-limit: 1 batch = 1 hit contra RPM (los créditos sí escalan linealmente).\n\nSoporta `Idempotency-Key` aplicado a todo el batch (hash del array). Cache 24h.","operationId":"analyzeBatch","security":[{"ApiKeyAuth":[]}],"tags":["Análisis"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,200}$"},"description":"Idempotency key opcional. Misma key + mismo array devuelve el batch cacheado (24h TTL)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["documents"],"properties":{"documents":{"type":"array","minItems":1,"maxItems":50,"description":"Array de documentos a analizar.","items":{"type":"object","required":["text"],"properties":{"id":{"type":"string","description":"ID opcional eco-back en la respuesta para correlación."},"text":{"type":"string","minLength":50,"maxLength":50000},"jurisdiction":{"type":"string","example":"ES"},"legalBranch":{"type":"string","example":"civil"},"language":{"type":"string","enum":["es","en"],"default":"es"},"mode":{"type":"string","enum":["standard","deep"],"default":"standard"}}}},"mode":{"type":"string","enum":["standard","deep"],"description":"Override de modo para TODOS los documentos del batch."},"concurrency":{"type":"integer","minimum":1,"maximum":10,"default":5,"description":"Análisis paralelos simultáneos."}}},"example":{"documents":[{"id":"doc-1","text":"Texto del contrato 1...","jurisdiction":"ES","legalBranch":"mercantil"},{"id":"doc-2","text":"Texto del contrato 2...","jurisdiction":"ES","legalBranch":"civil"}],"concurrency":5}}}},"responses":{"200":{"description":"Batch completado. Algunos documentos pueden tener `status: error` individualmente.","headers":{"X-Batch-Submitted":{"schema":{"type":"integer"}},"X-Batch-Succeeded":{"schema":{"type":"integer"}},"X-Batch-Failed":{"schema":{"type":"integer"}},"X-Credits-Charged":{"schema":{"type":"integer"}},"X-Processing-Ms":{"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"batchId":{"type":"string","format":"uuid"},"results":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","nullable":true},"status":{"type":"string","enum":["success"]},"analysis":{"type":"string"},"jurisdiction":{"type":"string"},"legalBranch":{"type":"string"},"model":{"type":"string"},"processingMs":{"type":"integer"}}},{"type":"object","properties":{"id":{"type":"string","nullable":true},"status":{"type":"string","enum":["error"]},"error":{"type":"string"},"code":{"type":"string","enum":["VALIDATION_ERROR","MODEL_ERROR","UNKNOWN_ERROR"]},"httpStatus":{"type":"integer"}}}]}},"totals":{"type":"object","properties":{"submitted":{"type":"integer"},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"creditsCharged":{"type":"integer"},"processingMs":{"type":"integer"},"concurrency":{"type":"integer"}}}}}}}},"402":{"description":"Créditos insuficientes para el batch completo (worst-case `documents.length` créditos).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCreditsError"}}}},"422":{"description":"`BATCH_TOO_LARGE` si supera 50 documentos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\nconst batch = await nexus.analyzeBatch({\n  documents: [\n    { id: \"doc-1\", text: \"...\", jurisdiction: \"ES\", legalBranch: \"mercantil\" },\n    { id: \"doc-2\", text: \"...\", jurisdiction: \"ES\", legalBranch: \"civil\"     },\n  ],\n  concurrency: 5,\n});\n\nconsole.log(`${batch.totals.succeeded}/${batch.totals.submitted} OK`);\nfor (const r of batch.results) {\n  if (r.status === \"success\") {\n    console.log(r.id, r.analysis.slice(0, 200));\n  } else {\n    console.error(r.id, r.error);\n  }\n}"},{"lang":"Shell","label":"curl","source":"curl -X POST https://legal.nexusquantum.legal/api/v1/analyze/batch \\\n  -H \"Authorization: Bearer $NEXUS_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"documents\": [\n      { \"id\": \"doc-1\", \"text\": \"...\", \"jurisdiction\": \"ES\", \"legalBranch\": \"mercantil\" },\n      { \"id\": \"doc-2\", \"text\": \"...\", \"jurisdiction\": \"ES\", \"legalBranch\": \"civil\" }\n    ],\n    \"concurrency\": 5\n  }'"}]}},"/api/v1/jobs":{"post":{"summary":"Encola un job de análisis asíncrono","description":"Crea un job y devuelve 202 con `{jobId}`. El cliente puede recoger el resultado por polling (`GET /api/v1/jobs/[id]`) o registrar un webhook (`POST /api/v1/webhooks`) que reciba `analysis.completed` o `analysis.failed` firmado HMAC-SHA256.\n\nSoporta `Idempotency-Key`: si el cliente reintenta con la misma key + kind, devuelve el `jobId` original sin duplicar.","operationId":"createJob","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,200}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","payload"],"properties":{"kind":{"type":"string","enum":["analyze","analyze_batch"]},"payload":{"type":"object","description":"Payload equivalente al endpoint sincrono."}}},"example":{"kind":"analyze","payload":{"text":"Texto del contrato...","jurisdiction":"ES","legalBranch":"civil"}}}}},"responses":{"202":{"description":"Job aceptado.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending"]},"createdAt":{"type":"string","format":"date-time"},"pollUrl":{"type":"string"}}}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\n// Opción A: poll-helper conveniente\nconst snap = await nexus.jobs.run({\n  kind: \"analyze\",\n  payload: { text: \"...\", jurisdiction: \"ES\", legalBranch: \"civil\" },\n});\nconsole.log(snap.status, snap.result);\n\n// Opción B: manual\nconst job = await nexus.jobs.create({\n  kind: \"analyze\",\n  payload: { text: \"...\", jurisdiction: \"ES\", legalBranch: \"civil\" },\n});\n// luego poll: const snap = await nexus.jobs.get(job.jobId);"}]},"get":{"summary":"Lista jobs del api_key","operationId":"listJobs","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["pending","running","completed","failed","cancelled"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}}],"responses":{"200":{"description":"Lista de jobs.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/jobs/{id}":{"get":{"summary":"Estado y resultado de un job","operationId":"getJob","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Snapshot del job.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["analyze","analyze_batch"]},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"createdAt":{"type":"string","format":"date-time"},"startedAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"creditsCharged":{"type":"integer"},"error":{"type":"object","nullable":true},"result":{"type":"object","nullable":true}}}}}},"404":{"description":"Job not found."}}},"delete":{"summary":"Cancela un job pendiente","operationId":"cancelJob","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelado."},"409":{"description":"Job no está en estado pending — no se puede cancelar."}}}},"/api/v1/webhooks":{"post":{"summary":"Registra un endpoint webhook","description":"Registra una URL que recibirá POST firmado HMAC-SHA256 ante eventos. El secret devuelto se muestra UNA SOLA VEZ — guárdalo para verificar la firma `X-Nexus-Signature`.\n\nEventos disponibles: `analysis.completed`, `analysis.failed`, `credits.low`.\n\nFirma: `X-Nexus-Signature: t=<unix-ts>,v1=<hex-hmac>` donde `hmac = HMAC-SHA256(secret, t + '.' + rawBody)`. Reintentos exponenciales hasta 5 veces (1m, 5m, 30m, 2h, 12h).","operationId":"createWebhook","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["analysis.completed","analysis.failed","credits.low"]}},"description":{"type":"string","maxLength":200}}},"example":{"url":"https://example.com/nexus-webhook","events":["analysis.completed","analysis.failed"],"description":"Production webhook for case-management integration"}}}},"responses":{"201":{"description":"Webhook creado. El campo `secret` solo se muestra aquí.","content":{"application/json":{"schema":{"type":"object"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient, verifyWebhookSignature } from \"@nexus-legal/sdk\";\n\n// 1. Registrar el endpoint (devuelve el secret una sola vez)\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\nconst wh = await nexus.webhooks.create({\n  url:    \"https://example.com/nexus-webhook\",\n  events: [\"analysis.completed\", \"analysis.failed\"],\n});\nconsole.log(\"Guarda este secret:\", wh.secret);\n\n// 2. En tu handler HTTP (Express, Next, etc.) verificar la firma:\n//\n//   const rawBody = await req.text();   // body sin parsear\n//   const valid = await verifyWebhookSignature({\n//     rawBody,\n//     signature: req.headers.get(\"x-nexus-signature\")!,\n//     secret:    process.env.WEBHOOK_SECRET!,\n//   });\n//   if (!valid) return new Response(\"invalid signature\", { status: 401 });\n//   const payload = JSON.parse(rawBody);"}]},"get":{"summary":"Lista los webhooks del api_key","operationId":"listWebhooks","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"responses":{"200":{"description":"Lista.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/webhooks/{id}":{"get":{"summary":"Detalle de un webhook (sin secret)","operationId":"getWebhook","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook."},"404":{"description":"Not found."}}},"patch":{"summary":"Actualiza un webhook (active / events / description)","operationId":"updateWebhook","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string","enum":["analysis.completed","analysis.failed","credits.low"]}},"description":{"type":"string","maxLength":200}}}}}},"responses":{"200":{"description":"Actualizado."}}},"delete":{"summary":"Elimina un webhook","operationId":"deleteWebhook","security":[{"ApiKeyAuth":[]}],"tags":["Async"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Borrado."}}}},"/api/v1/organizations":{"post":{"summary":"Crea una organización (multi-tenancy)","description":"Crea una organización y promueve la key actual a `role: master`. Solo claves con `role: solo` (default) pueden crear orgs. Las `role: child` reciben 403. Una key `master` que ya pertenece a otra org recibe 409 `ALREADY_MASTER`.\n\nCaso de uso: distributor / partner que necesita servir a N clientes finales con keys separadas + usage rollup. Tras crear la org, usa `POST /api/v1/organizations/{id}/keys` para generar sub-keys (`role: child`).","operationId":"createOrganization","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":2,"maxLength":80},"description":{"type":"string","maxLength":300}}},"example":{"name":"Lemontech LATAM","description":"Distributor LATAM with TimeBillingX + CaseTracking integration"}}}},"responses":{"201":{"description":"Org creada. La key autenticada queda promovida a master.","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"masterKeyId":{"type":"string","format":"uuid"}}}}}},"403":{"description":"Role child no puede crear orgs."},"409":{"description":"Key ya es master de otra org."},"422":{"description":"Límite de 5 orgs por usuario alcanzado."}},"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\nconst { organization, masterKeyId } = await nexus.organizations.create({\n  name: \"Lemontech LATAM\",\n});\nconsole.log(\"Org creada:\", organization.id, \"master:\", masterKeyId);"},{"lang":"Python","label":"nexus-legal","source":"from nexus_legal import NexusClient\n\nnexus = NexusClient(api_key=os.environ[\"NEXUS_API_KEY\"])\n\nresult = nexus.organizations.create(name=\"Lemontech LATAM\")\nprint(\"Org creada:\", result[\"organization\"][\"id\"], \"master:\", result[\"masterKeyId\"])"}]},"get":{"summary":"Lista las organizaciones del usuario","operationId":"listOrganizations","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"responses":{"200":{"description":"Array de orgs con sub_keys count por cada una.","content":{"application/json":{"schema":{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"sub_keys":{"type":"integer","description":"Sub-keys activas en esta org"}}}}}}}}}}}},"/api/v1/organizations/{id}":{"get":{"summary":"Detalle de una organización (incluye todas las keys)","operationId":"getOrganization","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Org + array de keys (master primero, children orden created_at DESC).","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/Organization"},"keys":{"type":"array","items":{"$ref":"#/components/schemas/OrgKey"}}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Org no existe.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Actualiza name / description de la org","operationId":"updateOrganization","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80},"description":{"type":"string","maxLength":300}}}}}},"responses":{"200":{"description":"Org actualizada.","content":{"application/json":{"schema":{"type":"object","properties":{"organization":{"$ref":"#/components/schemas/Organization"}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"summary":"Elimina la organización completa","description":"Revoca todas las sub-keys (active=false), libera la master_key (vuelve a role=solo) y borra la org. Operación destructiva pero los datos quedan preservados para audit trail.","operationId":"deleteOrganization","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Org borrada + sub-keys revocadas.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted_organization_id":{"type":"string","format":"uuid"},"revoked_sub_keys":{"type":"integer"}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations/{id}/keys":{"post":{"summary":"Crea una sub-key (child) en la organización","description":"La master genera una sub-key para un cliente final. Los scopes son strict subset de los scopes de la master (no se puede escalar). El secret se devuelve UNA SOLA VEZ.\n\nUsage queda atribuido a esta sub-key, visible vía `GET /api/v1/usage?organization_id=<orgId>` con desglose por `child_label`.","operationId":"createSubKey","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["childLabel"],"properties":{"childLabel":{"type":"string","minLength":2,"maxLength":80,"description":"Nombre del cliente final, ej. 'Despacho Vázquez'"},"scopes":{"type":"array","items":{"type":"string","enum":["analyze","query","mcp"]}},"expiresInDays":{"type":"integer","minimum":1,"maximum":3650}}},"example":{"childLabel":"Despacho Vázquez","scopes":["analyze","query"],"expiresInDays":365}}}},"responses":{"201":{"description":"Sub-key creada. El campo `key` se muestra una sola vez.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubKeyCreated"}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Límite de 200 sub-keys por org alcanzado o scopes inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"const sk = await nexus.organizations.createKey(orgId, {\n  childLabel: \"Despacho Vázquez\",\n  scopes:     [\"analyze\", \"query\"],\n});\nconsole.log(\"Entrégale esta key:\", sk.key);  // se muestra UNA SOLA VEZ"}]},"get":{"summary":"Lista todas las keys de la organización (master + children)","operationId":"listOrgKeys","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Array de keys ordenado por role (master primero), después created_at DESC.","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/OrgKey"}}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/organizations/{id}/keys/{keyId}":{"patch":{"summary":"Actualiza una sub-key (active / scopes / childLabel)","description":"No se puede modificar la master desde esta ruta (devolverá 409). Para la master usa `/api/developer/keys/{id}`.","operationId":"updateSubKey","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"scopes":{"type":"array","items":{"type":"string","enum":["analyze","query","mcp"]}},"childLabel":{"type":"string","maxLength":80}}}}}},"responses":{"200":{"description":"Sub-key actualizada.","content":{"application/json":{"schema":{"type":"object","properties":{"api_key":{"$ref":"#/components/schemas/OrgKey"}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Intentaste mutar la master desde esta ruta. Usa `/api/developer/keys/{id}`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"summary":"Revoca (soft-delete) una sub-key","description":"Marca la sub-key como `active: false`. Preserva audit trail y usage stats. No se puede revocar la master desde aquí (409); usa `DELETE /api/v1/organizations/{id}` para destruir toda la org.","operationId":"revokeSubKey","security":[{"ApiKeyAuth":[]}],"tags":["Multi-tenancy"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Sub-key revocada.","content":{"application/json":{"schema":{"type":"object","properties":{"revoked_key_id":{"type":"string","format":"uuid"}}}}}},"403":{"description":"No eres master de esta org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Intentaste revocar la master desde esta ruta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/branding":{"get":{"summary":"Devuelve la configuración white-label de la API key llamante","description":"Cada API key (master o solo) tiene su propia configuración white-label. Las sub-keys (role=child) heredan automáticamente la del master de su organización. Útil para que el partner (Distributor) consulte cómo se firma actualmente el output de los análisis.","operationId":"getBranding","security":[{"ApiKeyAuth":[]}],"tags":["White-label"],"responses":{"200":{"description":"Configuración actual.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandingConfig"}}}},"401":{"description":"API key inválida."}}},"put":{"summary":"Actualiza la configuración white-label","description":"Actualiza el branding aplicado a los outputs de análisis y emails. Si la key es master de una organización, los cambios se propagan automáticamente a TODAS las sub-keys hijas. Las sub-keys NO pueden editar su propio branding (403 FORBIDDEN_CHILD_KEY) — solo el master.\n\nModos disponibles:\n- `nexus` (default): outputs mantienen 'Nexus Legal'.\n- `partner`: sustituye 'Nexus Legal' por `partner_brand_name` en el cuerpo del análisis. El bloque NEXUS-AUDIT-TRAIL técnico se preserva.\n- `none`: elimina las menciones de 'Nexus Legal' del cuerpo.\n\nValidación anti-SSRF: `partner_logo_url` y `partner_website` deben ser URLs https públicas (no localhost, no IPs privadas, no metadata services). Mismo patrón que `/api/v1/webhooks`.","operationId":"updateBranding","security":[{"ApiKeyAuth":[]}],"tags":["White-label"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandingConfig"},"example":{"branding":"partner","partner_brand_name":"Lemontech AI","partner_legal_name":"Lemontech S.A.","partner_logo_url":"https://cdn.lemontech.com/logo.png","partner_primary_color":"#1A5F8B","partner_support_email":"soporte@lemontech.com","partner_website":"https://lemontech.com"}}}},"responses":{"200":{"description":"Configuración guardada. Devuelve `applied_to_keys` con el número de keys afectadas (1 si solo, N si master de org).","content":{"application/json":{"schema":{"type":"object","required":["ok","applied_to_keys","config"],"properties":{"ok":{"type":"boolean"},"applied_to_keys":{"type":"integer","description":"Número de keys donde se aplicó (1 si solo, N si master propagó a children)."},"config":{"$ref":"#/components/schemas/BrandingConfig"},"requestId":{"type":"string"}}}}}},"403":{"description":"Sub-key (role=child) no puede editar branding. Usa la master de la organización."},"422":{"description":"Validación falló (color hex inválido, email mal formado, URL anti-SSRF, etc.)."}}}},"/api/v1/corpus/coverage":{"get":{"summary":"Inventario en tiempo real del corpus indexado","description":"Devuelve el estado actual del corpus jurisprudencial: número de tablas, total de documentos, jurisdicción inferida por tabla, rango de fechas y top fuentes con conteo. AUTO-DESCUBRIBLE: cuando se añade una nueva tabla `jurisprudencia_<iso>` a Supabase con embeddings, aparece aquí sin cambios de código. Útil para que clientes (dashboards, MCP, agents) construyan UI de cobertura sin hardcodear nada. Caché: 5 min server-side + `Cache-Control` 5 min en respuesta. Requiere API key con cualquier scope válido (no consume créditos).","operationId":"getCorpusCoverage","security":[{"ApiKeyAuth":[]}],"tags":["Discovery"],"responses":{"200":{"description":"Inventario del corpus.","content":{"application/json":{"schema":{"type":"object","required":["generatedAt","cacheTtlSeconds","totalDocs","totalTables","tables"],"properties":{"generatedAt":{"type":"string","format":"date-time"},"cacheTtlSeconds":{"type":"integer"},"totalDocs":{"type":"integer","description":"Suma de docs en TODAS las tablas."},"totalTables":{"type":"integer"},"tables":{"type":"array","items":{"type":"object","properties":{"table":{"type":"string","example":"jurisprudencia_docs"},"inferredJurisdiction":{"type":"string","nullable":true,"example":"ES"},"totalDocs":{"type":"integer"},"oldestDoc":{"type":"string","nullable":true,"format":"date"},"newestDoc":{"type":"string","nullable":true,"format":"date"},"topSources":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"count":{"type":"integer"}}}},"sourceColumn":{"type":"string","nullable":true,"description":"Columna usada para agrupar topSources."}}}}}}}}},"401":{"description":"API key inválida."}}}},"/api/v1/jurisdictions":{"get":{"summary":"Lista las jurisdicciones soportadas","description":"Devuelve la lista completa de jurisdicciones soportadas por la API, incluyendo el sistema jurídico de cada una. No requiere autenticación.","operationId":"listJurisdictions","tags":["Discovery"],"responses":{"200":{"description":"Lista de jurisdicciones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JurisdictionsResponse"}}}}}}},"/api/v1/sandbox/analyze":{"post":{"summary":"Sandbox — análisis con respuestas pre-canned","description":"Endpoint sandbox: misma signature que `/api/v1/analyze` pero devuelve respuestas pre-canned realistas. NO consume créditos, NO llama LLM. Pensado para que devs partner integren su cliente sin gastar saldo durante desarrollo/CI.\n\nRate limit: 60 rpm (más generoso). Disponibles 6 samples: `contract_es`, `providencia_aeat`, `dictamen_teac`, `demanda_concursal_es`, `demand_co`, `laboral_cl`.","operationId":"sandboxAnalyze","security":[{"ApiKeyAuth":[]}],"tags":["Sandbox"],"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\nconst result = await nexus.sandbox.analyze({ sample: \"providencia_aeat\" });\nconsole.log(result.sample, result.analysis.slice(0, 200));"},{"lang":"Shell","label":"curl","source":"curl -X POST https://legal.nexusquantum.legal/api/v1/sandbox/analyze \\\n  -H \"Authorization: Bearer $NEXUS_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sample\": \"providencia_aeat\"}'"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"sample":{"type":"string","enum":["contract_es","providencia_aeat","dictamen_teac","demanda_concursal_es","demand_co","laboral_cl"],"description":"Selecciona qué respuesta canned devolver. Default: contract_es."},"text":{"type":"string","description":"Ignorado (sandbox)."},"jurisdiction":{"type":"string","description":"Sobrescribe la jurisdicción del sample."},"legalBranch":{"type":"string","description":"Sobrescribe la rama del sample."},"language":{"type":"string","enum":["es","en"]}}},"example":{"sample":"providencia_aeat"}}}},"responses":{"200":{"description":"Respuesta canned. Headers `X-Sandbox: true` y `X-Processing-Ms`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AnalyzeResponse"},{"type":"object","properties":{"sandbox":{"type":"boolean","example":true},"sample":{"type":"string","example":"contract_es"},"available_samples":{"type":"array","items":{"type":"string"}},"notes":{"type":"string"}}}]}}}}}},"get":{"summary":"Sandbox — descubre samples disponibles","description":"Devuelve metadata sobre los samples disponibles. No requiere body.","operationId":"sandboxList","security":[],"tags":["Sandbox"],"responses":{"200":{"description":"Lista de samples + ejemplo de request."}}}},"/api/v1/usage":{"get":{"summary":"Agregaciones de uso de la API","description":"Devuelve métricas agregadas de uso de la API para la cuenta autenticada. Necesario para partners que necesitan facturar a sus clientes según consumo individual por sub-key.\n\nFiltros: `from`/`to` (ISO date), `granularity` (day/week/month), `api_key_id`, `endpoint`. Formato: JSON (default) o CSV con `?format=csv`.","operationId":"getUsage","security":[{"ApiKeyAuth":[]}],"tags":["Discovery"],"parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"ISO date inclusive (default: 30d atrás)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"ISO date inclusive (default: hoy)"},{"name":"granularity","in":"query","schema":{"type":"string","enum":["day","week","month"],"default":"day"}},{"name":"api_key_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar a una sub-key concreta."},{"name":"endpoint","in":"query","schema":{"type":"string"},"description":"Filtrar por ruta (ej. /api/v1/analyze)."},{"name":"format","in":"query","schema":{"type":"string","enum":["json","csv"],"default":"json"}}],"responses":{"200":{"description":"Agregaciones de uso.","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"object","properties":{"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"granularity":{"type":"string","enum":["day","week","month"]}}},"totals":{"type":"object","properties":{"calls":{"type":"integer"},"errors":{"type":"integer"},"latency_p50_ms":{"type":"integer"},"latency_p95_ms":{"type":"integer"}}},"by_key":{"type":"array","items":{"type":"object"}},"by_endpoint":{"type":"array","items":{"type":"object"}},"buckets":{"type":"array","items":{"type":"object"}}}}},"text/csv":{"schema":{"type":"string","description":"Dump CSV con columnas: period_start,api_key_id,key_prefix,endpoint,calls,errors"}}}}}}},"/api/v1/jurisprudencia/search":{"post":{"summary":"Búsqueda semántica en corpus jurisprudencial","description":"Búsqueda vectorial (voyage-law-2) sobre el corpus jurisprudencial indexado de Nexus. El corpus crece de forma incremental — cobertura por jurisdicción, fuente, conteo actualizado y rango de fechas en `GET /api/v1/corpus/coverage` (no hardcodeado en esta descripción para evitar desactualizaciones). Cobertura actual incluye sentencias y doctrina de tribunales y reguladores en ES, EU, INT, CO, KR y otras jurisdicciones según la tabla de cobertura. Devuelve top-K resultados con título, fuente, extracto (2 000 chars) y URL permanente.","operationId":"jurisprudenciaSearch","security":[{"ApiKeyAuth":[]}],"tags":["Análisis"],"x-codeSamples":[{"lang":"TypeScript","label":"@nexus-legal/sdk","source":"import { NexusClient } from \"@nexus-legal/sdk\";\n\nconst nexus = new NexusClient({ apiKey: process.env.NEXUS_API_KEY! });\n\nconst { chunks } = await nexus.jurisprudencia.search({\n  query:        \"cláusula suelo abusiva entidad bancaria\",\n  jurisdiction: \"ES\",\n  topK:         5,\n});\n\nfor (const c of chunks) {\n  console.log(`📄 ${c.titulo} (${c.source})`);\n  console.log(`   → ${c.url}`);\n}"},{"lang":"Python","label":"requests","source":"import os, requests\n\nresp = requests.post(\n    \"https://legal.nexusquantum.legal/api/v1/jurisprudencia/search\",\n    headers={\"Authorization\": f\"Bearer {os.environ['NEXUS_API_KEY']}\"},\n    json={\n        \"query\":        \"cláusula suelo abusiva entidad bancaria\",\n        \"jurisdiction\": \"ES\",\n        \"top_k\":        5,\n    },\n)\nfor c in resp.json()[\"chunks\"]:\n    print(c[\"titulo\"], c[\"url\"])"},{"lang":"Shell","label":"curl","source":"curl -X POST https://legal.nexusquantum.legal/api/v1/jurisprudencia/search \\\n  -H \"Authorization: Bearer $NEXUS_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"query\":        \"cláusula suelo abusiva entidad bancaria\",\n    \"jurisdiction\": \"ES\",\n    \"top_k\":        5\n  }'"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","minLength":5,"example":"cláusula suelo abusiva entidad bancaria"},"jurisdiction":{"type":"string","default":"ES","description":"Acota corpus por jurisdicción."},"top_k":{"type":"integer","minimum":1,"maximum":50,"default":10}}}}}},"responses":{"200":{"description":"Resultados.","content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"jurisdiction":{"type":"string"},"intent":{"type":"string"},"count":{"type":"integer"},"chunks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"titulo":{"type":"string"},"texto":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}}}}}}}}},"tags":[{"name":"Análisis","description":"Endpoints de análisis de documentos legales y búsqueda jurisprudencial."},{"name":"Sandbox","description":"Endpoints sandbox con respuestas pre-canned. Sin coste, sin LLM, ideal para integraciones."},{"name":"Discovery","description":"Endpoints de descubrimiento, configuración y métricas de uso."}]}