Pathfinder Docs

Documentation Preview

Corps API Specification

Source: `docs/product/corps/CORPS_API_SPEC.md`View on GitHub

Corps API Specification

REST API Endpoints for the Volunteer Network & Event Management Module

Version: 1.0
Base Path: /api/corps


Authentication

All endpoints require authentication via Supabase Auth. Include the session cookie or Bearer token.

Role Requirements

RoleAccess Level
volunteerOwn data, public opportunities
recruiterOwn org data, manage opportunities
corps_adminFull org access, reporting
adminSystem-wide access

Volunteer Profiles

GET /api/corps/volunteers

List volunteers (filtered by permission level).

Query Parameters:

ParameterTypeDescription
org_idUUIDFilter by organization
skillsstring[]Filter by skills
is_activebooleanFilter active status
searchstringSearch name/email
pagenumberPage number (default: 1)
limitnumberItems per page (default: 25)

Response:

{
  "volunteers": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "skills": ["Food Service", "Customer Service"],
      "physical_capability": "moderate",
      "is_active": true,
      "total_hours": 47.5,
      "created_at": "2026-01-01T00:00:00Z"
    }
  ],
  "total": 150,
  "page": 1,
  "limit": 25
}

GET /api/corps/volunteers/[id]

Get volunteer profile details.

Response:

{
  "id": "uuid",
  "user_id": "uuid",
  "org_id": "uuid",
  "first_name": "Jane",
  "last_name": "Smith",
  "email": "jane@example.com",
  "bio": "Passionate about community service...",
  "skills": ["Food Service", "Customer Service"],
  "interests": ["homelessness", "food_security"],
  "physical_capability": "moderate",
  "tech_comfort": "intermediate",
  "availability": {
    "monday": [{ "start": "09:00", "end": "17:00" }],
    "saturday": [{ "start": "08:00", "end": "12:00" }]
  },
  "is_active": true,
  "onboarding_completed": true,
  "stats": {
    "total_hours": 47.5,
    "events_attended": 12,
    "pending_hours": 3.0
  },
  "achievements": [
    { "id": "uuid", "name": "Helping Hand", "icon": "✋", "awarded_at": "..." }
  ],
  "created_at": "2026-01-01T00:00:00Z"
}

GET /api/corps/volunteers/me

Get current user's volunteer profile.

POST /api/corps/volunteers

Create volunteer profile (self-registration).

Request Body:

{
  "bio": "string",
  "skills": ["string"],
  "interests": ["string"],
  "physical_capability": "light|moderate|heavy",
  "tech_comfort": "none|basic|intermediate|advanced",
  "availability": { /* schedule object */ },
  "emergency_contact": {
    "name": "string",
    "phone": "string",
    "relationship": "string"
  },
  "city": "string",
  "zip_code": "string",
  "max_travel_miles": 25
}

PATCH /api/corps/volunteers/[id]

Update volunteer profile.

Request Body: (partial update)

{
  "bio": "Updated bio...",
  "skills": ["Food Service", "Driving"]
}

POST /api/corps/volunteers/[id]/deactivate

Deactivate a volunteer profile.

Request Body:

{
  "reason": "Moving out of area"
}

Volunteer Opportunities

GET /api/corps/opportunities

List volunteer opportunities.

Query Parameters:

ParameterTypeDescription
org_idUUIDFilter by organization
statusstringdraft, published, full, completed, cancelled
labor_typestringFilter by labor type
skills_neededstring[]Filter by required skills
location_typestringonsite, remote, hybrid
start_afterdatetimeEvents starting after this time
start_beforedatetimeEvents starting before this time
zip_codestringNearby opportunities
radius_milesnumberSearch radius (default: 25)
searchstringSearch title/description
pagenumberPage number
limitnumberItems per page

Response:

{
  "opportunities": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "organization_name": "Downtown Shelter",
      "title": "Meal Service Volunteer",
      "short_description": "Help serve dinner to guests",
      "labor_type": "service",
      "physical_demand": "light",
      "location_type": "onsite",
      "address": "123 Main St",
      "city": "Portland",
      "start_datetime": "2026-01-20T17:00:00Z",
      "end_datetime": "2026-01-20T20:00:00Z",
      "max_volunteers": 10,
      "confirmed_count": 6,
      "status": "published",
      "is_full": false
    }
  ],
  "total": 45,
  "page": 1,
  "limit": 25
}

