@curviate/sdk 0.29.0 → 0.30.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.
package/CHANGELOG.md CHANGED
@@ -7,10 +7,173 @@ Versioning: semantic. Minor for additive changes, patch for bug fixes; no stabil
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.30.0] - 2026-09-09
11
+
12
+ Fixture and types regenerated against the deployed staging document
13
+ (`https://api.staging.curviate.com`, 125 paths); the exact source commit is
14
+ recorded in `fixtures/PROVENANCE.json`.
15
+
16
+ Product tiers are retired. Curviate no longer sells a Sales Navigator or
17
+ Recruiter product, no seat carries a tier flag, and one ordinary paid seat now
18
+ entitles the entire API surface.
19
+
20
+ **What actually happens to the error-code union: 45 codes to 67. Twenty-three
21
+ added, one removed, and the two retired tier codes KEPT.**
22
+
23
+ - **Added (23).** `NO_ACTIVE_SEAT` and `BETA_NOT_ENABLED`, plus twenty-one codes
24
+ the API already returned that this SDK had been erasing to `INTERNAL`.
25
+ - **Removed (1).** `RATE_LIMITED`, which the API never produced at all.
26
+ - **Kept, deprecated (2).** `TIER_NOT_ACTIVE` and `PREMIUM_CONFLICT` are still
27
+ exported. They are *not* gone, and that is deliberate: a client talks to a
28
+ deployment rather than to a changelog, and an API that predates the
29
+ seat-based entitlement rollout still answers them. See **Deprecated** below
30
+ before you delete a `case`.
31
+ - **Removed field (1).** `CurviateError.requiredTier`, with `RequiredTier`,
32
+ `REQUIRED_TIERS` and `KNOWN_REQUIRED_TIERS`. This one really is gone, so a
33
+ pre-rollout deployment gives you the code without the field. See **Removed**.
34
+
35
+ **Breaking, and unusually so for a 0.x minor**, in two narrow places rather than
36
+ across the union: the `requiredTier` field is gone, and `RATE_LIMITED` is gone.
37
+ Everything else here is additive or a deprecation.
38
+
39
+ Ripple: this release follows the server-side gate collapse, the connect rework
40
+ and the beta consent gate, each already deployed. Nothing here is an SDK-only
41
+ decision; the wire contract moved first and this release catches the typed
42
+ client up to it, in one release rather than three.
43
+
44
+ ### Deprecated
45
+
46
+ - **`TIER_NOT_ACTIVE` is replaced by `NO_ACTIVE_SEAT`, and `PREMIUM_CONFLICT`
47
+ is withdrawn with no replacement, but BOTH ARE STILL EXPORTED.**
48
+
49
+ Not a courtesy, and worth understanding before you delete a `case`: a client
50
+ talks to a DEPLOYMENT, not to a changelog. An API that has not taken the
51
+ seat-based entitlement rollout yet still answers `TIER_NOT_ACTIVE`, and a
52
+ union that had dropped the code would decode it to `INTERNAL` there, turning
53
+ a fixable billing refusal into what looks like a server fault, on exactly the
54
+ deployments most likely to send it. Carrying a dead code for one release is
55
+ the cheaper mistake.
56
+
57
+ So handle both for now: `NO_ACTIVE_SEAT` from a current deployment,
58
+ `TIER_NOT_ACTIVE` from an older one. They mean the same thing to a caller,
59
+ and both are `user_fixable` 403s whose remedy is a seat. Each is marked
60
+ `@deprecated` in its JSDoc with the replacement named, so your editor says so
61
+ at the call site.
62
+
63
+ They are removed in the first release after every deployment carries the new
64
+ contract. That removal will be breaking and will say so.
65
+
66
+ `CurviateError.requiredTier` is NOT kept, however: see Removed below. On a
67
+ pre-rollout deployment you get the code without the field, which is enough to
68
+ route on, since the message names the tier.
69
+
70
+ ### Removed
71
+
72
+ - **`RATE_LIMITED` is gone: the API never produced it.** The substrate's own
73
+ `RATE_LIMITED` is translated to `PLATFORM_RATE_LIMIT` (429) before any
74
+ response is written, so no caller could ever receive the code this SDK was
75
+ exporting. A `case "RATE_LIMITED"` was dead code, and worse than dead: it
76
+ read as the handled branch for a throttle that actually arrives as
77
+ `PLATFORM_RATE_LIMIT`, so the real one fell through to `default`.
78
+
79
+ The codes to branch on for throttling are `RATE_LIMIT_ACCOUNT`,
80
+ `RATE_LIMIT_TENANT`, `PLATFORM_RATE_LIMIT` and `LINKEDIN_RATE_LIMITED`. If
81
+ you matched `RATE_LIMITED`, delete the arm; nothing was reaching it.
82
+
83
+ - **`CurviateError.requiredTier` is gone**, along with the `RequiredTier` type,
84
+ the `REQUIRED_TIERS` array and the `KNOWN_REQUIRED_TIERS` set. There is no
85
+ tier to name on a refusal any more, so the field carried nothing. It is also
86
+ gone from `CurviateError.toJSON()`, so a serialized error no longer has the
87
+ key at all. Code reading `err.requiredTier` to route an upgrade should read
88
+ `err.code` instead and route on the three refusals described in the README.
89
+
90
+ ### Added
91
+
92
+ - **Twenty-one codes previously collapsed to `INTERNAL` now surface.** Each is
93
+ returned by a `/v1` route, the `/v1` catch-all, or a shared handler one of
94
+ them calls, so each already reached this SDK's decoder, and because the
95
+ exported union did not carry them, the decoder erased every one to
96
+ `INTERNAL`. A refusal the caller could usually fix arrived looking like a
97
+ server fault, with no `switch` arm possible.
98
+
99
+ Routing and reads: `NOT_FOUND`, `REACTION_NOT_FOUND`. Upstream failures on
100
+ connect and billing: `SUBSTRATE_LINK_FAILED`, `SUBSTRATE_CAP_REACHED`,
101
+ `BILLING_CHECKOUT_FAILED`, `BILLING_PORTAL_UNAVAILABLE`. Tenant standing:
102
+ `ACCOUNT_DISPUTED`, `ACCOUNT_LINKING_DISABLED`, `PERIOD_LOCKED`. Seat and
103
+ subscription conflicts: `SEAT_NOT_EMPTY`, `SEAT_PROVISIONAL`,
104
+ `SUBSCRIPTION_ALREADY_EXISTS`, `ALREADY_CANCELLED`,
105
+ `CANCELLATION_ALREADY_EFFECTIVE`, `INVALID_CANCELLATION_SOURCE`. Free trial:
106
+ `TRIAL_EXPIRED`, `TRIAL_SEAT_LIMIT`, `TRIAL_ACTIVE_SEAT_LIMIT`,
107
+ `TRIAL_IDENTITY_ALREADY_USED`, `TRIAL_IDENTITY_UNRESOLVED`. Admin tenants:
108
+ `ADMIN_BYPASS`.
109
+
110
+ Additive for anyone who was already handling `INTERNAL` as their fallback:
111
+ those cases now arrive with their real code instead, so a `default` arm that
112
+ retried on `retryLikelyToSucceed` keeps working and can be narrowed. If you
113
+ match on `INTERNAL` specifically to detect one of these, that check needs
114
+ updating.
115
+
116
+ `ADMIN_BYPASS` is a correction as much as an addition: this file previously
117
+ named it as an example of a code that never reaches a caller, while the
118
+ server constructed it at nine sites inside its versioned billing endpoints.
119
+
120
+ - **`BETA_NOT_ENABLED` (403).** A beta-gated operation refuses this until a
121
+ human enables beta operations for the workspace in Settings, or the request
122
+ carries the `X-Curviate-Beta` header. It is the third of three independent
123
+ 403 entitlement refusals, and nothing about it involves the seat or the
124
+ LinkedIn subscription.
125
+
126
+ - **`@beta` JSDoc markers on every operation the served document badges beta**,
127
+ which today is the whole Sales Navigator, Recruiter and inbox surface plus the
128
+ two company-inbox reads. Your editor now says so before you call one. The
129
+ badge is a superset of the gate: a badged operation is not necessarily gated,
130
+ so read the code on a refusal rather than inferring it from the badge. The
131
+ markers are derived from the served badge and checked in both directions, so
132
+ they cannot silently drift from it.
133
+
134
+ - **README: "Three refusals, three codes"**, a table separating
135
+ `NO_ACTIVE_SEAT` (fix it in Curviate billing) from
136
+ `LINKEDIN_FEATURE_NOT_SUBSCRIBED` (fix it on LinkedIn) from
137
+ `BETA_NOT_ENABLED` (a human opts the workspace in), because all three are 403
138
+ and all three read alike in prose. It also states that request validation runs
139
+ before every entitlement check, so an `INVALID_REQUEST` says nothing about
140
+ entitlement, and a 403 from one of the three proves the request itself
141
+ validated cleanly.
142
+
143
+ - **README: "Beta operations"**, on what the badge means and how it differs from
144
+ the gate.
145
+
146
+ ### Changed
147
+
148
+ - **Sales Navigator and Recruiter JSDoc no longer describes a Curviate-side
149
+ entitlement.** Neither namespace has a product to buy and neither carries a
150
+ tier: one ordinary paid seat entitles every method. What they do need is the
151
+ LinkedIn account's own subscription, and an account whose LinkedIn lacks it
152
+ refuses `LINKEDIN_FEATURE_NOT_SUBSCRIBED` with a remedy on LinkedIn rather
153
+ than in Curviate billing. The namespace headers now say that instead of naming
154
+ a tier.
155
+
156
+ - **`auth.intent()` connect scope is no longer seat-derived.** The connect asks
157
+ for every product (classic, company, sales_navigator, recruiter) and LinkedIn
158
+ activates whichever ones the account actually holds; `requested_products` on
159
+ the account reports what was ASKED for, not what is attached. The optional
160
+ `linkedin_premium` (`"sales_navigator"` | `"recruiter"`) narrows one
161
+ connection, and **is never remembered**: a later connect or reconnect that
162
+ omits it widens the scope back to every product, so it has to be restated on
163
+ every call where Recruiter must win. `trial_premium` is gone from the connect
164
+ surface.
165
+
166
+ - **The taxonomy is no longer documented as additive-only.** It tracks the
167
+ served document: a code the API stops returning is removed and a renamed code
168
+ is renamed, because a dead code in the union is worse than a breaking change
169
+ a caller can see. Such changes are called out here, as this entry does.
170
+
171
+ ---
172
+
10
173
  ## [0.29.0] - 2026-09-07
