Skip to main content
A common partner product: a volunteer self-service portal — a customer-facing web app where individual volunteers can see their own data (participations, hours, certificates, profile, upcoming shifts) without needing access to the VOMO admin UI. The customer wants the simplicity of “volunteers manage their own profile” without giving volunteers admin permissions; you build the experience by acting as the trusted intermediary. This recipe is different from the others — it’s not about data flow into or out of VOMO. It’s about product shape: how to architect a portal that lets each volunteer see only their own data, securely, while still using just one VOMO Bearer token (the partner’s). The architecture is what makes this work.

What you’ll build

A customer-facing volunteer portal that:
  • Authenticates volunteers via the customer’s identity provider (or your own auth layer)
  • Lets each volunteer see their own profile, participation history, hours total, certificates, and upcoming shifts
  • Lets volunteers update their own profile fields (which the partner pushes to VOMO via upsert)
  • Prevents volunteers from accessing other volunteers’ data
  • Handles the lookup challenge — VOMO doesn’t know which web session is which volunteer

When this recipe fits


The architecture problem

VOMO doesn’t have per-volunteer API tokens. The Bearer token belongs to the customer’s organization — it has full read access to all volunteers, projects, groups, etc. for that customer. This is the opposite of what a portal needs: each volunteer should see only their own data. The partner integration is the bridge — it authenticates the volunteer (using identity outside VOMO), then uses its admin-level VOMO token to fetch only that volunteer’s data and serve it back. The security boundary is the partner backend. It’s the only thing that holds the VOMO token; the volunteer’s browser never sees it.

Step 1: identity — who is this volunteer?

The first design choice: how does the portal know which VOMO user the logged-in volunteer corresponds to?

Option A: customer SSO + email mapping

The customer already has an identity system (Active Directory, Okta, Google Workspace, etc.). Volunteers log in with their corporate identity; the portal extracts the verified email; the partner maps email to VOMO User ID.
JavaScript
Pros: Customer’s existing identity is reused; no separate volunteer accounts to manage. Cons: Requires SSO integration; the email mapping breaks if a volunteer’s email changes in either system.

Option B: partner-managed accounts

The portal has its own login (email + password, magic link, etc.); volunteers register with the email they use in VOMO; the portal maps the registered email to VOMO User ID.
JavaScript
Pros: No SSO dependency; works for customers without existing identity systems. Cons: Volunteer must register; password recovery and verification are now your problem. A simpler partner-managed flow — no passwords, just emailed magic links:
JavaScript
Pros: No password management; no SSO required; low-friction for volunteers. Cons: Email deliverability matters; users without email access can’t log in. Most partner portals use Option A (when customer has SSO) or Option C (for broader reach). Option B requires the most operational work.

Step 2: the security boundary

The cardinal rule: the partner backend is the only thing that holds the VOMO token. The frontend (browser) must not have it; the volunteer must not have it; nothing untrusted gets the token. The backend exposes endpoints scoped to the authenticated volunteer’s identity: The portal API (/portal/api/me) returns only the authenticated volunteer’s data. The backend resolves the session to a VOMO User ID, then queries VOMO for that specific user — never for “all users.”

A reference portal API endpoint

JavaScript
The endpoint doesn’t take a user ID parameter from the request — the ID comes from the authenticated session. A volunteer can’t request /portal/api/me?userId=999 and get someone else’s data; the parameter is ignored if present, and the session’s ID is always used.

What NOT to do

JavaScript
This is an authorization vulnerability — any logged-in volunteer can fetch any other volunteer’s data by changing the URL. The frontend has no business specifying which user’s data to load; the session implicitly knows.

Defense in depth

Even with session-scoped IDs, defend against attacks that might somehow bypass the session check:
JavaScript
Re-verifying the email match catches the case where a volunteer’s email was changed in VOMO since they logged in — the session may still be valid by token but no longer match reality.

Step 3: what to expose

The portal should show the volunteer’s own data — but what subset? Not everything VOMO returns is appropriate to surface.

Profile data: safe to expose

Participation data: safe to expose

For the “other volunteers at the same Date” case: while GET /projects/date/{id} returns the full participant list, the portal should filter to show only the logged-in volunteer’s own entry.

Certificate data: safe to expose

Group membership: usually safe

Upcoming Project Dates they’re signed up for

What NOT to expose

A reference profile endpoint

JavaScript
Whitelisting is safer than blacklisting — explicitly list what to expose, and anything new in VOMO’s response (e.g., a future-added field) defaults to “not exposed” until you’ve decided.