GET /api/corps/opportunities/[id]

Get opportunity details.

Response:

{
  "id": "uuid",
  "org_id": "uuid",
  "organization_name": "Downtown Shelter",
  "created_by": "uuid",
  "title": "Meal Service Volunteer",
  "description": "Full description...",
  "short_description": "Help serve dinner to guests",
  "labor_type": "service",
  "physical_demand": "light",
  "tech_level": "none",
  "skills_needed": ["Food Service"],
  "requirements": "Must be 18+, food handler card preferred",
  "min_volunteers": 4,
  "max_volunteers": 10,
  "location_type": "onsite",
  "location_name": "Main Shelter Kitchen",
  "address": "123 Main St",
  "city": "Portland",
  "state": "Oregon",
  "zip_code": "97201",
  "latitude": 45.5155,
  "longitude": -122.6789,
  "location_instructions": "Enter through side door, check in at desk",
  "schedule_type": "one_time",
  "start_datetime": "2026-01-20T17:00:00Z",
  "end_datetime": "2026-01-20T20:00:00Z",
  "estimated_hours": 3.0,
  "status": "published",
  "auto_approve_signups": true,
  "auto_approve_hours": false,
  "allow_waitlist": true,
  "reminder_hours_before": 24,
  "image_url": "https://...",
  "confirmed_count": 6,
  "pending_count": 2,
  "waitlist_count": 0,
  "is_full": false,
  "user_signup_status": "confirmed", // null if not signed up
  "created_at": "2026-01-01T00:00:00Z"
}

POST /api/corps/opportunities

Create a new opportunity (recruiters+).

Request Body:

{
  "title": "Meal Service Volunteer",
  "description": "Full description...",
  "short_description": "Help serve dinner to guests",
  "labor_type": "service",
  "physical_demand": "light",
  "tech_level": "none",
  "skills_needed": ["Food Service"],
  "requirements": "Must be 18+",
  "min_volunteers": 4,
  "max_volunteers": 10,
  "location_type": "onsite",
  "location_name": "Main Shelter Kitchen",
  "address": "123 Main St",
  "city": "Portland",
  "state": "Oregon",
  "zip_code": "97201",
  "schedule_type": "one_time",
  "start_datetime": "2026-01-20T17:00:00Z",
  "end_datetime": "2026-01-20T20:00:00Z",
  "auto_approve_signups": true,
  "status": "draft"
}

PATCH /api/corps/opportunities/[id]

Update an opportunity.

POST /api/corps/opportunities/[id]/publish

Publish a draft opportunity.

POST /api/corps/opportunities/[id]/cancel

Cancel an opportunity.

Request Body:

{
  "reason": "Weather conditions unsafe",
  "notify_volunteers": true
}

POST /api/corps/opportunities/[id]/complete

Mark opportunity as completed.


Signups

GET /api/corps/opportunities/[id]/signups

List signups for an opportunity (recruiters+).

Response:

{
  "signups": [
    {
      "id": "uuid",
      "volunteer_id": "uuid",
      "volunteer_name": "Jane Smith",
      "volunteer_email": "jane@example.com",
      "status": "confirmed",
      "signed_up_at": "2026-01-15T10:00:00Z",
      "confirmed_at": "2026-01-15T12:00:00Z",
      "check_in_at": null,
      "check_out_at": null,
      "volunteer_notes": "Can bring extra supplies"
    }
  ],
  "total": 8
}

POST /api/corps/opportunities/[id]/signup

Sign up for an opportunity.

Request Body:

{
  "notes": "I can bring extra supplies"
}

Response:

{
  "id": "uuid",
  "status": "confirmed", // or "pending" or "waitlist"
  "message": "You're signed up! We'll send a reminder 24 hours before."
}

DELETE /api/corps/opportunities/[id]/signup

Cancel own signup.

PATCH /api/corps/signups/[id]

Update signup status (recruiters+).

Request Body:

{
  "status": "confirmed",
  "organizer_notes": "Assigned to serving station"
}

POST /api/corps/signups/[id]/check-in

