Build your job board

Turn the public Board API into jobs, content, candidate, and employer experiences.

This section takes you from a public jobs catalog to a complete custom job board. Each guide produces one working product surface and makes the boundary clear: Cavuno owns the data and business rules; your application owns the routes, interface, and deployment.

Before you start

Complete Set up the SDK by hand and export one shared board client. You also need:

  • A pk_… board key. The SDK uses https://api.cavuno.com by default.
  • A public origin such as https://jobs.example.com for canonical URLs.
  • A server-owned session before calling anything under board.me.

Do not assume every board enables every surface. Load the context once and use its flags to decide which routes and navigation items exist:

ts
const context = await board.context();
const routes = {
alerts: context.features.jobAlerts,
candidateAccount: context.features.candidates,
employerAccount: context.features.employers,
blog: context.features.blog,
publicJobPosting: context.features.publicJobSubmission,
candidatePaywall: context.features.candidatePaywall,
talent:
context.talentDirectoryVisibility === 'public' ||
context.talentDirectoryVisibility === 'employers_only',
};

features.talentDirectory is only true for a public directory. Use talentDirectoryVisibility when deciding whether to link /talent; an employers_only directory should lead anonymous visitors to an employer sign-in or upsell.

Choose a build sequence

  1. Build jobs and search, then companies. These public reads establish routing, pagination, error handling, and caching.
  2. Add blog and salaries only when you have content to publish.
  3. Add candidate accounts and talent, then applications.
  4. Add job alerts and messaging.
  5. Add employer self-service and job posting and checkout.
  6. Add candidate paywalls last, after authenticated sessions and protected rendering are correct.

Keep public and private reads separate

Public catalog pages can be cached. Candidate and employer responses cannot. On a server-rendered app, keep the shared client stateless and pass the viewer’s access token per request:

ts
const applications = await board.me.applications.list(
{ limit: 20 },
{
cache: 'no-store',
headers: { authorization: `Bearer ${accessToken}` },
},
);

Never place an access token in a public environment variable, shared cache key, or module-scoped SDK storage.

Verify each surface

For every guide, verify the same four things before moving on:

  1. The enabled route returns real board data.
  2. A disabled feature is removed from navigation and cannot be reached through your UI.
  3. A missing resource becomes your framework’s 404, while other BoardApiError values remain visible to logging and support.
  4. Authenticated responses use cache: 'no-store' and cannot cross user sessions.

Start with jobs and search.