@cavuno/board 4.8.0 → 4.10.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 (49) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/{_spec-DePC7a05.d.mts → _spec-BwUP7Wni.d.mts} +100 -1
  3. package/dist/{_spec-DePC7a05.d.ts → _spec-BwUP7Wni.d.ts} +100 -1
  4. package/dist/bin.mjs +174 -19
  5. package/dist/{blog-DbFr4VPW.d.mts → blog-C_xXYOxc.d.mts} +1 -1
  6. package/dist/{blog-ummlt6Uq.d.ts → blog-DC4BfQqF.d.ts} +1 -1
  7. package/dist/{board-zWyHbyi4.d.ts → board-CG8rDGTY.d.mts} +5 -3
  8. package/dist/{board-DxTMBgIf.d.mts → board-zdQ875ck.d.ts} +5 -3
  9. package/dist/{breadcrumbs-P4qh0zML.d.mts → breadcrumbs-BK7AqNp2.d.mts} +2 -2
  10. package/dist/{breadcrumbs-kbmspmfk.d.ts → breadcrumbs-qs9DC0yh.d.ts} +2 -2
  11. package/dist/browser/cavuno-board.global.min.js +1 -1
  12. package/dist/{chunk-IWWPHFK2.mjs → chunk-XEJWXRLK.mjs} +10 -0
  13. package/dist/doctor.d.mts +3 -1
  14. package/dist/doctor.d.ts +3 -1
  15. package/dist/doctor.js +169 -17
  16. package/dist/doctor.mjs +169 -17
  17. package/dist/filters.d.mts +2 -2
  18. package/dist/filters.d.ts +2 -2
  19. package/dist/format.d.mts +4 -4
  20. package/dist/format.d.ts +4 -4
  21. package/dist/index.d.mts +118 -24
  22. package/dist/index.d.ts +118 -24
  23. package/dist/index.js +127 -9
  24. package/dist/index.mjs +118 -10
  25. package/dist/{jobs-twt5TO9l.d.ts → jobs-B-uO7ffG.d.ts} +1 -1
  26. package/dist/{jobs-DTLDt57G.d.mts → jobs-BToqhRuU.d.mts} +1 -1
  27. package/dist/{search-f2UNlTfv.d.mts → search-CvwKD0Va.d.mts} +1 -1
  28. package/dist/{search-CHD6peIl.d.ts → search-Sl9-xi1z.d.ts} +1 -1
  29. package/dist/seo-B9b2AkSE.d.mts +12 -0
  30. package/dist/seo-D1Vc_0HF.d.ts +12 -0
  31. package/dist/seo-files.d.mts +36 -0
  32. package/dist/seo-files.d.ts +36 -0
  33. package/dist/seo-files.js +102 -0
  34. package/dist/seo-files.mjs +81 -0
  35. package/dist/seo.d.mts +6 -6
  36. package/dist/seo.d.ts +6 -6
  37. package/dist/server.d.mts +6 -5
  38. package/dist/server.d.ts +6 -5
  39. package/dist/server.mjs +1 -1
  40. package/dist/sitemap.d.mts +6 -5
  41. package/dist/sitemap.d.ts +6 -5
  42. package/dist/suggest.d.mts +2 -2
  43. package/dist/suggest.d.ts +2 -2
  44. package/package.json +11 -1
  45. package/skills/cavuno-board-account/SKILL.md +3 -1
  46. package/skills/cavuno-board-filters/SKILL.md +17 -3
  47. package/skills/cavuno-board-setup/SKILL.md +1 -0
  48. package/skills/cavuno-board-talent-access/SKILL.md +149 -0
  49. package/skills/manifest.json +8 -1
