Top-level methods

Reference for board context, SEO infrastructure, the raw client, and shared response conventions.

The SDK has public Board reads and one raw request escape hatch. Namespace methods use the same underlying BoardClient pipeline.

board.context()

ts
context(options?: FetchOptions): Promise<PublicBoard>

Returns the public configuration needed to build the board shell:

FieldUse
id, slug, nameBoard identity. id is immutable; slug can change.
languageRequired input for SDK display-formatting helpers.
logoUrl, iconsBoard logo and the favicon / app-icon pack derived from it (absolute URLs or null). Brand identity — map icons into root-layout <link rel="icon"> tags.
primaryDomain, showCavunoBrandingPublic domain and whitelabel state.
featuresCapability gates for alerts, candidates, employers, blog, talent directory (off | public | employers_only), registration wall, password protection, public submission, paywall, and impressum.
analyticsPublic analytics IDs and the cookie-consent requirement.
customFields, contactJob-field definitions (keyed by model) and public contact/social identity.
sandboxPlatform sandbox marker. Only doctor’s opted-in write probes should use it.
ts
const context = await board.context({ cache: 'force-cache' });
if (context.features.blog) {
// Register or render the public blog route.
}

Context is public and can normally use a shared cache. Feature flags guide presentation; they do not replace API authorization or entitlement enforcement.

board.seo()

ts
seo(options?: FetchOptions): Promise<BoardSeo>

Returns public platform infrastructure configuration:

  • canonicalBase for canonical links and the robots.txt sitemap line.
  • adsTxt and indexNowKey, each nullable.
  • googleSiteVerification, nullable.
  • manifest.name (board display name for a web manifest).

Icon URLs and themeColor are not returned — applications ship their own brand assets and presentation tokens.

ts
const seo = await board.seo({ cache: 'force-cache' });
export function adsTxtResponse() {
if (seo.adsTxt === null) return new Response('Not found', { status: 404 });
return new Response(seo.adsTxt, {
headers: { 'content-type': 'text/plain; charset=utf-8' },
});
}

board.seo() does not create page metadata or JSON-LD. Those pure builders live in @cavuno/board/seo.

Make a request to a custom endpoint

Use board.client.fetch() when an endpoint does not yet have a namespace method.

ts
fetch<T>(path: string, options?: FetchOptions): Promise<T>

Use the raw client for a live board-relative endpoint that does not yet have a namespace method. Prefer namespace methods when available because their bodies, queries, and responses are generated from the API contract.

ts
interface BoardStats {
object: 'board_stats';
activeJobs: number;
}
const stats = await board.client.fetch<BoardStats>('/custom/stats', {
query: { period: '30d' },
signal: AbortSignal.timeout(5_000),
});

The path is appended below /v1/boards/{identifier}. It must begin with / to produce the intended route. The generic type is an application assertion—the raw client does not validate the response at runtime.

Query behavior

Queries must be flat objects. Arrays serialize as repeated keys, preserving order:

ts
await board.client.fetch('/custom/jobs', {
query: {
remoteOption: ['remote', 'hybrid'],
cursor: null,
limit: 20,
},
});
// ?remoteOption=remote&remoteOption=hybrid&limit=20

null and undefined are omitted. Other values are converted with String(value). Nested objects are not recursively encoded.

Body and response behavior

  • Ordinary bodies are JSON-stringified and receive content-type: application/json.
  • Strings, URLSearchParams, FormData, Blob, ArrayBuffer, and ReadableStream are sent unchanged.
  • A successful HTTP 204 resolves to undefined without parsing.
  • Other successful responses are parsed as JSON.
  • Every non-2xx response throws BoardApiError; an invalid or non-JSON error body becomes unknown_error.
  • There is no automatic retry, response validation, or 401 refresh.

Shared list envelopes

List methods return ListEnvelope<T> and searches return SearchEnvelope<T>:

ts
const page = await board.jobs.list({ limit: 20 });
page.object; // "list"
page.data; // PublicJobCard[]
page.hasMore; // boolean
page.nextCursor; // string | null

Jobs catalog envelopes can also contain count, limit, offset, and gatedCount. Consumers must ignore unknown additive response fields. Echo nextCursor instead of inspecting it; it is opaque.

Verify the behavior

Use a deliberate 204 endpoint and a deliberate missing path in development. Confirm that the first resolves undefined, while the second throws BoardApiError and retains its status, code, raw envelope, and request ID.