Identity & Members
Profiles Service
The Profiles service manages user profiles in the biztechProfiles DynamoDB table. Profiles are created during membership grant/payment flow or manually via POST /profiles. Handlers are in services/profiles/handler.js.
Endpoints
| Method | Path | Auth | Handler | Description |
|---|---|---|---|---|
POST | /profiles | Cognito | create | Create profile for current user |
GET | /profiles/profile/{profileID} | Public | getPublicProfile | Get public-facing profile |
GET | /profiles/user/ | Cognito | getUserProfile | Get current user's full profile |
PATCH | /profiles/user/ | Cognito | updatePublicProfile | Update profile fields / visibility |
POST | /profiles/profile-pic-upload-url | Cognito | createProfilePicUploadUrl | Get presigned S3 upload URL |
Deprecated endpoints
These exist in the handler but are no longer part of the core product flow:
| Method | Path | Handler | Description |
|---|---|---|---|
POST | /profiles/partner/partial | createPartialPartnerProfile | Create partner profile + NFC |
POST | /profiles/company | createCompanyProfile | Create company profile + QR |
POST | /profiles/company/link-partner | linkPartnerToCompany | Link partner to a company |
POST | /profiles/sync-partner-data | syncPartnerData | Bulk sync partner profiles |
Table Structure
Table: biztechProfiles (constant PROFILES_TABLE). Composite key:
| Key | Attribute | Value |
|---|---|---|
| PK | compositeID | PROFILE#<profileID> for profile rows, PROFILE#<sourceProfileID> for connection rows |
| SK | type | PROFILE for profile rows, CONNECTION#<targetProfileID> for connection rows |
The profileID is generated by the human-id library (for example, SillyPandasDeny). The owning user's canonical profile identity is stored in biztechUsers.profileID.
DynamoDB enforces the compositeID and type keys. Other fields describe the effective write-path shape and may be absent unless noted otherwise. The current table has no GSIs.
Profile Row Shape
| Field | Type | Key / Index | Notes |
|---|---|---|---|
compositeID | String | PK | PROFILE#{profileID}. |
type | String | SK | "PROFILE" for profile row. |
profileID | String | - | Stored explicitly now for convenience. |
profileType | String | - | "ATTENDEE", "EXEC", or "PARTNER". |
fname | String | - | Public profile first name. |
lname | String | - | Public profile last name. |
pronouns | String | - | Refreshed from membership purchase. |
major | String | - | Refreshed from membership purchase. |
year | String/Number | - | Refreshed from membership purchase. |
hobby1 | String | - | Editable profile field. |
hobby2 | String | - | Editable profile field. |
funQuestion1 | String | - | Editable profile field. |
funQuestion2 | String | - | Editable profile field. |
linkedIn | String | - | Editable. |
profilePictureURL | String | - | Editable/uploaded. |
additionalLink | String | - | Editable. |
resumeURL | String | - | Resume link. |
description | String | - | Bio. |
company | String | - | Company name, when applicable. |
position | String | - | Position/title, when applicable. |
viewableMap | Object | - | Visibility flags. |
createdAt | Number | - | Epoch ms. |
updatedAt | Number | - | Epoch ms. |
Connection Row Shape
| Field | Type | Key / Index | Notes |
|---|---|---|---|
compositeID | String | PK | PROFILE#{sourceProfileID}. |
type | String | SK | CONNECTION#{targetProfileID}. |
connectionID | String | - | Target profileID. |
connectionType | String | - | Target profile type. |
createdAt | Number | - | First connection timestamp. |
fname, lname, pronouns, major, year, company, title | Mixed | - | Denormalized target snapshot. |
Connection records share this table - see Connections for details. Each connection normally creates two rows, one under each profile, making the relationship bidirectional.
Profile Types
The profileType field is set at creation:
| Type | Assigned when |
|---|---|
ATTENDEE | Normal paid membership |
EXEC | Exec/admin profile |
PARTNER | Partner profile |
POST /profiles - Create Profile
Creates a profile for the authenticated user. Uses the email from the Cognito JWT claims.
Behavior:
- Looks up the user in
biztechUsersby email. - Reads membership form data from
biztechMembers2027. - Generates a
profileIDusinghuman-id. - Creates a profile record with
compositeID: PROFILE#<profileID>,type: PROFILE, explicitprofileID, and defaultviewableMap. - Writes the generated
profileIDtobiztechUsers.profileID.
No request body is needed - fname, lname, pronouns, year, major, and profileType are read from the membership data.
Profile Identity Ownership
The source of truth for a user's profile ID is biztechUsers.profileID, not the yearly membership table.
Automatic creation
Profiles are also created automatically during membership grant and during payment webhook processing. Direct POST /profiles is mainly needed for manual profile creation.
GET /profiles/profile/{profileID} - Public Profile
Returns only the profile fields the user has marked as visible in their viewableMap.
Always returned: profileID, profileType, fname, lname, pronouns, year, major
Conditionally returned (based on viewableMap):
hobby1,hobby2funQuestion1,funQuestion2linkedInprofilePictureURLadditionalLinkresumeURLdescription
No authentication required. This is the endpoint used to display profiles on the companion app and live wall.
GET /profiles/user/ - Full Profile
Returns the complete profile for the authenticated user, including all fields and the viewableMap. Requires Cognito auth.
The response includes every field on the profile record:
{
"profileID": "SillyPandasDeny",
"profileType": "ATTENDEE",
"fname": "Kevin",
"lname": "Xiao",
"pronouns": "He/Him",
"year": "3rd Year",
"major": "BUCS",
"hobby1": "",
"hobby2": "",
"funQuestion1": "",
"funQuestion2": "",
"linkedIn": "",
"profilePictureURL": "",
"additionalLink": "",
"resumeURL": "",
"description": "",
"viewableMap": {
"fname": true,
"lname": true,
"pronouns": true,
"year": true,
"major": true,
"profileType": true,
"hobby1": false,
"hobby2": false,
"funQuestion1": false,
"funQuestion2": false,
"linkedIn": false,
"profilePictureURL": false,
"additionalLink": false,
"resumeURL": false,
"description": false,
"company": true,
"position": true
},
"compositeID": "PROFILE#SillyPandasDeny",
"type": "PROFILE",
"createdAt": 1754072664036,
"updatedAt": 1754072664036
}
PATCH /profiles/user/ - Update Profile
Updates mutable profile attributes and/or visibility settings. Requires Cognito auth.
Mutable attributes (defined in MUTABLE_PROFILE_ATTRIBUTES): hobby1, hobby2, funQuestion1, funQuestion2, linkedIn, profilePictureURL, additionalLink, resumeURL, description
Fields like fname, lname, pronouns, year, and major are refreshed from membership data and are not updated through this endpoint.
Request body:
{
"viewableMap": { "description": true },
"description": "Testing description"
}
The viewableMap field is required in the body (at minimum an empty object {}). Only the keys you include will be updated - omitted keys keep their current values.
POST /profiles/profile-pic-upload-url - Profile Picture Upload
Generates a presigned S3 URL for uploading a profile picture. Requires Cognito auth.
Request body:
| Field | Required | Description |
|---|---|---|
fileType | Yes | MIME type (must start with image/) |
fileName | Yes | Original file name |
prefix | No | Subfolder prefix (defaults to profile-photos) |
Optionally pass profileId as a query parameter - if omitted, uses the authenticated user's profile.
Response:
{
"uploadUrl": "https://s3...presigned-url",
"key": "profile-pictures/SillyPandasDeny/profile-photos/1735689600000.jpg",
"publicUrl": "https://bucket.s3.region.amazonaws.com/profile-pictures/..."
}
The presigned URL expires after 60 seconds.
Key Files
| File | Purpose |
|---|---|
services/profiles/handler.js | All endpoint handlers |
services/profiles/helpers.js | createProfile, filterPublicProfileFields, buildProfileUpdateParams |
services/profiles/constants.js | MUTABLE_PROFILE_ATTRIBUTES, PROFILE_TYPES, TYPES |
Related Pages
- User, Member & Profile Relationships - how profiles connect to users and members
- Membership Flow - how profiles are created during membership grant
- Connections - connection records that share the profiles table