11
174
 
12
175
  Fixture and types regenerated against the deployed staging document
13
- (`https://api.staging.curviate.com`, `server_git_sha 1447ffd4...`, 125 paths).
176
+ (`https://api.staging.curviate.com`, 125 paths; source commit in `fixtures/PROVENANCE.json`).
14
177
  Additive only: `safety-policy` now says where `limit_profile` came from, and an
15
178
  operator override can be released without a reconnect.
16
179
 
@@ -55,7 +218,7 @@ operator override can be released without a reconnect.
55
218
  ## [0.28.0] - 2026-09-07
56
219
 
57
220
  Fixture regenerated against the deployed staging document
58
- (`https://api.staging.curviate.com`, `server_git_sha 14fa8234...`, 125 paths).
221
+ (`https://api.staging.curviate.com`, 125 paths; source commit in `fixtures/PROVENANCE.json`).
59
222
  **BREAKING**: `messaging.searchChats` now rejects a `query` shorter than 3
60
223
  characters server-side. Pre-1.0 a breaking change ships as a minor, so a caret
61
224
  range on `0.27.x` will not pick this up.
@@ -97,7 +260,7 @@ range on `0.27.x` will not pick this up.
97
260
  ## [0.27.0] - 2026-09-06
98
261
 
99
262
  Fixture regenerated against the deployed production document
