Identity & Members
Members Service
The Members service manages yearly membership records in the biztechMembers2027 DynamoDB table. All endpoints are admin-only (caller must have a @ubcbiztech.com email). Handlers are in services/members/handler.js.
Endpoints
| Method | Path | Handler | Description |
|---|---|---|---|
POST | /members | create | Create a yearly member record |
GET | /members/{id} | get | Get a member by email |
GET | /members | getAll | List all members |
PATCH | /members/{id} | update | Update a member record |
DELETE | /members/{id} | del | Delete a member record |
POST | /members/grant | grantMembership | Grant membership |
GET | /members/email/{profileID} | getEmailFromProfile | Legacy lookup; prefer biztechUsers.profileID-index |
All endpoints require Cognito authentication + @ubcbiztech.com email.
Profile Lookup Ownership
Membership rows no longer own profile identity. Use biztechUsers.profileID and profileID-index as the source of truth for profile lookups.
Tables
The members service operates on multiple tables:
| Table | Role |
|---|---|
biztechUsers | Created or updated during grant/payment flow; owns the canonical profileID |
biztechMembers2027 | Primary yearly member records |
biztechProfiles | Created or refreshed when membership changes |
Year-Suffixed Table
The table name biztechMembers2027 includes the membership year. When the year rolls over, the corresponding table constant and DynamoDB table must be updated.
Member Record Shape
Table: biztechMembers2027
Purpose: yearly membership record. Row existence means active membership for 2027; this row does not own profile identity.
DynamoDB enforces only the id key. Other fields describe the effective write-path shape and may be absent; cardCount defaults to 0 on current create paths.
| Field | Type | Key / Index | Notes |
|---|---|---|---|
id | String | PK | Lowercase email. |
profileType | String | - | New field. Allowed values are "ATTENDEE", "EXEC", and "PARTNER". Normal paid membership should set "ATTENDEE". |
education | String | - | Membership form value. |
firstName | String | - | Membership form first name. |
lastName | String | - | Membership form last name. |
pronouns | String | - | Used to refresh profile. |
studentNumber | String/Number | - | Membership form student number. |
faculty | String | - | Membership form faculty. |
year | String/Number | - | Used to refresh profile. |
major | String | - | Used to refresh profile. |
prevMember | Boolean | - | Previous member answer. |
international | Boolean | - | International student answer. |
topics | String[] | - | Interest topics. |
heardFrom | String | - | Referral source. |
heardFromSpecify | String | - | Referral detail. |
diet | String | - | Dietary restrictions. |
university | String | - | School/university. |
highSchool | String | - | High school, if applicable. |
admin | Boolean | - | Existing admin snapshot. |
cardCount | Number | - | NFC/card tracking; defaults to 0. |
discordId | String | - | Existing location for now, though eventually user-level is cleaner. |
createdAt | Number | - | Epoch ms. |
updatedAt | Number | - | Epoch ms. |
Indexes
| Index | Key | Purpose |
|---|---|---|
| table primary key | id | Email to yearly membership. |
There are no GSIs on the current members table.
Deprecated/removed from docs as source of truth
| Field | Status |
|---|---|
profileID | Not part of the current row shape. Read it from biztechUsers. |
GET /members/{id}
Fetch a single member by email.
Request
| Headers | Type |
|---|---|
| Authorization | Bearer Token |
| Path Param | Description |
|---|---|
| id | Member's email |
Example Request
GET /members/student@example.com
Response
{
"id": "student@example.com",
"profileType": "ATTENDEE",
"firstName": "Alice",
"lastName": "Wong",
"faculty": "Commerce",
"year": "3",
"major": "Accounting",
"international": true,
"topics": ["Finance", "Tech"],
"createdAt": 1755702100000,
"updatedAt": 1755702100000
}
Errors
403Unauthorized400Invalid email404Member not found
GET /members/
Fetch all members.
Request
| Headers | Type |
|---|---|
| Authorization | Bearer Token |
Response
{
"message": "success",
"data": [
{
"id": "student@example.com",
"profileType": "ATTENDEE",
"firstName": "Alice",
"lastName": "Wong",
"year": "3",
"major": "Accounting",
"createdAt": 1755702100000,
"updatedAt": 1755702100000
},
{
"id": "bob@example.com",
"profileType": "ATTENDEE",
"firstName": "Bob",
"lastName": "Li",
"year": "4",
"major": "Finance"
}
]
}
PATCH /members/{id}
Update an existing member by email.
Request
| Headers | Type |
|---|---|
| Authorization | Bearer Token |
| Path Param | Description |
|---|---|
| id | Member's email |
Body
- Accepts any subset of the fields from the
biztechMembers2027record shape. - Do not write
profileID; profile identity belongs onbiztechUsers.
Example Request
{
"major": "Business Technology Management",
"year": "4",
"topics": ["Consulting", "AI"]
}
Response
{
"message": "Updated member with email student@example.com!",
"response": {
"Attributes": {
"major": "Business Technology Management",
"year": "4",
"topics": ["Consulting", "AI"],
"updatedAt": 1755703100000
}
}
}
Errors
403Unauthorized400Invalid email404Member not found
DELETE /members/{id}
Delete a yearly member record by email.
Request
| Headers | Type |
|---|---|
| Authorization | Bearer Token |
| Path Param | Description |
|---|---|
| id | Member's email |
Example Request
DELETE /members/student@example.com
Response
{
"message": "Member deleted!",
"response": {
"id": "student@example.com"
}
}
Errors
403Unauthorized400Invalid email404Member not found
POST /members/grant
Grant membership for a user. This endpoint creates or updates user, member, and profile records as needed. There is no revocation path - this handler only grants.
Request
| Headers | Type |
|---|---|
| Authorization | Bearer Token |
The body should contain the member fields needed to create or refresh the yearly membership:
| Body Property | Type | Description |
|---|---|---|
| String | User email (required) | |
| profileType | String | New field. Allowed values are "ATTENDEE", "EXEC", and "PARTNER". Normal paid membership should set "ATTENDEE" |
| firstName | String | First name |
| lastName | String | Last name |
| education | String | University or institution |
| studentNumber | String | Student number |
| pronouns | String | Pronouns |
| levelOfStudy / year | String | Year of study (handler checks both fields) |
| faculty | String | Faculty |
| major | String | Major |
| internationalStudent / international | Boolean | International student flag |
| previousMember / prevMember | Boolean | Was a member in a previous year |
| dietaryRestrictions / diet | String | Dietary restrictions |
| referral / heardFrom | String | How they heard about BizTech |
| heardFromSpecify | String | Referral detail |
| topics | String[] | Topics of interest |
Example Request
{
"email": "isaacliu@gmail.com",
"profileType": "ATTENDEE",
"firstName": "Isaac",
"lastName": "Liu",
"education": "University of British Columbia",
"studentNumber": "12345678",
"pronouns": "He/Him/His",
"levelOfStudy": "3",
"faculty": "Commerce",
"major": "BUCS",
"internationalStudent": false,
"previousMember": true,
"dietaryRestrictions": "None",
"referral": "Friend",
"topics": ["Finance", "Tech"]
}
Response
{
"message": "Membership granted"
}
Behavior
- If the user does not exist in
biztechUsers, creates a new user record with the latest basic fields. - If the user exists, updates the user record with the latest basic fields.
- Creates a yearly member row in
biztechMembers2027withprofileType: "ATTENDEE"if no explicit profile type is provided. - Reads
biztechUsers.profileID. - If
profileIDexists, refreshes the existingbiztechProfilesrow with membership fields such aspronouns,major, andyear. - If
profileIDis missing, creates a profile viacreateProfile(email, "ATTENDEE")and writes the generated profile ID tobiztechUsers.profileID.
No Revocation
Despite the handler name, grantMembership only grants. Membership is revoked by deleting the yearly member row; DELETE /members/{id} does not delete the user or profile.
Errors
403Unauthorized if caller is not@ubcbiztech.com400Invalid email format
Related Pages
- User, Member & Profile Relationships - how the three records relate
- Membership Flow - paid membership write path
- Users Service - account identity and
profileID-index - Profiles Service - profile rows and connection rows