Flows

Profile Sync

How profiles are created, what they store, and how they connect to members, connections, and the companion app.


Profile Data Model

Profiles live in biztechProfiles. This table uses a composite key structure to store multiple record types:

Record TypecompositeID (PK)type (SK)Purpose
ProfilePROFILE#<profileID>PROFILEThe user's public profile
ConnectionPROFILE#<profileID>CONNECTION#<connProfileID>A connection to another user

This means a single partition key holds both the profile and all of its connections. Querying compositeID = PROFILE#abc returns the profile plus every connection that user has made.

Profile Fields

FieldTypeNotes
compositeIDStringPROFILE#<profileID> (partition key)
typeStringPROFILE for the profile row (sort key)
profileIDStringHuman-readable ID (generated by humanId())
profileTypeStringATTENDEE, PARTNER, or EXEC
firstName, lastNameStringFrom member data
pronouns, major, yearString/NumberFrom member data
hobby1, hobby2StringUser-editable fun facts
funQuestion1, funQuestion2StringUser-editable ice-breaker answers
linkedIn, additionalLinkStringSocial links
profilePictureURLStringS3 URL for profile picture
descriptionStringBio
viewableMapObjectPrivacy toggles ({ "linkedIn": true, "hobby1": false, ... })

Connection Row Fields

Each connection is stored as two rows (one for each side):

FieldTypeNotes
compositeIDStringPROFILE#<userProfileID> (partition key)
typeStringCONNECTION#<otherProfileID> (sort key)
connectionNameStringDenormalized name of the connected person
connectionMajor, connectionYearString/NumberDenormalized data
createdAtNumberTimestamp

Profile Creation

Profiles are created via createProfile() in services/profiles/helpers.js. This function is called from three places:

CallerWhen
services/payments/handler.jsOAuthMemberSignup()After membership payment webhook
services/members/handler.jsgrantMembership()After admin grants membership
services/profiles/handler.jscreate()Direct API call POST /profiles

createProfile() Steps

  1. Fetches the member record from biztechMembers2026 by email
  2. Returns 404 if no member exists; returns error if profileID is already set (duplicate)
  3. Generates a human-readable profileID using the human-id library
  4. Builds the profile object with composite key PROFILE#<profileID>, type PROFILE
  5. Copies firstName, lastName, pronouns, major, year from the member record
  6. Sets a default viewableMap with all fields visible
  7. Executes a DynamoDB TransactWrite that atomically:
    • Put the new profile into biztechProfiles
    • Update the member in biztechMembers2026 to set its profileID field

The transaction ensures the member's profileID always points to a valid profile.


Profile Types

TypeWhoCreated by
ATTENDEERegular memberscreateProfile() during membership flow
PARTNERSponsor/partner representativescreatePartialPartnerProfile() — creates a partner profile + QR entry
EXECBizTech executive teamcreateProfile() with type override

Partner profiles have additional fields like company, role, and companyProfileID.


Profile Picture Upload

Members upload profile pictures via POST /profiles/profile-pic-upload-url:

  1. Validates fileType starts with image/
  2. Generates an S3 key: profile-pictures/<profileId>/optimized/<timestamp>.<ext>
  3. Returns a pre-signed PUT URL for the biztech-profile-pictures S3 bucket (60-second expiry)
  4. The frontend uploads directly to S3, then updates the profile with the URL

Public vs Private Profile Views

Public view (GET /profiles/profile/{profileID}):

  • Filters through the viewableMap — only returns fields where the toggle is true
  • Used by anyone viewing another person's profile (e.g., after an NFC tap)

Private view (GET /profiles/user/):

  • Returns the full profile with all fields, regardless of viewableMap
  • Used by the profile owner to edit their own profile
  • Looks up the caller's profileID via their biztechMembers2026 record

Profile Updates

PATCH /profiles/user/ only allows updating fields in MUTABLE_PROFILE_ATTRIBUTES:

  • hobby1, hobby2, funQuestion1, funQuestion2
  • linkedIn, profilePictureURL, additionalLink, description

The viewableMap can also be updated (toggling field visibility). Core identity fields (firstName, lastName, major, etc.) are set at creation time from the member record and cannot be changed through this endpoint.


Company Profiles

Company profiles represent organizations (sponsors, partner companies). They are created via POST /profiles/company:

  1. Derives a companyId from the company name (lowercase alphanumeric)
  2. Creates a profile record in biztechProfiles
  3. Creates a QR entry in biztechQRs

Partner profiles can be linked to company profiles via POST /profiles/company/link-partner, which:

  • Sets companyProfileID and companyProfilePictureURL on the partner profile
  • Appends the partner's profileID to the company's delegateProfileIDs list

Connections (How They're Made)

Connections are created by the interactions service when two users tap NFC cards or scan QR codes:

  1. POST /interactions/ with eventType: "CONNECTION" and eventParam (scanned profile data)
  2. The handler resolves both users' profileID values from biztechMembers2026
  3. Fetches both profiles from biztechProfiles
  4. Checks for duplicate (same connection already exists)
  5. Writes two CONNECTION rows into biztechProfiles — one for each user, with denormalized names
  6. Broadcasts the new connection to the Live Wall via WebSocket
  7. Logs the connection to bizLiveConnections for wall replay

The GET /interactions/journal/ endpoint returns all of a user's connections by querying biztechProfiles where type begins_with CONNECTION#.


Profiles Service Endpoints

MethodPathAuthDescription
POST/profilesCognitoCreate profile (calls createProfile())
POST/profiles/partner/partialPublicCreate partner profile (deprecated)
POST/profiles/companyPublicCreate company profile
POST/profiles/company/link-partnerPublicLink partner to company
POST/profiles/sync-partner-dataPublicSync partner profiles with registrations
GET/profiles/profile/{profileID}PublicGet public profile (filtered by viewableMap)
GET/profiles/user/CognitoGet own profile (full, unfiltered)
PATCH/profiles/user/CognitoUpdate own profile
POST/profiles/profile-pic-upload-urlCognitoGet S3 presigned URL for profile picture

Key Files

FilePurpose
services/profiles/handler.jsAll profile endpoint handlers
services/profiles/helpers.jscreateProfile() and profile helper functions
services/profiles/constants.jsProfile types, mutable attributes
services/interactions/handler.jsConnection creation via NFC/QR
services/interactions/helpers.jshandleConnection() — writes connection rows
services/members/handler.jsgrantMembership() calls createProfile()

Previous
Event Check-In