Skip to content

Registry

Party and license register

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

  1. Manual entry โ€” operator keys party and licence.
  2. Excel / CSV import โ€” batch with per-row match / create / error outcomes.
  3. 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.

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_status on party; registered_number, tax_number, and related dates on party_organisation.
  • Contacts and addresses are separate entities; primary flags are scoped per type.
  • license_register_source records each ingest/update event (not a single provenance row).
  • Beneficial owners are stored as declared party_role rows; 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.