---
title: "Sandbox — analysis with pre-canned responses"
method: POST
path: "/api/v1/sandbox/analyze"
tags: ["Sandbox"]
---

# Sandbox — analysis with pre-canned responses

`POST /api/v1/sandbox/analyze`

## Request

- Base URL: `https://legal.nexusquantum.legal`
- URL: `https://legal.nexusquantum.legal/api/v1/sandbox/analyze`
- Auth: HTTP bearer

Sandbox endpoint: same signature as `/api/v1/analyze` but returns realistic pre-canned responses. Consumes NO credits, calls NO LLM. Designed so partner devs can integrate their client without spending balance during development/CI.

Rate limit: 60 rpm — 30 rpm for `sandbox_unbilled` keys. Sandbox is SG-only (test jurisdiction): 2 samples available, in English and with pre-verified citations: `contract_sg`, `employment_sg`. `jurisdiction` accepts only `SG` (or omitted) — any other value returns `400 VALIDATION_ERROR`.

**Unbilled sandbox (per-key)**: by default this endpoint requires an account with balance > 0. Keys flagged by the operator with `sandbox_unbilled` may use it WITHOUT balance, at a reduced limit of 30 requests/minute. The flag is operator-only (granted by the Nexus operator; not self-settable via the API).

## Request body

- object
  - `sample` 'contract_sg' | 'employment_sg': Which canned response to return. Default: contract_sg.
  - `text` string: Ignored (sandbox).
  - `jurisdiction` string: SG only (or omitted) — otherwise 400 VALIDATION_ERROR. The sandbox is a single test jurisdiction; for other jurisdictions use POST /api/v1/analyze (billed).
  - `legalBranch` string: Overrides the sample's legal branch.
  - `language` 'es' | 'en': Ignored (sandbox): canned responses are in English, the language of the SG test jurisdiction (language follows the jurisdiction).

## Response `200`

Canned response. Headers `X-Sandbox: true` and `X-Processing-Ms`.

