Skip to main content

nexus-legal

PyPI version License: MIT

Official Python SDK for the Nexus Legal API v1 — multi-jurisdictional legal analysis with defensible certainty locks, vector case-law search, async jobs, signed webhooks, organizations and sub-keys.

pip install nexus-legal

Requires Python ≥ 3.9.

Quick start

import os
from nexus_legal import NexusClient

nexus = NexusClient(api_key=os.environ["NEXUS_API_KEY"])

result = nexus.analyze(
    text="Por el presente contrato, el arrendador cede el uso…",
    jurisdiction="ES",
    legal_branch="civil",
)

print(result["analysis"])
print("Rate limit remaining:", result["rate_limit"]["remaining"])

Get an API key at /developers. Full docs at /developers/docs.

Features

  • Synchronous — built on requests. Async (httpx) coming in a future release.
  • Auto idempotency — every POST carries an auto-generated Idempotency-Key (UUID4) reused across retries → no double-charge.
  • Smart retries — 429 / 5xx with exponential backoff + jitter, honoring Retry-After.
  • Typed errorsRateLimitError, InsufficientCreditsError, IdempotencyConflictError, ValidationError, AuthenticationError, ServerError, TimeoutError.
  • Rate-limit telemetry — every response exposes result["rate_limit"].
  • Audit-trail parserextract_audit_trail() returns a structured TypedDict without any YAML dependency.
  • Webhook signature verificationverify_webhook_signature() for incoming HMAC-signed deliveries.
  • Multi-agent mode=deep (v1.6.0+) — Nodo A (Claude Opus 4.7) + Nodo B (DeepSeek V4-Pro) adversarial auditor. Structured findings in result["audit"].
  • Citation verification (v0.9.0+) — result["citationChecks"] carries the deterministic anti-hallucination verdict of every citation in the analysis (verified | mismatch | not_found | derogated | not_verifiable | pending), checked against the official corpus (BOE / CENDOJ). Typed accessor: get_citation_checks(result). Zero LLM cost.

Analysis modes

Mode Credits Pipeline Use case
standard (default) 1 Nodo A only (Claude Opus 4.7) Most analyses
deep 2 Nodo A → Nodo B adversarial audit (DeepSeek V4-Pro) Critical reviews, due diligence, hallucination-sensitive contexts

Aliases for backward-compat: agilstandard, auditoriadeep. The server normalizes them.

result = nexus.analyze(
    text=contract_text,
    jurisdiction="ES",
    mode="deep",     # 2 credits — Nodo B auditará el output de Nodo A
)

if result.get("audit"):
    audit = result["audit"]
    print(f"Overall severity: {audit['overallSeverity']}")
    for finding in audit["findings"]:
        print(f"[{finding['severity']}] {finding['type']}: {finding['description']}")
    print("Recommendations:", audit["recommendations"])

The audit key is present only when mode=deep. Its shape:

{
    "auditor":         "deepseek-v4-pro",
    "text":            "...",                       # free-form audit prose
    "findings": [
        {
            "type":        "hallucination",         # | missing_clause | weak_reasoning
                                                    # | citation_error | other
            "severity":    "medium",                # low | medium | high
            "description": "...",
        }
    ],
    "overallSeverity": "medium",
    "recommendations": "...",
    "processingMs":    1234,
}

API surface

nexus = NexusClient(
    api_key="nlk_...",
    base_url="https://legal.nexusquantum.legal",   # default
    timeout=120.0,                                  # seconds
    max_retries=3,
    auto_idempotency=True,
)

# Sync
nexus.analyze(text=..., jurisdiction="ES", legal_branch="civil")
nexus.analyze_batch(documents=[...], concurrency=5)

# Sandbox (no credits)
nexus.sandbox.analyze(sample="providencia_aeat")
nexus.sandbox.metadata()

# Case-law search
nexus.jurisprudencia.search(query="...", jurisdiction="ES", top_k=5)

