Job alerts namespace
Reference for anonymous double-opt-in job-alert subscriptions and preference management.
A
JUse board.jobAlerts for anonymous email subscriptions with double opt-in and token-authenticated self-service. These methods do not use a board-user session. New subscription UI is available when board.context().features.jobAlerts is true.
The subscribe form follows the board password gate. Confirmation and management links remain open so recipients can confirm, unsubscribe, or change an existing subscription; their email token is the credential.
Shared behavior
Every method returns the Board API wire body unchanged. A non-2xx response throws BoardApiError; inspect status, code, details, and requestId. The final FetchOptions argument passes abort signals, headers, and other fetch options through to fetch.
Confirmation and management responses use Cache-Control: no-store. Treat every confirmation or management token as a credential: do not log it, send it to analytics, or expose the containing URL through third-party referrers.
board.jobAlerts.subscribe
Create or find an email subscription and send the double-opt-in message.
1234board.jobAlerts.subscribe(input: JobAlertSubscribeInput,options?: FetchOptions,): Promise<JobAlertSubscription>
JobAlertSubscribeInput contains:
| Field | Type | Behavior |
|---|---|---|
email | string | Subscriber email. |
consent | true | Required literal; the server rejects anything else. |
frequency | 'weekly' | Optional; the only supported cadence and the default. |
filters | JobAlertFiltersInput | Optional digest and preference filters. |
context | { source?; jobId?; jobSlug? } | Optional originating page/job context. |
filters.jobFunctions, placeSlugs, and remoteOptions affect digest matching. seniorityLevels and salary fields are stored for the preference UI but do not currently filter delivery. Unknown place slugs are dropped when Cavuno resolves them.
12345678910111213const subscription = await board.jobAlerts.subscribe({email: 'reader@example.com',consent: true,frequency: 'weekly',filters: {jobFunctions: ['engineering'],remoteOptions: ['remote'],placeSlugs: ['australia'],},context: { source: 'jobs_listing' },});console.log(subscription.status, subscription.requiresConfirmation);
The result is { object: 'job_alert_subscription', status, requiresConfirmation, confirmed }. status is created or duplicate. Repeating the same email/filter subscription returns duplicate rather than creating another copy.
job_alerts_disabled returns status 422; remove the form while the feature is off. A password-protected board without a valid board grant returns board_password_required with status 401. Invalid input returns 400, and the per-IP/email protection can return 429. An identical retry after an unknown response is state-safe because the duplicate result is explicit, but never loop on 429—wait and let the user retry.
Related methods: board.jobAlerts.confirm and board.jobAlerts.resendConfirmation.
board.jobAlerts.confirm
Complete double opt-in with the token from the confirmation email.
1234board.jobAlerts.confirm(input: { token: string },options?: FetchOptions,): Promise<JobAlertConfirmation>
1234567891011121314const confirmation = await board.jobAlerts.confirm({ token });switch (confirmation.status) {case 'confirmed':case 'already_confirmed':console.log('Subscribed');break;case 'expired':console.log('Request another confirmation email');break;case 'not_found':console.log('Invalid confirmation link');break;}
The result is { object: 'job_alert_confirmation', status }. confirmed, already_confirmed, expired, and not_found all return HTTP 200 so the page can render an outcome. Repeating a successful confirmation is safe and yields already_confirmed.
Malformed requests can return 400 and rate limiting can return 429. The route is anonymous and open through the board password wall. Do not treat an expired or unknown token status as a transport exception.
Related method: board.jobAlerts.resendConfirmation.
board.jobAlerts.resendConfirmation
Request another double-opt-in email for an unconfirmed subscription.
1234board.jobAlerts.resendConfirmation(input: { email: string },options?: FetchOptions,): Promise<JobAlertResendResult>
12345const resend = await board.jobAlerts.resendConfirmation({email: 'reader@example.com',});console.log(resend.status);
The result is { object: 'job_alert_confirmation_resend', status }, where status is sent, not_found, already_confirmed, throttled, or send_failed. Those five outcomes return HTTP 200.
The server throttles resends per subscription and per IP. Render throttled as a wait state and send_failed as a later-retry state. The outer API limiter can also return 429. Never automatically retry email delivery; require a user-triggered retry after waiting.
Related methods: board.jobAlerts.subscribe and board.jobAlerts.confirm.
board.jobAlerts.manage
Load one subscription and its preferences with the subscription-wide HMAC manage token from an alert email.
1234board.jobAlerts.manage(query: JobAlertManageQuery,options?: FetchOptions,): Promise<JobAlertManageState>
JobAlertManageQuery requires subscription and token. Both are sent in the query string, so remove sensitive query data from logs and referrers.
12345678910const state = await board.jobAlerts.manage({subscription: subscriptionId,token: manageToken,});console.log(state.email, state.confirmed, state.unsubscribed);for (const preference of state.preferences) {console.log(preference.id, preference.frequency, preference.isActive);}
JobAlertManageState contains email, confirmed, unsubscribed, and preferences. Each preference includes its ID, label, frequency, active state, stored filters, and a new preference-bound manageToken for update/delete calls.
An invalid or mismatched subscription token returns 403. A missing board/subscription returns job_alert_not_found with status 404. Retrying this GET once after a transient network failure is safe. Do not cache the result.
Related methods: board.jobAlerts.updatePreference, board.jobAlerts.unsubscribe, and board.jobAlerts.deletePreference.
board.jobAlerts.unsubscribe
Deactivate an entire subscription or one preference with a matching manage token.
1234board.jobAlerts.unsubscribe(input: JobAlertManageTokenInput,options?: FetchOptions,): Promise<JobAlertManageResult>
JobAlertManageTokenInput requires subscriptionId and token; preferenceId is optional. Omit preferenceId when using the subscription-wide token. Include it with the preference-bound token returned by manage when targeting one preference.
123456const result = await board.jobAlerts.unsubscribe({subscriptionId,token: manageToken,});console.log(result.success);
The result is { object: 'job_alert_manage_result', success: true }. Invalid/mismatched tokens return 403, missing subscriptions/preferences return job_alert_not_found with status 404, malformed inputs return 400, and throttling returns 429.
After an unknown response, reload manage before retrying so the UI reflects the actual state. Do not assume a write failed because the response was lost.
Related methods: board.jobAlerts.resubscribe and board.jobAlerts.manage.
board.jobAlerts.resubscribe
Reactivate a previously unsubscribed subscription or preference with its manage token.
1234board.jobAlerts.resubscribe(input: JobAlertManageTokenInput,options?: FetchOptions,): Promise<JobAlertManageResult>
123456const result = await board.jobAlerts.resubscribe({subscriptionId,token: manageToken,});console.log(result.success);
The input and { object: 'job_alert_manage_result', success } response match unsubscribe. Invalid/mismatched tokens return 403, missing subscriptions/preferences return job_alert_not_found with status 404, invalid inputs return 400, and throttling returns 429.
After an unknown response, reload manage before retrying. A successful resubscribe does not bypass the original double-opt-in state.
Related methods: board.jobAlerts.unsubscribe and board.jobAlerts.manage.
board.jobAlerts.updatePreference
Replace one preference’s frequency and filters with a preference-bound manage token.
1234board.jobAlerts.updatePreference(input: JobAlertUpdatePreferenceInput,options?: FetchOptions,): Promise<JobAlertManageResult>
The input requires subscriptionId, preferenceId, the preference’s token, and frequency: 'weekly'. filters is optional, but the update is a full replacement: restate every filter you want to preserve. Legacy daily values are not accepted for new or updated preferences.
12345678910111213141516171819202122232425const state = await board.jobAlerts.manage({subscription: subscriptionId,token: subscriptionToken,});const preference = state.preferences[0];if (preference) {const result = await board.jobAlerts.updatePreference({subscriptionId,preferenceId: preference.id,token: preference.manageToken,frequency: 'weekly',filters: {jobFunctions: preference.filters.jobFunctions,seniorityLevels: preference.filters.seniorityLevels,remoteOptions: ['remote', 'hybrid'],salaryMin: preference.filters.salaryMin ?? undefined,salaryMax: preference.filters.salaryMax ?? undefined,salaryCurrency: preference.filters.salaryCurrency ?? undefined,},});console.log(result.success);}
The result is { object: 'job_alert_manage_result', success: true }. manage returns stored placeIds, while updates accept public placeSlugs; do not pass IDs as slugs. Resolve the user’s selected public places back to slugs before submitting.
An invalid/mismatched preference token returns 403. A missing subscription/preference returns job_alert_not_found with status 404. Invalid filters return 400 and throttling returns 429. After an unknown response, reload manage before retrying because the full replacement may already have succeeded.
Related methods: board.jobAlerts.manage and board.jobAlerts.deletePreference.
board.jobAlerts.deletePreference
Delete one preference with its preference-bound manage token.
1234board.jobAlerts.deletePreference(input: JobAlertDeletePreferenceInput,options?: FetchOptions,): Promise<JobAlertManageResult>
JobAlertDeletePreferenceInput requires subscriptionId, preferenceId, and token.
1234567const result = await board.jobAlerts.deletePreference({subscriptionId,preferenceId,token: preferenceManageToken,});console.log(result.success);
The result is { object: 'job_alert_manage_result', success: true }. Invalid/mismatched tokens return 403, missing subscriptions/preferences return job_alert_not_found with status 404, malformed input returns 400, and throttling returns 429.
After an unknown response, reload manage before retrying so the UI reflects the actual preference list.
Related methods: board.jobAlerts.manage and board.jobAlerts.subscribe.