Check volunteer in.

Request Body:

{
  "check_in_at": "2026-01-20T16:55:00Z"
}

POST /api/corps/signups/[id]/check-out

Check volunteer out.

Request Body:

{
  "check_out_at": "2026-01-20T20:05:00Z"
}

Hours

GET /api/corps/hours

List volunteer hours.

Query Parameters:

ParameterTypeDescription
volunteer_idUUIDFilter by volunteer
org_idUUIDFilter by organization
opportunity_idUUIDFilter by opportunity
statusstringpending, approved, rejected, auto_approved
date_fromdateFilter by date range start
date_todateFilter by date range end
labor_typestringFilter by labor type
pagenumberPage number
limitnumberItems per page

Response:

{
  "hours": [
    {
      "id": "uuid",
      "volunteer_id": "uuid",
      "volunteer_name": "Jane Smith",
      "opportunity_id": "uuid",
      "opportunity_title": "Meal Service",
      "hours_worked": 3.0,
      "date_worked": "2026-01-20",
      "labor_type": "service",
      "description": "Served dinner, helped with cleanup",
      "status": "approved",
      "submitted_at": "2026-01-20T20:30:00Z",
      "approved_at": "2026-01-21T09:00:00Z",
      "approved_by_name": "John Coordinator"
    }
  ],
  "total": 47,
  "total_hours": 142.5,
  "page": 1,
  "limit": 25
}

GET /api/corps/hours/me

Get current volunteer's hours.

POST /api/corps/hours

Log volunteer hours (manual entry).

Request Body:

{
  "opportunity_id": "uuid", // optional
  "hours_worked": 2.5,
  "date_worked": "2026-01-18",
  "labor_type": "administrative",
  "description": "Data entry for client intake forms"
}

PATCH /api/corps/hours/[id]

Update hours entry (before approval).

POST /api/corps/hours/[id]/approve

Approve hours (recruiters+).

Request Body:

{
  "hours_worked": 2.5 // optional override
}

POST /api/corps/hours/[id]/reject

Reject hours (recruiters+).

Request Body:

{
  "reason": "Hours don't match event records"
}

GET /api/corps/hours/pending

List pending hours for approval (recruiters+).


Notifications

GET /api/corps/notifications

List notifications for current user.

Query Parameters:

ParameterTypeDescription
unread_onlybooleanFilter unread only
typestringFilter by notification type
pagenumberPage number
limitnumberItems per page

Response:

{
  "notifications": [
    {
      "id": "uuid",
      "notification_type": "event_reminder",
      "title": "Reminder: Meal Service Tomorrow",
      "message": "Don't forget your shift at Downtown Shelter...",
      "opportunity_id": "uuid",
      "action_url": "/corps/volunteer/my-shifts",
      "delivered_at": "2026-01-19T17:00:00Z",
      "read_at": null
    }
  ],
  "unread_count": 3,
  "total": 25
}

POST /api/corps/notifications/[id]/read

Mark notification as read.

POST /api/corps/notifications/read-all

Mark all notifications as read.

POST /api/corps/notifications/send

Send notification (recruiters+).

Request Body:

{
  "notification_type": "volunteer_needed",
  "title": "Urgent: Volunteers Needed Tonight",
  "message": "We're short-staffed for tonight's dinner service...",
  "opportunity_id": "uuid",
  "target_type": "skill_match",
  "target_criteria": {
    "skills": ["Food Service"],
    "zip_codes": ["97201", "97202", "97203"]
  },
  "channels": ["in_app", "email"]
}

Reports

GET /api/corps/reports/hours-summary

Get hours summary report (recruiters+).

Query Parameters:

ParameterTypeDescription
org_idUUIDFilter by organization
date_fromdateStart date
date_todateEnd date
group_bystringday, week, month, labor_type, volunteer

Response:

{
  "summary": {
    "total_hours": 1250.5,
    "total_volunteers": 85,
    "total_events": 42,
    "average_hours_per_volunteer": 14.7
  },
  "by_period": [
    { "period": "2026-01", "hours": 450.0, "volunteers": 45 },
    { "period": "2025-12", "hours": 380.0, "volunteers": 38 }
  ],
  "by_labor_type": [
    { "labor_type": "service", "hours": 800.0, "percentage": 64 },
    { "labor_type": "administrative", "hours": 250.0, "percentage": 20 }
  ]
}