# Vigent legislation — Spain (ES) live: full BOE corpus indexed
# (~356k vigent articles, semantic search). Other jurisdictions may
# still be rails-only; check per-jurisdiction status with coverage().
nexus.normativa.articulo(                      # literal a fecha
    norma="BOE-A-1889-4763",
    articulo="1124",
    fecha="2026-05-21",
    jurisdiccion="ES",
)
nexus.normativa.search(                        # búsqueda semántica
    query="responsabilidad contractual del arrendatario",
    jurisdiccion="ES",
    top_k=10,
)
nexus.normativa.get_versiones("BOE-A-1889-4763")
nexus.normativa.coverage()                     # status + totals, no auth

# DMS — analiza un fichero de Box por id (sin descargarlo tú) y, por
# defecto, escribe el análisis de vuelta a Box como metadata. Requiere
# que el usuario tras la API key tenga una conexión Box activa (OAuth).
box = nexus.dms.box.analyze(
    file_id="1234567890",
    jurisdiction="ES",
    legal_branch="civil",
    # write_metadata=False,   # desactiva el write-back a Box
)
print(box["analysis"], box["source"]["file_name"], box["metadata_written"])

# Catalogs and usage
nexus.jurisdictions.list()
nexus.usage.get(from_="2026-04-01", to="2026-05-01", granularity="day")
nexus.usage.csv(from_="2026-04-01", to="2026-05-01")

# Time tracking (v0.10.0+) — útil para Office add-ins y facturación
cats = nexus.time_categories.list(locale="es")
nexus.time_entries.create(
    case_id="7f3a...",
    category_id=cats["categories"][0]["id"],
    minutes=45,
    description="Revisión de contrato",
)
entries = nexus.time_entries.list(case_id="7f3a...", unbilled_only=True)

# SLA status (v0.10.0+) — uptime/latencia del mes en curso + log mensual
sla = nexus.sla.status(limit_history=12)
print(sla["current"]["uptime_pct"], sla["lifetime"]["total_credits"])

# IP allowlist (v0.10.0+) — restringe la API key a tus rangos
# (0 entries = allow all; se activa al añadir la primera)
nexus.security.ip_allowlist.add(cidr="203.0.113.0/24", label="Oficina")
ips = nexus.security.ip_allowlist.list()
nexus.security.ip_allowlist.delete(ips["entries"][0]["id"])

# Async jobs
job = nexus.jobs.create(kind="analyze", payload={"text": ..., "jurisdiction": "ES"})
snap = nexus.jobs.get(job["jobId"])
final = nexus.jobs.run(kind="analyze", payload={...})   # poll helper

# Webhooks
wh = nexus.webhooks.create(url="https://example.com/hook", events=["analysis.completed"])
# Guarda wh["secret"] AHORA en tu vault — se muestra UNA SOLA VEZ.

# v0.4.0+ — inspect deliveries (debug)
deliveries = nexus.webhooks.get_deliveries(wh["id"], limit=100, status="failed")
for d in deliveries["deliveries"]:
    print(d["event"], d["status"], d["last_status_code"], d["attempts"])

# DSR export (GDPR Art.15 + Art.20 — v0.3.0+)
bundle = nexus.account.export_dsr()
# Masters: bundle = nexus.account.export_dsr(for_sub_key="<uuid>")

# Organizations + sub-keys
org = nexus.organizations.create(name="Mi Despacho LATAM")
sk  = nexus.organizations.create_key(org["organization"]["id"], child_label="Despacho Vázquez")
# sk["key"] solo se muestra UNA SOLA VEZ

# White-label config (partner/distributor — v1.7.0+)
nexus.branding.get()
nexus.branding.update(
    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",
)
# Si la key es master de una org, los cambios se propagan a todas las
# sub-keys automáticamente. Sub-keys (child) reciben 403.

Advanced examples

# Cross-reference: statutes cited in a judgment (UUID del corpus)
articulos = nexus.cruce.articulos_de_sentencia(jurisprudencia_id)

# Cross-reference: judgments citing a statute article
sentencias = nexus.cruce.sentencias_de_articulo("400", norma_alias="LEC", top=10)

# Procedural deadlines calculator (LEC España)
plazo = nexus.plazos.calcular(
    fecha_base="2026-05-29",
    cantidad=20,
    tipo_computo="habiles",
    ccaa="ES-MD",
)
# → { fecha_vencimiento, regla_aplicable, ajustes_aplicados, ... }

