Member Person Lookup

Lets a desk operator find an existing member of the current organisation from a few known details — date of birth, ID/passport number, phone, or email — and open a record showing who they are plus their membership history.

This page is the as-built reference for the delivered contract. For the screen designs and flows see Member Add and the M03 / M04 use-case specifications; for the security rationale of returning person data directly (rather than progressive-match tokens) see ADR-0007.

Overview

The feature is delivered as:

  • A Member Person Lookup screen (M03) — an auto-detecting search box plus an explicit Search by selector.

  • A membership-centric Person Detail screen (M04) — an identity summary plus the person’s membership history.

  • A new org-scoped, direct-data backend endpoint (below).

  • A frontend OrgContextService that supplies the current organisation id.

Only people who hold a non-draft membership in the current organisation are returned. Non-members and global-only persons never appear; global (non-member) search remains on progressive-match.

API: POST /api/people/member-search

Finds members of the resolved organisation and returns their demographic summary directly.

  • Authentication: required (isAuthenticated()).

  • Deprecates nothing you should still call: prefer this over the deprecated /api/people/query, /api/people/match and /api/people/idsearch.

  • Responses: 200 with the matching members (an empty array when none match); 403 when the caller may not read the resolved organisation; 406 when the value is below the minimum searchable length — note 406 also carries an empty array, so callers must branch on the status code, not on emptiness.

Request

Body:

{
  "field": "AUTO",
  "value": "9001015800086"
}
Field Type Notes

field

SearchField

One of AUTO, ID_NUMBER, DOB, PHONE, EMAIL, NAME.

value

string

The search value. For DOB the caller must send a local yyyy-MM-dd string (never a UTC datetime), so the backend LocalDate does not day-shift in UTC+n.

Query parameters:

Parameter Notes

organisationId

Optional hint. The organisation is resolved server-side (JWT orgId, falling back to this parameter) and then access-checked. A foreign organisation the caller cannot read is rejected with 403 — the parameter can never widen the result set to another organisation’s members (ADR-0007).

Standard paging (page, size, sort)

The candidate set and response are capped server-side.

Field resolution

AUTO infers the field from the value:

  • contains @ → email (person_email);

  • a parseable 6/8-digit date → date of birth (date_of_birth);

  • other numeric → ID number and phone (id_numbercontact_number);

  • other text → first / last name.

Every explicit field searches only its own meta key. In particular ID_NUMBER searches id_number only — never other_number and never names.

Response — PersonSummaryDTO[]

A deliberately lightweight summary. It carries no medical, emergency-contact, parent, doctor, scheme/billing or free identity fields (ADR-0007).

[
  {
    "id": 1234,
    "name": "Mary Member",
    "firstName": "Mary",
    "lastName": "Member",
    "dateOfBirth": "1990-01-01",
    "gender": "FEMALE",
    "phone": "0821234567",
    "email": "[email protected]",
    "membershipNumber": "HNR-00421"
  }
]

membershipNumber is the person’s current membership number in the resolved organisation. It is populated only by the member-search mapping (PersonMapper.toDtoMemberSummary) and is annotated @JsonInclude(NON_EMPTY), so it is absent from the JSON entirely — not null — when the person has none. phone and email are likewise populated only by this mapping; other consumers of PersonSummaryDTO leave them null.

An empty array is returned when nothing matches. Every result is guaranteed to be a non-draft member of the resolved organisation.

Organisation context (frontend)

OrgContextService (membership-ui) is the single source of truth for the current organisation id. It derives the organisation from the authenticated principal’s JWT orgId claim, falling back to a configured default (Helderberg Nature Reserve) when the token carries no such claim. New features must obtain the organisation from this service — for the member search the screen passes organisationId = OrgContextService.getOrgId() — rather than hard-coding a literal.

The service exposes a synchronous getOrgId(): number, an orgId$ observable, and a refresh() that re-resolves the claim and republishes it (called after login, since the org is read from the JWT). It is intentionally forward-compatible with a future multi-organisation / org-switching model even though a single active organisation is delivered today.

  • ADR-0007 — Org-scoped member search returns person data directly.

  • M03 — Member Person Lookup (screen specification).

  • M04 — Member Person Detail (screen specification).

  • Member Add.