Salaries namespace

Reference for salary data by title, skill, and location.

Programmatic salary directory and detail data. Use the formatting and SEO helper packages for presentation.

Salary pages are public. Pass { locale: context.language } to overlay board-language names and canonical slugs; omitting it uses the English identity path.

Company salary index

ts
board.salaries.companies.list(
options?: FetchOptions,
): Promise<ListEnvelope<SalaryCompany>>

Returns companies ranked for the /salaries/companies hub.

ts
const { data: companies } = await board.salaries.companies.list();

Company-specific salary detail methods live under board.companies.salaries because they share the public company namespace.

Title methods

ts
board.salaries.titles.list(
query?: { locale?: string },
options?: FetchOptions,
): Promise<ListEnvelope<SalaryTitle>>
board.salaries.titles.retrieve(
titleSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<TitleSalaryDetail>
board.salaries.titles.locations(
titleSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<TitleLocationsIndex>
board.salaries.titles.location(
titleSlug: string,
locationSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<TitleLocationSalary>
ts
const { language } = await board.context();
const { data: titles } = await board.salaries.titles.list({
locale: language,
});
const detail = await board.salaries.titles.retrieve(
titles[0].slug,
{ locale: language },
);

Skill methods

ts
board.salaries.skills.list(
query?: { locale?: string },
options?: FetchOptions,
): Promise<ListEnvelope<SalarySkill>>
board.salaries.skills.retrieve(
skillSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<SkillSalaryDetail>
board.salaries.skills.locations(
skillSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<SkillLocationsIndex>
board.salaries.skills.location(
skillSlug: string,
locationSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<SkillLocationSalary>

Location methods

ts
board.salaries.locations.list(
query?: { locale?: string },
options?: FetchOptions,
): Promise<ListEnvelope<SalaryLocation>>
board.salaries.locations.retrieve(
locationSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<LocationSalaryDetail>
board.salaries.locations.titles(
locationSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<LocationTitlesIndex>
board.salaries.locations.skills(
locationSlug: string,
query?: { locale?: string },
options?: FetchOptions,
): Promise<LocationSkillsIndex>

SalaryLocation rows form a flattened hierarchy through parentSlug; rebuild country, region, and city navigation from those edges.

Canonical slug handling

Detail responses carry source and canonical slug fields. The API returns data rather than performing a redirect. When the inbound slug differs from the response’s canonical slug, issue a permanent redirect from the frontend route.

ts
const detail = await board.salaries.skills.retrieve(inboundSlug, {
locale: context.language,
});
if (detail.canonicalSlug !== inboundSlug) {
return Response.redirect(
new URL(`/salaries/skills/${detail.canonicalSlug}`, origin),
308,
);
}

Cross-axis detail responses include canonical source/entity/place slugs appropriate to both axes. Build paths with @cavuno/board/paths instead of concatenating translated names.

Presentation and errors

Use the salary builders in @cavuno/board/seo for Occupation, distribution, list, and FAQ structured data. Use @cavuno/board/format for board-language range and seniority copy.

Missing data throws the axis-specific 404 code: salaries_title_not_found, salaries_skill_not_found, salaries_location_not_found, salaries_title_location_not_found, or salaries_skill_location_not_found. Render a true 404 or a related salary index—do not turn missing salary evidence into zero pay.