Skip to content
Updated Jun 9, 2026

Member Portal API

The Member Portal API is a stateless Backend-for-Frontend (BFF) for the Olly member web portal and mobile app. It owns no data and runs no migrations: every response is composed by calling one or more downstream services and handing the result back to the client. Owned by the member-portal-api service.

Field reference: the response shapes below are the BFF's own member-friendly projections, not catalog tables. The underlying domain columns live in the catalog: glossary terms Claim, Party, Coverage, Accumulator, ConsentRecord, NotificationPreference. This page is the narrative.

Member scoping

Every /me/* route is scoped to the authenticated member; no party identifier appears in the path. memberScopeMiddleware reads a custom partyLocator JWT claim if present and otherwise falls back to the Keycloak sub claim, then injects that locator into the request context. Handlers pass it to downstream services, so a member only ever reads or writes their own data. A request that carries no claims, or no usable locator, gets a 401.

The BFF keeps the member-facing surface stable while the downstream topology changes underneath it. A breaking change to Eligibility, Claims or Consent is absorbed by editing the BFF, not the mobile client.

API routes

All routes sit behind JWT auth and member scoping. /healthz and /readyz are the only unauthenticated endpoints.

MethodPathDownstreamDescription
GET/me/coverageEligibilityMember's active coverage summary
GET/me/accumulatorsEligibilityDeductible and out-of-pocket balances
GET/me/claimsClaimsList the member's claims
POST/me/claimsClaimsSubmit a claim (returns 201 Created)
GET/me/claims/{locator}ClaimsGet one claim
GET/me/documentsDocument ServiceList the member's documents (EOBs, policy docs)
GET/me/documents/{locator}Document ServiceGet one document's metadata (type, generation date, and a downloadUrl pointing to the binary)
GET/me/profilePolicy AdminMember's party details
PATCH/me/profilePolicy AdminUpdate profile (first/last name, phone, email)
GET/me/preferencesNotificationsNotification channel preferences (email/push/sms)
PUT/me/preferencesNotificationsReplace notification preferences
GET/me/consentConsentCurrent consent state
PUT/me/consentConsentReplace consent preferences

POST /me/claims requires a serviceDate in the body and returns 422 if it is missing. A malformed JSON body on any write route returns 400.

Documents are metadata, not bytes

GET /me/documents/{locator} returns JSON, not a PDF. The handler fetches a DocumentSummary from the Document Service and re-serialises it as application/json. The binary lives behind the downloadUrl field on that metadata; the BFF never streams document bytes (no Content-Disposition / octet-stream path exists in the service).

Dependencies

The BFF holds one HTTP client per downstream, each configured from a required *_URL environment variable. A missing or unreachable downstream surfaces as 502, never a partial response.

ServiceRoutes served
EligibilityGET /me/coverage, GET /me/accumulators
ClaimsGET /me/claims, POST /me/claims, GET /me/claims/{locator}
Document ServiceGET /me/documents, GET /me/documents/{locator}
Policy AdminGET /me/profile, PATCH /me/profile
NotificationsGET /me/preferences, PUT /me/preferences
ConsentGET /me/consent, PUT /me/consent

Events

The Member Portal API neither publishes nor consumes Kafka events. It is request/response only; any eventing happens in the downstream services it calls.

Invariants

  • No database, no migrations, no local state. Any instance can serve any request, and the service scales independently of the data owners.
  • Every /me/* response is scoped to the caller's partyLocator (custom claim, else sub); a request with no resolvable identity is rejected with 401.
  • Downstream errors map to a fixed contract: ErrNotFound404, ErrUpstream (5xx or unreachable) → 502, anything else → 500. The mobile client gets the same semantics regardless of which downstream failed.

Caveats

  • It is a pass-through, not a cache. There is no read-through caching or fan-in aggregation across services in a single call; each route maps to one downstream call.
  • Response shapes are the BFF's own. The structs returned (CoverageResponse, AccumulatorsResponse, ClaimSummary, DocumentSummary, etc.) are trimmed member-facing projections, not the raw downstream payloads, so field-for-field they will not match the catalog domain tables.

Olly Health Insurance Platform