@curviate/sdk 0.1.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 ADDED
@@ -0,0 +1,141 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@curviate/sdk` are documented here.
4
+
5
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
+ Versioning: semantic — minor for additive changes, patch for bug fixes; no stability promise before 1.0.
7
+
8
+ ---
9
+
10
+ ## [0.1.0] — 2026-06-21
11
+
12
+ Initial public release.
13
+
14
+ ### Added
15
+
16
+ **Client and auth**
17
+ - `new Curviate({ apiKey })` — configures the SDK with a Bearer API key, base URL (`https://api.curviate.com`), timeout, and max retries.
18
+ - `curviate.account(accountId)` — returns an account-scoped accessor; all LinkedIn operations are tied to a managed account.
19
+
20
+ **Resource surface**
21
+
22
+ - `curviate.accounts.*`:
23
+ - `list(params?)` — list managed accounts with optional `limit` and `cursor`.
24
+ - `get(accountId)` — fetch a single managed account.
25
+ - `link(body)` — connect a LinkedIn account via cookie or API key; returns an account or a checkpoint.
26
+ - `reconnect(accountId, body)` — reconnect a disconnected account.
27
+ - `refresh(accountId)` — force a session refresh.
28
+ - `update(accountId, body)` — update account settings.
29
+ - `disconnect(accountId)` — disconnect a managed account.
30
+ - `submitCheckpoint(body)` — submit a 2FA / CAPTCHA checkpoint challenge.
31
+ - `pollCheckpoint(body)` — poll for checkpoint resolution.
32
+ - `createConnectLink(body)` — generate a hosted connect link.
33
+
34
+ - `curviate.account(id).messaging.*`:
35
+ - `listChats(params?)` — list conversation threads.
36
+ - `getChat(chatId)` — fetch a single chat.
37
+ - `startChat(body)` — start a new conversation.
38
+ - `listMessages(chatId, params?)` — list messages in a chat.
39
+ - `getMessage(messageId)` — fetch a single message.
40
+ - `sendMessage(chatId, body)` — send a message into a chat.
41
+ - `editMessage(messageId, body)` — edit a sent message.
42
+ - `deleteMessage(messageId)` — delete a sent message.
43
+ - `addReaction(messageId, body)` — add an emoji reaction.
44
+ - `getAttachment(messageId, attachmentId)` — download a message attachment.
45
+ - `syncChat(chatId)` — trigger a chat history sync.
46
+ - `syncMessages(params?)` — trigger a full message sync.
47
+ - `sendInMail(body)` — send an InMail.
48
+ - `getInMailBalance(params?)` — fetch InMail credit balance.
49
+
50
+ - `curviate.account(id).profiles.*`:
51
+ - `get(profileId, params?)` — fetch a LinkedIn profile by id or handle.
52
+ - `getMe()` — fetch the profile for the managed account holder.
53
+ - `getCompany(companyId)` — fetch a company profile.
54
+ - `listConnections(params?)` — list the account's connections.
55
+ - `listFollowers(profileId, params?)` — list a profile's followers.
56
+ - `listPosts(profileId, params?)` — list a profile's posts.
57
+ - `listComments(profileId, params?)` — list a profile's post comments.
58
+ - `listReactions(profileId, params?)` — list a profile's post reactions.
59
+ - `endorse(profileId, body)` — endorse skills on a profile.
60
+
61
+ - `curviate.account(id).invites.*`:
62
+ - `send(body)` — send a connection invitation.
63
+ - `listSent(params?)` — list sent invitations.
64
+ - `listReceived(params?)` — list received invitations.
65
+ - `respond(invitationId, body)` — accept or ignore a received invitation.
66
+ - `cancel(invitationId)` — cancel a sent invitation.
67
+
68
+ - `curviate.account(id).posts.*`:
69
+ - `list(params?)` — list posts visible to the account.
70
+ - `get(postId)` — fetch a single post.
71
+ - `create(body)` — create a post.
72
+ - `listComments(postId, params?)` — list comments on a post.
73
+ - `comment(postId, body)` — comment on a post.
74
+ - `listReactions(postId, params?)` — list reactions on a post.
75
+ - `react(postId, body)` — react to a post.
76
+
77
+ - `curviate.account(id).salesNavigator.*`:
78
+ - `searchPeople(body, params?)` — search people via Sales Navigator.
79
+ - `searchCompanies(body, params?)` — search companies via Sales Navigator.
80
+ - `getProfile(identifier, params?)` — fetch a Sales Navigator profile.
81
+ - `getParameters(params)` — fetch available search filter parameters.
82
+ - `saveLead(userId, body)` — save a lead.
83
+ - `startChat(body)` — start a Sales Navigator conversation.
84
+ - `syncMessages(params)` — sync Sales Navigator messages.
85
+
86
+ - `curviate.account(id).recruiter.*`:
87
+ - `searchPeople(body, params?)` — search candidates via LinkedIn Recruiter.
88
+ - `getProfile(identifier, params?)` — fetch a Recruiter candidate profile.
89
+ - `getParameters(params)` — fetch available search filter parameters.
90
+ - `syncMessages(params)` — sync Recruiter messages.
91
+ - `startChat(body)` — start a Recruiter conversation.
92
+ - `listProjects(params?)` — list Recruiter projects.
93
+ - `getProject(projectId)` — fetch a single Recruiter project.
94
+ - `addCandidate(userId, body)` — add a candidate to a project.
95
+ - `addApplicant(userId, body)` — add an applicant to a job.
96
+ - `rejectApplicant(userId, body)` — reject an applicant.
97
+ - `listJobs(params?)` — list Recruiter job postings.
98
+ - `createJob(body)` — create a Recruiter job posting.
99
+ - `publishJob(jobId, body)` — publish a Recruiter job posting.
100
+ - `solveJobCheckpoint(jobId, body)` — solve a job-posting checkpoint.
101
+ - `listApplicants(jobId, params?)` — list applicants for a job.
102
+ - `getApplicant(applicantId)` — fetch a single applicant.
103
+ - `downloadResume(applicantId)` — download an applicant's resume.
104
+
105
+ - `curviate.account(id).search.*` (classic LinkedIn search):
106
+ - `people(body)` — search people.
107
+ - `companies(body)` — search companies.
108
+ - `posts(body)` — search posts.
109
+ - `jobs(body)` — search jobs.
110
+ - `getParameters(query)` — fetch available search filter parameters.
111
+
112
+ - `curviate.webhooks.*`:
113
+ - `create(body)` — register a new webhook endpoint.
114
+ - `list(params?)` — list registered webhooks.
115
+ - `update(id, body)` — update a webhook endpoint.
116
+ - `delete(id)` — delete a webhook endpoint.
117
+ - `listEvents()` — list available webhook event types.
118
+ - `getStateDiff(accountId, params?)` — fetch an account state diff.
119
+
120
+ **Typed error model**
121
+ - `CurviateError` — single thrown type for all API errors, carrying a stable `code`, `httpStatus`, `retryHint`, `userFixable`, `retryLikelyToSucceed`, and `requiredTier`.
122
+ - `isCurviateError(err)` — type guard for narrowing in `catch` blocks.
123
+ - 34-member `ErrorCode` union covering every observable API error code.
124
+
125
+ **HTTP transport with retry and backoff**
126
+ - Exponential backoff with jitter for retryable GET/HEAD errors (configurable `maxRetries`).
127
+ - `Retry-After` header respected on 429 responses.
128
+ - Per-attempt abort controller timeout.
129
+ - JSON and binary (ArrayBuffer) response parsing.
130
+
131
+ **Cursor pagination**
132
+ - `curviate.paginate(method, params?)` — async iterator that follows the `cursor` field automatically.
133
+
134
+ **Webhook signature verification**
135
+ - `constructEvent(rawBody, signatureHeader, secret, opts?)` — verifies `HMAC-SHA256` webhook signatures using Web Crypto (`globalThis.crypto.subtle`); constant-time comparison prevents timing attacks; configurable replay window via `replayWindowSecs` (default 300 s). Always async — always `await` it.
136
+ - `WebhookSignatureError` — thrown on malformed header, invalid signature, or replay; not a `CurviateError`.
137
+ - `CurviateEvent` — 19-member discriminated union covering all canonical webhook event types.
138
+
139
+ **TypeScript types**
140
+ - Full OpenAPI-generated types at `src/generated/types.ts` — request bodies, responses, and path params.
141
+ - ESM-only build targeting ES2020; works in Node 18+, Cloudflare Workers, Vercel Edge.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Redmer Holding GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # @curviate/sdk
2
+
3
+ The official TypeScript SDK for the [Curviate API](https://docs.curviate.com) — agent-native
4
+ LinkedIn infrastructure for AI engineers and agent builders.
5
+
6
+ > **Status:** `0.1.0` — pre-GA. The surface is public but not yet stability-promised.
7
+
8
+ ---
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @curviate/sdk
14
+ ```
15
+
16
+ Requires Node 18+. Works in Cloudflare Workers and Vercel Edge.
17
+
18
+ ---
19
+
20
+ ## Auth — construct the client
21
+
22
+ Get your API key from the Curviate dashboard. Store it as an environment variable.
23
+
24
+ ```ts
25
+ import { Curviate } from "@curviate/sdk";
26
+
27
+ const curviate = new Curviate({
28
+ apiKey: process.env.CURVIATE_API_KEY!,
29
+ // Optional:
30
+ // baseUrl: "https://api.curviate.com", // default
31
+ // timeout: 30_000, // per-attempt timeout, ms
32
+ // maxRetries: 3, // GET/HEAD retries with backoff
33
+ });
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Account-scoped accessor
39
+
40
+ Every LinkedIn operation (messages, profiles, invites, posts) is tied to a **managed account** — a LinkedIn session you have connected via `curviate.accounts.link()`. The `curviate.account(id)` accessor fixes the `account_id` on every call so you do not have to thread it manually:
41
+
42
+ ```ts
43
+ // Root-level: tenant-wide operations
44
+ const { items: accounts } = await curviate.accounts.list();
45
+
46
+ // Account-scoped: all LinkedIn ops under a specific account
47
+ const acc = curviate.account(accounts?.[0]?.account_id ?? "");
48
+
49
+ // Now every resource call is scoped to that account:
50
+ const { items: chats } = await acc.messaging.listChats();
51
+ const profile = await acc.profiles.get("some-profile-id");
52
+ ```
53
+
54
+ ---
55
+
56
+ ## First end-to-end call
57
+
58
+ This snippet lists your connected accounts, picks the first one, and sends a message — something an agent might do to automate outreach:
59
+
60
+ ```ts
61
+ import { Curviate, isCurviateError } from "@curviate/sdk";
62
+
63
+ const curviate = new Curviate({ apiKey: process.env.CURVIATE_API_KEY! });
64
+
65
+ async function sendFirstMessage() {
66
+ // 1. List connected accounts
67
+ const { items: accounts } = await curviate.accounts.list();
68
+ if (!accounts || accounts.length === 0) throw new Error("No active accounts.");
69
+
70
+ const first = accounts[0]!;
71
+ const acc = curviate.account(first.account_id ?? "");
72
+
73
+ // 2. List recent chats
74
+ const { items: chats } = await acc.messaging.listChats({ limit: 5 });
75
+ if (!chats || chats.length === 0) return;
76
+
77
+ // 3. Send a message to the first chat
78
+ const chat = chats[0]!;
79
+ await acc.messaging.sendMessage(chat.id ?? "", {
80
+ text: "Hi — following up from our conversation.",
81
+ });
82
+
83
+ console.log("Message sent.");
84
+ }
85
+
86
+ sendFirstMessage().catch(console.error);
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Typed error handling
92
+
93
+ Every API error is a `CurviateError`. Use `isCurviateError` to narrow in `catch`, then switch on `err.code` for exhaustive handling:
94
+
95
+ ```ts
96
+ import { isCurviateError } from "@curviate/sdk";
97
+
98
+ try {
99
+ await acc.messaging.sendMessage("c_123", { text: "hello" });
100
+ } catch (err) {
101
+ if (!isCurviateError(err)) throw err; // re-throw network errors etc.
102
+
103
+ switch (err.code) {
104
+ case "RECIPIENT_UNREACHABLE":
105
+ console.warn("Recipient can't receive messages.");
106
+ break;
107
+ case "RATE_LIMIT_ACCOUNT":
108
+ // err.retryAfterMs tells you exactly how long to wait
109
+ await sleep(err.retryAfterMs ?? 5_000);
110
+ break;
111
+ case "ACCOUNT_NOT_FOUND":
112
+ console.error("Account does not exist for this tenant.");
113
+ break;
114
+ default:
115
+ if (err.retryLikelyToSucceed) {
116
+ // Safe to retry — server-side transient error
117
+ await retry();
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ All 34 error codes are documented in the [API reference](https://docs.curviate.com).
124
+
125
+ ---
126
+
127
+ ## Cursor pagination
128
+
129
+ Resources that return lists support cursor pagination. `curviate.paginate()` is an async iterator that follows the `cursor` field automatically — pull items one at a time without managing cursors:
130
+
131
+ ```ts
132
+ // Iterate over every chat across all pages
133
+ for await (const chat of curviate.paginate(acc.messaging.listChats.bind(acc.messaging), {})) {
134
+ console.log(chat.id, chat.unread_count);
135
+ }
136
+
137
+ // With initial params
138
+ for await (const account of curviate.paginate(
139
+ curviate.accounts.list.bind(curviate.accounts),
140
+ { limit: 50 },
141
+ )) {
142
+ console.log(account.account_id);
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Webhook verification
149
+
150
+ Register a webhook to receive real-time events, then verify each delivery with `constructEvent`:
151
+
152
+ ```ts
153
+ import { constructEvent, WebhookSignatureError } from "@curviate/sdk";
154
+
155
+ // Express (Node 18+)
156
+ app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
157
+ const sig = req.headers["x-curviate-signature"] as string;
158
+ const secret = process.env.CURVIATE_WEBHOOK_SECRET!;
159
+
160
+ let event;
161
+ try {
162
+ event = await constructEvent(req.body, sig, secret);
163
+ } catch (err) {
164
+ if (err instanceof WebhookSignatureError) {
165
+ console.warn("Bad webhook:", err.reason); // 'invalid_signature' | 'replay_detected' | 'malformed_header'
166
+ return res.sendStatus(400);
167
+ }
168
+ throw err;
169
+ }
170
+
171
+ switch (event.type) {
172
+ case "message.received":
173
+ // event.data is MessagePayload — account_id, message_id, etc.
174
+ handleNewMessage(event.data);
175
+ break;
176
+ case "account.connected":
177
+ handleAccountConnected(event.data);
178
+ break;
179
+ // 17 more event types — all in the CurviateEvent union
180
+ }
181
+
182
+ res.sendStatus(200);
183
+ });
184
+
185
+ // Hono / Vercel Edge — always await (Web Crypto is async)
186
+ app.post("/webhook", async (c) => {
187
+ const rawBody = await c.req.text();
188
+ const event = await constructEvent(rawBody, c.req.header("x-curviate-signature")!, secret);
189
+ return c.text("ok");
190
+ });
191
+ ```
192
+
193
+ `constructEvent` always returns a `Promise<CurviateEvent>` — always `await` it.
194
+
195
+ `WebhookSignatureError` is NOT a `CurviateError` — narrow with `instanceof WebhookSignatureError`.
196
+
197
+ ---
198
+
199
+ ## Links
200
+
201
+ - API reference: https://docs.curviate.com
202
+ - Issues: https://github.com/curviate/sdk/issues
203
+ - Changelog: [CHANGELOG.md](./CHANGELOG.md)
204
+
205
+ ## License
206
+
207
+ MIT © Redmer Holding GmbH