- object
  - `skill` 'standard' | 'agil' | 'auditoria', required: WHICH ANALYSIS CORE RAN. `mode` and `skill` are two different questions that share a name on the way IN: `mode` is the BILLING axis (does Node B audit? `standard` vs `deep`), `skill` is which Node A core was loaded. Sending `mode: "auditoria"` bills as `deep` AND loads the forensic-audit core, so the response comes back with `mode: "deep"` and `skill: "auditoria"` — and the report header says `MODE: auditoria`. Until 2026-08-18 only the billing axis came back, so that header looked like it contradicted the JSON. It never did.
  - `analysis` string, required: Legal analysis generated by Node A (Claude Opus 4.7).
  - `jurisdiction` string, required: ISO code of the processed jurisdiction.
  - `legalBranch` string, required: Legal branch applied.
  - `model` string, required: Label of the engine that ANSWERED, not a stable model identifier. It is the chain's own label — 'DeepSeek V4-Pro', 'Opus 4.8 · OpenRouter' — or, when the firm brings its own engine, that engine's model id. In mode=deep it is composed as '<analyst>+<auditor>'. `unknown` means nobody could be named: the value is never guessed. Under partner or neutral branding it is replaced by a neutral label. Do NOT key logic on this string: which engine answers is a deployment decision and the chain falls back on its own.
  - `processingMs` number, required: Total analysis latency in milliseconds.
  - `mode` 'standard' | 'deep', required: Canonical mode applied (aliases normalized).
  - `creditsCharged` integer: Credits charged for this analysis (1 standard / 2 deep).
  - `audit` AuditResult: Node B adversarial audit of the Node A analysis. Present only when mode=deep. IMPORTANT: check `audited` before reading `findings` or `overallSeverity`. When `audited` is false the audit produced no usable verdict, and those two fields are filler — an empty `findings` there means "we could not audit", NOT "nothing was found". Engine failures (no credit, rotated key, quota, unreachable provider) do NOT arrive here at all: they return 424 with a machine code, so a 200 with an audit always means your engine answered.
    - `auditor` string, required: Label of the engine that produced the audit. `unknown` when none could be named — which is what you get if every engine in the chain failed and `audited` is false. Same caveat as `model`: do not key logic on it.
    - `audited` boolean: True when the auditor produced a readable verdict. False when it answered but its output could not be parsed into one. Absent on responses predating 2026-07-26; treat absence as unknown rather than as true.
    - `text` string, required: Auditor's textual analysis, free-form.
    - `findings` AuditFinding[], required
      - `type` 'hallucination' | 'missing_clause' | 'weak_reasoning' | 'citation_error' | 'other', required: Audit finding category.
      - `severity` 'low' | 'medium' | 'high', required
      - `description` string, required: Specific description of the finding. Max 1000 chars.
    - `overallSeverity` 'low' | 'medium' | 'high', required
    - `recommendations` string, required: Specific recommendations, free-form.
    - `processingMs` number, required
  - `citationChecks` CitationVerificationSummary
    - `checks` CitationCheck[], required
      - `citation` string, required: Citation exactly as it appeared in the analysis.
      - `kind` 'L4-N' | 'L2-J', required: Legislation (L4-N) or case law (L2-J).
      - `status` 'verified' | 'mismatch' | 'not_found' | 'derogated' | 'pending' | 'not_verifiable', required: verified=exists and matches; mismatch=exists but diverges; not_found=absent from the active corpus; not_verifiable=jurisdiction with no active corpus; pending=not parseable.
      - `similarity` number, nullable, required: Lexical similarity (Jaccard) against the canonical text.
      - `semantic` number, nullable, required: Semantic similarity (cosine), only in the lexical gray zone.
      - `url` string, nullable, required: Official source URL for THAT jurisdiction when one is located (BOE · CENDOJ · Singapore Statutes Online · eLitigation · SUIN-Juriscol…).
      - `mismatchKind` 'meaning' | 'stitched' | 'lexical' | 'party': Only with `status: mismatch`, and ADDITIVE (may be missing from older checks). Grades the severity, which used to be lost: `meaning` = a meaning veto fired (negation, modality, quantifier, omitted exception, subject) → a REAL divergence of meaning. `stitched` = the quoted fragments are literal but do not come from a CONTINUOUS passage of the official text, or they alter its order — an ASSEMBLED quote. The defect is structural: a divergence of meaning has NOT been proven, so it must not be presented as one (added 2026-07-25; measured by contiguity since 2026-07-27). `lexical` = only the lexical ratio failed: an honest paraphrase that cannot be checked literally. `party` = the citation EXISTS but resolves to a DIFFERENT decision from the one the text names (incompatible party names — typically a transposed court, e.g. `Spandeck … [2007] SGHC 37` when the case is SGCA 37). It is neither a non-existent citation nor a divergence of meaning: it points elsewhere. `similarity` comes back `null` on purpose — the claim is not compared against a decision that is not the one named (added 2026-07-27).
      - `note` string, nullable: Human-readable note from the verifier (deterministic, server-side — never from the model). E.g. STATUTE-LEVEL verification of a law cited without an article, or the notice that the registry proved existence without returning the text.
      - `canonicalText` string, nullable: Official text the verifier downloaded to check the citation against, when the corpus returns it. For case law this is the full judgment: truncate it before displaying.
    - `counts` object, required: Count per status.
    - `anyBroken` boolean, required: There is at least one broken citation (mismatch/not_found/derogated). EXCLUDES `pending` (not parseable) and `not_verifiable` (jurisdiction with no active corpus): those statuses are NOT breakages. READ `composition` BEFORE ACTING ON THIS: `anyBroken: false` means every citation is faithful to its source ONE BY ONE. It does NOT mean the argument they support holds — the inferential step that joins them is not verified by this field.
    - `composition` object: SCOPE OF THE COMPOSITION — what `anyBroken` cannot tell you. Verifying each citation proves fidelity to its source; it says nothing about the step that JOINS them. Two impeccable citations can support a false conclusion. This object carries the axes of that step that ARE decidable without understanding the argument — repealed premises, premises not yet in force, and above all validity windows that never overlapped (a defect invisible when citations are checked one at a time) — and, just as important, the axes that are NOT checked, each with its reason. IT IS ALWAYS PRESENT, including when there are no findings: if it appeared only on problems, its absence would again mean "this is fine", which is the reading it exists to prevent. `no_comprobado` permanently includes `validez_logica`: WHETHER THE CONCLUSION FOLLOWS FROM THE PREMISES IS NOT CHECKED, and that is not a pending task — it is not a mechanically decidable question. An empty `hallazgos` is not a statement that the reasoning is valid.
      - `comprobado` string[]: Axes actually examined. An axis only enters here if there was MATERIAL for it to produce a finding — never merely because the code ran.
      - `hallazgos` object[]: ANOMALIES. Named, never an aggregate boolean. A fact only qualifies as a finding if it is measurably RARE in correct work — see `observaciones` for the ones that are not.
        - `eje` string
        - `clase` 'ventanas_sin_solape' | 'premisa_derogada' | 'premisa_no_nacida' | 'fuera_de_fecha' | 'fuera_de_territorio' | 'sin_autoridad_vinculante'
        - `citas` string[]
        - `detalle` string
        - `no_afirma` string: What the finding does NOT assert. Travels with it so it cannot be quoted alone as a verdict.
      - `observaciones` object[]: FACTS ABOUT THE SET THAT ARE NOT ANOMALIES, each carrying its measured base rate. Citing authority from another British forum fires on 49.71% of real Scottish and Northern Irish judgments, and in 18.21% every authority cited is from another forum (7,494 judgments, 23,334 resolved citations, measured 2026-08-15) — it is normal practice, so reporting it as a finding would train integrators to ignore the findings that matter. Treat these as checklist items, not defects. `hallazgos: []` WITH a populated `observaciones` is a NORMAL result, not a clean one.
        - `eje` string
        - `clase` 'autoridad_de_otro_foro'
        - `citas` string[]
        - `detalle` string
        - `base_medida` string: How often this appears in CORRECT work, with the measurement behind it.
        - `no_afirma` string
      - `no_comprobado` object[]: Axes not examined and why. `validez_logica` is permanently here and is NOT decidable; the rest appear only when the data for them was absent, each with its reason.
        - `eje` string
        - `motivo` string
      - `sin_dato` object[]: Citations left out of a comparison for lack of data. A missing datum never becomes a finding.
        - `eje` string
        - `citas` string[]
    - `total` integer, required: Number of verifiable citations detected.
    - `error` boolean: ADDITIVE and optional; present (and always `true`) ONLY when the deterministic verification COULD NOT RUN at all (exception, timeout, corpus unreachable). READ IT BEFORE `anyBroken` or `counts`: with `error: true` the summary carries `total: 0`, `anyBroken: false` and all counters at zero — that is FILLER, not a verdict. It means "the citations were NOT checked", NOT "the citations were checked and are clean". Treat every citation in the answer as unverified and review it against the official source. It is a new optional FIELD on purpose, never a new value inside the closed `status` enum, so integrators that `switch` on status do not break. Absent on every successful verification.
    - `unverifiableClaims` integer: ADDITIVE and optional (present only when > 0). Number of legal assertions written in PROSE that invoke authority without a formal, parseable citation ("as the Court held in Vega, the appeal was dismissed"). No parser extracts them, so they never appear in `checks` and never count as broken — but with `total: 0` they are the difference between "no citations" and "authority claimed with nothing to check it against". Use it for a caution, never to mark a citation verified or not_found.
    - `coverage` object: WHAT COULD BE LOOKED AT, not just what was found. Read this BEFORE `counts` or `anyBroken`. Three situations used to reach the client indistinguishable: a report with no authorities to verify, a report whose citations were all checked, and a report that ASSERTS doctrine with no recognisable citation apparatus — so nothing could be checked at all. The third one is the dangerous one and it is what any prose without citation apparatus produces.
      - `state` 'checked' | 'partially_checked' | 'no_authority' | 'unverifiable' | 'not_measured' | 'check_failed', required: `checked` = there were citations and they were contrasted (see `checks`), and nothing else in the text asserts authority. `partially_checked` = there were citations AND ALSO statements attributing holdings to courts with no checkable reference of their own. The green verdict on the citations does NOT cover those: a single verifiable citation anywhere in the document (an appendix table of authorities, say) must not be readable as a verdict on the whole body. `no_authority` = neither citations nor authority claims: there was genuinely nothing to verify. `unverifiable` = the text attributes holdings or doctrine to courts WITHOUT any checkable reference, so NOTHING was contrasted against the corpus. This is NOT a statement that the content is wrong — it is a statement that it is unverified, which is not the same as a clean report. `not_measured` = no citation found AND the report's language has no authority-claim lexicon, so the zero is a not-measured, not a "none". `check_failed` = the verifier itself could not run (mirrors `error: true`).
      - `citations` integer, required: Citations the verifier was able to look at (equals `total`).
      - `authorityClaims` integer, required: Statements attributing a holding or settled doctrine to an authority with no checkable citation in the same sentence.
      - `examples` string[]: Up to three of those statements, truncated. Present only when there are any: "there are 4" without saying which is not actionable.
      - `language` string, required: Language the report was produced in, as resolved by the platform. Decides whether a zero in `authorityClaims` is measurable.
  - `partyScreening` PartyScreening: What was actually screened, and what was not. Read `screened` and `failed` TOGETHER: a party that could not be checked is not a party that came back clean.
    - `screened` integer: Parties ACTUALLY checked against the list. Never includes the ones that failed. This field was previously named `checked` and counted both, so a partial failure reported "3 parties screened" having screened one.
    - `failed` integer: Parties that were attempted and could NOT be checked. `failed > 0` with `entries: []` is NOT a clean result.
    - `failureReason` string: Why it failed, verbatim from your provider (e.g. `HTTP 401`), so you can retry or tell them. Screening STOPS at the first failure: we never substitute your provider with anything of ours.
    - `list` string: The list actually queried. Absent when none was.
    - `entries` object[]: Only the hits. A clean screening has an empty array AND `failed: 0` — check both.
      - `entityName` string
      - `status` 'SANCTIONED' | 'POTENTIAL_MATCH' | 'CLEAN' | 'SKIPPED': `SANCTIONED` means a NAME matched at ≥0.80. It never means the party is sanctioned: secondary identifiers were not checked.
      - `score` number
      - `matchedList` string
      - `matchedName` string: The name that matched. May be an ALIAS.
      - `primaryName` string: The entity's primary name when `matchedName` is an alias. Without it you cannot adjudicate: OFAC lists «HIZBALLAH» as the entity and «LEBANESE HEZBOLLAH» among its aliases.
      - `provider` 'local' | 'byo', nullable: Who actually screened. Was previously reported as `sdn`/`opensanctions` regardless of the truth: a match from a CSV on our own disk was labelled OFAC SDN.
  - `firmMemory` FirmMemoryApplication: Whether the firm's standing preferences (Firm Memory: tone, risk criteria, model clauses, report structure — the entries managed at `/api/v1/firm-memory`) were applied to THIS analysis. READ IT BEFORE ASSUMING THEY WERE: until 2026-07-26 they were injected only by the web analysis route, so an analysis requested over REST never applied them — while the setup screen promised that every Nexus analysis would. That gap is now closed and this field is how you can tell. The firm is derived from the API key / session that signs the request. A `firm_id` in the request body is neither read nor honoured: firm scope is never client-supplied. ADDITIVE and optional, with its own enum — never a new value inside an already-published one. ABSENT when the request carried no account holder, in which case the response makes no claim either way.
    - `status` 'applied' | 'none' | 'unavailable', required: `applied` — preferences were read and injected into the Node A prompt. `none` — they were read and this firm has none for the scope of this analysis (jurisdiction + legal branch + global). `unavailable` — they COULD NOT BE READ, and the report was produced WITHOUT them. This is NOT `none`: it does not mean the firm has no preferences, it means we do not know. The analysis is still returned and still charged (the document was processed); if the preferences matter for this document, re-run it.
    - `entries` object[]: WHICH standing instructions were applied, one by one. Present when `status` is `applied`. Until 2026-07-27 the injected block instructed the model not to mention or cite these preferences in its output — binding instructions the result was forbidden to declare. That order has been removed: preferences still must not appear as meta-commentary in the body of the report, but the fact that they applied, and which ones, is declared here. A firm with a compliance function cannot accept standing instructions it is unable to evidence.
      - `id` string, uuid, required
      - `title` string, required
      - `category` 'clausula' | 'tono' | 'riesgo' | 'formato' | 'general', required
      - `scope` 'global' | 'jurisdiction' | 'subject_matter' | 'case', required: How wide the entry is. `global` binds every analysis of the firm; the rest only match this analysis's jurisdiction, subject matter or case file. Relevant when auditing why a report reads the way it does.
      - `version` integer, required
    - `clausesRefused` integer: Number of firm-memory clauses REMOVED before the prompt was assembled because they asked for something firm memory does not decide. Firm memory governs HOW the analysis is written (tone, terminology, structure, emphasis, risk posture, model clauses, sections that must always appear). It cannot switch off verifications or safeguards, change the jurisdiction or the output language, suppress warnings or adverse findings, order concealment, or redefine the model's role. A non-zero value means an entry in the firm's memory tried one of those and the rest of that entry was still applied — inspect the entries at `/api/v1/firm-memory`. Absent when nothing was removed.
    - `refusedAxes` string[]: Which capability was being attempted, for each removed clause. The clause text is deliberately not returned: it may quote case facts.
  - `sandbox` boolean
  - `sample` string
  - `available_samples` string[]
  - `notes` string

## Other responses

- `402`: INSUFFICIENT_CREDITS — the sandbox does not debit credits but requires an account with balance > 0 (no free trial). Exception: keys flagged by the operator with `sandbox_unbilled` skip this gate (reduced limit of 30 rpm).

---

[API](https://skmtc.dev/nexusquantum/apis/nexus-legal-api.md) · [All operations](https://skmtc.dev/nexusquantum/apis/nexus-legal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nexusquantum/apis/nexus-legal-api/revisions/4084d8d4eb0b?raw)
