nexus-legal
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
POSTcarries an auto-generatedIdempotency-Key(UUID4) reused across retries → no double-charge. - Smart retries — 429 / 5xx with exponential backoff + jitter, honoring
Retry-After. - Typed errors —
RateLimitError,InsufficientCreditsError,IdempotencyConflictError,ValidationError,AuthenticationError,ServerError,TimeoutError. - Rate-limit telemetry — every response exposes
result["rate_limit"]. - Audit-trail parser —
extract_audit_trail()returns a structuredTypedDictwithout any YAML dependency. - Webhook signature verification —
verify_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 inresult["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: agil ⇄ standard, auditoria ⇄ deep. 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()
# ...
Governance (events, ledger, attestations)
Pull the governance event bus (append-only; records every emission even without a subscribed webhook), page the credit ledger, and verify Ed25519 attestations offline.
# Event bus — cursor pagination + filters
page = client.events.list(type=["citation.broken", "spend_cap.reached"])
for ev in client.events.iter(since="2026-07-01T00:00:00Z"):
print(ev["type"], ev["created_at"], ev["payload"]) # payload is ZR-safe
# Credit ledger — per-sub-key, cursor keyset
for entry in client.credits.iter_ledger(kind="consumption"):
print(entry["created_at"], entry["amount"], entry["api_key_id"])
Attestations are signed (Ed25519) and verify offline against the public
JWKS — a regulator or the end client validates without a Nexus account or a
shared secret (unlike the HMAC used for webhooks). Requires the cryptography
extra: pip install "nexus-legal[attestation]".
import requests
from nexus_legal import verify_attestation
att = client.outputs.attestation(output_id)["attestation"]
jwks = requests.get(
"https://legal.nexusquantum.legal/api/v1/attestations/keys"
).json()
res = verify_attestation(att, content=analysis_text, jwks=jwks)
assert res["signature_valid"] and res["content_match"]
content_hash is sha256(NFC(content).strip()), so content_match proves the
text you hold is exactly what was attested. For a server-side check that also
re-verifies citations against the corpus, use
client.attestations.verify(attestation=att, content=analysis_text) →
{signature_valid, content_match, citation_recheck}.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nexus_legal-0.17.0.tar.gz.
File metadata
- Download URL: nexus_legal-0.17.0.tar.gz
- Upload date:
- Size: 58.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff09b2519694c8d39f5dc229e1fe012e1256daf9272e723fb03dd7c1f050292d
|
|
| MD5 |
92f52932b644b15ab91b23a5fc7efd34
|
|
| BLAKE2b-256 |
8971a822b8b9b31241f8261e45dc02b3d43cc67fc91b8d26bcc8ec4c4ca7edec
|
File details
Details for the file nexus_legal-0.17.0-py3-none-any.whl.
File metadata
- Download URL: nexus_legal-0.17.0-py3-none-any.whl
- Upload date:
- Size: 63.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
775a1e42c7053581ee589a607f66a5229007e09e8b16133e9415833c6299f28a
|
|
| MD5 |
92440a05d5a4b417342282e9f9eaa1ad
|
|
| BLAKE2b-256 |
04c209a2b4aaf62ba770428b527c42b1eb49115054e5d19de427b1b067beae4f
|