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

MethodPathAuthHandlerDescription
POST/profilesCognitocreateCreate profile for current user
GET/profiles/profile/{profileID}PublicgetPublicProfileGet public-facing profile
GET/profiles/user/CognitogetUserProfileGet current user's full profile
PATCH/profiles/user/CognitoupdatePublicProfileUpdate profile fields / visibility
POST/profiles/profile-pic-upload-urlCognitocreateProfilePicUploadUrlGet presigned S3 upload URL

Deprecated endpoints

These exist in the handler but are no longer part of the core product flow:

MethodPathHandlerDescription
POST/profiles/partner/partialcreatePartialPartnerProfileCreate partner profile + NFC
POST/profiles/companycreateCompanyProfileCreate company profile + QR
POST/profiles/company/link-partnerlinkPartnerToCompanyLink partner to a company
POST/profiles/sync-partner-datasyncPartnerDataBulk sync partner profiles

Table Structure

Table: biztechProfiles (constant PROFILES_TABLE). Composite key:

KeyAttributeValue
PKcompositeIDPROFILE#<profileID> for profile rows, PROFILE#<sourceProfileID> for connection rows
SKtypePROFILE 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

FieldTypeKey / IndexNotes
compositeIDStringPKPROFILE#{profileID}.
typeStringSK"PROFILE" for profile row.
profileIDString-Stored explicitly now for convenience.
profileTypeString-"ATTENDEE", "EXEC", or "PARTNER".
fnameString-Public profile first name.
lnameString-Public profile last name.
pronounsString-Refreshed from membership purchase.
majorString-Refreshed from membership purchase.
yearString/Number-Refreshed from membership purchase.
hobby1String-Editable profile field.
hobby2String-Editable profile field.
funQuestion1String-Editable profile field.
funQuestion2String-Editable profile field.
linkedInString-Editable.
profilePictureURLString-Editable/uploaded.
additionalLinkString-Editable.
resumeURLString-Resume link.
descriptionString-Bio.
companyString-Company name, when applicable.
positionString-Position/title, when applicable.
viewableMapObject-Visibility flags.
createdAtNumber-Epoch ms.
updatedAtNumber-Epoch ms.

Connection Row Shape

FieldTypeKey / IndexNotes
compositeIDStringPKPROFILE#{sourceProfileID}.
typeStringSKCONNECTION#{targetProfileID}.
connectionIDString-Target profileID.
connectionTypeString-Target profile type.
createdAtNumber-First connection timestamp.
fname, lname, pronouns, major, year, company, titleMixed-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:

TypeAssigned when
ATTENDEENormal paid membership
EXECExec/admin profile
PARTNERPartner profile

POST /profiles - Create Profile

Creates a profile for the authenticated user. Uses the email from the Cognito JWT claims.

Behavior:

  1. Looks up the user in biztechUsers by email.
  2. Reads membership form data from biztechMembers2027.
  3. Generates a profileID using human-id.
  4. Creates a profile record with compositeID: PROFILE#<profileID>, type: PROFILE, explicit profileID, and default viewableMap.
  5. Writes the generated profileID to biztechUsers.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, hobby2
  • funQuestion1, funQuestion2
  • linkedIn
  • profilePictureURL
  • additionalLink
  • resumeURL
  • description

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:

FieldRequiredDescription
fileTypeYesMIME type (must start with image/)
fileNameYesOriginal file name
prefixNoSubfolder 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

FilePurpose
services/profiles/handler.jsAll endpoint handlers
services/profiles/helpers.jscreateProfile, filterPublicProfileFields, buildProfileUpdateParams
services/profiles/constants.jsMUTABLE_PROFILE_ATTRIBUTES, PROFILE_TYPES, TYPES

Previous
Members Service