Data map
Every field of personal data we hold, what it is for, where it lives, who can read it, and when it goes.
This table is generated from the same file our build checks against the database: a column we add without describing it here fails the build. We collect nothing about patients and nothing about a clinician's own health. Google Analytics counts visits; no ad pixel, no session replay.
Fields
| What | Why we hold it | Where it lives | Who can read it | When it goes |
|---|---|---|---|---|
| Account id | Ties your sign-in to your settings. A random id, not derived from anything about you. | identity.user.id | The identity role. The compensation side never sees it; a salary row carries a keyed hash of it instead. | With the account, at once. |
| Account dates | When the account was made and, if you asked, when it was removed. | identity.user.created_atidentity.user.deleted_at | The identity role. | With the account. |
| Login email | To sign you in and to send account notices. Never shown, never given to a recruiter. | identity.user.login_email | The identity role. | With the account, at once. An institutional email used only to verify you is removed 30 days after confirmation unless you keep it as your login. |
| NPI, encrypted | To look your registry record up again for a later step of the gate. Kept only when such a step is switched on (the license comparison or the practice phone code); by default it is empty and the NPI is never stored. | identity.npi_check.npi_encrypted | The identity role, for 30 days. | 30 days after verification, by a scheduled purge. The date is computed from verified_at. |
| NPI, salted hash | To stop one clinician opening two accounts. It cannot be turned back into the NPI. | identity.npi_check.npi_hash | The identity role. | With the account. |
| Verification results | Whether the registry match succeeded, and when; and, for the steps that are switched on, the license check, the phone code and the work email. The last four digits of the practice phone are kept only when that step is on, so the page can say where the code went. | identity.npi_check.user_ididentity.npi_check.name_matchedidentity.npi_check.taxonomyidentity.npi_check.license_stateidentity.npi_check.license_matchedidentity.npi_check.phone_last4identity.npi_check.phone_verified_atidentity.npi_check.email_verified_atidentity.npi_check.no_work_emailidentity.npi_check.verified_at | The identity role. | With the account. |
| Verification codes | The one-time code sent to your practice phone or work email, stored as a hash with where it went, how many tries were made and when it expires. Ten minutes, five tries. | identity.verification_code.* | The identity role. | When the code is used, and with the account. |
| The linking key | The only way to connect a person to their salary row. It is a secret key, not personal data, but it is listed because it is what keeps the two apart. | identity.hmac_key.* | The identity role can read it. No other role can, and the compensation side never holds it. | Rotated, never exported. |
| Your pay row | The compensation figures you submit, the specialty, geography, experience band and job attributes that give them meaning. | comp.salary_record.* | Public only in groups of five or more. The row itself is readable by the compensation role and moderators. It carries no name, NPI, email or employer name you did not choose to give. | When you delete it, within 24 hours. Excluded from every public view by the age rules after 36 months regardless. |
| Submitter key | A keyed hash of your account id, so your row can be found for edit and delete without storing who you are next to your pay. | comp.salary_record.submitter_key | The compensation role sees the hash. Only the identity role can compute it. | With the row. |
| Screening flags on your row | What the automated screen or a reviewer noted about the row (an outlier, a duplicate, arithmetic that does not add up), so a person can decide before it publishes. | comp.row_flag.* | The compensation role and moderators, next to the row and never with who you are. | With the row. |
| Submission fingerprint | A hash of your network address and browser at the moment you submitted, to catch one device submitting under several identities, and the values you confirmed as unusual on the form. | comp.submission_meta.* | The compensation role. The fingerprint is compared by the screen only. | The fingerprint is cleared 30 days after submission; the rest goes with the row. |
| Three-digit ZIP | To place the row in a metro. Stored, never displayed, never in any public cut. | comp.salary_record.zip3 | The compensation role. | With the row. |
| Your profile | What you save so the ask on a job page comes filled, which is your name, your phone, your specialty and the states you would consider. Nothing is required. | identity.profile.user_ididentity.profile.nameidentity.profile.phoneidentity.profile.specialty_ididentity.profile.statesidentity.profile.created_atidentity.profile.updated_atidentity.profile.weekly_matchesidentity.profile.weekly_sent_atidentity.profile.weekly_token | The identity role. You, on your profile page; the site's operator, on the review page. An employer or a recruiter sees it only in an ask you send. | When you clear it, and with your account. |
| Your licenses | The state, the number and the expiry of each license you list, so an ask can say where you are licensed. The number and the expiry are yours to give or not. | identity.profile_license.* | The identity role. You; the site's operator sees how many you listed. | When you remove one, and with your account. |
| Your résumé on file | The CV you keep on file, with its name, type, size and hash, so one press sends it with an ask or an application. The bytes sit in the row, or in the object store under a key the row holds when one is configured (migration 0047); the store knows nothing of whose file it is. | identity.profile_file.* | The identity role. You. A copy goes to MedStaff or to the employer only with an ask you send, and is then kept beside that ask as its own record. The operator sees that one is on file and its name, never the file. | When you remove or replace it, and with your account. |
| Your saved jobs | The postings you saved from the board with "Save job", as the board showed them (the title, the health system, the place, the pay stated, the page), so your account page can list them. | identity.saved_job.* | The identity role. You, on your account page. Nobody else; not MedStaff, not an employer, and the site's operator sees nothing of the list. | When you remove one, and with your account. |
| Consent records | The legal record of each time you turned the referral setting on or off, with the exact text you agreed to and who the recipient was at that moment. | referral.consent_record.idreferral.consent_record.user_idreferral.consent_record.consent_version_idreferral.consent_record.actionreferral.consent_record.recipient_snapshotreferral.consent_record.created_at | The referral role. Shown to you on your account page. | Kept, with your account id replaced by a tombstone when you delete your account. The record is the proof of what you agreed to. |
| Consent request fingerprints | A hash of the network address and browser at the moment of consent, to show the consent was given from your session. | referral.consent_record.ip_hashreferral.consent_record.user_agent_hash | The referral role. | Cleared when you delete your account. |
| Referral preferences | Off by default. If you turn it on, the specialties, places, pay floor and role types you would consider. A recruiter search must fall inside them to find you, and sees a random reference, not your account id. | referral.opt_in.user_idreferral.opt_in.candidate_refreferral.opt_in.activereferral.opt_in.active_consent_record_idreferral.opt_in.specialtiesreferral.opt_in.statesreferral.opt_in.cbsa_codesreferral.opt_in.comp_floorreferral.opt_in.role_typesreferral.opt_in.updated_at | The referral role. One recruiting firm, MedStaff Nationwide, through a view that answers only a complete search. | When you turn the setting off the row leaves the view at once; the row is removed when you delete your account. |
| Contact details for referral | The channel and the phone number or email you entered for a recruiter to use. Entered here, not copied from your login. | referral.opt_in.contact_channelreferral.opt_in.contact_value | Revealed to a recruiter only by a function that writes the contact log first, at most 50 reveals a day per recruiter. | With the referral row. |
| Contact log | Every time a recruiter revealed your contact details: when, which channel, which recruiter, and their note. Shown to you on your account page. | referral.contact_log.* | The referral role. You. | Kept, with your account id replaced by a tombstone when you delete your account. |
| Recruiter directory | The display name of each MedStaff recruiter login, so your contact log can say who looked you up. | referral.recruiter.* | The referral role. You, on your account page. | When the recruiter's access ends. |
| Job lead | A request you sent MedStaff Nationwide from a job page ("I'm interested" or "Ask a question"), when, the consent sentence you ticked with its hash, and when the notice reached MedStaff. Joined to your account by the email you gave, once you sign in with it. | referral.job_lead.idreferral.job_lead.kindreferral.job_lead.created_atreferral.job_lead.user_idreferral.job_lead.consent_textreferral.job_lead.consent_sha256referral.job_lead.notified_at | MedStaff Nationwide, by email the moment you send it. Shown to you on your account page. | With your account, the name, email, phone and message are cleared and the request stays on record without them. |
| Job lead contact details | The name, email, phone and message you typed into the form, so MedStaff can reply. | referral.job_lead.namereferral.job_lead.emailreferral.job_lead.phonereferral.job_lead.message | MedStaff Nationwide, in the notice. Never a recruiter login, never an employer. | Cleared when your account is deleted. |
| Job lead request hashes | One-way hashes of the address and browser the form came from, so a disputed consent can be traced. | referral.job_lead.ip_hashreferral.job_lead.user_agent_hash | Nobody in the ordinary course; a person answering a dispute. | Cleared when your account is deleted. |
| Job lead, the job as shown | The job the request was about, copied from the page at the moment you sent it. Nothing about you. | referral.job_lead.job_sourcereferral.job_lead.job_external_idreferral.job_lead.job_titlereferral.job_lead.job_systemreferral.job_lead.job_specialtyreferral.job_lead.job_cityreferral.job_lead.job_statereferral.job_lead.job_payreferral.job_lead.job_pathreferral.job_lead.job_url | MedStaff Nationwide, in the notice. | Kept, as the record of the request. |
| Job lead résumé | The CV you chose to attach to a request, with its name, type, size and hash, so the notice to MedStaff could carry it and the record shows what was sent. | referral.job_lead_file.lead_idreferral.job_lead_file.created_atreferral.job_lead_file.filenamereferral.job_lead_file.content_typereferral.job_lead_file.sizereferral.job_lead_file.sha256referral.job_lead_file.bytes | MedStaff Nationwide, attached to the notice the moment you send it. Nobody else; nothing public reads it. | With your account, and in any case 180 days after you sent it; the request stays on record without it. |
| Job alert | The new-postings email you asked for (a specialty, a state, pay stated or not), whether it is live, when it was confirmed by your sign-in, when the last digest went, and the token that stops it with one click. | referral.job_alert.idreferral.job_alert.created_atreferral.job_alert.user_idreferral.job_alert.specialty_idreferral.job_alert.statereferral.job_alert.with_payreferral.job_alert.activereferral.job_alert.confirmed_atreferral.job_alert.stop_tokenreferral.job_alert.last_sent_atreferral.job_alert.stopped_at | Shown to you on your account page. Nobody else. | Stopped when your account is deleted; the row stays as the record of the stop. |
| Job alert address | Where the digest goes. | referral.job_alert.email | The mail provider that carries the digest. Never a recruiter, never an employer. | Cleared when your account is deleted. |
| Job alert request hashes | One-way hashes of the address and browser the request came from, so a disputed request can be traced. | referral.job_alert.ip_hashreferral.job_alert.user_agent_hash | Nobody in the ordinary course; a person answering a dispute. | Cleared when your account is deleted. |
| Employer details | An account that posts jobs, the company it posts for and its website, which name the postings on the board, and when it declared itself the employer rather than a staffing or recruitment firm. The account id is the one the identity side gave; the jobs side never reads that side. | jobs.employer.user_idjobs.employer.source_idjobs.employer.companyjobs.employer.websitejobs.employer.created_atjobs.employer.updated_atjobs.employer.verified_atjobs.employer.digest_sent_atjobs.employer.declared_direct_at | The employer role, which can only run the posting functions. The company name and website are public on every posting. | The account id stays as the key of the closed postings, which name no person; the company name stays as the posting's employer. |
| Employer contact | Who to tell when a posting is approved or declined. Never shown on a page; the moderator sees it beside the posting. | jobs.employer.contact_namejobs.employer.contact_emailjobs.employer.contact_phone | The employer role. The moderator, on the review page. | Cleared when the account is deleted. |
| Employer posting | A job an employer posted here, as written, with the apply link or address it gave, when it was submitted, the moderator's decision with its reason, and when it runs out. Public on the board once approved, under the employer's name. | jobs.direct_posting.idjobs.direct_posting.employer_idjobs.direct_posting.posting_idjobs.direct_posting.statusjobs.direct_posting.titlejobs.direct_posting.specialty_idjobs.direct_posting.site_namejobs.direct_posting.cityjobs.direct_posting.statejobs.direct_posting.postal_codejobs.direct_posting.employment_typejobs.direct_posting.pay_lowjobs.direct_posting.pay_highjobs.direct_posting.pay_periodjobs.direct_posting.descriptionjobs.direct_posting.apply_urljobs.direct_posting.apply_emailjobs.direct_posting.submitted_atjobs.direct_posting.decided_atjobs.direct_posting.decided_byjobs.direct_posting.decision_reasonjobs.direct_posting.expires_atjobs.direct_posting.closed_at | The employer role. The moderator. The public, once approved. | Closed when the account is deleted; the posting stays on record, closed, under the company's name. |
| Employer posting request hashes | Keyed hashes of the network address and browser that submitted the posting, to tell a flood of postings from one place apart from many employers. Not the address itself. | jobs.direct_posting.ip_hashjobs.direct_posting.user_agent_hash | The employer role. | With the posting. |
| Application to an employer posting | A request you sent an employer from a posting it wrote here ("I'm interested" or "Ask a question"), when, the consent sentence naming that employer with its hash, when the employer was emailed and when it first read it. Joined to your account by the email you gave, once you sign in with it. | jobs.application.idjobs.application.direct_idjobs.application.employer_idjobs.application.kindjobs.application.created_atjobs.application.user_idjobs.application.consent_textjobs.application.consent_sha256jobs.application.notified_atjobs.application.seen_atjobs.application.statusjobs.application.status_at | The employer whose posting it is, on its account page and by email. The site's operator, on the review page. No recruiter. | Kept as a record; your details on it are cleared when you delete your account. |
| Application details | Your name, email, phone and note, as you typed them, so the employer can reply. | jobs.application.namejobs.application.emailjobs.application.phonejobs.application.message | The employer whose posting it is. The site's operator, on the review page. No recruiter. | Cleared when you delete your account. |
| Application request hashes | Keyed hashes of the network address and browser that sent it, to tell a flood from one place apart from many people. Not the address itself. | jobs.application.ip_hashjobs.application.user_agent_hash | The employer role, through the functions only. | Cleared when you delete your account. |
| The employer's note on an applicant | What the employer wrote to itself about an applicant on its applicants page, beside the status it set. The status shows on your account; the note never does. | jobs.application.note | The employer, and the site's operator. Never the candidate, never a recruiter. | Cleared when the employer clears it; with the employer's account. |
| Employer posting edits | An edit an employer sent for a posting already on the board, as written, waiting for a person to apply or decline it with a reason. Nothing about a candidate. | jobs.direct_revision.idjobs.direct_revision.direct_idjobs.direct_revision.employer_idjobs.direct_revision.statusjobs.direct_revision.titlejobs.direct_revision.specialty_idjobs.direct_revision.site_namejobs.direct_revision.cityjobs.direct_revision.statejobs.direct_revision.postal_codejobs.direct_revision.employment_typejobs.direct_revision.pay_lowjobs.direct_revision.pay_highjobs.direct_revision.pay_periodjobs.direct_revision.descriptionjobs.direct_revision.apply_urljobs.direct_revision.apply_emailjobs.direct_revision.submitted_atjobs.direct_revision.decided_atjobs.direct_revision.decided_byjobs.direct_revision.decision_reason | The employer role. The moderator. The public, once applied, as the posting. | Kept with the posting's record. |
| Employer posting edit request hashes | One-way hashes of the address and browser the edit came from, so a disputed edit can be traced. | jobs.direct_revision.ip_hashjobs.direct_revision.user_agent_hash | Nobody in the ordinary course; a person answering a dispute. | Kept with the edit's record. |
| Résumé on an application | The CV you attached, with its name, type, size and hash, so the employer can read it. The bytes sit in the row, or in the object store under a key the row holds when one is configured (migration 0047). | jobs.application_file.* | The employer whose posting it is, by email and on its account page. | 180 days after it was sent, by the nightly purge, or when you delete your account. |
| Form guard hits | One hash a press on a public form, made from the site secret, the form and the address or the account, with its time, so a burst from one place waits an hour. The address itself is never stored. | ops.form_hit.keyops.form_hit.at | Nobody; the database counts them for the guard and clears them within a day. | Within a day of the press, by the guard itself. |
| Visits, in Google Analytics | Which pages are visited, from where, on what kind of device and for how long, so we can see what people use and improve it (the founder's call, 2026-10-06). Google's script runs in your browser on every page. It sets two cookies for two years so it can tell a returning browser from a new one, and sends Google each page you open, the page you came from, your browser's name and its address, which Google uses for a rough location and says it does not keep. | Google Analytics, on Google’s servers; the _ga cookies in your browser | The site's operator, as Google's reports and totals; Google, under its own terms. | The cookies two years after your last visit, or when you clear them; Google keeps the visit data fourteen months (the property's setting) and the totals after that. |
| The map link on a job page, to Google Maps | A job page carries a link to Google Maps for the posting's place and nothing more (the founder, 2026-10-10; the still image of 2026-10-07 is gone). The page fetches nothing from Google; following the link is a visit to Google's site like any other. | Google Maps, on Google’s servers, only once you follow the link | Google, under its own terms. The site's operator sees nothing of it. | Nothing is stored by us; Google's own retention applies to the request. |
| Page views | How many times a page was looked at on a day, whether by a person or a crawler (told apart by what the browser calls itself), and the site a visitor came from, so we can see whether anyone is finding the site. One number on a row; no cookie is set, nothing runs in your browser, and your address, your browser's name and any id are read once by our own server and never stored. | ops.page_view.dayops.page_view.pathops.page_view.kindops.page_view.referrer_hostops.page_view.n | The site's operator, as totals and lists on the review page. | After 400 days, as new views are counted. |
Change log
- Google Analytics added at the founder's decision, so its script runs on every page and sets its cookies; the "no trackers" sentence is withdrawn and the new row says what Google receives. Page views are also counted on our own server with no cookie, no script and no address or id stored.
- The account id and the login email now go with the account at once, since the account page deletes an account itself; the form guard's hits and the object store's keys added.
- Employer posting edits, the employer's status and note on an applicant, and whether an employer is verified added.
- The candidate's profile added, which is the details, the licenses and the résumé a signed-in clinician keeps on file, on the identity side.
- Employer details, the employer's contact, employer postings and their request hashes added, with the jobs schema now checked against the map.
- Page published, generated from the data map file that CI checks against the database.