---
title: "List the firm's LLM providers (owner-only) · 0 credits"
method: GET
path: "/api/v1/llm-providers"
tags: ["BYO-LLM"]
---

# List the firm's LLM providers (owner-only) · 0 credits

`GET /api/v1/llm-providers`

## Request

- Base URL: `https://legal.nexusquantum.legal`
- URL: `https://legal.nexusquantum.legal/api/v1/llm-providers`
- Auth: HTTP bearer

Lists the firm's BYO-LLM configurations (the key holder's). Firm-scoped OWNER-ONLY (`.eq(firm_owner_id, userId)`). NEVER returns credentials: each item exposes only non-secret fields (`key_fingerprint`, `kind`, `model_default`, `enabled`…). No charge. Scope: mcp | * (NOT cases).

**Read `byo_llm_required` before reading `data`.** An empty `data` means two OPPOSITE things: on a normal account, "BYO is not configured yet" and generation runs on the platform chain; on a BYO-mandatory account (`byo_llm_required: true`), "nothing generative will run" — every generative call returns `409 BYO_PROVIDER_REQUIRED` until an engine is registered and enabled, with no platform fallback. `generative_access` states which of the two it is, which roles are blocked, and where to register an engine. Additive (2026-07-27): existing fields and behaviour are unchanged, and this does NOT relax the 409.

## Response `200`

Provider list (non-secret projection) + the account's generative state: `{ data, byo_llm_required, generative_access, requestId }`.

- object
  - `data` LlmProvider[]
    - `id` string, uuid
    - `label` string: Human-readable name, unique within the firm.
    - `kind` 'openai_compatible' | 'anthropic' | 'bedrock': Adapter family.
    - `base_url` string, nullable: Endpoint (openai_compatible: required; anthropic: optional; bedrock: null, derived from region). Validated against SSRF.
    - `model_default` string: Default model.
    - `model_map` object: Role→model map (e.g. { "A": "claude-opus-4-8", "B": "gpt-4o" }). No silent defaults: each node uses exactly the declared model (or `model_default` when the role is not mapped). Recommendation (not a default): Node A → `claude-fable-5` · Node B → `claude-opus-4-8` · Node C → `claude-opus-4-8` or another equally strong reasoning model; any model of the 3 kinds works in any node. `B` ≠ `A` is recommended to preserve the independence of the audit.
    - `key_fingerprint` string, nullable: NON-secret, NON-reversible fingerprint of the credential (hash, e.g. `sha256:a1b2c3d4e5f6`; older rows may show the legacy format `sk-p…a1b2`). The ONLY representation of the key that leaves the API.
    - `extra_headers` object: Optional extra headers for the provider.
    - `routing_policy` 'own_only' | 'own_then_platform': own_then_platform leaves the platform chain as a fallback if your provider fails recoverably.
    - `role` 'A' | 'B' | 'C', nullable: Node this engine is assigned to: 'A' (primary analysis), 'B' (adversarial audit) or 'C' (adversarial counterparty). Omit or null = the firm's DEFAULT engine, which serves every node and role (consulta, chat, draft…). Precedence: node engine → default engine → (when the policy forbids platform) Node A engine → platform. Allows a DIFFERENT engine per node: B and C should not share a model with A, or the cross-audit loses its value. Additive (per-node BYO, 2026-07-24).
    - `billing_mode` string: Billing mode (default platform).
    - `enabled` boolean: Whether it is active for routing firm traffic.
    - `last_verified_at` string, date-time, nullable: Last successful /test health check.
    - `created_at` string, date-time
    - `updated_at` string, date-time
    - `secret_at_rest` 'encrypted_aes_256_gcm' | 'plaintext': Creation response only: at-rest encryption status of the credential.
  - `byo_llm_required` boolean: true = this account is provisioned to run generation ONLY on the firm's own engine (operator-managed; not settable via the API). With `data: []` that means generative calls are blocked, NOT that BYO is merely unconfigured. Additive (2026-07-27).
  - `generative_access` GenerativeAccess: Whether this account can call the generative endpoints AT ALL, returned by `GET /api/v1/llm-providers` so an empty `data` is never mistaken for "not configured yet". It does NOT change the gate: the 409 `BYO_PROVIDER_REQUIRED` of each generative route is still the enforcement, and this object is computed with the SAME per-node precedence that decides it. Additive (2026-07-27).
    - `blocked` boolean: true = at least one generative role returns 409 BYO_PROVIDER_REQUIRED right now. `false` is an assertion, not a silence: every generative role resolves to an engine.
    - `blocked_roles` string[]: The roles that would 409 (subset of A, B, C, consulta). Empty ⇔ blocked:false.
    - `code` 'BYO_PROVIDER_REQUIRED', nullable: The SAME stable code the call would return. null when nothing is blocked.
    - `message` string: What is happening and what to do, in prose: attribution (account configuration, not an outage), the fact that retrying returns the same 409, and the next action.
    - `register_endpoint` string: Where an engine is registered: `POST /api/v1/llm-providers`.
    - `docs` string, uri: BYO-LLM documentation (application host, anchor `#byo-llm`). The developer portal requires a signed-in session; the public machine-readable source of the same contract is this spec, `GET /api/v1/openapi.json`. The action that needs no browser is `register_endpoint`.
    - `depth` 'config_only': How far the check went: CONFIGURATION only. The provider was NOT called — a stored key is not a live engine. `POST /api/v1/llm-providers/{id}/test` is what calls it.
  - `requestId` string

---

[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)
