How to build a candidate account

Compose profile, résumé, saved jobs, weekly alerts, applications, access, and conversations.

A candidate account is a private shell around board.me. Establish the server-owned session first, then load independent panels in parallel. Every request in this recipe must bypass shared caches.

Load the account shell

ts
const options = {
cache: 'no-store' as const,
headers: { authorization: `Bearer ${accessToken}` },
};
const [me, profile, savedJobs, applications, alerts, conversations] =
await Promise.all([
board.me.retrieve(undefined, options),
board.me.profile.retrieve(undefined, options),
board.me.savedJobs.list({ limit: 20 }, options),
board.me.applications.list({ limit: 20 }, options),
board.me.alerts.list(options),
board.me.conversations.list({ limit: 20 }, options),
]);

Treat a 401 as an expired or missing session and run the one explicit refresh path. Treat 403 as an access decision, not a sign-out signal.

Build profile editing

Use the focused methods under board.me.profile for the core profile, experience, education, skills, languages, and avatar. Validate fields in the UI, while preserving server validation details when the API rejects a write.

Résumé upload starts automated parsing and returns parseStatus: 'parsing'. Poll board.me.resume.retrieve(options) until the status is parsed or failed, then reload the profile fields populated by the parser. Explain what parsing changes and let the candidate review and correct imported data.

Ask for explicit candidate consent before setting keepResumeOnFile. When it is false, Cavuno deletes the original file after parsing. When it is true, the original file is retained until the candidate deletes or replaces it, or deletes their account. board.me.resume.delete(options) withdraws keep-on-file consent and deletes the original résumé, but it does not remove fields already imported into the profile. Those fields remain until the candidate edits them or permanently deletes the account with board.me.delete(options). Cavuno does not currently enforce a time-based résumé retention period, so do not promise one in the frontend or privacy notice.

Build saved jobs and applications

Use board.me.savedJobs.save and unsave; do not mirror save state in local storage. Applications come from board.me.applications.list and retrieve. Candidate-supported updates include facts and withdrawal; the API decides which application states still permit them.

Create weekly job alerts

ts
await board.me.alerts.create(
{
label: 'Remote product roles',
frequency: 'weekly',
jobFunctions: [selectedJobFunction],
remoteOptions: ['remote'],
},
options,
);

Weekly is the only supported alert frequency. Do not show a daily selector or send a daily value retained from an older integration.

Add access and messaging

Use board.me.access for candidate paid access and checkout state. Use conversations for candidate-employer messaging, including unread counts, replies, reporting, and blocks. Authorization and feature gates can change between page load and mutation, so handle typed denials at the action boundary.

Verify the candidate account

  1. Sign in as two candidates and confirm profiles, saves, alerts, applications, and messages never cross.
  2. Edit every profile subsection and reload from the API rather than trusting optimistic state.
  3. Upload a résumé, observe parsing through a terminal state, review the imported profile, then delete the stored file and confirm the documented retention behavior.
  4. Create, update, and delete a weekly alert; confirm no daily option is visible or accepted.
  5. Save and unsave a job in two tabs and confirm the API remains the source of truth.
  6. Expire the access token and confirm concurrent private reads share one explicit refresh.

Continue with Employer workflows for the company-owned side.