Skip to main content

Team & Settings

Everything under the Settings sidebar item. Four sections.

Agency profile

  • Name — displayed on owner digests + tenant report pages.
  • ABN — displayed in footer of owner comms.
  • Logo URL — paste-in today; proper upload planned. Rendered on owner digests and the tenant /report/{token} page (rendering wire-up partly shipped).

Team members

Roles:

RoleCan do
AGENCY_ADMINEverything, including inviting other admins
PMAll maintenance / compliance / contractor / owner workflows. Cannot invite or manage billing.
OWNER(no login today — future)
TENANT(no login — the /report/{token} flow is public)
CONTRACTOR(no login — magic-link flow)

Invites:

  1. Click Invite in Settings → Team members
  2. Enter email + pick role (PM or AGENCY_ADMIN)
  3. Copy the URL (email delivery via SES is planned but not yet wired)
  4. Share the URL however you like — the recipient opens it and sets a password to claim the seat

Invites expire in 7 days. The pending list shows all unused invites with a Copy URL and Cancel button per row.

Single sign-on — Google & Microsoft Entra ID

Both providers ride the same Spring Security OAuth2/OIDC machinery: the provider asserts identity, OAuth2LoginSuccessHandler finds-or-provisions the user by email and mints our own session JWT. The login page only shows buttons for providers this deployment actually has configured (GET /api/v1/auth/sso-providers, driven by the registered clients) — an unconfigured provider simply doesn't render.

Google

Set GOOGLE_OAUTH_CLIENT_ID + GOOGLE_OAUTH_CLIENT_SECRET (App Runner env). Redirect URI in the Google console: https://app.pmfriend.com/login/oauth2/code/google.

Microsoft Entra ID (Azure AD)

For agencies on Microsoft 365 — their staff sign in with their work account, and the organisation's MFA/conditional-access policies apply to PMFriend automatically.

Setup (Entra admin centre → App registrations → New registration):

  1. Supported account types: Accounts in this organizational directory only (single tenant). Multi-tenant (common/organizations) is deliberately unsupported — Spring's OIDC issuer validation rejects the templated multi-tenant issuer.

  2. Redirect URI (type Web): https://app.pmfriend.com/login/oauth2/code/azure

  3. Certificates & secrets → new client secret (note the value, not the id).

  4. Set the three env vars on App Runner and redeploy:

    Env varEntra value
    AZURE_ENTRA_CLIENT_IDApplication (client) ID
    AZURE_ENTRA_CLIENT_SECRETthe client-secret value
    AZURE_ENTRA_TENANT_IDDirectory (tenant) ID — never common

    All three unset → the registration is skipped and the Microsoft button never renders. Discovery runs against https://login.microsoftonline.com/{tenant}/v2.0 at startup.

Email-claim gotcha: Entra id_tokens omit the email claim for users without a Mail attribute. The success handler falls back to preferred_username (the UPN) when it's email-shaped, so those users still provision correctly. Users are keyed by email — a person who signs in via Google and via Entra with the same address is one account.

Audit log

/audit-logs (admin-only). Every INSERT / UPDATE / DELETE on watched aggregates writes an audit_events row via a Hibernate event listener.

Watched aggregates:

  • MaintenanceRequest
  • WorkOrder
  • ComplianceTask
  • Property
  • Owner
  • Tenant
  • Contractor
  • Agency (profile edits)
  • TeamInvite (issue + accept + cancel)

Filters: entity type, event type (INSERT/UPDATE/DELETE), date range, actor (who made the change). Expandable rows show before/after JSON diff.

Retention: 7 years. No delete API — the audit log is append-only. If you need to prune historic rows for DB size, talk to us.

Notifications

The sidebar Notifications feed. Two tabs:

  • Assigned to me (default) — notifications targeted at the calling PM, plus agency-wide ones (compliance, contractor insurance) that every PM should glance at.
  • All in agency — every notification across every PM, useful for agency admins who want a full picture.

Routing

When a PM's action triggers an event, the resulting notification is assigned to that PM. They see it in their "Assigned to me" tab; it counts towards their sidebar unread badge.

Notifications without a specific PM (background scanner finds — say — an overdue compliance task) are agency-wide: every PM sees them in both tabs, and each PM independently marks them read.

Per-user read state

Read + dismiss state is per-user. PM Alice marking a notification read does not hide it for PM Bob. (Pre-V20 it did — that's been fixed.)

Kinds

KindTriggerTypically routed to
MAINTENANCE_URGENTNew urgent request, SLA breach, duplicate detectedTenant submission → agency-wide; PM-created → that PM
WO_DISPATCHEDWorkOrder transitioned to ASSIGNEDThe PM who dispatched
WO_OVER_BUDGETInvoice > costCeilingThe PM who created the WO
WO_COMPLETEDWorkOrder transitioned to COMPLETEDThe PM who created the WO
COMPLIANCE_DUECompliance task due within 14 daysAgency-wide (scanner)
COMPLIANCE_OVERDUEPast dueAgency-wide (scanner)
CONTRACTOR_INSURANCE_EXPIRINGWithin 30 daysAgency-wide (scanner)

Sources

  • Hibernate post-insert / post-update listener — fires immediately on entity changes (new MR, WO status transition). Reads SecurityContextHolder to capture the acting user → assigned to that PM.
  • Daily scanner — runs at 21:00 UTC. No HTTP request context → agency-wide.

Unread badge

The nav shows the calling user's "assigned to me" unread count from GET /api/v1/notifications?scope=MINE&limit=1. Independent of what your colleagues have or haven't cleared.

See also