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
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
Option C: magic-link-only
A simpler partner-managed flow — no passwords, just emailed magic links:JavaScript
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
/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
Defense in depth
Even with session-scoped IDs, defend against attacks that might somehow bypass the session check:JavaScript
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
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
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
Server-side render with cached data
For SSR or partial-prefetch patterns, pre-load the common data:JavaScript
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
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 includevomo_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
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.