The Project (template) vs. Project Date (occurrence) distinction in detail — fields, endpoints, the Happening pattern, and the practical patterns for reading and managing volunteer opportunities.
Projects and Project Dates are two distinct concepts that often get confused. A Project is the template — the volunteer opportunity itself, describing what’s being done, where, by whom, with what policies. A Project Date is one specific scheduled occurrence — Saturday March 15, 9am to 1pm, at this address, with these participants.This page covers both resources in detail: the field shapes, the endpoints, the relationship between them, and the practical patterns for reading and managing them.
Project (the recurring meeting series) ├── Project Date #1011 (Saturday March 15, 2025) ├── Project Date #1012 (Saturday March 22, 2025) ├── Project Date #1013 (Saturday March 29, 2025) └── Project Date #1014 (Saturday April 5, 2025)
The Project carries the long-lived information — name, description, address, eligibility rules, designated organizers. The Project Date carries the per-occurrence information — start time, end time, participants who actually showed up.
Two paths in this list relate to Project Dates (/projects/today and /projects/date/{id}) rather than Projects themselves. The next sections distinguish them.
⚠️ Spec gap (audit #34): The endpoint GET /projects/date/{id} has an unconventional path (verb-like singular noun in the middle). The audit recommends renaming to GET /projects/{projectId}/dates/{dateId} in a future spec revision. For now, use the documented /projects/date/{id} path.
GET /projects returns an array of ProjectResource objects — the metadata describing the volunteer opportunity:
Field
Type
Description
type
string
"project" — VOMO object type
id
integer
The Project’s stable ID
name
string
The Project’s display name
project_name
string
Duplicate of name — see warning below
description
string
The Project’s description
url
string
A URL to the Project’s page in the VOMO UI
organization
string
The Organization name
organization_id
integer
The Organization ID
organization_slug
string
The Organization’s slug (URL-safe identifier)
updated_at
string
ISO 8601 datetime
created_at
string
ISO 8601 datetime
published_at
string
When the Project was published (or null if draft)
images
array
Project images
campaigns
array
Campaigns this Project is attached to
owners
array
Project organizers/owners
address
object
The Project’s location
certificates
array
Certificates required for this Project
form_completions
array
Forms attached to this Project
⚠️ Spec gap (audit #42):ProjectResource defines both name AND project_name as separate properties with identical descriptions. This is a duplicate field — consumers can’t know which to use. Use name (which aligns with other Virtuous APIs); treat project_name as a deprecated alias likely to be removed.
⚠️ Spec gap (audit #43):ProjectResource.address is typed array in the spec, but an address is conceptually a single object (street, city, state, etc.), not a collection. The live API likely returns an object; code should parse it as such regardless of the spec’s declaration.
GET /projects/{id} returns a ProjectDetailResource — a superset of ProjectResource plus the operational policy fields:
Field
Type
Description
point_person
object
Contact for the Project day
details
string
Operational notes (what to wear, what to bring)
allow_guests
boolean
Can volunteers bring guests?
age_limit
integer
Minimum volunteer age
volunteer_question
string
Optional question asked during signup
participant_approval_required
boolean
Do organizers approve signups?
background_check_required
boolean
Background check required to participate?
show_volunteer_counter
boolean
Display volunteer count publicly?
volunteer_counter_threshold
integer
Show counter only above this count
draft
boolean
Is the Project in draft (not yet published)?
privacy
string
"PUBLIC" or "PRIVATE"
all_dates
array
All scheduled Project Dates for this Project
next_date
object
The next upcoming Project Date
The all_dates and next_date fields are particularly useful for partner integrations — they let you read the Project’s schedule without separate Project Date fetches.
true or false — limit to active or inactive Projects
published
true or false — limit to published or draft Projects
anytime
true or false — include/exclude “anytime” Projects (those without specific dates)
org_slug
Filter by Organization slug (comma-separated for multiple)
name_like
Substring match against Project name
created_before
Projects created on or before a date
created_after
Projects created on or after a date
updated_before
Projects updated on or before a date
updated_after
Projects updated on or after a date
dates_before
Projects with dates that started at or before this datetime
dates_after
Projects with dates that started at or after this datetime
The dates_before and dates_after filters are distinctive — they filter Projects by the timing of their Project Dates, not by the Project’s own creation or update time. Useful for “Projects with shifts coming up this month” queries.
⚠️ Spec gap (audit #31, #32, #33): The POST /projects request body is defined as an anonymous inline object in the spec (not a named schema), and PUT /projects/{id} contains many field descriptions that are literal placeholder text ("Update Project"). The exact valid field set, validation rules, and create vs. update field differences aren’t well-documented.For production use, confirm the field set against the live API by inspecting actual create responses or by coordinating with VOMO support.
A successful create returns the new Project. Capture the id for subsequent updates.
curl -X PUT https://api.vomo.org/v1/projects/789 \ -H "Authorization: Bearer $VOMO_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Saturday Food Bank Volunteer Shift", "description": "Updated description with new details", "age_limit": 18, /* ... all other fields ... */ }'
PUT /projects/{id} is a full replacement — the request body must contain every field that should persist. Fields omitted from the request may be set to default values or null.
For partial updates, fetch the current record, modify the fields you need, then PUT the full record back:
JavaScript
async function updateProjectDescription(projectId, newDescription) { // 1. Fetch current state const current = await getProjectDetail(projectId); if (!current) throw new Error('Project not found'); // 2. Apply changes const updated = { ...current, description: newDescription, }; // 3. PUT the full record const response = await fetch( `https://api.vomo.org/v1/projects/${projectId}`, { method: 'PUT', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(updated), } ); if (!response.ok) throw new Error(`Update failed: ${response.status}`); return response.json();}
The pattern is common in REST APIs without PATCH support. The cost: an extra read per update. For high-frequency update workloads, this can add up — see API Performance Tips for caching patterns.
This is the “what’s happening today” feed — useful for daily-summary dashboards, day-of-event reports, and check-in tools.
The VOMO term “Happening” is used internally for a Project Date. HappeningResource is the schema; Project Date is the conceptual name. Both refer to the same thing.
Returns a specific Project Date with full detail including the participants:
JavaScript
async function getProjectDate(projectDateId) { const response = await fetch( `https://api.vomo.org/v1/projects/date/${projectDateId}`, { headers: { Authorization: `Bearer ${token}` } } ); if (response.status === 404) return null; if (!response.ok) throw new Error(`Failed: ${response.status}`); return response.json();}
⚠️ Spec gap (audit #4): The GET /projects/date/{id} response uses empty schema: {} in the spec. The response shape is documented only through inline examples — the exact field set is not formally specified. Build parsers from the actual response shape.
The participants embedded in a Project Date response use the Participant schema:
Field
Type
Description
full_name
string
The participant’s full name
display_name
string
The display name (often includes guest count)
guest
array
The participant’s guests for this date
Participant is the display representation; ParticipationResource (returned on UserDetailResource.participations) is the record representation with timing and verification details. The two are related but distinct.
Most of these are intentional — they preserve the customer’s organizer-controlled scheduling and check-in workflow. Partner integrations that need to push participation data into VOMO should coordinate with VOMO’s admin team for alternative paths.See Understand Write Limitations.