Registry¶
Tier: Record ยท Status: ๐ก In progress (manual registration + CSV/Excel import)
Purpose¶
Registry is the system of record for parties, the licences they hold in the license register, and the portal mandates that allow people to act for those licences. It answers three questions the rest of the platform asks constantly: is this party licensed to do this, what must it therefore file, and is this user allowed to act for it.
Licence grant, suspension, and revocation workflows live in Licensing. File storage lives in Vault. Registry houses the resulting facts and links; it does not run those workflows or store bytes.
Scope¶
| Capability | Status |
|---|---|
| Parties (individual and organisation) | ๐ก Register + amend + status change |
| Register search (name, registration, tax, licence, identity) | ๐ก Paged, filtered by type, status, licence status, category, country |
| Identities, contacts, and addresses | ๐ก One-shot at register + add / amend / remove later |
| Party roles (director, officer, beneficial owner, compliance officer, auditor, โฆ) | ๐ก Assign / amend / soft-terminate / hard-remove |
| Custom attributes beyond core columns | ๐ก attributes jsonb on registration and amend |
| License register (category, status, effective dates, source history, status history) | ๐ก Record / amend / status transitions with history |
| Portal accounts and mandates | โฌ |
| Links to Vault documents | โ Firm / person Documents tab + platform catalogue |
| Licence ingestion (manual, Excel/CSV, API) | ๐ก Manual + CSV/Excel import |
Model¶
Party¶
Every person or organisation Registry knows about is a party.
| Entity | Role |
|---|---|
party |
Shared spine: type, display name, status, tax status, attributes (jsonb) |
party_individual |
Natural-person details |
party_organisation |
Organisation details including registration and tax references |
A licensed firm is an organisation party with one or more rows in the license register. There is
no separate firms table.
Party (party)¶
| Field | Notes |
|---|---|
party_type_code |
individual or organisation |
display_name |
Derived on save from subtype (see below) unless operator overrides |
status |
Reference data (active, inactive, โฆ) |
tax_status |
Reference data (fully_taxed, tax_exempt, โฆ) โ applies to all party types |
attributes |
jsonb for regulator-specific extras |
Display name: for individuals, title + given_name + family_name; for organisations,
trading_name if present, otherwise legal_name.
Individual (party_individual)¶
| Field | Notes |
|---|---|
title |
Reference data (Mr, Mrs, Ms, Dr, Prof, โฆ) |
given_name |
|
middle_name |
Optional |
family_name |
|
initials |
Optional |
date_of_birth |
|
gender |
Reference data; optional |
nationality |
Organisation (party_organisation)¶
| Field | Notes |
|---|---|
legal_name |
Registered / legal name |
trading_name |
Optional |
entity_form |
Reference data (company, partnership, โฆ) |
incorporation_date |
|
country_of_incorporation |
|
registered_number |
Company / entity registration โ primary lookup for import and matching |
registered_date |
Date of registration |
tax_number |
Primary tax identifier (TIN / BP number) |
tax_registered_date |
Optional |
vat_number |
Optional |
Secondary or alternate identifiers (passport-style company refs, cross-border IDs) remain in
party_identity. Primary registration and tax numbers live on the organisation row.
Identities¶
Formal identifiers issued externally. A party may hold many.
party_identity: party_id, identity_type_code, value, issuing_country, issued_on,
expires_on, is_primary, status.
Identity types are reference data (for example national_id, passport). One primary identity
per party is allowed (is_primary).
Licence numbers belong on license_register, not as party identities.
Contacts¶
How to reach a party: email, phone, mobile, fax. A party may hold many.
party_contact: party_id, contact_type, value, purpose, is_primary, status.
is_primary is scoped per contact type (one primary email, one primary mobile, etc.).
Addresses¶
Structured physical or postal locations. A party may hold many.
party_address: party_id, address_type (registered_office, branch, postal, residential,
โฆ), line1, line2, city, province, postal_code, country, is_primary.
is_primary is scoped per address type. A branch is an additional address on the same
organisation party unless it is separately licensed (then it is its own organisation party).
Party roles¶
A dated relationship between two parties: this party holds this role in relation to that party.
party_role: party_id (actor), related_party_id (subject), role_type_code, effective_from,
effective_to, attributes (jsonb).
Role types are reference data (director, beneficial_owner, compliance_officer, auditor,
officer, โฆ). Role-specific facts (ownership percentage, board position, independence flag) live
in attributes.
A beneficial owner (beneficial_owner role) is the person or organisation that ultimately
owns or controls the firm. The role may point to an individual or to another organisation (for
example a holding company). Registry stores what is declared; it does not automatically walk
multi-layer ownership chains at MVP.
Reference data¶
The coded sets these fields validate against โ statuses, titles, entity forms, licence categories,
role and identity types, countries โ are served by the Reference component
(GET /api/reference), not by Registry. Registry stores codes; Reference says which codes exist.
Sets are currently held in code and each is flagged provisional when its values were inferred
rather than confirmed by the regulator. Moving to a maintained table replaces one class.
Custom attributes¶
Core columns cover stable, cross-jurisdiction facts. Regulator- or programme-specific extras use
attributes jsonb on party, party_role, or license_register. Optional metadata does not
require a migration; new structural entities do.
License register¶
Registry stores licence facts. It does not decide or process the grant.
| Entity | Role |
|---|---|
license_register |
One row per licence held by a party |
license_register_status_history |
Append-only status changes |
license_register_source |
How and when each version of the row entered Registry (many rows per licence) |
license_import_batches / license_import_rows |
Bulk import audit and per-row outcomes |
license_register¶
| Field | Notes |
|---|---|
party_id |
Organisation party that holds the licence |
licence_category |
Drives obligations via Cadence |
licence_number |
|
status |
Reference data: active, suspended, revoked, expired, โฆ |
issued_date / expiry_date |
|
effective_from / effective_to |
Valid-time window for obligations |
attributes |
jsonb |
Cadence rule: obligations apply when status = active and today falls within
[effective_from, effective_to).
license_register_status_history¶
Append-only: status, changed_at, changed_by, reason.
license_register_source¶
Records each time a licence row was created or refreshed from an external route:
| Field | Notes |
|---|---|
source_type |
manual, excel_import, api |
source_reference |
Batch id, file row, API correlation id |
recorded_at |
|
recorded_by |
Operator or system principal |
Ingestion routes¶
- Manual entry โ operator keys party and licence.
- Excel / CSV import โ batch with per-row match / create / error outcomes.
- API โ push or pull from Licensing or an external licensing system.
Portal accounts and mandates¶
| Entity | Role |
|---|---|
portal_account |
Links an individual party to Keycloak (keycloak_subject_id) |
mandate |
Which account may prepare, certify, or respond for which licence |
Self-service registration means registering a portal account against an existing licence record. Keycloak holds credentials; Registry holds the subject id and mandates only.
Documents¶
Attachments on parties or licences are Vault documents linked by document_link.
Reading related data¶
Storage is normalised. API responses assemble aggregate views at read time (same pattern as Fordsworth party modules):
| View | Assembly |
|---|---|
| Party profile | party + subtype + identities + contacts + addresses |
| Firm dossier | Party profile + license_register rows + status history + sources |
| Directors / officers | party_role where related_party_id = firm โ load each actor party |
| Portal context | portal_account โ individual party โ mandates โ licence rows |
Child collections are loaded by party_id (or by role query). They are not embedded columns on
the party row.
Entity-relationship diagrams are maintained in draw.io under docs/diagrams/,
not as inline Mermaid ER blocks.
graph LR
subgraph Sources
Manual([Manual entry]) --> Registry
Excel([Excel / CSV]) --> Registry
LicensingMod([Licensing module / API]) -.-> Registry
end
Registry([Registry]) --> Register([Party + role + license register])
Registry -->|subject id| Keycloak([Keycloak])
Keycloak -.-> PortalAccount([Portal account])
PortalAccount -->|mandate| Register
Registry -.->|document_link| Vault([Vault])
Register --> Cadence([Cadence])
Register --> Prism([Prism])
classDef bc fill:#dae8fc,stroke:#6c8ebf,color:#000
classDef ext fill:#fff2cc,stroke:#d6b656,color:#000,stroke-dasharray:5 5
class Registry,Register,PortalAccount bc
class Manual,Excel,LicensingMod,Keycloak,Vault,Cadence,Prism ext
Platform conventions¶
All Registry entities extend AuditableEntity with embedded audit fields. Application errors use
RFC 7807 Problem Details via the shared platform mappers. See Application platform.
Domain status history (license_register_status_history) is separate from row audit.
Why licence category matters¶
Obligations attach to licence category ร return template ร period, not to the party alone. Cadence materialises obligations from the license register. Effective dating and active status are required inputs to deadline arithmetic.
Owns¶
- Parties, subtypes, identities, contacts, addresses, and roles
- License register, status history, source history, and import batches
- Portal accounts and mandates
- Links from parties and licences to Vault documents
Does not own¶
- Licensing โ grant, fit-and-proper, application checklists
- Vault โ file bytes, mime type, storage backend
- Authentication credentials โ Keycloak
- Return obligations โ Cadence
- Risk ratings โ Prism
- Enforcement history โ Docket
Decisions¶
- Registry is a source-agnostic license register; Licensing (or an external process) is the source of grant decisions.
tax_statusonparty;registered_number,tax_number, and related dates onparty_organisation.- Contacts and addresses are separate entities; primary flags are scoped per type.
license_register_sourcerecords each ingest/update event (not a single provenance row).- Beneficial owners are stored as declared
party_rolerows; automatic multi-layer ownership resolution is out of scope for MVP. - Portal accounts store only
keycloak_subject_id. - Document storage is Vault; Registry only links.
Open questions¶
- MVP ingestion: manual only, Excel import, or both โ and the shape of the existing firm book.
- Portal registration verification rule (fields that must match; admin worklist on mismatch).
- Firm-submitted master-data changes (R29 Change Notification): workflow to apply approved changes.
Related¶
- Registry UI โ screen-level specification for the module
- Licensing
- Vault
- Cadence
- Assay
- Architecture doctrine โ bi-temporal time
- Glossary