100
- (`https://api.curviate.com`, `server_git_sha 2a86542f...`, 125 paths).
263
+ (`https://api.curviate.com`, 125 paths; source commit in `fixtures/PROVENANCE.json`).
101
264
  **BREAKING**: the `message.delivered` webhook event is gone from the catalogue,
102
265
  from `CurviateEvent`, and from the events a subscription can be created with.
103
266
  Everything else here is additive or description-only. Pre-1.0 a breaking change
@@ -166,7 +329,7 @@ ships as a minor, so a caret range on `0.26.x` will not pick this up.
166
329
  ## [0.26.0] - 2026-09-06
167
330
 
168
331
  Fixture regenerated against the deployed production document
169
- (`https://api.curviate.com`, `server_git_sha bd795601...`, 125 paths).
332
+ (`https://api.curviate.com`, 125 paths; source commit in `fixtures/PROVENANCE.json`).
170
333
  **BREAKING at the type level** for a consumer that reads `row` off an error
171
334
  or a safety warning as a `string`: it is now `string | null` on both. Three
172
335
  response enums also widen, which reds an exhaustive `switch` with a `never`
@@ -559,7 +722,8 @@ version bump. A `0.21.0` would have reached nobody, because a caret on a `0.x`
559
722
  version never resolves the next minor.
560
723
 
561
724
  The generated types are a mirror of the served OpenAPI document. This release
562
- is a regeneration against that document at server commit `686b70f4`; nothing
725
+ is a regeneration against that document, at the commit recorded in
726
+ `fixtures/PROVENANCE.json`; nothing
563
727
  was hand-edited.
564
728
 
565
729
  ### Fixed
@@ -664,9 +828,10 @@ on the next install with no consumer-side version bump.
664
828
 
665
829
  Generated types are rebuilt from the **served** OpenAPI document rather than a
666
830
  hand-refreshed snapshot, and the snapshot itself now records where it came from.