Step 4: profile update — writing back to VOMO

For portals that let volunteers update their own profile (phone, address, birthday, etc.), the partner backend handles the upsert:
JavaScript

Critical: don’t let users change their email

The email-change problem (from the upsert workflow) means a volunteer changing their email via the portal would either:
  • Create a new VOMO user (because upsert matches by email)
  • Orphan the existing record
Block email changes at the portal level. Direct volunteers to the customer’s admin team for email updates — they have access to the admin UI’s merge tooling.

Step 5: caching for portal performance

A portal page might show: profile, recent participations, hour totals, upcoming shifts, certificates. That’s potentially 5+ VOMO API calls per page load. Without caching, the portal will be slow and rate-limit-bound.

Per-user, per-session cache

JavaScript
A 60-second TTL is reasonable for portal use — fresh enough that “I just signed up for a shift” appears within a minute, while reducing API calls dramatically. Invalidate on writes: after a profile update, invalidate the user’s profile cache.

Server-side render with cached data

For SSR or partial-prefetch patterns, pre-load the common data:
JavaScript
The three calls run in parallel; the page renders once they all complete (or progressively as each finishes).

Step 6: handle the volunteer-not-in-VOMO case

Sometimes a logged-in user has no corresponding VOMO record:
  • They were deleted in VOMO (but their portal account remains)
  • Their email changed in VOMO
  • They never had a VOMO record (registered for the portal but aren’t a volunteer)
JavaScript
Surface a clear “account needs reconciliation” UI for these cases — not a generic error.

Step 7: things volunteers might want that aren’t possible

A portal naturally raises product expectations. Some common requests don’t have API support: Set these expectations clearly in the portal UI. For shift signup specifically, link out to the VOMO admin UI — https://portal.vomo.org/projects/{slug} is typically the user-facing signup path. See Understand Write Limitations for the full picture.

Architecture summary

The components:

Things to watch for

Email is the join key — protect it

The portal-to-VOMO mapping depends on email. If the volunteer changes their email in the customer’s identity provider (SSO) but not in VOMO, the next login fails to find the VOMO user. Build the “email mismatch” alert path; ideally proactively detect and handle.

Don’t expose VOMO IDs in URLs or APIs

The portal frontend shouldn’t include vomo_user_id in URLs or any client-visible state. Internal references should be the partner’s own person ID (or session-scoped).

Profile fields can contain sensitive data

Some customers store sensitive data in custom profile fields (background-check status, accommodations, etc.). Default to hiding profile fields unless explicitly whitelisted; let the customer configure which fields the portal exposes.

Rate limits apply to portal traffic

Every page load is N API requests. For high-traffic customers (e.g., a corporate volunteer program with thousands of weekly portal visits), the portal can easily become the dominant rate-budget consumer. Cache aggressively, batch when possible, and consider per-customer rate budgets — see Rate Limits and API Performance Tips.

Session lifecycle and security

Standard web security practices apply — HTTPS only, secure cookies, CSRF protection, session timeouts, etc. The novel concern for this product is the “session VOMO User ID is stale” case; build re-verification into session refresh.

The “lapsed volunteer” UX

Volunteers whose VOMO record has gone inactive (no recent participations, possibly archived) may still log into the portal and see a confusing empty state. Build a friendly “you haven’t volunteered with us recently — here’s how to get involved again” experience for low-activity users.

What you’ve built

After this recipe:
  • ✅ Identity flow connecting external auth to VOMO User IDs
  • ✅ Backend API exposing per-volunteer scoped endpoints
  • ✅ Strict authorization — session implicit, no client-supplied IDs
  • ✅ Whitelisted field exposure with friendly labels
  • ✅ Profile update flow with email-change protection
  • ✅ Caching layer for portal performance
  • ✅ Handling for the orphaned-account and email-mismatch cases
  • ✅ Clear product expectations for what the API doesn’t allow
This is a product-shape recipe — the architecture matters more than any single piece of code. The patterns transfer to any partner-built customer-facing experience on top of the Volunteer API.

Where to go next

Combine Volunteer Data with CRM+ Data

The cross-API recipe — joins beautifully with the portal experience.

Sync Users to External System

The foundational sync pattern this recipe builds on.

Security and Credential Management

The security patterns this portal architecture depends on.

Understand Write Limitations

The “what’s not possible via API” reference for portal product decisions.
Last modified on May 22, 2026