@cavuno/board 4.33.0 → 4.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/{_spec-BdqHycDm.d.mts → _spec-DBpKC6Rv.d.mts} +73 -11
  3. package/dist/{_spec-BdqHycDm.d.ts → _spec-DBpKC6Rv.d.ts} +73 -11
  4. package/dist/analytics.d.mts +39 -2
  5. package/dist/analytics.d.ts +39 -2
  6. package/dist/analytics.js +57 -1
  7. package/dist/analytics.mjs +57 -1
  8. package/dist/{blog-D85cD8W0.d.ts → blog-Bk6oHqIR.d.ts} +1 -1
  9. package/dist/{blog-CE0g5csE.d.mts → blog-DXCzYj2q.d.mts} +1 -1
  10. package/dist/{board-CdG7z-Vq.d.mts → board-BpOyLLl_.d.ts} +11 -3
  11. package/dist/{board-CEsr8xMh.d.ts → board-D6yRj3IN.d.mts} +11 -3
  12. package/dist/{breadcrumbs-DmVbf4_6.d.ts → breadcrumbs-B5qrRrgN.d.ts} +2 -2
  13. package/dist/{breadcrumbs-B3n9k95M.d.mts → breadcrumbs-BnYHHx40.d.mts} +2 -2
  14. package/dist/browser/cavuno-board.global.min.js +1 -1
  15. package/dist/filters.d.mts +2 -2
  16. package/dist/filters.d.ts +2 -2
  17. package/dist/format.d.mts +4 -4
  18. package/dist/format.d.ts +4 -4
  19. package/dist/index.d.mts +68 -18
  20. package/dist/index.d.ts +68 -18
  21. package/dist/index.js +1 -1
  22. package/dist/index.mjs +1 -1
  23. package/dist/{jobs-DbZuagy-.d.ts → jobs-Cgh8dwXB.d.mts} +13 -2
  24. package/dist/{jobs-COOBWXWV.d.mts → jobs-DX_wDSzt.d.ts} +13 -2
  25. package/dist/{search-BV2B73ty.d.ts → search-DOmwaBud.d.ts} +1 -1
  26. package/dist/{search-BapnDodc.d.mts → search-DsR5-O8E.d.mts} +1 -1
  27. package/dist/{seo-C_yEotTb.d.mts → seo-DbzhNmAY.d.mts} +1 -1
  28. package/dist/{seo-BcC8Tv0d.d.ts → seo-DtLE8C9q.d.ts} +1 -1
  29. package/dist/seo-files.d.mts +2 -2
  30. package/dist/seo-files.d.ts +2 -2
  31. package/dist/seo.d.mts +6 -6
  32. package/dist/seo.d.ts +6 -6
  33. package/dist/server.d.mts +6 -6
  34. package/dist/server.d.ts +6 -6
  35. package/dist/sitemap.d.mts +6 -6
  36. package/dist/sitemap.d.ts +6 -6
  37. package/dist/suggest.d.mts +2 -2
  38. package/dist/suggest.d.ts +2 -2
  39. package/package.json +1 -1
  40. package/skills/manifest.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,51 @@
3
3
  This changelog records changes that affect Board API compatibility, exported
4
4
  types, runtime behavior, or supported integration patterns.
5
5
 
6
+ ## 4.35.0 — 2026-10-05
7
+
8
+ - Boards can define custom employment types, such as "Casual", each with a
9
+ built-in type as its Google equivalent. Jobs gain
10
+ `customEmploymentType: { key, label } | null`; `employmentType` still holds
11
+ the built-in type, which is what Google for Jobs markup and job feeds use.
12
+ Show `customEmploymentType.label` when it is set.
13
+
14
+ - The board context lists a board's custom types under
15
+ `jobForm.employmentType.customTypes` (`key`, `label`, `employmentType`,
16
+ `offered`) and the full display order under `jobForm.employmentType.order`.
17
+ `allowedOptions` and `customTypes` are returned in that order. Types that
18
+ are not offered stay listed so existing jobs keep their label.
19
+
20
+ - Job search and listing accept a `customEmploymentType` filter (keys). A
21
+ built-in `employmentType` filter value now matches jobs of that type that
22
+ have no custom type, so "Part-time" does not return "Casual" jobs.
23
+
24
+ - Job posting and employer job writes accept `customEmploymentType` (a key);
25
+ the matching built-in type is set automatically.
26
+
27
+ - Links to non-image files in public company custom fields are signed and
28
+ expire after at least 24 hours, so they work on password-protected boards.
29
+ Request fresh company data rather than storing these links.
30
+
31
+ ## 4.34.0 — 2026-10-05
32
+
33
+ - Added `analytics.recordConsent` to record a visitor's cookie-banner choice
34
+ (accepted, denied, or withdrawn) as proof of consent. It works without
35
+ `install` and sets no cookies.
36
+
37
+ - The default analytics script URL points at a new tracker version. On boards
38
+ that require cookie consent, load the tracker with `analytics.install()`
39
+ only after the visitor accepts.
40
+
41
+ - Jobs can have the status `pending_approval`: the job is waiting for an
42
+ operator to approve it and is not public, like a draft. Approve it with
43
+ `POST /v1/jobs/:id/publish` and reject it with `DELETE /v1/jobs/:id`.
44
+ Code that switches on job status should handle the new value.
45
+
46
+ - Public company responses include `customFieldMedia`, which resolves files
47
+ in public image gallery and file custom fields to
48
+ `{ id, name, contentType, sizeBytes, url }`, keyed by field key. Use `url`
49
+ to render them.
50
+
6
51
  ## 4.33.0 — 2026-10-04
