This document outlines the specifications for developing a Python backend API to support the Opportunity Hack application forms for mentors, sponsors, and judges.
- Overview
- Common API Structure
- Mentor Application API
- Sponsor Application API
- Judge Application API
- Database Schema
- Authentication & Authorization
- Error Handling
- Implementation Checklist
The backend API will serve three main application forms:
- Mentor applications
- Sponsor applications
- Judge applications
Each application type requires endpoints for:
- Submitting new applications
- Updating existing applications
- Retrieving previously submitted applications
- Admin operations (listing, selecting, and managing applications)
The API should be implemented using Python with a modern web framework (such as FastAPI or Flask) and should integrate with the existing authorization system (PropelAuth).
All application APIs should follow this structure:
/api/{role}/application/{event_id}/{action}
Where:
{role}is one of:mentor,sponsor, orjudge{event_id}is the hackathon event identifier{action}is one of:submit,update,get
All endpoints must require authentication via Bearer tokens from PropelAuth.
All responses should follow this structure:
{
"success": true|false,
"message": "Human-readable message",
"data": {} // Optional response data object
}- URL:
/api/mentor/application/{event_id}/submit - Method:
POST - Auth Required: Yes
- Request Body:
{
"timestamp": "ISO-8601 timestamp",
"email": "string",
"name": "string",
"pronouns": "string",
"company": "string",
"bio": "string",
"picture": "string URL",
"linkedin": "string URL",
"inPerson": "string",
"expertise": "string (comma-separated)",
"softwareEngineeringSpecifics": "string (comma-separated)",
"availability": "string (comma-separated)",
"participationCount": "string",
"country": "string",
"state": "string",
"agreedToCodeOfConduct": "boolean",
"additionalInfo": "string",
"shirtSize": "string",
"linkedinProfile": "string URL",
"shortBio": "string",
"photoUrl": "string URL",
"volunteer_type": "mentor",
"isInPerson": "boolean",
"type": "mentors",
"slack_user_id": "string (optional)"
}- URL:
/api/mentor/application/{event_id}/update - Method:
POST - Auth Required: Yes
- Request Body: Same as submit
- URL:
/api/mentor/application/{event_id}/get - Method:
GET - Auth Required: Yes
- Response: Returns the user's previously submitted application if it exists
- URL:
/api/admin/mentors/{event_id} - Method:
GET - Auth Required: Yes (Admin role)
- Query Parameters:
page: Page number (default: 1)limit: Results per page (default: 20)selected: Filter by selection status (optional)
- URL:
/api/sponsor/application/{event_id}/submit - Method:
POST - Auth Required: Yes
- Request Body:
{
"timestamp": "ISO-8601 timestamp",
"email": "string",
"company": "string",
"companyName": "string",
"useLogo": "string",
"phoneNumber": "string",
"sponsorshipTypes": "string",
"otherInvolvement": "string",
"volunteerType": "string (comma-separated)",
"volunteerCount": "string|number",
"volunteerHours": "string|number",
"name": "string",
"title": "string",
"preferredContact": "string",
"howHeard": "string",
"photoUrl": "string URL",
"logoUrl": "string URL",
"type": "sponsors",
"volunteer_type": "sponsor",
"isSelected": "boolean",
"event_id": "string",
"slack_user_id": "string (optional)",
"artifacts": "object (optional)"
}- URL:
/api/sponsor/application/{event_id}/update - Method:
POST - Auth Required: Yes
- Request Body: Same as submit
- URL:
/api/sponsor/application/{event_id}/get - Method:
GET - Auth Required: Yes
- Response: Returns the user's previously submitted application if it exists
- URL:
/api/admin/sponsors/{event_id} - Method:
GET - Auth Required: Yes (Admin role)
- Query Parameters:
page: Page number (default: 1)limit: Results per page (default: 20)selected: Filter by selection status (optional)
- URL:
/api/judge/application/{event_id}/submit - Method:
POST - Auth Required: Yes
- Request Body:
{
"timestamp": "ISO-8601 timestamp",
"email": "string",
"helpedBefore": "string",
"hasHelpedBefore": "string (alternative field name)",
"name": "string",
"title": "string",
"biography": "string",
"shortBiography": "string",
"whyJudge": "string",
"availability": "string",
"canAttendJudging": "string",
"inPerson": "string",
"additionalInfo": "string",
"companyName": "string",
"background": "string (comma-separated)",
"backgroundAreas": "string (comma-separated)",
"participationCount": "string",
"agreedToCodeOfConduct": "boolean",
"linkedinProfile": "string",
"shortBio": "string",
"photoUrl": "string URL",
"pronouns": "string",
"country": "string",
"state": "string",
"volunteer_type": "judge",
"isInPerson": "boolean",
"isSelected": "boolean",
"type": "judges",
"event_id": "string",
"slack_user_id": "string (optional)",
"artifacts": "object (optional)"
}- URL:
/api/judge/application/{event_id}/update - Method:
POST - Auth Required: Yes
- Request Body: Same as submit
- URL:
/api/judge/application/{event_id}/get - Method:
GET - Auth Required: Yes
- Response: Returns the user's previously submitted application if it exists
- URL:
/api/admin/judges/{event_id} - Method:
GET - Auth Required: Yes (Admin role)
- Query Parameters:
page: Page number (default: 1)limit: Results per page (default: 20)selected: Filter by selection status (optional)
{
"_id": "ObjectId",
"id": "string", # Optional secondary ID for backward compatibility
"user_id": "string", # PropelAuth user ID
"event_id": "string",
"timestamp": "datetime",
"email": "string",
"name": "string",
"type": "string", # "mentors", "sponsors", or "judges"
"volunteer_type": "string", # "mentor", "sponsor", or "judge"
"isSelected": "boolean",
"slack_user_id": "string", # Optional Slack user ID for integrations
# Common fields
"photoUrl": "string",
"shortBio": "string",
# Tracking and audit fields
"created_by": "string", # User who created the record
"created_timestamp": "datetime", # When the record was created
"updated_by": "string", # User who last updated the record
"updated_timestamp": "datetime", # When the record was last updated
# Artifacts and references
"artifacts": "object", # Optional JSON object for storing related artifacts
# Type-specific fields stored as subdocuments
"mentor_info": { # Only for mentors
"pronouns": "string",
"company": "string",
"expertise": "string",
"softwareEngineeringSpecifics": "string",
"availability": "string",
"participationCount": "string",
"country": "string",
"state": "string",
"isInPerson": "boolean",
"shirtSize": "string",
"linkedinProfile": "string",
"additionalInfo": "string",
},
"sponsor_info": { # Only for sponsors
"company": "string",
"companyName": "string",
"useLogo": "string",
"phoneNumber": "string",
"sponsorshipTypes": "string",
"otherInvolvement": "string",
"volunteerType": "string",
"volunteerCount": "number",
"volunteerHours": "number",
"title": "string",
"preferredContact": "string",
"howHeard": "string",
"logoUrl": "string"
},
"judge_info": { # Only for judges
"helpedBefore": "string", # Can also be stored as hasHelpedBefore for compatibility
"title": "string",
"biography": "string",
"shortBiography": "string", # Alternative field name for biography
"whyJudge": "string",
"availability": "string",
"canAttendJudging": "string",
"inPerson": "string",
"additionalInfo": "string",
"companyName": "string",
"background": "string",
"participationCount": "string",
"pronouns": "string",
"country": "string",
"state": "string",
"isInPerson": "boolean"
},
"agreedToCodeOfConduct": "boolean",
"created_at": "datetime",
"updated_at": "datetime"
}- Use PropelAuth for authentication
- All API endpoints must verify the Bearer token
- Extract user_id from the authenticated token
- Admin endpoints should check for admin role
- Regular users should only access their own application data
- Implement middleware to check permissions before handling requests
Implement consistent error handling throughout the API:
def error_response(status_code, message):
return {
"success": False,
"message": message,
"status_code": status_code
}, status_codeCommon error cases to handle:
- 400: Bad Request (invalid parameters)
- 401: Unauthorized (missing or invalid token)
- 403: Forbidden (insufficient permissions)
- 404: Not Found (resource doesn't exist)
- 409: Conflict (duplicate submission)
- 500: Internal Server Error
- Set up Python project structure
- Configure database connection (MongoDB recommended)
- Implement PropelAuth integration
- Create database models
- Implement mentor application endpoints
- Implement sponsor application endpoints
- Implement judge application endpoints
- Implement admin endpoints
- Add validation for all input fields
- Implement error handling
- Add logging
- Write tests
- Document API endpoints
- Deploy and test with frontend
- Store form data with minimal transformation to maintain compatibility with frontend expectations
- Implement proper data validation for each field
- Support both creation and updating of applications
- Ensure that users can only update their own applications
- Implement admin views for reviewing and selecting applicants
- Store image/logo URLs rather than the files themselves (files should be uploaded to a CDN)
- Add appropriate logging and monitoring
- Ensure CORS is properly configured
- Include tracking fields (created_by, updated_by, created_timestamp, updated_timestamp) for audit purposes
- Support alternative field names for compatibility with existing data (e.g., hasHelpedBefore vs helpedBefore)
- Consider implementing an API version strategy for future changes