@@ -137,7 +137,9 @@ if (data.length === 0 && (skills.data.length === 0 || resume.parseStatus !== 'pa
137
137
  Authenticated settings expose `messageEmails`, `applicationEmails`, and
138
138
  `recommendedJobEmails`. The first two default subscribed when no stored
139
139
  preference exists; recommendation email is explicit opt-in and defaults
140
- unsubscribed. Updating one channel returns the full set.
140
+ unsubscribed. Each preference includes additive `waitlisted`. Updating
141
+ one channel returns the full set. A subscribe at the Email subscribers
142
+ cap waitlists instead of failing.
141
143
 
142
144
  ```ts snippet
143
145
  const preferences = await board.me.notificationPreferences.retrieve();
@@ -67,7 +67,9 @@ application's copy (message catalog or hard-coded board language).
67
67
 
68
68
  Category and skill collections contain terms backed by published jobs. Each
69
69
  term already carries a board-language `displayName`, immutable English
70
- `sourceSlug` for filtering, and board-language `canonicalSlug` for links.
70
+ `sourceSlug` for filtering, board-language `canonicalSlug` for links, and
71
+ a live `jobCount` (published jobs on the board — not a tally of the current
72
+ list page).
71
73
 
72
74
  ```ts snippet
73
75
  const categories = await board.taxonomy.categories.list({ limit: 50 });
@@ -77,11 +79,23 @@ const categoryOptions = categories.data.map((term) => ({
77
79
  label: term.displayName,
78
80
  filterValue: term.sourceSlug,
79
81
  href: `/jobs/${term.canonicalSlug}`,
82
+ jobCount: term.jobCount,
80
83
  }));
81
84
  ```
82
85
 
83
- Category and skill lists accept `q`, `limit` (1–100), and opaque `cursor`.
84
- Pass `nextCursor` unchanged to the next request.
86
+ For homepage "browse by category" tiles, ask for the busiest terms rather
87
+ than the first page of the name-sorted collection:
88
+
89
+ ```ts snippet
90
+ const top = await board.taxonomy.categories.list({
91
+ limit: 8,
92
+ sort: 'jobCount',
93
+ });
94
+ ```
95
+
96
+ Category and skill lists accept `q`, `limit` (1–100), `sort` (`name` or
97
+ `jobCount`), and opaque `cursor`. Pass `nextCursor` unchanged to the next
98
+ request. The cursor is bound to `q`, `limit`, and `sort`.
85
99
 
86
100
  ```ts snippet
87
101
  const first = await board.taxonomy.categories.list({ limit: 50 });
@@ -64,6 +64,7 @@ Route each enabled capability to its focused skill:
64
64
  | Saved jobs and applications | `cavuno-board-applications` |
65
65
  | Messaging | `cavuno-board-messaging` |
66
66
  | Candidate paywall | `cavuno-board-paywall` |
67
+ | Employer talent access | `cavuno-board-talent-access` |
67
68
  | Public job posting | `cavuno-board-post-a-job` |
68
69
  | Localized chrome | `cavuno-board-i18n` |
69
70
  | Metadata and structured data | `cavuno-board-seo` |
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: cavuno-board-talent-access
3
+ description: Employer talent-access paywall with @cavuno/board. Use for charging models, remaining credits, per-candidate unlock gates, embedded checkout, in-place upgrades, or the company billing portal.
4
+ ---
5
+
6
+ # Employer talent-access paywall
7
+
8
+ Operators sell access to the candidate directory: profile-unlock credits and/or first-message credits. Credits are company-scoped. This is a different buyer from the candidate job-access paywall (`board.me.access.*`, skill `cavuno-board-paywall`).
9
+
10
+ Two charging models (from `retrieve().accessModel`):
11
+
12
+ - `paid_messaging` — full profiles; a first cold message spends a message credit.
13
+ - `paid_unlocks_and_messaging` — directory cards redact; opaque `/p/{id}` requires an unlock credit; messaging stays credit-gated.
14
+
15
+ When no public `talent_access` plan exists, `accessModel` is `none` and the paywall is inert.
16
+
17
+ ## Read the CTA and remaining credits
18
+
19
+ `board.me.talentAccess.retrieve()` is the gating signal for a Message-vs-upsell CTA. Never cache it.
20
+
21
+ ```ts snippet
22
+ const access = await board.me.talentAccess.retrieve();
23
+ access.hasTalentAccess;
24
+ access.accessModel;
25
+ access.unlockCreditsRemaining;
26
+ access.messageCreditsRemaining;
27
+ access.hasUnlimitedUnlocks;
28
+ access.hasUnlimitedMessages;
29
+ access.companyId;
30
+ ```
31
+
32
+ `companyId` is set when the viewer has exactly one approved membership. It is `null` when they have none or more than one — pass `companyId` on writes or the API returns `company_required`.
33
+
34
+ ## Gate one candidate
35
+
36
+ Per-candidate already-unlocked state is a sibling read so the singleton never takes a candidate id.
37
+
38
+ ```ts snippet
39
+ const gate = await board.me.talentAccess.retrieveCandidate('bu_candidate');
40
+ if (!gate.alreadyUnlocked && !gate.hasUnlimitedUnlocks) {
41
+ renderUnlockCta(gate.unlockCreditsRemaining);
42
+ }
43
+ ```
44
+
45
+ ## Mint and mount checkout
46
+
47
+ `checkout` requires a signed-in approved employer and returns a mount kit. `returnPath` is relative; session metadata is `origin: talent_access`. Public talent plans come from `board.plans.list({ purpose: 'talent_access' })`.
48
+
49
+ ```ts snippet
50
+ import { loadStripe } from '@stripe/stripe-js';
51
+
52
+ const kit = await board.me.talentAccess.checkout({
53
+ planId: 'plan_1',
54
+ returnPath: '/employers',
55
+ colorMode: 'light',
56
+ });
57
+
58
+ const stripe = await loadStripe(kit.publishableKey, {
59
+ stripeAccount: kit.stripeAccountId,
60
+ });
61
+ ```
62
+
63
+ The kit contains `sessionId`, `clientSecret`, `stripeAccountId`, `publishableKey`, and `offerType`. The host owns Stripe.js and embedded-checkout mounting. Gate failures use `talent_access_unavailable`, `company_required`, and `stripe_not_connected`; handle them with `cavuno-board-errors`.
64
+
65
+ ## Confirm from remaining credits
66
+
67
+ `retrieveCheckout` reports `open`, `complete`, or `expired`. Open sessions are remountable with their `clientSecret`; expired sessions require a new checkout. After completion, poll `retrieve()` because webhook delivery is asynchronous.
68
+
69
+ ```ts snippet
70
+ const checkout = await board.me.talentAccess.retrieveCheckout(kit.sessionId);
71
+
72
+ if (checkout.status === 'complete') {
73
+ let access = await board.me.talentAccess.retrieve();
74
+ for (let poll = 0; !access.hasTalentAccess && poll < 10; poll++) {
75
+ await new Promise((resolve) => setTimeout(resolve, 1000));
76
+ access = await board.me.talentAccess.retrieve();
77
+ }
78
+ if (access.hasTalentAccess) renderUnlockedDirectory();
79
+ else renderPendingAccess();
80
+ }
81
+ ```
82
+
83
+ ## Spend an unlock
84
+
85
+ Unlock is idempotent: a second call for the same company/candidate pair returns `alreadyUnlocked: true` with no decrement.
86
+
87
+ ```ts snippet
88
+ const unlock = await board.me.talentAccess.unlock({
89
+ candidateId: 'bu_candidate',
90
+ });
91
+ if (unlock.alreadyUnlocked) renderProfile();
92
+ else renderProfile(unlock.unlockCreditsRemaining);
93
+ ```
94
+
95
+ `talent_access_required` means no plan or no remaining unlock credits.
96
+
97
+ ## Upgrade in place
98
+
99
+ Out-of-credits plan changes call `upgrade`, never a second checkout. Checkout would mint a parallel subscription.
100
+
101
+ ```ts snippet
102
+ await board.me.talentAccess.upgrade({ planId: 'plan_pro' });
103
+ ```
104
+
105
+ `already_on_plan` means the company already holds that plan. `talent_access_required` means there is no current talent_access subscription to swap.
106
+
107
+ ## Open the company billing portal
108
+
109
+ Job-posting and talent-access subscriptions share one Stripe customer per company. There is no `me.talentAccess.portal`.
110
+
111
+ ```ts snippet
112
+ const { url } = await board.me.companies.billingPortal.create('acme', {
113
+ returnPath: '/employers/billing',
114
+ });
115
+ location.href = url;
116
+ ```
117
+
118
+ ## Message credits spend on start
119
+
120
+ There is no separate message-credit spend endpoint. The composer gates on `hasTalentAccess` and remaining message credits; `conversations.start` still 403s `messaging_talent_access_required` on a cold send without credit.
121
+
122
+ ```ts snippet
123
+ await board.me.conversations.start({
124
+ candidateBoardUserId: 'bu_candidate',
125
+ body: 'Hi, your profile looks like a great fit.',
126
+ });
127
+ ```
128
+
129
+ ## Named vs opaque profile routes
130
+
131
+ Named `/p/{handle}` is the share bypass and always shows a full `public` profile. Opaque `/p/{id}` is the unlock-gated route. When `accessModel === 'paid_unlocks_and_messaging'`, directory cards redact for viewers who are not unlimited.
132
+
133
+ ```ts snippet
134
+ const named = await board.talent.retrieve('jane-doe');
135
+ const opaque = await board.talent.retrieve('bu_candidate');
136
+ ```
137
+
138
+ ## Completion gate
139
+
140
+ - `retrieve()` drives the Message-vs-upsell CTA and remaining-credit hints.
141
+ - Opaque `/p/{id}` uses `retrieveCandidate` then `unlock`; named `/p/{handle}` does not.
142
+ - Checkout reaches `complete`, then bounded `retrieve()` polling reaches `hasTalentAccess: true`.
143
+ - `upgrade` is the out-of-credits plan swap; never a second checkout session.
144
+ - Billing management uses `me.companies.billingPortal.create`, not a talent-access portal.
145
+ - First-message spend stays on `conversations.start`.
146
+
147
+ ## Cavuno SDK reference
148
+
149
+ For setup and API details beyond this workflow, use the [Cavuno Board SDK documentation](https://cavuno.com/docs/sdk).
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.8.0",
2
+ "version": "4.10.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",
@@ -154,6 +154,13 @@
154
154
  "path": "skills/cavuno-board-smoke-test/SKILL.md",
155
155
  "framework": null,
156
156
  "category": "core"
157
+ },
158
+ {
159
+ "name": "cavuno-board-talent-access",
160
+ "description": "Employer talent-access paywall with @cavuno/board. Use for charging models, remaining credits, per-candidate unlock gates, embedded checkout, in-place upgrades, or the company billing portal.",
161
+ "path": "skills/cavuno-board-talent-access/SKILL.md",
162
+ "framework": null,
163
+ "category": "core"
157
164
  }
158
165
  ]
159
166
  }