7
52
 
8
53
  - The default analytics script URL now includes a version query, so browsers
@@ -319,6 +319,16 @@ interface components {
319
319
  enabled: boolean;
320
320
  boardName: string;
321
321
  };
322
+ BoardCustomEmploymentType: {
323
+ /** @description Stable key. Send it as `customEmploymentType` when posting a job, and filter jobs by it. */
324
+ key: string;
325
+ /** @description Display name, e.g. `Casual`. */
326
+ label: string;
327
+ /** @description The custom type's Google equivalent: a built-in employment type. Send it as `employmentType` alongside the key; it is what Google for Jobs and job feeds see. */
328
+ employmentType: string;
329
+ /** @description Whether posters can pick this type. An un-offered type still labels the jobs that already use it; leave it out of a posting form. */
330
+ offered: boolean;
331
+ };
322
332
  BoardFormBuiltinField: {
323
333
  /**
324
334
  * @description discriminator enum property added by openapi-typescript
@@ -704,6 +714,10 @@ interface components {
704
714
  customFieldValues: {
705
715
  [key: string]: string | number | boolean | string[];
706
716
  };
717
+ /** @description Resolved files for public `image_gallery` and `file` company custom fields, keyed by field key like `customFieldValues`, in the stored order. Each file `id` matches a value in `customFieldValues`. Use `url` to render: published images are permanent public asset URLs (e.g. `https://assets.cavuno.com/...`); other files, and images still being published, use a signed download URL that expires after at least 24 hours. Fields with no files are omitted. */
718
+ customFieldMedia: {
719
+ [key: string]: components["schemas"]["ProfileFieldMedia"][];
720
+ };
707
721
  /** @description Resolved public object references attached to this company. Private references are omitted. */
708
722
  objectReferences: components["schemas"]["ResolvedProfileObjectReference"][];
709
723
  links: components["schemas"]["PublicCompanyLinks"];
@@ -944,6 +958,8 @@ interface components {
944
958
  title: string;
945
959
  description: string;
946
960
  employmentType: string;
961
+ /** @description Key of one of the board's custom employment types (board context `jobForm.employmentType.customTypes`). `employmentType` must be the Google equivalent of that type; an unknown key, a type with `offered: false`, or a different `employmentType` returns `400`. */
962
+ customEmploymentType?: string;
947
963
  remoteOption: string;
948
964
  officeLocations: {
949
965
  /**
@@ -1240,10 +1256,12 @@ interface components {
1240
1256
  /** @description URL-friendly slug for the job. Auto-generated from `title` when omitted. */
1241
1257
  slug?: string;
1242
1258
  /**
1243
- * @description Employment type of the role.
1259
+ * @description Employment type of the role. When `customEmploymentType` is also sent, this must be the Google equivalent of that custom type (or be omitted: the server sets it). Sending a built-in type without `customEmploymentType` clears any custom type on the job.
1244
1260
  * @enum {string}
1245
1261
  */
1246
1262
  employmentType?: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other";
1263
+ /** @description Key of one of the board's custom employment types (`GET /v1/settings/job-form` → `employmentType.customTypes`). The job is saved with the Google equivalent of that type as its built-in `employmentType`, which is what Google for Jobs and feeds see. An unknown key, a type the board no longer offers (`offered: false`), or an `employmentType` that differs from its Google equivalent returns `400 jobs_constraint_violation`. Pass `null` to clear the custom type and keep the built-in; omitted means unchanged. */
1264
+ customEmploymentType?: string | null;
1247
1265
  /**
1248
1266
  * @description **Required.** Whether the role is on-site, hybrid, or fully remote: one of `on_site`, `hybrid`, `remote`. Omitting it or sending `null` returns `400`. It is not valid on its own either: `on_site` and `hybrid` require at least one `officeLocations` entry, and `remote` requires `remotePermits` (use `[{"type":"worldwide","value":"worldwide"}]` for anywhere; `remoteTimezones` then auto-derives on POST).
1249
1267
  * @enum {string}
@@ -1425,17 +1443,24 @@ interface components {
1425
1443
  /** @description URL-friendly slug used in public board URLs, or `null` if no slug is set. */
1426
1444
  slug: string | null;
1427
1445
  /**
1428
- * @description Current status of the job. One of `draft`, `published`, `expired`, or `archived`.
1446
+ * @description Current status of the job. One of `draft`, `published`, `expired`, `archived`, or `pending_approval`. A `pending_approval` job is waiting for an operator to approve it: approve with `POST /v1/jobs/:id/publish`, reject with `DELETE /v1/jobs/:id`. It is not public, like a draft.
1429
1447
  * @enum {string}
1430
1448
  */
1431
- status: "draft" | "published" | "expired" | "archived";
1449
+ status: "draft" | "published" | "expired" | "archived" | "pending_approval";
1432
1450
  /** @description Identifier of the company the job belongs to, or `null` if no company is attached. */
1433
1451
  companyId: string | null;
1434
1452
  /**
1435
- * @description Employment type of the role, or `null` if not specified.
1453
+ * @description Employment type of the role, or `null` if not specified. For a job with a custom employment type, this is the Google equivalent of the custom type.
1436
1454
  * @enum {string|null}
1437
1455
  */
1438
1456
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
1457
+ /** @description The board's custom employment type for this job (for example `Casual`), or `null` when the job uses a built-in type only. A deleted custom type reads back as `null` and the job shows its built-in `employmentType`. */
1458
+ customEmploymentType: {
1459
+ /** @description Stable key of the custom employment type. Never changes. */
1460
+ key: string;
1461
+ /** @description Display name of the custom employment type, as configured. */
1462
+ label: string;
1463
+ } | null;
1439
1464
  /**
1440
1465
  * @description Whether the role is on-site, hybrid, or fully remote. `null` only on older jobs saved without one; create requires it and `PATCH` cannot clear it.
1441
1466
  * @enum {string|null}
@@ -1535,10 +1560,12 @@ interface components {
1535
1560
  /** @description URL-friendly slug for the job. Auto-generated from `title` when omitted. */
1536
1561
  slug?: string;
1537
1562
  /**
1538
- * @description Employment type of the role.
1563
+ * @description Employment type of the role. When `customEmploymentType` is also sent, this must be the Google equivalent of that custom type (or be omitted: the server sets it). Sending a built-in type without `customEmploymentType` clears any custom type on the job.
1539
1564
  * @enum {string}
1540
1565
  */
1541
1566
  employmentType?: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other";
1567
+ /** @description Key of one of the board's custom employment types (`GET /v1/settings/job-form` → `employmentType.customTypes`). The job is saved with the Google equivalent of that type as its built-in `employmentType`, which is what Google for Jobs and feeds see. An unknown key, a type the board no longer offers (`offered: false`), or an `employmentType` that differs from its Google equivalent returns `400 jobs_constraint_violation`. Pass `null` to clear the custom type and keep the built-in; omitted means unchanged. */
1568
+ customEmploymentType?: string | null;
1542
1569
  /**
1543
1570
  * @description Whether the role is on-site, hybrid, or fully remote. Omitted means unchanged; `null` returns `400` because a job's workplace type can be changed but not cleared. **Never valid on its own:** `on_site` and `hybrid` require at least one `officeLocations` entry, and `remote` requires `remotePermits` (use `[{"type":"worldwide","value":"worldwide"}]` for anywhere; `remoteTimezones` then auto-derives on POST). Sending it alone returns `400`.
1544
1571
  * @enum {string}
@@ -2130,6 +2157,18 @@ interface components {
2130
2157
  min?: number;
2131
2158
  max?: number;
2132
2159
  };
2160
+ ProfileFieldMedia: {
2161
+ /** @description File identifier. Matches the value stored for the field in `customFieldValues`. */
2162
+ id: string;
2163
+ /** @description Original file name. */
2164
+ name: string;
2165
+ /** @description MIME type of the file. */
2166
+ contentType: string;
2167
+ /** @description Size of the file, in bytes. */
2168
+ sizeBytes: number;
2169
+ /** @description Absolute, browser-loadable URL. Published images are permanent public asset URLs (e.g. `https://assets.cavuno.com/...`) safe to cache and hotlink. Other files, and an image saved moments ago that is still being published, use a signed download URL that expires after at least 24 hours and works on password-protected boards without a password grant, so read the URL from the response rather than storing it. */
2170
+ url: string;
2171
+ };
2133
2172
  ProfileFieldValuesBody: {
2134
2173
  values: {
2135
2174
  [key: string]: string | number | boolean | string[] | unknown;
@@ -2385,8 +2424,12 @@ interface components {
2385
2424
  allowedOptions: string[];
2386
2425
  };
2387
2426
  employmentType: {
2388
- /** @description Employment types the board accepts, fully resolved like `seniority.allowedOptions`. A single entry means the form must collapse the field. */
2427
+ /** @description Built-in employment types the board accepts, fully resolved like `seniority.allowedOptions` and sorted by `order`. Empty when the board offers only custom types (`customTypes`). When the built-ins and offered custom types together come to a single option, the form must collapse the field. */
2389
2428
  allowedOptions: string[];
2429
+ /** @description The board's custom employment types, sorted by `order`, offered or not. Offer the `offered` ones next to `allowedOptions` in a posting form's employment type picker; use all of them to label jobs. Always an array (empty when the board defines none). */
2430
+ customTypes: components["schemas"]["BoardCustomEmploymentType"][];
2431
+ /** @description The board's employment type display order: every built-in value and every custom type key, each once. Sort a posting form's employment type picker by it. */
2432
+ order: string[];
2390
2433
  };
2391
2434
  };
2392
2435
  forms: components["schemas"]["BoardForms"];
@@ -2461,17 +2504,24 @@ interface components {
2461
2504
  /** @description URL-friendly slug used in public board URLs, or `null` if no slug is set. */
2462
2505
  slug: string | null;
2463
2506
  /**
2464
- * @description Current status of the job. One of `draft`, `published`, `expired`, or `archived`.
2507
+ * @description Current status of the job. One of `draft`, `published`, `expired`, `archived`, or `pending_approval`. A `pending_approval` job is waiting for an operator to approve it: approve with `POST /v1/jobs/:id/publish`, reject with `DELETE /v1/jobs/:id`. It is not public, like a draft.
2465
2508
  * @enum {string}
2466
2509
  */
2467
- status: "draft" | "published" | "expired" | "archived";
2510
+ status: "draft" | "published" | "expired" | "archived" | "pending_approval";
2468
2511
  /** @description Identifier of the company the job belongs to, or `null` if no company is attached. */
2469
2512
  companyId: string | null;
2470
2513
  /**
2471
- * @description Employment type of the role, or `null` if not specified.
2514
+ * @description Employment type of the role, or `null` if not specified. For a job with a custom employment type, this is the Google equivalent of the custom type.
2472
2515
  * @enum {string|null}
2473
2516
  */
2474
2517
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
2518
+ /** @description The board's custom employment type for this job (for example `Casual`), or `null` when the job uses a built-in type only. A deleted custom type reads back as `null` and the job shows its built-in `employmentType`. */
2519
+ customEmploymentType: {
2520
+ /** @description Stable key of the custom employment type. Never changes. */
2521
+ key: string;
2522
+ /** @description Display name of the custom employment type, as configured. */
2523
+ label: string;
2524
+ } | null;
2475
2525
  /**
2476
2526
  * @description Whether the role is on-site, hybrid, or fully remote. `null` only on older jobs saved without one; create requires it and `PATCH` cannot clear it.
2477
2527
  * @enum {string|null}
@@ -2640,8 +2690,18 @@ interface components {
2640
2690
  * @description ISO 8601 publish timestamp, or `null` if unpublished.
2641
2691
  */
2642
2692
  publishedAt: string | null;
2643
- /** @enum {string|null} */
2693
+ /**
2694
+ * @description Built-in employment type. For a job with a custom employment type, the Google equivalent of that type.
2695
+ * @enum {string|null}
2696
+ */
2644
2697
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
2698
+ /** @description The board's custom employment type for this job (show its `label` instead of the built-in), or `null`. */
2699
+ customEmploymentType: {
2700
+ /** @description Stable key of the custom employment type. Never changes. */
2701
+ key: string;
2702
+ /** @description Display name of the custom employment type, as configured. */
2703
+ label: string;
2704
+ } | null;
2645
2705
  /** @enum {string|null} */
2646
2706
  remoteOption: "on_site" | "hybrid" | "remote" | null;
2647
2707
  /** @description Display region label for remote jobs (e.g. "United States", "Worldwide"); `null` for non-remote jobs. */
@@ -2757,8 +2817,10 @@ interface components {
2757
2817
  companySlug?: string[];
2758
2818
  /** @description Only return jobs with any of the given remote-work options. Up to 10 values. */
2759
2819
  remoteOption?: ("on_site" | "hybrid" | "remote")[];
2760
- /** @description Only return jobs with any of the given employment types. Up to 10 values. */
2820
+ /** @description Only return jobs with any of the given built-in employment types. Up to 10 values. A built-in value matches jobs of that type that have no custom employment type: a job with a custom type matches only through `customEmploymentType`. Combined with `customEmploymentType` as a union. */
2761
2821
  employmentType?: ("full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other")[];
2822
+ /** @description Only return jobs with any of the given custom employment type keys (the board's `customTypes`). Up to 10 values. Combined with `employmentType` as a union: a job matches when it has one of these custom types OR one of the given built-in types and no custom type. */
2823
+ customEmploymentType?: string[];
2762
2824
  /** @description Only return jobs at any of the given seniority levels. Up to 10 values. */
2763
2825
  seniority?: ("entry_level" | "associate" | "mid_level" | "senior" | "lead" | "principal" | "director" | "executive")[];
2764
2826
  /** @description Only return jobs tagged with any of the given category slugs. Up to 10 values. Accepts the `sourceSlug` or the board-language `canonicalSlug` from `GET /v1/boards/{identifier}/categories`, or a `slug` from a job card's `categories`. Unknown slugs match no jobs. */
@@ -319,6 +319,16 @@ interface components {
319
319
  enabled: boolean;
320
320
  boardName: string;
321
321
  };
322
+ BoardCustomEmploymentType: {
323
+ /** @description Stable key. Send it as `customEmploymentType` when posting a job, and filter jobs by it. */
324
+ key: string;
325
+ /** @description Display name, e.g. `Casual`. */
326
+ label: string;
327
+ /** @description The custom type's Google equivalent: a built-in employment type. Send it as `employmentType` alongside the key; it is what Google for Jobs and job feeds see. */
328
+ employmentType: string;
329
+ /** @description Whether posters can pick this type. An un-offered type still labels the jobs that already use it; leave it out of a posting form. */
330
+ offered: boolean;
331
+ };
322
332
  BoardFormBuiltinField: {
323
333
  /**
324
334
  * @description discriminator enum property added by openapi-typescript
@@ -704,6 +714,10 @@ interface components {
704
714
  customFieldValues: {
705
715
  [key: string]: string | number | boolean | string[];
706
716
  };
717
+ /** @description Resolved files for public `image_gallery` and `file` company custom fields, keyed by field key like `customFieldValues`, in the stored order. Each file `id` matches a value in `customFieldValues`. Use `url` to render: published images are permanent public asset URLs (e.g. `https://assets.cavuno.com/...`); other files, and images still being published, use a signed download URL that expires after at least 24 hours. Fields with no files are omitted. */
718
+ customFieldMedia: {
719
+ [key: string]: components["schemas"]["ProfileFieldMedia"][];
720
+ };
707
721
  /** @description Resolved public object references attached to this company. Private references are omitted. */
708
722
  objectReferences: components["schemas"]["ResolvedProfileObjectReference"][];
709
723
  links: components["schemas"]["PublicCompanyLinks"];
@@ -944,6 +958,8 @@ interface components {
944
958
  title: string;
945
959
  description: string;
946
960
  employmentType: string;
961
+ /** @description Key of one of the board's custom employment types (board context `jobForm.employmentType.customTypes`). `employmentType` must be the Google equivalent of that type; an unknown key, a type with `offered: false`, or a different `employmentType` returns `400`. */
962
+ customEmploymentType?: string;
947
963
  remoteOption: string;
948
964
  officeLocations: {
949
965
  /**
@@ -1240,10 +1256,12 @@ interface components {
1240
1256
  /** @description URL-friendly slug for the job. Auto-generated from `title` when omitted. */
1241
1257
  slug?: string;
1242
1258
  /**
1243
- * @description Employment type of the role.
1259
+ * @description Employment type of the role. When `customEmploymentType` is also sent, this must be the Google equivalent of that custom type (or be omitted: the server sets it). Sending a built-in type without `customEmploymentType` clears any custom type on the job.
1244
1260
  * @enum {string}
1245
1261
  */
1246
1262
  employmentType?: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other";
1263
+ /** @description Key of one of the board's custom employment types (`GET /v1/settings/job-form` → `employmentType.customTypes`). The job is saved with the Google equivalent of that type as its built-in `employmentType`, which is what Google for Jobs and feeds see. An unknown key, a type the board no longer offers (`offered: false`), or an `employmentType` that differs from its Google equivalent returns `400 jobs_constraint_violation`. Pass `null` to clear the custom type and keep the built-in; omitted means unchanged. */
1264
+ customEmploymentType?: string | null;
1247
1265
  /**
1248
1266
  * @description **Required.** Whether the role is on-site, hybrid, or fully remote: one of `on_site`, `hybrid`, `remote`. Omitting it or sending `null` returns `400`. It is not valid on its own either: `on_site` and `hybrid` require at least one `officeLocations` entry, and `remote` requires `remotePermits` (use `[{"type":"worldwide","value":"worldwide"}]` for anywhere; `remoteTimezones` then auto-derives on POST).
1249
1267
  * @enum {string}
@@ -1425,17 +1443,24 @@ interface components {
1425
1443
  /** @description URL-friendly slug used in public board URLs, or `null` if no slug is set. */
1426
1444
  slug: string | null;
1427
1445
  /**
1428
- * @description Current status of the job. One of `draft`, `published`, `expired`, or `archived`.
1446
+ * @description Current status of the job. One of `draft`, `published`, `expired`, `archived`, or `pending_approval`. A `pending_approval` job is waiting for an operator to approve it: approve with `POST /v1/jobs/:id/publish`, reject with `DELETE /v1/jobs/:id`. It is not public, like a draft.
1429
1447
  * @enum {string}
1430
1448
  */
1431
- status: "draft" | "published" | "expired" | "archived";
1449
+ status: "draft" | "published" | "expired" | "archived" | "pending_approval";
1432
1450
  /** @description Identifier of the company the job belongs to, or `null` if no company is attached. */
1433
1451
  companyId: string | null;
1434
1452
  /**
1435
- * @description Employment type of the role, or `null` if not specified.
1453
+ * @description Employment type of the role, or `null` if not specified. For a job with a custom employment type, this is the Google equivalent of the custom type.
1436
1454
  * @enum {string|null}
1437
1455
  */
1438
1456
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
1457
+ /** @description The board's custom employment type for this job (for example `Casual`), or `null` when the job uses a built-in type only. A deleted custom type reads back as `null` and the job shows its built-in `employmentType`. */
1458
+ customEmploymentType: {
1459
+ /** @description Stable key of the custom employment type. Never changes. */
1460
+ key: string;
1461
+ /** @description Display name of the custom employment type, as configured. */
1462
+ label: string;
1463
+ } | null;
1439
1464
  /**
1440
1465
  * @description Whether the role is on-site, hybrid, or fully remote. `null` only on older jobs saved without one; create requires it and `PATCH` cannot clear it.
1441
1466
  * @enum {string|null}
@@ -1535,10 +1560,12 @@ interface components {
1535
1560
  /** @description URL-friendly slug for the job. Auto-generated from `title` when omitted. */
1536
1561
  slug?: string;
1537
1562
  /**
1538
- * @description Employment type of the role.
1563
+ * @description Employment type of the role. When `customEmploymentType` is also sent, this must be the Google equivalent of that custom type (or be omitted: the server sets it). Sending a built-in type without `customEmploymentType` clears any custom type on the job.
1539
1564
  * @enum {string}
1540
1565
  */
1541
1566
  employmentType?: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other";
1567
+ /** @description Key of one of the board's custom employment types (`GET /v1/settings/job-form` → `employmentType.customTypes`). The job is saved with the Google equivalent of that type as its built-in `employmentType`, which is what Google for Jobs and feeds see. An unknown key, a type the board no longer offers (`offered: false`), or an `employmentType` that differs from its Google equivalent returns `400 jobs_constraint_violation`. Pass `null` to clear the custom type and keep the built-in; omitted means unchanged. */
1568
+ customEmploymentType?: string | null;
1542
1569
  /**
1543
1570
  * @description Whether the role is on-site, hybrid, or fully remote. Omitted means unchanged; `null` returns `400` because a job's workplace type can be changed but not cleared. **Never valid on its own:** `on_site` and `hybrid` require at least one `officeLocations` entry, and `remote` requires `remotePermits` (use `[{"type":"worldwide","value":"worldwide"}]` for anywhere; `remoteTimezones` then auto-derives on POST). Sending it alone returns `400`.
1544
1571
  * @enum {string}
@@ -2130,6 +2157,18 @@ interface components {
2130
2157
  min?: number;
2131
2158
  max?: number;
2132
2159
  };
2160
+ ProfileFieldMedia: {
2161
+ /** @description File identifier. Matches the value stored for the field in `customFieldValues`. */
2162
+ id: string;
2163
+ /** @description Original file name. */
2164
+ name: string;
2165
+ /** @description MIME type of the file. */
2166
+ contentType: string;
2167
+ /** @description Size of the file, in bytes. */
2168
+ sizeBytes: number;
2169
+ /** @description Absolute, browser-loadable URL. Published images are permanent public asset URLs (e.g. `https://assets.cavuno.com/...`) safe to cache and hotlink. Other files, and an image saved moments ago that is still being published, use a signed download URL that expires after at least 24 hours and works on password-protected boards without a password grant, so read the URL from the response rather than storing it. */
2170
+ url: string;
2171
+ };
2133
2172
  ProfileFieldValuesBody: {
2134
2173
  values: {
2135
2174
  [key: string]: string | number | boolean | string[] | unknown;
@@ -2385,8 +2424,12 @@ interface components {
2385
2424
  allowedOptions: string[];
2386
2425
  };
2387
2426
  employmentType: {
2388
- /** @description Employment types the board accepts, fully resolved like `seniority.allowedOptions`. A single entry means the form must collapse the field. */
2427
+ /** @description Built-in employment types the board accepts, fully resolved like `seniority.allowedOptions` and sorted by `order`. Empty when the board offers only custom types (`customTypes`). When the built-ins and offered custom types together come to a single option, the form must collapse the field. */
2389
2428
  allowedOptions: string[];
2429
+ /** @description The board's custom employment types, sorted by `order`, offered or not. Offer the `offered` ones next to `allowedOptions` in a posting form's employment type picker; use all of them to label jobs. Always an array (empty when the board defines none). */
2430
+ customTypes: components["schemas"]["BoardCustomEmploymentType"][];
2431
+ /** @description The board's employment type display order: every built-in value and every custom type key, each once. Sort a posting form's employment type picker by it. */
2432
+ order: string[];
2390
2433
  };
2391
2434
  };
2392
2435
  forms: components["schemas"]["BoardForms"];
@@ -2461,17 +2504,24 @@ interface components {
2461
2504
  /** @description URL-friendly slug used in public board URLs, or `null` if no slug is set. */
2462
2505
  slug: string | null;
2463
2506
  /**
2464
- * @description Current status of the job. One of `draft`, `published`, `expired`, or `archived`.
2507
+ * @description Current status of the job. One of `draft`, `published`, `expired`, `archived`, or `pending_approval`. A `pending_approval` job is waiting for an operator to approve it: approve with `POST /v1/jobs/:id/publish`, reject with `DELETE /v1/jobs/:id`. It is not public, like a draft.
2465
2508
  * @enum {string}
2466
2509
  */
2467
- status: "draft" | "published" | "expired" | "archived";
2510
+ status: "draft" | "published" | "expired" | "archived" | "pending_approval";
2468
2511
  /** @description Identifier of the company the job belongs to, or `null` if no company is attached. */
2469
2512
  companyId: string | null;
2470
2513
  /**
2471
- * @description Employment type of the role, or `null` if not specified.
2514
+ * @description Employment type of the role, or `null` if not specified. For a job with a custom employment type, this is the Google equivalent of the custom type.
2472
2515
  * @enum {string|null}
2473
2516
  */
2474
2517
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
2518
+ /** @description The board's custom employment type for this job (for example `Casual`), or `null` when the job uses a built-in type only. A deleted custom type reads back as `null` and the job shows its built-in `employmentType`. */
2519
+ customEmploymentType: {
2520
+ /** @description Stable key of the custom employment type. Never changes. */
2521
+ key: string;
2522
+ /** @description Display name of the custom employment type, as configured. */
2523
+ label: string;
2524
+ } | null;
2475
2525
  /**
2476
2526
  * @description Whether the role is on-site, hybrid, or fully remote. `null` only on older jobs saved without one; create requires it and `PATCH` cannot clear it.
2477
2527
  * @enum {string|null}
@@ -2640,8 +2690,18 @@ interface components {
2640
2690
  * @description ISO 8601 publish timestamp, or `null` if unpublished.
2641
2691
  */
2642
2692
  publishedAt: string | null;
2643
- /** @enum {string|null} */
2693
+ /**
2694
+ * @description Built-in employment type. For a job with a custom employment type, the Google equivalent of that type.
2695
+ * @enum {string|null}
2696
+ */
2644
2697
  employmentType: "full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other" | null;
2698
+ /** @description The board's custom employment type for this job (show its `label` instead of the built-in), or `null`. */
2699
+ customEmploymentType: {
2700
+ /** @description Stable key of the custom employment type. Never changes. */
2701
+ key: string;
2702
+ /** @description Display name of the custom employment type, as configured. */
2703
+ label: string;
2704
+ } | null;
2645
2705
  /** @enum {string|null} */
2646
2706
  remoteOption: "on_site" | "hybrid" | "remote" | null;
2647
2707
  /** @description Display region label for remote jobs (e.g. "United States", "Worldwide"); `null` for non-remote jobs. */
@@ -2757,8 +2817,10 @@ interface components {
2757
2817
  companySlug?: string[];
2758
2818
  /** @description Only return jobs with any of the given remote-work options. Up to 10 values. */
2759
2819
  remoteOption?: ("on_site" | "hybrid" | "remote")[];
2760
- /** @description Only return jobs with any of the given employment types. Up to 10 values. */
2820
+ /** @description Only return jobs with any of the given built-in employment types. Up to 10 values. A built-in value matches jobs of that type that have no custom employment type: a job with a custom type matches only through `customEmploymentType`. Combined with `customEmploymentType` as a union. */
2761
2821
  employmentType?: ("full_time" | "part_time" | "contract" | "internship" | "temporary" | "volunteer" | "other")[];
2822
+ /** @description Only return jobs with any of the given custom employment type keys (the board's `customTypes`). Up to 10 values. Combined with `employmentType` as a union: a job matches when it has one of these custom types OR one of the given built-in types and no custom type. */
2823
+ customEmploymentType?: string[];
2762
2824
  /** @description Only return jobs at any of the given seniority levels. Up to 10 values. */
2763
2825
  seniority?: ("entry_level" | "associate" | "mid_level" | "senior" | "lead" | "principal" | "director" | "executive")[];
2764
2826
  /** @description Only return jobs tagged with any of the given category slugs. Up to 10 values. Accepts the `sourceSlug` or the board-language `canonicalSlug` from `GET /v1/boards/{identifier}/categories`, or a `slug` from a job card's `categories`. Unknown slugs match no jobs. */
@@ -4,7 +4,7 @@ declare const DEFAULT_COLLECT_URL = "https://cavuno.com/api/analytics/collect";
4
4
  * new release reaches returning visitors instead of waiting out their browser
5
5
  * cache. The unversioned URL keeps serving the current script.
6
6
  */
7
- declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js?v=1.7.1-cavuno.1";
7
+ declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js?v=1.7.1-cavuno.2";
8
8
  /** Placeholder tenant; collect rewrites from the publishable key. */
9
9
  declare const PENDING_TENANT_ID = "boards_pending";
10
10
  declare const WELL_KNOWN_ANALYTICS_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
@@ -40,6 +40,12 @@ type AnalyticsInstallOptions = {
40
40
  /**
41
41
  * Load the Cavuno-hosted metrics script and remember collect credentials.
42
42
  * Safe to call once per page; subsequent calls update collect targets only.
43
+ *
44
+ * The script sets a first-party `session-id` cookie (a random identifier
45
+ * that expires after 30 minutes without activity) and sends page view,
46
+ * engagement, and web-vital events. Where your board requires cookie consent
47
+ * (`analytics.cookieConsentRequired` from `board.context()`), call `install`
48
+ * only after the visitor accepts. Until it runs, `track` sends nothing.
43
49
  */
44
50
  declare function install(options: AnalyticsInstallOptions): void;
45
51
  /**
@@ -47,9 +53,40 @@ declare function install(options: AnalyticsInstallOptions): void;
47
53
  * Prefer `install` first so pageviews and vitals are also recorded.
48
54
  */
49
55
  declare function track(action: string, payload?: Record<string, unknown>): void;
56
+ /** A visitor's answer to the cookie banner, as recorded by `recordConsent`. */
57
+ type ConsentChoice = 'accepted' | 'denied' | 'withdrawn';
58
+ type RecordConsentInput = {
59
+ /** The board's publishable key (`pk_…`), the same one `install` takes. */
60
+ publishableKey: string;
61
+ /**
62
+ * Random UUID the banner generates once and keeps in its own consent
63
+ * cookie, so every choice from the same browser shares one id.
64
+ */
65
+ consentId: string;
66
+ choice: ConsentChoice;
67
+ /** Identifies the banner text the visitor saw. 1–128 characters. */
68
+ bannerVersion: string;
69
+ /** Default: Cavuno central collect, as for `install`. */
70
+ collectUrl?: string;
71
+ };
72
+ /**
73
+ * Record a cookie-banner choice as proof of consent. Call it on every
74
+ * Accept, Decline and withdrawal.
75
+ *
76
+ * Works without `install`, so a Decline is recorded even though analytics
77
+ * never load. It reads and sets no cookie and sends no session id: the
78
+ * record holds only the consent id, the choice, the banner version, the
79
+ * board and the time it was received.
80
+ *
81
+ * Sends `POST {collectUrl}` with
82
+ * `{ publishableKey, action: 'consent', payload: { consent_id, choice, banner_version } }`.
83
+ * Throws on invalid input; network failures are swallowed.
84
+ */
85
+ declare function recordConsent(input: RecordConsentInput): void;
50
86
  declare const analytics: {
51
87
  install: typeof install;
52
88
  track: typeof track;
89
+ recordConsent: typeof recordConsent;
53
90
  matchAnalyticsWellKnown: typeof matchAnalyticsWellKnown;
54
91
  DEFAULT_COLLECT_URL: string;
55
92
  DEFAULT_SCRIPT_URL: string;
@@ -60,4 +97,4 @@ declare const analytics: {
60
97
  WELL_KNOWN_COLLECT_EVENTS_PATH: string;
61
98
  };
62
99
 
63
- export { type AnalyticsInstallOptions, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, track };
100
+ export { type AnalyticsInstallOptions, type ConsentChoice, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, type RecordConsentInput, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, recordConsent, track };
@@ -4,7 +4,7 @@ declare const DEFAULT_COLLECT_URL = "https://cavuno.com/api/analytics/collect";
4
4
  * new release reaches returning visitors instead of waiting out their browser
5
5
  * cache. The unversioned URL keeps serving the current script.
6
6
  */
7
- declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js?v=1.7.1-cavuno.1";
7
+ declare const DEFAULT_SCRIPT_URL = "https://cavuno.com/js/metrics.js?v=1.7.1-cavuno.2";
8
8
  /** Placeholder tenant; collect rewrites from the publishable key. */
9
9
  declare const PENDING_TENANT_ID = "boards_pending";
10
10
  declare const WELL_KNOWN_ANALYTICS_SCRIPT_PATH = "/.well-known/cavuno/analytics.js";
@@ -40,6 +40,12 @@ type AnalyticsInstallOptions = {
40
40
  /**
41
41
  * Load the Cavuno-hosted metrics script and remember collect credentials.
42
42
  * Safe to call once per page; subsequent calls update collect targets only.
43
+ *
44
+ * The script sets a first-party `session-id` cookie (a random identifier
45
+ * that expires after 30 minutes without activity) and sends page view,
46
+ * engagement, and web-vital events. Where your board requires cookie consent
47
+ * (`analytics.cookieConsentRequired` from `board.context()`), call `install`
48
+ * only after the visitor accepts. Until it runs, `track` sends nothing.
43
49
  */
44
50
  declare function install(options: AnalyticsInstallOptions): void;
45
51
  /**
@@ -47,9 +53,40 @@ declare function install(options: AnalyticsInstallOptions): void;
47
53
  * Prefer `install` first so pageviews and vitals are also recorded.
48
54
  */
49
55
  declare function track(action: string, payload?: Record<string, unknown>): void;
56
+ /** A visitor's answer to the cookie banner, as recorded by `recordConsent`. */
57
+ type ConsentChoice = 'accepted' | 'denied' | 'withdrawn';
58
+ type RecordConsentInput = {
59
+ /** The board's publishable key (`pk_…`), the same one `install` takes. */
60
+ publishableKey: string;
61
+ /**
62
+ * Random UUID the banner generates once and keeps in its own consent
63
+ * cookie, so every choice from the same browser shares one id.
64
+ */
65
+ consentId: string;
66
+ choice: ConsentChoice;
67
+ /** Identifies the banner text the visitor saw. 1–128 characters. */
68
+ bannerVersion: string;
69
+ /** Default: Cavuno central collect, as for `install`. */
70
+ collectUrl?: string;
71
+ };
72
+ /**
73
+ * Record a cookie-banner choice as proof of consent. Call it on every
74
+ * Accept, Decline and withdrawal.
75
+ *
76
+ * Works without `install`, so a Decline is recorded even though analytics
77
+ * never load. It reads and sets no cookie and sends no session id: the
78
+ * record holds only the consent id, the choice, the banner version, the
79
+ * board and the time it was received.
80
+ *
81
+ * Sends `POST {collectUrl}` with
82
+ * `{ publishableKey, action: 'consent', payload: { consent_id, choice, banner_version } }`.
83
+ * Throws on invalid input; network failures are swallowed.
84
+ */
85
+ declare function recordConsent(input: RecordConsentInput): void;
50
86
  declare const analytics: {
51
87
  install: typeof install;
52
88
  track: typeof track;
89
+ recordConsent: typeof recordConsent;
53
90
  matchAnalyticsWellKnown: typeof matchAnalyticsWellKnown;
54
91
  DEFAULT_COLLECT_URL: string;
55
92
  DEFAULT_SCRIPT_URL: string;
@@ -60,4 +97,4 @@ declare const analytics: {
60
97
  WELL_KNOWN_COLLECT_EVENTS_PATH: string;
61
98
  };
62
99
 
63
- export { type AnalyticsInstallOptions, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, track };
100
+ export { type AnalyticsInstallOptions, type ConsentChoice, DEFAULT_COLLECT_URL, DEFAULT_SCRIPT_URL, PENDING_TENANT_ID, type RecordConsentInput, WELL_KNOWN_ANALYTICS_SCRIPT_PATH, WELL_KNOWN_COLLECT_EVENTS_PATH, WELL_KNOWN_COLLECT_PATH, WELL_KNOWN_SCRIPT_PATH, analytics, install, matchAnalyticsWellKnown, recordConsent, track };