Reg BI Recommendation Audit-Trail Schema Template
Reg BI exam outcomes hinge on whether the recommendation event log has the structural fields an examiner walks through. This template ships a canonical JSON schema for that event log — calibrated against the SEC Division of Examinations' Reg BI sweep findings — so engineering can adopt a defensible structure on day one rather than retrofitting after the first finding.
What you walk away with
~20 min · 4 slots · 13 blocks- A canonical JSON schema for the recommendation event log, ready to import into the firm's data pipeline.
- Field-level annotations explaining what each field is for and what 'good' looks like.
- Cross-reference to the Reg BI Audit Data Checklist field-by-field.
Variables
Live document preview
Reg BI Recommendation Audit-Trail Schema — [FIRM_NAME]
Schema version: 1.0.0 · Registration: bd
This schema documents the canonical structure for the recommendation event log. Every recommendation made by [FIRM_NAME] should produce a `recommendation_event` record matching this schema, with the linked `disclosure_event` and `acknowledgment_event` records as applicable.
Engineering should treat this as a canonical schema — application code may project additional fields, but every field below should be present in the persisted record. Optional fields are marked. Tenant isolation is via `firm_id` per the firm's standard convention.
1. Recommendation event
json{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://[FIRM_NAME]/schemas/reg-bi/recommendation-event/v1.0.0.json", "title": "Reg BI recommendation event", "type": "object", "required": [ "firm_id", "event_id", "event_timestamp", "client_id", "recommending_individual_id", "recommendation_type", "suitability_snapshot", "rationale", "disclosure_event_ids", "supervisory_review" ], "properties": { "firm_id": { "type": "string", "description": "Firm identifier" }, "event_id": { "type": "string", "description": "Unique id (UUIDv4)" }, "event_timestamp": { "type": "string", "format": "date-time", "description": "ISO 8601 timestamp at the moment the recommendation was made" }, "client_id": { "type": "string", "description": "Internal client identifier (relationship-level)" }, "household_id": { "type": ["string", "null"], "description": "Household identifier when applicable" }, "recommending_individual_id": { "type": "string", "description": "Internal id of the recommending RR / IAR / algorithm" }, "recommending_individual_kind": { "type": "string", "enum": ["registered_representative", "investment_adviser_rep", "algorithmic", "hybrid"] }, "recommendation_type": { "type": "string", "description": "Firm-defined granular type, e.g. '401k_rollover_to_ira'" }, "recommendation_subject": { "type": "object", "description": "What the recommendation is about (security, account, allocation)" }, "suitability_snapshot": { "type": "object", "description": "Frozen snapshot of suitability fields at recommendation time", "required": ["risk_tolerance_score", "time_horizon", "investment_objectives", "liquidity_needs", "tax_status"], "properties": { "risk_tolerance_score": { "type": "number" }, "time_horizon": { "type": "string", "enum": ["short", "medium", "long"] }, "investment_objectives": { "type": "array", "items": { "type": "string" } }, "liquidity_needs": { "type": "string" }, "tax_status": { "type": "string" }, "investment_experience": { "type": "string", "enum": ["none", "limited", "moderate", "extensive"] }, "kyc_last_refreshed": { "type": "string", "format": "date-time" }, "cognitive_status_flag": { "type": ["string", "null"], "enum": [null, "none", "mild", "moderate", "impaired"] }, "trusted_contact_on_file": { "type": "boolean" }, "poa_on_file": { "type": "boolean" } } }, "rationale": { "type": "object", "description": "Why this recommendation was made — structurally captured, not free-text", "required": ["primary_basis", "fields_referenced", "alternative_considered"], "properties": { "primary_basis": { "type": "string", "description": "e.g. 'income_replacement_in_retirement'" }, "fields_referenced": { "type": "array", "items": { "type": "string" }, "description": "Which suitability_snapshot fields drove the decision" }, "alternative_considered": { "type": "object", "description": "What alternative was evaluated and why it was rejected", "required": ["alternative_subject", "rejection_reason"] } } }, "disclosure_event_ids": { "type": "array", "items": { "type": "string" }, "description": "Linked disclosure events fired in conjunction" }, "acknowledgment_event_ids": { "type": "array", "items": { "type": "string" }, "description": "Linked client acknowledgment events" }, "supervisory_review": { "type": "object", "required": ["required", "completed"], "properties": { "required": { "type": "boolean" }, "completed": { "type": "boolean" }, "reviewer_id": { "type": ["string", "null"] }, "reviewed_at": { "type": ["string", "null"], "format": "date-time" }, "outcome": { "type": ["string", "null"], "enum": [null, "approved", "modified", "rejected"] }, "outcome_notes": { "type": ["string", "null"] } } }, "compensation_disclosure": { "type": "object", "description": "Compensation conflict at recommendation time", "properties": { "fee_basis": { "type": "string", "enum": ["advisory", "commission", "embedded", "none"] }, "proprietary_product": { "type": "boolean" }, "revenue_share": { "type": "boolean" } } }, "rollover_specific": { "type": ["object", "null"], "description": "Required when recommendation_type is a rollover", "properties": { "source_plan_summary": { "type": "object" }, "destination_plan_summary": { "type": "object" }, "comparative_analysis": { "type": "object", "description": "Fees, services, investment-options comparison with sources" } } }, "schema_version": { "type": "string", "description": "Should equal '1.0.0'" } } }
2. Disclosure event (linked)
json{ "$id": "https://[FIRM_NAME]/schemas/reg-bi/disclosure-event/v1.0.0.json", "title": "Reg BI disclosure event", "type": "object", "required": ["firm_id", "event_id", "event_timestamp", "client_id", "disclosure_kind", "delivery_channel", "delivered"], "properties": { "firm_id": { "type": "string" }, "event_id": { "type": "string" }, "event_timestamp": { "type": "string", "format": "date-time" }, "client_id": { "type": "string" }, "linked_recommendation_event_id": { "type": ["string", "null"] }, "disclosure_kind": { "type": "string", "enum": ["form_crs", "fee_schedule", "scope_of_relationship", "ipo_alloc", "restricted_product", "rollover_comparison", "regulation_best_interest_summary"] }, "disclosure_version": { "type": "string", "description": "Version of the disclosed document" }, "delivery_channel": { "type": "string", "enum": ["paper", "email", "in_app", "advisor_in_person"] }, "delivered": { "type": "boolean" }, "acknowledgment_required": { "type": "boolean" }, "acknowledgment_event_id": { "type": ["string", "null"] } } }
3. Acknowledgment event (linked)
json{ "$id": "https://[FIRM_NAME]/schemas/reg-bi/acknowledgment-event/v1.0.0.json", "title": "Reg BI acknowledgment event", "type": "object", "required": ["firm_id", "event_id", "event_timestamp", "client_id", "acknowledged_artifact"], "properties": { "firm_id": { "type": "string" }, "event_id": { "type": "string" }, "event_timestamp": { "type": "string", "format": "date-time" }, "client_id": { "type": "string" }, "linked_disclosure_event_id": { "type": "string" }, "acknowledged_artifact": { "type": "string", "description": "What the client acknowledged (form_crs / fee_schedule / etc.)" }, "acknowledgment_method": { "type": "string", "enum": ["e_signature", "in_app_click_through", "verbal_attested", "paper_signature"] }, "client_attestation_text": { "type": "string", "description": "Verbatim text the client agreed to" } } }
4. Adoption checklist
- Persist `recommendation_event` records to a tamper-evident log (append-only, hashed).
- Wire surveillance to query the log on the same query plane the SEC examiner will use during an exam.
- Add a daily cron that validates new records against this schema; reject (and alert) on schema drift.
- Version the schema. When you add fields, bump `schema_version`; when you remove fields, deprecate over two release cycles.
- Map every field to the corresponding row in the Reg BI Audit Data Checklist as the firm's evidence catalog.
These records contain NPI by definition — client_id resolves to a real customer. Apply the firm's data-classification controls accordingly. Test environments must use synthetic households (no real client_id values) — the WealthSynth Reg BI Suitability Audit Pack ships archetypes designed to populate this schema cleanly.
Unfilled slots show as [VARIABLE_NAME] so the partial document still reads. Filling in the form on the left substitutes them inline.
What to do with this
Hand this to engineering. The schema is canonical — every recommendation event the firm produces should match. Validate in CI. Surface to compliance as the underlying structure of the firm's Reg BI evidence. Re-version on material change.
FAQ
Why JSON Schema specifically?
Because it's standardized, validatable in any modern language, and human-readable. The firm can add language-specific generated types (TypeScript, Python, Go) on top — but the source of truth stays declarative.
Can we add firm-specific fields?
Yes. JSON Schema's `additionalProperties` is unrestricted by default; you can extend the schema for your firm's product line. Don't remove fields without a documented reason — every field is there because it was a published Reg BI exam focus area.