667
- Source of this release: `https://api.staging.curviate.com` at server commit
668
- `1015999221a2dafc505cc565369a40b8ceca1888`, recorded in
669
- `fixtures/PROVENANCE.json`.
831
+ Source of this release: `https://api.staging.curviate.com`. The exact source
832
+ commit is recorded in `fixtures/PROVENANCE.json` rather than here, because this
833
+ file ships inside the published package and an API commit id is not something a
834
+ consumer of it can resolve.
670
835
 
671
836
  ### Changed
672
837
 
package/README.md CHANGED
@@ -144,6 +144,76 @@ exported `ErrorCode` type is the complete set, so `tsc` tells you when a `switch
144
144
 
145
145
  ---
146
146
 
147
+ ## Three refusals, three codes
148
+
149
+ A `403` from this API is one of three completely separate refusals. They read alike
150
+ in prose and they are fixed in three different places, so branch on `err.code` and
151
+ never on the message.
152
+
153
+ | Code | What is missing | Who fixes it, and where |
154
+ |---|---|---|
155
+ | `NO_ACTIVE_SEAT` | Your Curviate workspace has no active paid seat covering the account. | You, in Curviate billing. Buy or attach a seat, then retry. |
156
+ | `LINKEDIN_FEATURE_NOT_SUBSCRIBED` | The LinkedIn account itself lacks the LinkedIn subscription the operation needs, such as Sales Navigator or Recruiter. | The account owner, on LinkedIn. No amount of Curviate billing lifts this one. |
157
+ | `BETA_NOT_ENABLED` | The operation is beta-gated and your workspace has not opted into beta operations. | A human, in Settings ("Allow beta operations"), or per request with the `X-Curviate-Beta` header. |
158
+
159
+ **There is no product tier.** One ordinary paid seat entitles the whole API surface,
160
+ Sales Navigator and Recruiter included. Nothing on a seat names a product, no refusal
161
+ asks you to upgrade a plan, and there is no field on the error saying which tier to
162
+ buy, because there are no tiers to buy. What a premium namespace still needs is the
163
+ LinkedIn account's own subscription, which is the second row above.
164
+
165
+ ```ts
166
+ try {
167
+ await acc.salesNavigator.searchPeople({ keywords: "cto" });
168
+ } catch (err) {
169
+ if (!isCurviateError(err)) throw err;
170
+ switch (err.code) {
171
+ case "NO_ACTIVE_SEAT":
172
+ // Curviate-side. Attach a seat in Billing.
173
+ break;
174
+ case "LINKEDIN_FEATURE_NOT_SUBSCRIBED":
175
+ // LinkedIn-side. The account needs its own Sales Navigator subscription.
176
+ break;
177
+ case "BETA_NOT_ENABLED":
178
+ // Consent-side. A human enables beta in Settings for this workspace.
179
+ break;
180
+ }
181
+ }
182
+ ```
183
+
184
+ **Request validation runs before every entitlement check, so `INVALID_REQUEST` tells
185
+ you nothing about entitlement.** A malformed body is rejected with `400
186
+ INVALID_REQUEST` while the seat, the LinkedIn subscription and beta consent are all
187
+ still unexamined. A caller that reads a `400` as "my request was fine, my entitlement
188
+ is not" has it backwards: fix the request, send it again, and only then does a `403`
189
+ mean anything about what you are entitled to. The reverse holds too, and is the more
190
+ useful half: a `403` from one of the three codes above proves the request itself
191
+ parsed and validated cleanly.
192
+
193
+ ---
194
+
195
+ ## Beta operations
196
+
197
+ Some operations are marked beta, because they have not been exercised against a real
198
+ LinkedIn subscription yet and their response shapes may still move. Every beta method
199
+ in this SDK carries an `@beta` tag in its JSDoc, so your editor tells you before you
200
+ call it; whole namespaces that are beta say so on the namespace as well.
201
+
202
+ The Sales Navigator, Recruiter and `inboxes` surfaces are beta in full today. Beta is
203
+ per operation, not per namespace, so a mostly-stable namespace can carry a beta method:
204
+ `companies.chats()` and `companies.searchChats()` are beta while the rest of
205
+ `companies` is not. Read the tag on the method you are calling rather than inferring it
206
+ from its neighbours.
207
+
208
+ The badge is a superset of the gate: an operation can be badged beta and still be
209
+ callable by anyone. A beta-GATED operation is the narrower set that actually refuses
210
+ `BETA_NOT_ENABLED` until a human enables beta operations for the workspace in
211
+ Settings, or the individual request carries `X-Curviate-Beta: true`. Because the two
212
+ move independently, read the code on a refusal rather than inferring the gate from
213
+ the badge.
214
+
215
+ ---
216
+
147
217
  ## Retrieval modes
148
218
 
149
219
  Some reads can be answered from Curviate's own store instead of a live LinkedIn