Skip to content

Administration Guide

What a bank administrator or operations manager does with Bookend after it is installed: people and permissions, every setting and where it lives, the loan lifecycle and who may move it, integrations, licensing and metering, scheduled work, the audit trail, and the operating routine. The Installation Guide gets you to an activated install; the runbooks (Upgrade, Backup and restore, Incidents, Resilience) are the day-of checklists.

Almost everything is done signed in as an administrator through Settings (Rules, Documents, Approval record, Core boarding, Other options), Users & access, and System (Diagnostics, Metering & license, Audit, Release notes) in the left navigation. Every screen calls the same /v1/* endpoints the API reference documents (/scalar/v1 on the api, or the docs site), so anything below can also be scripted.

Three principles shape the administration model:

  • Configuration lives in the database. Every runtime setting is a row in settings (namespace.key, typed, secrets encrypted with the master key). There are no config files to edit on the host after install; changes apply within 30 seconds, except the few flagged restart required.
  • Segregation of duties is enforced by the server, not by convention: the person who staged a boarding record or a wire cannot approve it, even as an administrator; overrides need a manager; approvals need a person (API keys are refused).
  • Everything is on the record. Data changes go to audit_log, sign-ins and MCP calls to auth_events, and everything that happens to a loan to its hash-chained evidence. Administrators read these under System → Audit and on each loan.
Role Who May
admin Bank administrator Everything below plus: the wizard, users and roles, API keys, all settings, rule switches and bank rules, LAR profiles, core field maps, teaching documents, diagnostics, metering actions
manager Loan operations manager Everything a specialist may, plus override findings, mark funded, seal, read metering and the audit log; receives escalations
specialist Closing specialist Create and assign loans, upload packages and LARs, reprocess, accept or escalate findings, approve or reject packages, stage boarding, stage and export wires
boarding_checker Boarding operator (the “checker”) Approve boarding records and wire requests someone else staged, commit approved boarding records, read loans and evidence
auditor_readonly Auditor / examiner Read every loan, finding, boarding status and evidence packet; verify chains; nothing that changes state
api_service Integration principal Not assignable to a person: what an API key becomes when exchanged for a token, limited further by the key’s scopes

A person can hold several roles (for example specialist + manager). The server’s policies compose them: read-only = any human role; specialist = specialist, manager or admin; manager = manager or admin; boarding-checker = boarding checker or admin (but never the person who staged the record in question).

Action Roles Guard
Create loan, upload package / LAR, reprocess specialist +; API key with intake:write Documents and reprocessing only up to review; create and upload are refused once the license has expired
Assign a loan specialist +; API key with intake:write (PATCH … assigneeId) Assignee is emailed
Accept / escalate a finding specialist + (human) Only findings of the latest run, only while the loan is in review; escalation emails managers and admins
Override a finding manager + (human) Reason code from review.reason_codes and a justification of at least review.justification_min_length characters
Approve / reject the package specialist + (human) Approve needs a note and no unresolved or un-actioned exception
Stage boarding specialist + (human) Loan must be approved; Idempotency-Key replays safely
Approve boarding boarding checker (human) Must not be the person who staged it
Commit boarding boarding checker (human) Only after approval (or to retry a failed commit)
Stage / export wire specialist + (human) Loan must be boarded unless boarding.allow_wire_before_boarded
Approve wire boarding checker (human) Must not be the person who staged it
Mark funded manager + (human) From boarded
Seal manager + (human) From boarded or funded; refused unless the evidence chain verifies; terminal, read-only
Verify / export evidence any human role; API key with evidence:read
Read loans, findings, dashboard any human role; API key with status:read
Users, API keys, settings, rule switches and bank rules, LAR profiles, core field maps, teaching documents, diagnostics, heartbeat, usage reports admin
Audit log, metering status manager +

Users & access (left navigation, admins) manages people and integration keys in the app; everything below is also scriptable. Add a user either emails an invite link (72 h, single-use) that lets the person set a password, or creates the user with a password directly. Each user has a display name, email, roles and an active flag; PATCH /v1/users/{id} edits them, POST /v1/users/{id}/roles replaces the role set, and Deactivate (DELETE /v1/users/{id}) switches the account off. User rows are never deleted, because evidence and audit reference them. Deactivating a user, changing their roles or setting a new password for them revokes their refresh tokens at once, so the change takes full effect within one access-token lifetime (auth.access_token_minutes). You cannot deactivate yourself or remove your own admin role.

Multi-factor (authenticator apps). Turn on auth.login_totp_enabled and anyone who has confirmed an authenticator (their account page, reached by clicking their name → Set up authenticator: add the setup key to Google or Microsoft Authenticator or a password manager, then confirm with a first code) must enter the current six-digit code at every password sign-in. Enrollment is voluntary per user until the bank makes it part of onboarding; a user without one signs in normally, so turning the policy on never locks anyone out. Codes are single-use with one 30-second step of clock tolerance. A user who loses the device is reset by an administrator (Reset MFA in Users & access, or DELETE /v1/users/{id}/totp), which also revokes their sessions; they sign back in with a password or an email link and enroll again. Every enrollment, confirmation, disable and code failure is in the auth trail.

Single sign-on (OIDC). Register Bookend at the bank’s identity provider (Entra ID, Okta, ADFS or any OpenID-certified IdP) as a confidential web client with redirect URI https://<bookend-host>/oidc/callback, then fill in auth.oidc_authority (the issuer URL), auth.oidc_client_id, auth.oidc_client_secret and auth.oidc_provider_name (the display name), and turn on auth.oidc_enabled. The sign-in screen gains “Continue with ”. The IdP authenticates; Bookend authorizes: the proven email must match an existing, active Bookend user (accounts are never created on the fly), roles stay Bookend’s own, and the session that comes back is the same short-lived token pair as every other method. Disabling a user in Bookend therefore ends their access regardless of their IdP status, and the audit trail shows oidc sign-ins alongside the rest. SAML-only shops federate through their IdP’s OIDC support (all the major ones have it).

Sign-in methods (Settings → Other options → auth): password (auth.login_password_enabled), email link (auth.login_otp_enabled, link lifetime auth.otp_ttl_minutes), and auth.allowed_email_domains, which refuses creating or inviting users outside the listed domains. Passwords are Argon2id-hashed with a minimum length of auth.password_min_length (8 to 128). Users change their own password with POST /v1/auth/password.

Tokens. Access tokens are RS256 JWTs (auth.access_token_minutes, 1 to 60) refreshed silently by the SPA; refresh tokens rotate (auth.refresh_token_hours, 1 to 720) and reuse of a rotated token revokes all of that user’s tokens. Tokens live only in browser memory, so a reload means signing in again; there are no cookies or server sessions. auth.signing_key is generated on first boot and is read-only in the UI. Rotating it (delete the row, restart the api) signs everyone out and also changes the key that signs air-gapped usage reports.

Rate limits: 10 attempts per minute per client address on the credential endpoints (password, email link, API-key exchange, OIDC); 300 requests per minute per principal elsewhere; 429 beyond that. Both can be changed with the api environment variables RateLimiting__LoginAttemptsPerMinute and RateLimiting__PerPrincipalPerMinute.

Users & access → API keys (Issue an API key). A key is bk_<prefix>_<secret>, shown once, stored hashed, with scopes status:read (read loans, findings, queue stats, boarding preview), intake:write (create loans, upload packages) and evidence:read (evidence summaries and exports). A caller exchanges it at POST /v1/auth/token for a short-lived JWT carrying api_service and those scopes; the same token works for REST and for the MCP server at /mcp (Claude and other agents; see the MCP server guide). Keys can never act on findings, approvals, boarding or wires. Issuing, exchanging and revoking a key are recorded in auth_events, and every MCP call is written there too (mcp_call) with the tool, a hash of its arguments and the outcome; System → Audit → “Sign-ins & API” → preset MCP calls lists them.

Where: Settings in the left navigation. The four core areas (Rules, Documents, Approval record, Core boarding) are tabs; everyone signed in sees Rules, and the other tabs are for administrators. Everything else is under Other options (admin only): choose a group under “Show options for”, edit type-aware inputs (secrets masked; saving a blank secret keeps the stored value; read-only rows shown as values), and use the test buttons for smtp, core and inference. The same page has Reopen the setup wizard…, which keeps settings and data but returns the install to setup: until an administrator activates it again (step 10), every user is sent to the wizard and scheduled heartbeats pause. Everything below can also be scripted via GET/PUT /v1/settings/{ns}.

Rules have their own tab. Settings → Rules is the workbench: every check Bookend runs, on one screen. Three things happen there:

  • Switch any rule off or on (admins). Disabling is bank policy, not deletion: the rule is skipped from the next reconciliation run onward, and the skip is recorded on the run summary and in the evidence chain, so an examiner always sees which checks were active for a given loan.
  • Author bank rules in one sentence (admins): pick a canonical field, pick the check (every source must agree, documents must match the approval, or must be present, optionally on one document type) and pick how much it matters (exception blocks approval; warning and info do not). No code and no JSON: the platform derives the id (CUSTOM-…), validates the field against the catalog, and evaluates the rule with the same deterministic engine and tolerances as the shipped catalog.
  • Tune how rules judge: tolerances (rules.money_tolerance_cents, rate_tolerance, date_tolerance_days, name_normalization) and reviewer reason codes live one tab away under Settings → Other options.

The shipped rules themselves (principal agreement, party matching, execution completeness and the rest) remain versioned, release-tested code with 100% branch coverage; they can be switched off but not edited. A check beyond what the one-sentence form can express is a release request to Bookend.

Settings → Other options lists every namespace; GET /v1/settings/{ns} / PUT /v1/settings/{ns} are the same thing. Unknown keys, read-only keys and wrong types are refused with field errors. The complete catalog (defaults in parentheses):

Namespace Keys
app public_url: what sign-in links and emails point at
auth access_token_minutes (15), refresh_token_hours (8), otp_ttl_minutes (15), password_min_length (12), login_password_enabled (true), login_otp_enabled (true), login_totp_enabled (false; when on, users who have confirmed an authenticator must enter its code at password sign-in), oidc_enabled / oidc_authority / oidc_client_id / oidc_client_secret (secret) / oidc_provider_name (federated sign-in through the bank’s OpenID Connect IdP), allowed_email_domains (JSON list, empty = any), otp_link_path (/auth/otp), signing_key (secret) and signing_key_id (both read-only, generated on first boot)
smtp host, port (587), tls_mode (none / starttls / ssl; default starttls), username, password (secret), from_address, from_name (Bookend). Read per send, no restart
documents watch_folder (/data/watch, restart required), watch_interval_seconds (60), watch_stable_seconds (5), max_package_mb (200), retention_days (2555 = 7 years; the nightly sweep purges the bytes of loans sealed past it; rows, hashes and evidence stay), last_retention (read-only, the last sweep’s outcome), page_render_dpi (110), taxonomy (read-only catalog)
fields catalog: the canonical field catalog (read-only)
execution templates: where signatures, initials, dates and notary blocks are expected per document type
extraction templates: the bank’s own document templates, taught under Settings → Documents
ai endpoint, model, api_key (secret): an optional OpenAI-compatible endpoint that can draft a document template from a sample; never used at runtime
inference url: the inference service (http://inference:9090 inside the stack)
pipeline max_attempts (8), retry_base_seconds (5): stage retries back off base × 2^attempt up to 10 min
rules money_tolerance_cents (0), rate_tolerance (fraction, 0.00001), date_tolerance_days (0), name_normalization (lenient / strict; default lenient), disabled and custom (managed on the Rules tab), catalog and ruleset_version (read-only)
review reason_codes (JSON list the override dialog offers), justification_min_length (10)
core provider (file.export / jackhenry.jxchange; default file.export), file_export_path (/data/exports), jxchange.endpoint, jxchange.username, jxchange.password (secret), jxchange.institution_id
boarding allow_wire_before_boarded (false)
evidence last_verification: result of the nightly sweep (read-only)
license key (secret, the license JWT); institution, tier, expires_at (read-only, written when the wizard accepts a key)
metering heartbeat_enabled (true), air_gapped (false), endpoint, api_key (secret, optional)
setup wizard state (state, completed_steps, smtp_tested, core_tested, lar_profile_id, activated_at), read-only and managed by the wizard and the Approval record tab

The reference catalogs (documents.taxonomy, fields.catalog, rules.catalog) and the seeded core field maps ship with the release and change only through a release (ADR 0003). The per-bank knobs are the thresholds, reason codes, rule switches and bank rules, execution templates, taught document templates, retention and the integration settings.

The three test buttons (SMTP sends a message to you, core probes the adapter, inference calls /healthz) show the result on screen. The SMTP and core results are recorded (setup.smtp_tested, setup.core_tested) and are what activation checks.

intake ─► processing ─► review ─► approved ─► boarding_staged ─► boarded ─► funded ─► sealed
│ │
│ └─► rejected
└─► (stage failed → retry / Reprocess)

Intake. A package enters four ways: the UI (Loans → New loan → drag the PDFs and lar.json), the API (POST /v1/loans, POST /v1/loans/{ref}/documents, POST /v1/loans/{ref}/lar), the MCP submit_package tool, or the watch folder (documents.watch_folder, LOANREF/*.pdf + lar.*; ingested once the files have been quiet for watch_stable_seconds, then moved to processed/, or to rejected/ with a note for an invalid reference, a duplicate or an error). A reference already in use is refused with 409 unless the caller asks for a new package version (v2, v3…); a package is never overwritten. Identical bytes are idempotent. documents.max_package_mb caps the upload.

Pipeline. Each document runs split → classify → OCR → extract → locate as Quartz jobs with retries. Scanned documents (no text layer) are recognized by Tesseract in the inference container: values read off a scan carry the OCR method with confidence scaled by recognition quality, recognized tokens of three characters or fewer are capped at the review threshold so a person confirms them, and execution zones on scans are decided by the ink above the signature line; the loan page shows the document × stage grid and the evidence chain records every attempt. When every non-LAR document completes, the loan moves to review and the reconciliation run evaluates the 17 shipped rules and any bank rules (thresholds from rules.*) into findings (exceptions, warnings and infos), each pointing at the exact page and box it was decided on. Reprocess starts a new run; replacing the LAR on a loan in review re-runs reconciliation.

Review. The workstation shows findings with their sources; a specialist accepts (acknowledges; an accepted exception still blocks), a manager overrides (reason code + justification), a specialist or above may escalate (managers and admins are emailed). Approve needs a note and is refused while any exception is unresolved or has never been actioned by a person; the approval writes the finding checklist into evidence. Reject needs a note.

Boarding (two-phase, maker-checker). Preview maps canonical values through the active core field map with provenance per field; Stage freezes that record; a boarding checker who did not stage it approves; Commit sends it to the core. The record goes committing first so a crash is visible and resumable, then committed with the core reference (or failed with the provider’s error verbatim; commit again is allowed and never boards twice, because the jXchange adapter resolves a 409 by inquiry). File export writes boarding_{ref}_v{n}.json|xml|csv into core.file_export_path.

Wires. Stage builds the request from the DR&A’s wire disbursement line and the LAR’s beneficiary bank/ABA/account, each field with its source; a different boarding checker approves; Export produces the PDF wire request (also dropped under {export}/wires) or a JSON file drop. Bookend never transmits a wire.

Closing. Mark funded records the funding; Seal verifies the evidence chain and, if intact, makes the loan read-only forever. Download the evidence packet (PDF for people, JSON bundle for machines): the verification banner, every document with its SHA-256, the findings ledger, approvals and every event.

core.provider selects the adapter. file.export writes boarding files to a folder the bank’s core team imports (the api’s app user must be able to write there: mount the bank’s share at /data/exports or point core.file_export_path at a mounted path). jackhenry.jxchange talks to the bank’s jXchange endpoint with the credentials and institution id in core.jxchange.*; the bindings, field map and two-phase behavior are on the Jack Henry jXchange adapter page. Test the connection from Settings → Other options → core. Each boarding record stores the provider and field map version it was staged with, but a commit goes through the adapter that is active at the time, so finish in-flight boardings before switching providers.

Core field maps (Settings → Core boarding)

Section titled “Core field maps (Settings → Core boarding)”

A map is a versioned JSON document per provider: canonical field → core field code, transform (text, upper, money, rate5, date formats, list, expand_list, bool_to_code:A,B and others), required flag. Seeded: jackhenry.jxchange v1 and file.export v1. New version creates the next version, which becomes the active one when saved; previous versions stay for records staged with them. Preview any saved version against a real loan (it must target the active core adapter) to see exactly what would board, without boarding anything. Details: the Core field maps page.

LAR profiles (Settings → Approval record)

Section titled “LAR profiles (Settings → Approval record)”

A profile maps the bank’s approval record onto the canonical fields with per-format locators: JSON paths ($.terms.principal.amount, $.guarantors[*].legalName), CSV columns by header name ($.Principal, $.Guarantor[*] across data rows; delimiter sniffed, RFC 4180 quoting), XML element paths ($.loan.guarantors.guarantor[*].name, trailing @attr) and DOCX label lookups over tables and “Label:” paragraphs (the preview sample travels as base64). A PDF approval record is uploaded with the package instead, and the extraction pipeline reads it like any document. lar-json-v1 is seeded for the canonical JSON. Add a format (or a new version of an existing one) saves the mapping as a new version, which is used for intake when Use this version for intake is ticked (the wizard’s step 8 choice, setup.lar_profile_id); the saved version can then be previewed against a pasted sample. An uploaded LAR must match the format of the profile in use, and a field the profile marks required must be found. Every LAR document records the profile version that parsed it.

Teaching documents (Settings → Documents)

Section titled “Teaching documents (Settings → Documents)”

The template studio extends extraction to the bank’s own document families as data, and the everyday way to do it is to point, not to write rules. Upload one example of the form (Settings → Documents), drag a box around each value on the rendered page and say which field it is: Bookend reads the box back on the spot (“Reads $1,250,000.00 · will follow the label “Principal””), shows the value it would extract, and records the label beside the box as the anchor the rule follows when a scan is offset or the form re-flows. Draw the title the same way to teach recognition, choose the document type, use Check it on this example to see every value drawn on the page, then Save; the template applies from the next loan on and replaces any template already taught for that document type. On a real loan, an administrator can also open any value’s source page from the workstation and use Fix where this comes from: the box they draw becomes that field’s rule for that document type, with the same read-back, and Reprocess this loan now applies it. The raw template JSON (including regex sentence rules) stays under “Show the technical details…” for the rare layout that needs it. A configured AI model (ai.endpoint/ai.model/ai.api_key under Settings → Other options: any OpenAI-compatible endpoint, including a local model inside the bank’s network) can draft a whole template from a sample through POST /v1/extraction-templates/suggest; the draft is checked on the sample and nothing is stored until someone saves it. Runtime extraction stays deterministic.

Any SMTP relay (smtp.*, TLS mode none, starttls or ssl). Templates: sign-in link, password reset, invitation, SMTP test, loan assigned, exception escalated. The relay is read per send, so changes need no restart.

inference.url points at the inference container. Scanned pages are recognized by Tesseract 5 inside that container. Its Ocr__* environment sets the languages, DPI, the per-page timeout and the number of pages recognized at once (Ocr__MaxConcurrentPages, default processors ÷ Ocr__ThreadLimit, at most 8). Leave Ocr__ThreadLimit at 1: Tesseract’s OpenMP threads busy-wait and starve each other on shared cores, and throughput comes from concurrent pages instead. The api’s envelope for one inference call is Inference__TimeoutSeconds (default 600), because a whole scanned document is recognized in one call. /healthz and Diagnostics report the OCR engine and version. Values read off a scan carry the OCR method with a confidence scaled by recognition quality, so a poor scan routes to a person through the normal review cap. Built-in extraction is deterministic and covers the document templates Bookend ships; other forms are taught in the template studio (above). Extraction accuracy is measured, not asserted: every release can regenerate the randomized corpus and its accuracy report by field kind (Developer Guide, Bookend.CorpusEval), the basis of the SR 11-7 validation pack.

License (license.key): an RS256 JWT issued by Bookend and verified offline, carrying institution, tier, install_id, expiry and the air-gapped flag. Production installs verify it against Bookend’s release public key, shipped in the release bundle and configured with Licensing__PublicKeyPath (Installation Guide). System → Diagnostics shows it; GET /v1/license feeds the banner. When it expires, intake becomes read-only (no new loans or uploads) and everything already in the system keeps working: review, boarding, wires, evidence. Install a renewed key under Settings → Other options → license → key; intake reopens within 30 seconds, with no restart. Settings does not validate the key when it is saved, so confirm the new expiry on System → Metering & license or Diagnostics afterward. (The read-only license.institution, tier and expires_at rows are refreshed only when a key goes through the wizard’s Licensing step or PUT /v1/setup/licensing; the banner and GET /v1/license always read the key itself.)

Heartbeat (metering.heartbeat_enabled, metering.endpoint): daily at 03:15 UTC (plus once shortly after the api starts and once at activation) the api POSTs { install_id, version, period, closed_loan_count, document_page_count, health, license_key, generated_at }: the month-to-date count of sealed loans, the pages of documents received, coarse health words and the license key, and nothing else. The endpoint is the one Bookend gives you with your license (https://metering.usebookend.com/v1/heartbeat), so allow it for the api container in your egress rules; metering.api_key (secret) holds the metering API key, if Bookend issued one to you. The receiver validates the license key the heartbeat carries, and its verdict appears in the delivery history. System → Metering & license shows the exact payload before it is sent, the delivery history and errors, and (for admins) Send heartbeat now. A banner warns managers and admins when no heartbeat has been delivered for 7 days.

Air-gapped (metering.air_gapped): no outbound calls; instead an admin generates a signed quarterly usage report (System → Metering & license → quarter as YYYYQn → Generate signed usage report): the same payload for the quarter, canonical JSON signed with the install’s RS256 key, downloadable from the history and verifiable against the key the install publishes at /v1/.well-known/jwks.json. Send it to Bookend by whatever channel the bank allows. The Air-gapped operations page covers releases and mail in that mode.

Job Schedule What it does
Pipeline stages on demand, retries retry_base_seconds × 2^n split / classify / ocr / extract / locate per document, one durable job per attempt
watch-folder-poll every 15 s (throttled to watch_interval_seconds) ingests quiet LOANREF/ folders
evidence-verifier-nightly 02:00 UTC re-hashes every loan’s chain; result in evidence.last_verification and Diagnostics; a broken chain is an incident
metering-heartbeat-daily 03:15 UTC (+ startup, + activation) the heartbeat above; skipped until the install is activated, or when air-gapped or disabled
document-retention-nightly 04:00 UTC deletes stored bytes and page renders of loans sealed longer than documents.retention_days ago; metadata, hashes and the evidence chain stay, so sealed packets still verify; deduplicated blobs still referenced in-policy are kept; result in documents.last_retention

Jobs are persisted in the database (Quartz ADO store), so a restart resumes them and a misfired schedule fires at the next start. Diagnostics lists every job with previous and next fire time. What every component tolerates on crash or restart, and the quarterly restore drill that proves the backup, are in the Resilience runbook.

  • System → Audit → Data changes: every insert/update/delete on business tables (table, entity, actor, before/after JSON) from audit_log, written in the same transaction as the change. Secret settings appear there only in encrypted form.
  • System → Audit → Sign-ins & API: auth_events: sign-ins, failures, email-link requests, token refreshes and revocations, API-key issue, exchange and revocation, MCP calls, with address, user agent and detail; presets for sign-ins, failed sign-ins and MCP calls.
  • Per loan → Evidence: the hash chain (intake.created, every pipeline stage, reconciliation.run, finding.raised/actioned, review.approved, boarding and wire events, loan.funded, loan.sealed) with expandable payloads and a verify badge. evidence_events, auth_events, finding_actions and audit_log are append-only for the application principal at the database level.
  • Dashboard: queue counts, throughput, review time, exception rate by rule, aging, all derived from recorded events; there are no separate metrics tables.
When Do
Daily Glance at Dashboard aging and the Exceptions queue; check the banners (license, heartbeat); docker compose ps all healthy
Nightly Automated backup (Backup and restore runbook); the evidence sweep and heartbeat run themselves
Weekly System → Diagnostics: last evidence sweep clean, jobs firing, disk on the documents volume; System → Audit: failed sign-ins and MCP calls look expected
Quarterly Restore rehearsal into a scratch stack; air-gapped installs generate and send the usage report; review users and API keys, remove leavers
On release Upgrade runbook: backup, pull, up -d --wait, smoke test, read System → Release notes
On alert Incidents runbook: the symptom table; escalate with the Diagnostics JSON and logs, never with loan documents
Thing Where
Bootstrap secrets deploy/.env (master key, database passwords), kept in a vault
Everything else settings table, via Settings (tabs and Other options)
Documents documents volume, /data/documents/{ab}/{sha256}.pdf + page renders
Boarding files, wire PDFs exports volume, /data/exports, /data/exports/wires
Watch folder watch volume, /data/watch/{LOANREF}/ → processed/ or rejected/
Loans, findings, evidence, audit, jobs the database
Rule documentation in-product ? on a finding, GET /v1/rules/{id}/doc, the Rules pages of the documentation
API reference /scalar/v1 on the api; docs/api/openapi.json; the docs site
Support bundle System → Diagnostics (no loan data) + docker compose logs --since 1h api