GET /api/corps/reports/participation

Get participation metrics.

Response:

{
  "active_volunteers": 85,
  "new_volunteers_this_month": 12,
  "retention_rate": 0.78,
  "average_events_per_volunteer": 3.2,
  "no_show_rate": 0.05,
  "top_contributors": [
    { "volunteer_name": "Jane Smith", "total_hours": 47.5 }
  ]
}

GET /api/corps/reports/opportunities

Get opportunity metrics.

Response:

{
  "total_opportunities": 42,
  "by_status": {
    "published": 8,
    "completed": 30,
    "cancelled": 4
  },
  "fill_rate": 0.85,
  "average_volunteers_per_event": 6.2
}

GET /api/corps/reports/export

Export report data.

Query Parameters:

ParameterTypeDescription
report_typestringhours, volunteers, opportunities
formatstringcsv, pdf
date_fromdateStart date
date_todateEnd date

Response: File download


Achievements

GET /api/corps/achievements

List available achievements.

Response:

{
  "achievements": [
    {
      "id": "uuid",
      "name": "Century Volunteer",
      "description": "Logged 100 volunteer hours",
      "icon": "💯",
      "badge_color": "#F59E0B",
      "trigger_type": "hours_milestone",
      "trigger_value": 100,
      "is_public": true
    }
  ]
}

GET /api/corps/volunteers/[id]/achievements

Get volunteer's earned achievements.

POST /api/corps/achievements/[id]/award

Manually award achievement (corps_admin+).

Request Body:

{
  "volunteer_id": "uuid",
  "notes": "For exceptional leadership during crisis response"
}

Skills

GET /api/corps/skills

List available skills.

Query Parameters:

ParameterTypeDescription
categorystringFilter by category

Response:

{
  "skills": [
    {
      "id": "uuid",
      "name": "Food Service",
      "category": "Service",
      "description": "Preparing and serving meals",
      "icon": "🍽️"
    }
  ]
}

POST /api/corps/skills

Create custom skill (corps_admin+).


Dashboard Data

GET /api/corps/dashboard/volunteer

Get volunteer dashboard data.

Response:

{
  "profile": { /* volunteer profile */ },
  "upcoming_shifts": [ /* next 5 signups */ ],
  "hours_this_month": 12.5,
  "hours_this_year": 47.5,
  "recent_achievements": [ /* last 3 */ ],
  "recommended_opportunities": [ /* 3-5 matched */ ]
}

GET /api/corps/dashboard/recruiter

Get recruiter dashboard data.

Response:

{
  "upcoming_events": [ /* next 5 opportunities */ ],
  "events_needing_volunteers": [ /* under min_volunteers */ ],
  "pending_signups": 8,
  "pending_hours": 15,
  "hours_this_month": 450.0,
  "active_volunteers": 42
}

GET /api/corps/dashboard/admin

Get admin dashboard data.

Response:

{
  "total_hours_this_period": 1250.5,
  "total_hours_previous_period": 1100.0,
  "hours_change_percent": 13.7,
  "active_volunteers": 85,
  "new_volunteers_this_month": 12,
  "upcoming_events": 8,
  "hours_by_labor_type": [ /* breakdown */ ],
  "participation_trend": [ /* weekly data */ ]
}

Error Responses

All endpoints return standard error format:

{
  "error": {
    "code": "SIGNUP_FULL",
    "message": "This opportunity is full. You have been added to the waitlist.",
    "details": {
      "waitlist_position": 3
    }
  }
}

Common Error Codes

CodeHTTP StatusDescription
UNAUTHORIZED401Not authenticated
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
VALIDATION_ERROR400Invalid request data
SIGNUP_FULL409Opportunity at capacity
ALREADY_SIGNED_UP409Already registered
SIGNUP_CLOSED400Signups not available
HOURS_ALREADY_APPROVED400Cannot modify approved hours

Webhooks (Future)

For external integrations, webhooks can be configured:

  • volunteer.created
  • signup.created
  • signup.confirmed
  • hours.approved
  • opportunity.published
  • achievement.awarded

Use links in each imported doc to open its source.