# Verify a statute citation against the vigent corpus (anti-hallucination)
cita = nexus.normativa.verify_cita(
    citation="Art. 1902 CC",
    claim_text="El que por acción u omisión causa daño a otro...",
)
# → { status: "verified"|"mismatch"|"not_found"|"derogated"|"pending", similarity, url }

# Verify a case-law citation (ECLI / ROJ)
cita_juris = nexus.jurisprudencia.verify_cita(
    citation="ECLI:ES:TS:2023:1234",
    claim_text="El Tribunal declara la nulidad parcial...",
)

# Corpus coverage statistics
coverage = nexus.corpus.coverage()

# Case email inbox (email-to-matter)
inbox = nexus.cases.emails.list(case_id, limit=20)
alias = NexusClient.inbound_email_alias(case_id)  # sin llamada de red

Error handling

from nexus_legal import (
    InsufficientCreditsError,
    RateLimitError,
    ValidationError,
)

try:
    nexus.analyze(text=text, jurisdiction="ES")
except InsufficientCreditsError as e:
    print(f"Need {e.required}, have {e.balance}")
except RateLimitError as e:
    time.sleep(e.retry_after)
except ValidationError as e:
    print(f"Bad request: {e}")
    # e.code is a stable identifier (VALIDATION_ERROR, BATCH_TOO_LARGE, ...)
    print(f"Code: {e.code}, request_id: {e.request_id}")

Error format (v1.6.0+) — stable, Stripe-style:

{
  "error": {
    "type":       "invalid_request_error",
    "code":       "VALIDATION_ERROR",
    "message":    "Texto demasiado corto (mín. 50 chars).",
    "request_id": "req_abc-123",
    "details":    { "field": "text", "min": 50, "got": 12 }
  },
  "error_message": "Texto demasiado corto (mín. 50 chars).",
  "code":          "VALIDATION_ERROR"
}

Every response (success or error) includes header X-Request-Id: req_<uuid>. The SDK surfaces it as e.request_id — reference it in support tickets. You can also pre-set your own via X-Request-Id header.

Error types: invalid_request_error, authentication_error, permission_error, rate_limit_error, insufficient_credits_error, not_found_error, conflict_error, api_error.

Webhook signature verification

import os
from nexus_legal import verify_webhook_signature
from flask import Flask, request

app = Flask(__name__)

@app.post("/nexus-webhook")
def webhook():
    raw = request.get_data()                       # bytes, pre-parse
    sig = request.headers.get("X-Nexus-Signature")
    if not verify_webhook_signature(
        raw_body=raw,
        signature=sig,
        secret=os.environ["WEBHOOK_SECRET"],
    ):
        return ("invalid signature", 401)
    payload = request.get_json()
    # ...

Audit-trail parsing

from nexus_legal import extract_audit_trail

trail = extract_audit_trail(result["analysis"])
if trail and trail["levels_emitted"].get("L5-C", 0) > 0:
    # Critical contractual risk — escalate to human reviewer
    ...

The parser handles levels_emitted, review_flags, modules_active, kill_switches_triggered, rag_sources, executive_summary and exposes the raw YAML in trail["raw"] for callers who want to re-parse with PyYAML.

Versioning

This SDK targets API v1.x and follows SemVer:

  • MAJOR — drops support for an API breaking change.
  • MINOR — new endpoints, new fields, new options.
  • PATCH — bug fixes, doc improvements.

See the API changelog.

License

MIT — see LICENSE.


Built by Nexus Legal. Questions? Email support@nexusquantum.legal.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nexus_legal-0.13.0.tar.gz (41.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nexus_legal-0.13.0-py3-none-any.whl (44.1 kB view details)

Uploaded Python 3

Release history Release notifications | RSS feed

0.27.0

2 files

0.26.0

2 files

0.25.0

2 files

0.24.0

2 files

0.22.0

2 files

0.20.0

2 files

0.19.0

2 files

0.17.1

2 files

0.17.0

2 files

0.13.1

2 files

This release

0.13.0 This release

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page