@curviate/sdk 0.14.1 → 0.16.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 +185 -0
- package/README.md +6 -5
- package/dist/index.d.ts +26230 -16263
- package/dist/index.js +1505 -751
- package/package.json +1 -1
- package/src/generated/types.ts +24882 -15771
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,191 @@ Versioning: semantic — minor for additive changes, patch for bug fixes; no sta
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [0.16.0] — 2026-07-17
|
|
13
|
+
|
|
14
|
+
Mostly additive, plus one breaking removal on the connect/reconnect body.
|
|
15
|
+
|
|
16
|
+
### Breaking
|
|
17
|
+
|
|
18
|
+
- **`disabled_features` is removed from `auth.intent()`'s request body**
|
|
19
|
+
(`AuthIntentBody`). The negative-list model could not express the `company`
|
|
20
|
+
product or the one-Premium-per-profile XOR. Connection scope (which
|
|
21
|
+
LinkedIn products get synced) is now **seat-derived**: there is no products
|
|
22
|
+
input on connect/reconnect at all. Drop any `disabled_features` you were
|
|
23
|
+
passing: a body still carrying it is now rejected 400 by the server. The
|
|
24
|
+
scope actually recorded for an account reads back as the new
|
|
25
|
+
`requested_products` field (see Added, below). A reconnect that changes
|
|
26
|
+
scope must use `auth_method: "credentials"`, since a cookie replay cannot
|
|
27
|
+
change scope and now throws `CurviateError(code: "REAUTH_REQUIRED")`.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **New account-scoped `inboxes` namespace (2 methods, Beta), the reply-as-a-page
|
|
32
|
+
surface.** `inboxes.list(query?)` discovers the account's personal inbox
|
|
33
|
+
plus, when the company product is attached, one entry per company page (id
|
|
34
|
+
like `"COMPANY_83734124_PRIMARY"`). `inboxes.listChats(inboxId, query?)`
|
|
35
|
+
lists a single inbox's conversations, cursor-paginated. Every returned chat
|
|
36
|
+
`id` is send-ready: pass it straight to the existing `messaging.sendMessage()`
|
|
37
|
+
to reply, no separate start/send endpoint. Company pages are reply-only
|
|
38
|
+
(`reply_only: true`) and cannot start a new conversation. **Beta:**
|
|
39
|
+
single-page listing is verified; deep pagination against a busier inbox is
|
|
40
|
+
still being validated.
|
|
41
|
+
- **`sendMessage()` echoes the acting identity as `sent_as`,** additive on
|
|
42
|
+
the existing send-message response. A `COMPANY_` chat id (from
|
|
43
|
+
`inboxes.listChats()`) sends AS THE PAGE and echoes
|
|
44
|
+
`sent_as: { kind: "company", company_id, name }` (`company_id` is `null`
|
|
45
|
+
when the page could not be correlated to a managed page). Any other chat id
|
|
46
|
+
sends as the connected member and echoes `sent_as: { kind: "personal" }`.
|
|
47
|
+
Never infer the acting identity from a message's `sender` field, only from
|
|
48
|
+
`sent_as`.
|
|
49
|
+
- **`accounts.list()` / `accounts.get()` gain `requested_products`,** the
|
|
50
|
+
seat-derived connection scope (e.g. `["classic", "company",
|
|
51
|
+
"sales_navigator"]`) the account was last connected with. `null` for
|
|
52
|
+
accounts connected before this was recorded, and not attachment truth for
|
|
53
|
+
Company Pages (that is decidable only via `inboxes.list()`).
|
|
54
|
+
- **Two new error codes:** `PREMIUM_CONFLICT` (a seat resolving to both
|
|
55
|
+
individual-Premium tiers at once, since LinkedIn permits only one per
|
|
56
|
+
profile; `user_fixable`, never retryable) and `REAUTH_REQUIRED` (a
|
|
57
|
+
scope-changing reconnect attempted with a cookie instead of credentials;
|
|
58
|
+
`user_fixable`, never retryable).
|
|
59
|
+
- **New account-scoped `profile` namespace (4 methods)** — the connected
|
|
60
|
+
account's own insight surface: `profile.subscription()`, `profile.analytics()`,
|
|
61
|
+
`profile.visitors(query?)`, `profile.ssi()`. Distinct from the retired
|
|
62
|
+
`profiles` namespace (renamed to `users` in 0.15.0).
|
|
63
|
+
- **New account-scoped `groups` namespace (3 methods)** — `groups.list(query?)`
|
|
64
|
+
(own groups by default, or another member's via `{ profile }`),
|
|
65
|
+
`groups.get(group)`, and `groups.members(group, query?)` (with the folded-in
|
|
66
|
+
`{ name }` member search).
|
|
67
|
+
- **New account-scoped `feed` namespace (1 method)** — `feed.home(query?)` reads
|
|
68
|
+
the connected account's home feed as agent-actionable posts, with `relevant`
|
|
69
|
+
or `recent` sort orders.
|
|
70
|
+
- **`companies` gains 3 company-insights methods** — `companies.managed(query?)`
|
|
71
|
+
lists the pages the connected account administers; `companies.followers(identifier,
|
|
72
|
+
query?)` lists a page's followers (**re-added** under a different item shape —
|
|
73
|
+
`company_follower`, carrying `degree`/`followed_at` — than the pre-0.15.0
|
|
74
|
+
method of the same name); `companies.invitableFollowers(identifier, query?)`
|
|
75
|
+
lists connections invitable to follow the page. All three require the
|
|
76
|
+
connected account to administer the target page.
|
|
77
|
+
- **`companies` gains 5 Beta company-inbox methods** — `companies.chats(identifier,
|
|
78
|
+
query?)`, `companies.chat(identifier, chatId)`, `companies.messages(identifier,
|
|
79
|
+
chatId, query?)`, `companies.message(identifier, chatId, messageId)`, and
|
|
80
|
+
`companies.searchChats(identifier, query?)` (exactly one of `query`/`topic`/
|
|
81
|
+
`unread` per call). A distinct conversation surface from the account's own
|
|
82
|
+
`messaging` namespace, scoped to one administered company page. **Beta:**
|
|
83
|
+
single-page listing and termination are verified; deep pagination (many
|
|
84
|
+
pages / large cursor round-trips) is still being validated against a busier
|
|
85
|
+
inbox. `companies` is now 12 methods (was 4).
|
|
86
|
+
- **`search` gains 3 methods** — `search.groups(query)` searches LinkedIn
|
|
87
|
+
groups by keyword; `search.services(body & query)` searches Services
|
|
88
|
+
Marketplace providers with structured filters; `search.getServiceParameters(query)`
|
|
89
|
+
resolves human-readable service-category/location terms into the opaque
|
|
90
|
+
filter ids `services()` accepts. `search` is now 9 methods (was 6).
|
|
91
|
+
- **`messaging` gains 1 method** — `messaging.searchChats(params)` free-text
|
|
92
|
+
searches the account's own inbox (participant names and message content).
|
|
93
|
+
`messaging` is now 13 methods (was 12).
|
|
94
|
+
- **`posts` gains 3 saved-posts methods** — `posts.listSaved(query?)` lists the
|
|
95
|
+
connected account's own saved posts (a self resource — previews only,
|
|
96
|
+
`snippet` capped at ≤140 chars, never the full body); `posts.save(postId)`
|
|
97
|
+
and `posts.unsave(postId)` add/remove a bookmark, both idempotent and
|
|
98
|
+
accepting either `urn:li:activity:<id>` or a bare numeric id. `posts` is now
|
|
99
|
+
12 methods (was 9).
|
|
100
|
+
- **New account-scoped `notifications` namespace (3 methods)** — the connected
|
|
101
|
+
account's own notification centre: `notifications.list(query?)` (cards +
|
|
102
|
+
the account-level `unread_count`/`latest_published_at`);
|
|
103
|
+
`notifications.delete(cardUrn)` and `notifications.showLess(cardUrn)`, two
|
|
104
|
+
self-action writes on the account's own cards. Both writes are idempotent
|
|
105
|
+
and take effect within a few seconds — a list read immediately after may
|
|
106
|
+
still show the card for a moment, which is not a failure signal. The SDK
|
|
107
|
+
percent-encodes `cardUrn` (which embeds `(`, `)`, `:`, `,`) into the path.
|
|
108
|
+
- **Parity pin: 143 methods across 18 namespaces** (was 135 / 16 at the start
|
|
109
|
+
of this release cycle). The `inboxes` namespace is the final addition.
|
|
110
|
+
|
|
111
|
+
## [0.15.0] — 2026-07-11
|
|
112
|
+
|
|
113
|
+
Full v2 parity. The SDK is re-aligned 1:1 to the served API surface: every
|
|
114
|
+
operation is now exactly one method at the exact wire encoding, the entire
|
|
115
|
+
account-scoped surface moves to the account-first path grammar, and three
|
|
116
|
+
namespaces are reorganized. This is a **breaking** release touching nearly
|
|
117
|
+
every namespace. The five breaking categories below — removals, namespace
|
|
118
|
+
reorganization, method renames/relocations, request/response shape changes, and
|
|
119
|
+
the account-first path-grammar migration — are the complete set of breaks a
|
|
120
|
+
`0.14.1` → `0.15.0` upgrade must reconcile, gathered here so a consumer finds
|
|
121
|
+
every break in one place.
|
|
122
|
+
|
|
123
|
+
### Removed (BREAKING)
|
|
124
|
+
|
|
125
|
+
- **14 methods that map to no served operation are removed, with no alias:**
|
|
126
|
+
- `accounts.createConnectLink()`, `accounts.createReconnectLink()`, `accounts.reconnect()` — hosted connect/reconnect link minting and in-place reconnect are no longer part of the API surface.
|
|
127
|
+
- `messaging.syncChat()`, `messaging.syncMessages()`, `recruiter.syncMessages()`, `salesNavigator.syncMessages()` — explicit sync operations are gone; accounts sync continuously and deliver via webhooks.
|
|
128
|
+
- `posts.list()` — the standalone feed list has no served operation (use `posts.listUserPosts(userId)` for a member's posts).
|
|
129
|
+
- `companies.followers()` — no served operation.
|
|
130
|
+
- `recruiter.addApplicant()`, `recruiter.rejectApplicant()`, `recruiter.solveJobCheckpoint()` — superseded by the project-centric pipeline surface.
|
|
131
|
+
- `webhooks.getStateDiff()` — no served operation.
|
|
132
|
+
- **`invites.respond()` is removed** — split into two dedicated bodyless methods, `invites.accept(invitationId)` and `invites.decline(invitationId)` (see below).
|
|
133
|
+
- **`profiles.getCompany()` stays removed** — dropped ahead of this release with no return; company reads live on the `companies` namespace.
|
|
134
|
+
|
|
135
|
+
### Changed (BREAKING) — namespaces reorganized
|
|
136
|
+
|
|
137
|
+
- **`profiles` namespace renamed to `users`.** Every `curviate.account(id).profiles.*` call becomes `…users.*`; the served group is "Users" and every path is `/users/{user_id}`.
|
|
138
|
+
- **New root-scoped `auth` namespace, split out of `accounts`.** The connect/checkpoint operations move off `accounts`: `accounts.link` → `auth.intent`, `accounts.solveCheckpoint` → `auth.solveCheckpoint`, plus `auth.requestCheckpoint`, `auth.pollCheckpoint`, and `accounts.getConnectSession` → `auth.getSession(sessionId)`. `accounts` now carries only `list` / `get` / `update` / `disconnect`.
|
|
139
|
+
- **New account-scoped `comments` namespace.** The comment-thread surface (create / edit / delete / reply / list replies / reactions) plus the relocated `listUserComments` live here rather than on `posts`.
|
|
140
|
+
- **Root and account surfaces are now strictly disjoint.** The root client exposes only `accounts`, `auth`, `webhooks`. Every other namespace is reachable exclusively through `curviate.account(id)` — the account-scoped namespaces are no longer mounted on the root client (they cannot build a valid path without a bound `account_id`).
|
|
141
|
+
|
|
142
|
+
### Changed (BREAKING) — methods renamed / relocated
|
|
143
|
+
|
|
144
|
+
- `profiles.endorse(userId, { skill_endorsement_id })` → **`users.endorseSkill(userId, { endorsement_id })`** — renamed, and the body key changed from `skill_endorsement_id` to `endorsement_id`.
|
|
145
|
+
- `profiles.listConnections()` → **`users.listRelations()`**.
|
|
146
|
+
- `messaging.getInMailBalance()` → **`users.getInMailCredits()`** (relocated onto `users`).
|
|
147
|
+
- `posts.comment(postId, …)` → **`comments.create(postId, …)`**.
|
|
148
|
+
- `profiles.listComments(userId)` → **`comments.listUserComments(userId)`**.
|
|
149
|
+
- `profiles.listPosts(userId)` → **`posts.listUserPosts(userId)`**.
|
|
150
|
+
- `profiles.listReactions(userId)` → **`posts.listUserReactions(userId)`**.
|
|
151
|
+
- `invites.respond(...)` → **`invites.accept(invitationId)` / `invites.decline(invitationId)`** (split; both bodyless).
|
|
152
|
+
- `recruiter.addCandidate(...)` → **`recruiter.saveCandidate(projectId, { stage_id, candidate_id })`**.
|
|
153
|
+
- `recruiter.getParameters(...)` (GET) → **`recruiter.searchParameters(body)` (POST)** — the HTTP verb changed GET→POST, with a `source`-discriminated body.
|
|
154
|
+
- `recruiter.listProjectJobs(projectId)` → **`recruiter.getProjectJob(projectId)`** — the operation returns the single attached job posting, not a list.
|
|
155
|
+
|
|
156
|
+
### Changed (BREAKING) — request / response shapes
|
|
157
|
+
|
|
158
|
+
- **`users.update(userId, body)`** never sends `description`; accepted keys are `{ first_name, last_name, headline, bio, skills, picture, background_picture }`.
|
|
159
|
+
- **`users.endorseSkill`** body key is `endorsement_id` (was `skill_endorsement_id`).
|
|
160
|
+
- **`jobs.publish` / `recruiter.publishJob`** take a `mode`-discriminated body (`FREE` | `PROMOTED` | `PROMOTED_PLUS`; the `PROMOTED*` modes require a `budget { currency, amount, scope }`) and respond `{ object, job_state }`.
|
|
161
|
+
- **`jobs.close` / `recruiter.closeJob`** are bodyless POSTs responding `{ object }`.
|
|
162
|
+
- **`invites.cancel`** withdrawal now discriminates on `invitation_withdrawn`.
|
|
163
|
+
- **Message operations are re-homed under the chat.** `getMessage` / `editMessage` / `deleteMessage` / `addReaction` / `getAttachment` now path under `/chats/{chat_id}/messages/…` and take `chatId` as the leading argument.
|
|
164
|
+
- **`messaging.sendInMail`** body drops the `surface` field.
|
|
165
|
+
- **Media-bearing writes moved from multipart to JSON + base64** where the surface retired multipart (recruiter, sales-navigator, posts, messaging); read each operation's declared content-type — some remain multipart.
|
|
166
|
+
- **Sales Navigator `saveAccount` / `saveLead` bodies shrank** to the minimal saved-entity shape.
|
|
167
|
+
- **`recruiter.startChat`** now requires a `signature` (alongside `attendees_ids`, `text`, `subject`).
|
|
168
|
+
- **`recruiter.createJob`** now requires `project_name` and responds `{ job_id, object, project_id }` (was a full job object).
|
|
169
|
+
- **`recruiter.saveCandidate`** body is `{ stage_id, candidate_id }`.
|
|
170
|
+
|
|
171
|
+
### Changed (BREAKING) — account-first path grammar
|
|
172
|
+
|
|
173
|
+
- **Every account-scoped method now carries `account_id` as the leading path segment** (`/v1/{account_id}/…`), never as a query parameter, body field, or omitted. Bind it once with `curviate.account(id)` and it is injected on every call. Companies and relations, which previously carried `account_id` as a query argument, drop it entirely.
|
|
174
|
+
|
|
175
|
+
### Added
|
|
176
|
+
|
|
177
|
+
- **`comments` namespace (9 methods):** `listUserComments`, `create`, `edit`, `delete`, `reply`, `listReplies`, `listReactions`, `addReaction`, `removeReaction` — `removeReaction` is a DELETE that carries a `{ reaction }` body.
|
|
178
|
+
- **`jobs` write surface (9 new methods):** `list`, `create`, `update`, `getBudget`, `publish`, `close`, `listApplicants`, `getApplicant`, `downloadResume` (binary `ArrayBuffer`). `jobs.create` takes object-shaped `job_title` / `company` and a `method`-discriminated `apply_method`.
|
|
179
|
+
- **Project-centric `recruiter` surface (9 new methods):** `searchTalentPool`, `searchFromUrl`, `updateProject`, `listPipeline`, `getProjectJob`, `createProjectJob`, `getProjectJobBudget`, `updateProjectJob`, `closeJob`, with `getApplicant` / `downloadResume` re-homed under the project scope.
|
|
180
|
+
- **`users.follow` / `users.unfollow`** (bodyless POST / DELETE), **`users.listFollowing`**, **`users.update`**.
|
|
181
|
+
- **`messaging.markChatRead(chatId, { read })`**.
|
|
182
|
+
- **`search.fromUrl({ url })`** and **`salesNavigator.searchFromUrl({ url })`** — resolve a LinkedIn search-results URL into a search.
|
|
183
|
+
- **`posts.delete`** (bodyless, 204) and **`posts.unreact(postId, { reaction })`** (DELETE with body).
|
|
184
|
+
- **`users.get(userId)` accepts `'me'`** — `users.get('me')` reads the caller's own profile (folds in the old `getMe`).
|
|
185
|
+
- **New `ErrorCode` value: `LINKEDIN_OPERATION_NOT_SUPPORTED`.** The `422` a permanent LinkedIn platform limitation for the attempted operation returns (e.g. listing a non-self user's following list) — `user_fixable: true`, `retry_likely_to_succeed: false` (not a transient failure; retrying will not help). Added to the `ErrorCode` union and the transport's known-code set — previously this narrowed to `INTERNAL`, which is retryable.
|
|
186
|
+
|
|
187
|
+
### Fixed
|
|
188
|
+
|
|
189
|
+
- **`CONNECTION_REQUEST_CONFLICT` was silently downgraded to `INTERNAL`.** The `409` the API returns when a connect-request to a member already exists (or you are already a first-degree connection) is a real, documented error code, but the transport's runtime known-code set omitted it — so callers received `code: "INTERNAL"` with the wrong semantics (`INTERNAL` is retryable, whereas this conflict is `user_fixable: true`, `retry_likely_to_succeed: false` and must never be re-sent). The transport now decodes `CONNECTION_REQUEST_CONFLICT` to itself. To eliminate the class of bug: the `ErrorCode` type and the transport's runtime known-code set are now both **derived from one source array**, so the code a caller narrows on and the code the transport recognizes can never drift apart again.
|
|
190
|
+
- **`RATE_LIMITED` was silently downgraded to `INTERNAL`.** The `429` the Recruiter and Sales Navigator read surface returns under LinkedIn-platform throttling (dedicated `RateLimit-Policy` / `RateLimit` / `Retry-After` response headers, `retry_likely_to_succeed: true`) is a real, documented error code, but the transport's runtime known-code set omitted it — so callers received `code: "INTERNAL"`, which happened to auto-retry on `GET`/`HEAD` by accident but with the wrong (generic backoff) delay instead of honoring `Retry-After`. The transport now decodes `RATE_LIMITED` to itself and retries it on `GET`/`HEAD` like the other rate-limit codes, honoring `Retry-After`.
|
|
191
|
+
- **`users.update` forwarded an unsupported `description` key if a caller smuggled one in.** The `UserUpdateBody` type never declared `description`, so a typed caller was already rejected at compile time — but an untyped/JS caller (or an `as`-cast) could still put it on the object, and `update()` forwarded the body verbatim with no runtime check. `update()` now strips `description` from the outgoing payload before the request is sent, regardless of caller strictness.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
10
195
|
## [0.14.1] — 2026-07-07
|
|
11
196
|
|
|
12
197
|
### Fixed
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The official TypeScript SDK for the [Curviate API](https://docs.curviate.com) — agent-native
|
|
4
4
|
LinkedIn infrastructure for AI engineers and agent builders.
|
|
5
5
|
|
|
6
|
-
> **Status:** `0.
|
|
6
|
+
> **Status:** `0.16.0` — pre-1.0. Full v2 API parity; the surface is public but not yet stability-promised.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -37,10 +37,10 @@ const curviate = new Curviate({
|
|
|
37
37
|
|
|
38
38
|
## Account-scoped accessor
|
|
39
39
|
|
|
40
|
-
Every LinkedIn operation (messages, profiles, invites, posts) is tied to a **managed account** — a LinkedIn session you have connected via `curviate.
|
|
40
|
+
Every LinkedIn operation (messages, member profiles, invites, posts) is tied to a **managed account** — a LinkedIn session you have connected via the connect flow (`curviate.auth.intent()`). The `curviate.account(id)` accessor fixes the `account_id` on every call so you do not have to thread it manually:
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
-
// Root-level: tenant-wide operations
|
|
43
|
+
// Root-level: tenant-wide operations (accounts, auth, webhooks)
|
|
44
44
|
const { items: accounts } = await curviate.accounts.list();
|
|
45
45
|
|
|
46
46
|
// Account-scoped: all LinkedIn ops under a specific account
|
|
@@ -48,7 +48,8 @@ const acc = curviate.account(accounts?.[0]?.account_id ?? "");
|
|
|
48
48
|
|
|
49
49
|
// Now every resource call is scoped to that account:
|
|
50
50
|
const { items: chats } = await acc.messaging.listChats();
|
|
51
|
-
const
|
|
51
|
+
const me = await acc.users.get("me"); // your own profile
|
|
52
|
+
const profile = await acc.users.get("some-user-id"); // someone else's
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
---
|
|
@@ -131,7 +132,7 @@ Resources that return lists support cursor pagination. `curviate.paginate()` is
|
|
|
131
132
|
```ts
|
|
132
133
|
// Iterate over every chat across all pages
|
|
133
134
|
for await (const chat of curviate.paginate(acc.messaging.listChats.bind(acc.messaging), {})) {
|
|
134
|
-
console.log(chat.id
|
|
135
|
+
console.log(chat.id);
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
// With initial params
|