@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/dist/index.js CHANGED
@@ -1,4 +1,82 @@
1
1
  // src/errors.ts
2
+ var ERROR_CODES = [
3
+ // Authentication / authorization
4
+ "UNAUTHORIZED",
5
+ "INVALID_REQUEST",
6
+ "UNSUPPORTED_MEDIA_TYPE",
7
+ "PAYLOAD_TOO_LARGE",
8
+ // Account state
9
+ "ACCOUNT_NOT_FOUND",
10
+ "ACCOUNT_RESTRICTED",
11
+ // Duplicate connect — reconnect or adopt the existing account instead of
12
+ // linking again. Not retryable.
13
+ "ACCOUNT_ALREADY_LINKED",
14
+ "RESOURCE_NOT_FOUND",
15
+ "RESOURCE_ACCESS_RESTRICTED",
16
+ // Tier / subscription gating
17
+ "TIER_NOT_ACTIVE",
18
+ "LINKEDIN_FEATURE_NOT_SUBSCRIBED",
19
+ // Rate limits
20
+ "RATE_LIMIT_ACCOUNT",
21
+ "RATE_LIMIT_TENANT",
22
+ "PLATFORM_RATE_LIMIT",
23
+ // LinkedIn-platform-level throttling on the Recruiter / Sales Navigator read
24
+ // surface (carries dedicated RateLimit-Policy / RateLimit / Retry-After
25
+ // response headers). Distinct from the account/tenant/platform trio above.
26
+ // Always retry-safe (retry_likely_to_succeed: true) — see RETRYABLE_CODES.
27
+ "RATE_LIMITED",
28
+ // Platform errors
29
+ "PLATFORM_ERROR",
30
+ "PLATFORM_NOT_IMPLEMENTED",
31
+ // Permanent LinkedIn platform limitation for the attempted operation (e.g.
32
+ // listing a non-self user's following list). Not a transient failure —
33
+ // retrying will not help.
34
+ "LINKEDIN_OPERATION_NOT_SUPPORTED",
35
+ // Checkpoint (account connect flow)
36
+ "CHECKPOINT_NOT_FOUND",
37
+ "CHECKPOINT_EXPIRED",
38
+ "CHECKPOINT_INVALID_CODE",
39
+ "CHECKPOINT_MAX_ATTEMPTS",
40
+ "CHECKPOINT_ALREADY_RESOLVED",
41
+ "CHECKPOINT_UNSUPPORTED",
42
+ "CONNECTION_IN_PROGRESS",
43
+ // One-premium boundary rejection — LinkedIn permits only one individual
44
+ // Premium subscription per profile. Surfaces on connect/reconnect (a seat
45
+ // resolving to both premiums) and on the billing seat/tier endpoints (a
46
+ // "naked enable" of one premium while the seat already holds the other).
47
+ // user_fixable, never retryable — the remedy is two seats, or pairing
48
+ // enable with disable of the current premium in one call.
49
+ "PREMIUM_CONFLICT",
50
+ // A reconnect whose seat-derived scope differs from the account's recorded
51
+ // scope was attempted with cookie auth — a cookie replay cannot change
52
+ // scope, so a full credentials re-authentication is required. user_fixable,
53
+ // never retryable.
54
+ "REAUTH_REQUIRED",
55
+ // LinkedIn-specific connect errors
56
+ "LINKEDIN_AUTH_FAILED",
57
+ "LINKEDIN_RATE_LIMITED",
58
+ "LINKEDIN_COOKIE_INVALID",
59
+ "LINKEDIN_SERVICE_UNAVAILABLE",
60
+ // Messaging / connect-request mutation
61
+ "MESSAGE_WINDOW_EXPIRED",
62
+ "RECIPIENT_UNREACHABLE",
63
+ // Duplicate / already-connected connect-request conflict: a send to a
64
+ // recipient who already has a pending request from this account, or is already
65
+ // a first-degree connection. user_fixable, never retryable — do not re-send.
66
+ "CONNECTION_REQUEST_CONFLICT",
67
+ // Billing (surface: tenant-management)
68
+ "PAYMENT_REQUIRED",
69
+ "PAYMENT_FAILED",
70
+ "SUBSCRIPTION_BUSY",
71
+ "SUBSCRIPTION_NOT_FOUND",
72
+ "SEAT_NOT_FOUND",
73
+ "SEAT_CANCELLED",
74
+ // Generic
75
+ "INTERNAL"
76
+ ];
77
+ var KNOWN_ERROR_CODES = new Set(ERROR_CODES);
78
+ var REQUIRED_TIERS = ["core", "sn", "sales_nav", "recruiter"];
79
+ var KNOWN_REQUIRED_TIERS = new Set(REQUIRED_TIERS);
2
80
  var CurviateError = class _CurviateError extends Error {
3
81
  constructor(init) {
4
82
  super(init.message);
@@ -66,53 +144,10 @@ var RETRYABLE_CODES = /* @__PURE__ */ new Set([
66
144
  "PLATFORM_ERROR",
67
145
  "PLATFORM_RATE_LIMIT",
68
146
  "RATE_LIMIT_ACCOUNT",
69
- "RATE_LIMIT_TENANT"
70
- ]);
71
- var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
72
- var KNOWN_CODES = /* @__PURE__ */ new Set([
73
- "UNAUTHORIZED",
74
- "INVALID_REQUEST",
75
- "UNSUPPORTED_MEDIA_TYPE",
76
- "PAYLOAD_TOO_LARGE",
77
- "ACCOUNT_NOT_FOUND",
78
- "ACCOUNT_RESTRICTED",
79
- "ACCOUNT_ALREADY_LINKED",
80
- "RESOURCE_NOT_FOUND",
81
- "RESOURCE_ACCESS_RESTRICTED",
82
- "TIER_NOT_ACTIVE",
83
- "LINKEDIN_FEATURE_NOT_SUBSCRIBED",
84
- "RATE_LIMIT_ACCOUNT",
85
147
  "RATE_LIMIT_TENANT",
86
- "PLATFORM_RATE_LIMIT",
87
- "PLATFORM_ERROR",
88
- "PLATFORM_NOT_IMPLEMENTED",
89
- "CHECKPOINT_NOT_FOUND",
90
- "CHECKPOINT_EXPIRED",
91
- "CHECKPOINT_INVALID_CODE",
92
- "CHECKPOINT_MAX_ATTEMPTS",
93
- "CHECKPOINT_ALREADY_RESOLVED",
94
- "CHECKPOINT_UNSUPPORTED",
95
- "CONNECTION_IN_PROGRESS",
96
- "LINKEDIN_AUTH_FAILED",
97
- "LINKEDIN_RATE_LIMITED",
98
- "LINKEDIN_COOKIE_INVALID",
99
- "LINKEDIN_SERVICE_UNAVAILABLE",
100
- "MESSAGE_WINDOW_EXPIRED",
101
- "RECIPIENT_UNREACHABLE",
102
- "PAYMENT_REQUIRED",
103
- "PAYMENT_FAILED",
104
- "SUBSCRIPTION_BUSY",
105
- "SUBSCRIPTION_NOT_FOUND",
106
- "SEAT_NOT_FOUND",
107
- "SEAT_CANCELLED",
108
- "INTERNAL"
109
- ]);
110
- var REQUIRED_TIERS = /* @__PURE__ */ new Set([
111
- "core",
112
- "sn",
113
- "sales_nav",
114
- "recruiter"
148
+ "RATE_LIMITED"
115
149
  ]);
150
+ var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PATCH", "PUT", "DELETE"]);
116
151
  function defaultSleep(ms) {
117
152
  return new Promise((resolve) => setTimeout(resolve, ms));
118
153
  }
@@ -127,7 +162,7 @@ function backoffDelay(n, jitter) {
127
162
  return exp + jitter;
128
163
  }
129
164
  function toErrorCode(code) {
130
- return code && KNOWN_CODES.has(code) ? code : "INTERNAL";
165
+ return code && KNOWN_ERROR_CODES.has(code) ? code : "INTERNAL";
131
166
  }
132
167
  function toRetryHint(hint) {
133
168
  if (!hint || typeof hint !== "object") return null;
@@ -145,7 +180,7 @@ async function errorFromResponse(res) {
145
180
  } catch {
146
181
  env = void 0;
147
182
  }
148
- const requiredTier = env?.required_tier && REQUIRED_TIERS.has(env.required_tier) ? env.required_tier : void 0;
183
+ const requiredTier = env?.required_tier && KNOWN_REQUIRED_TIERS.has(env.required_tier) ? env.required_tier : void 0;
149
184
  return new CurviateError({
150
185
  code: toErrorCode(env?.code),
151
186
  message: env?.message ?? `Request failed with status ${res.status}.`,
@@ -216,7 +251,7 @@ async function execute(method, path, opts) {
216
251
  let res;
217
252
  try {
218
253
  res = await doFetch(url, buildInit(method, opts, controller.signal));
219
- } catch (cause) {
254
+ } catch {
220
255
  clearTimeout(timer);
221
256
  const aborted = controller.signal.aborted;
222
257
  const err2 = new CurviateError({
@@ -251,37 +286,22 @@ async function execute(method, path, opts) {
251
286
  }
252
287
 
253
288
  // src/internal/context.ts
254
- function injectAccountIdIntoBody(body, accountId) {
255
- if (body instanceof FormData) {
256
- if (!body.has("account_id")) body.append("account_id", accountId);
257
- return body;
258
- }
259
- if (body !== null && typeof body === "object") {
260
- const obj = body;
261
- return "account_id" in obj ? obj : { account_id: accountId, ...obj };
262
- }
263
- return body;
289
+ var ACCOUNT_ID_PLACEHOLDER = "{account_id}";
290
+ function injectAccountIdIntoPath(path, accountId) {
291
+ if (accountId === void 0) return path;
292
+ return path.split(ACCOUNT_ID_PLACEHOLDER).join(accountId);
264
293
  }
265
294
  function createContext(config, accountId) {
266
295
  const request = (args) => {
267
- const target = args.accountIdIn ?? "query";
268
- let { query, body } = args;
269
- if (accountId !== void 0) {
270
- if (target === "body") {
271
- body = injectAccountIdIntoBody(body, accountId);
272
- } else if (target === "none") {
273
- } else {
274
- query = { account_id: accountId, ...args.query };
275
- }
276
- }
277
- return execute(args.method, args.path, {
296
+ const path = injectAccountIdIntoPath(args.path, accountId);
297
+ return execute(args.method, path, {
278
298
  apiKey: config.apiKey,
279
299
  baseUrl: config.baseUrl,
280
300
  timeout: config.timeout,
281
301
  maxRetries: config.maxRetries,
282
302
  ...config.fetch ? { fetch: config.fetch } : {},
283
- ...query !== void 0 ? { query } : {},
284
- ...body !== void 0 ? { body } : {}
303
+ ...args.query !== void 0 ? { query: args.query } : {},
304
+ ...args.body !== void 0 ? { body: args.body } : {}
285
305
  });
286
306
  };
287
307
  return { request, accountId };
@@ -330,450 +350,438 @@ var AccountsResource = class {
330
350
  });
331
351
  }
332
352
  /**
333
- * Connect a LinkedIn account to an empty seat.
334
- *
335
- * Returns an account (201) or a checkpoint challenge (202) when LinkedIn
336
- * requires verification. Callers discriminate on `result.object`; resolve a
337
- * 202 with {@link solveCheckpoint} (code) or {@link pollCheckpoint}
338
- * (mobile-app approval).
339
- *
340
- * For `auth_method: "cookie"`, `user_agent` is **required** — connecting by
341
- * session cookie without one is rejected with `INVALID_REQUEST`. It stays
342
- * optional for `auth_method: "credentials"`.
353
+ * Return metadata and current state for one connected account, including the
354
+ * central `quotas[]` view for all tracked quota families and `seat_id` (the
355
+ * seat this account occupies, `null` for an admin seatless account).
343
356
  *
344
- * On a 201 account, `recovered` is `true` only when the connect reclaimed a
345
- * LinkedIn identity already present on the workspace (claiming it into your
346
- * account) rather than connecting a brand-new one — it is absent on a normal
347
- * connect. Its `status` reflects the account's real observed state: as well as
348
- * `active` it may be `reconnect_needed`, `restricted`, or `disconnected` (a
349
- * recovered identity often needs a reconnect).
357
+ * This is a stale-while-revalidate read — it always returns immediately
358
+ * from the cached row (never blocks on a live substrate call). The cached
359
+ * enrichment fields `full_name` and `substrate_created_at` are populated by
360
+ * a background enrichment pass; `username`, `premium_id`,
361
+ * `public_identifier`, `signatures`, and `groups` are not sourced on the
362
+ * current connection surface and read `null`/`[]`.
350
363
  */
351
- link(body) {
364
+ get(accountId) {
352
365
  return this.ctx.request({
353
- method: "POST",
354
- path: "/v1/accounts/link",
355
- body
366
+ method: "GET",
367
+ path: `/v1/accounts/${accountId}`
356
368
  });
357
369
  }
358
370
  /**
359
- * Solve a checkpoint challenge by submitting an OTP or 2FA code.
360
- *
361
- * The account is addressed by `accountId` (the provisional `account_id`
362
- * returned on the 202 `checkpoint_required` response); the body carries the
363
- * `code`. Returns the connected account (201) or, when LinkedIn chains a
364
- * second challenge, another checkpoint (202) — resolve that one with a
365
- * further `solveCheckpoint` call for the same `accountId`.
366
- *
367
- * On the 201 account, `recovered` is `true` only when solving the challenge
368
- * reclaimed a LinkedIn identity already present on the workspace (rather than
369
- * connecting a brand-new one); it is absent otherwise. Its `status` reflects
370
- * the account's real observed state — as well as `active` it may be
371
- * `reconnect_needed`, `restricted`, or `disconnected`.
371
+ * Update an account's configuration.
372
372
  *
373
- * @param accountId - the provisional `account_id` from the 202 response.
374
- * @param body - the verification `code`.
373
+ * `metadata` is a flat string→string map that **replaces** the account's
374
+ * custom-data store wholesale (keys not provided are removed). `proxy` sets a
375
+ * custom egress proxy, or clears it (reverting to automatic proxy protection)
376
+ * when passed as `null`. The `proxy.password`, if given, is stored securely
377
+ * and never returned.
375
378
  */
376
- solveCheckpoint(accountId, body) {
379
+ update(accountId, body) {
377
380
  return this.ctx.request({
378
- method: "POST",
379
- path: `/v1/accounts/${accountId}/checkpoint/solve`,
381
+ method: "PATCH",
382
+ path: `/v1/accounts/${accountId}`,
380
383
  body
381
384
  });
382
385
  }
383
386
  /**
384
- * Re-request the pending verification challenge notification for an account.
385
- *
386
- * Useful when the end user says they never received the code or push
387
- * notification for a pending checkpoint. `resent` echoes the outcome
388
- * honestly: `true` once the notification was actually re-sent, `false` when
389
- * there was nothing to re-send for that challenge type (this call never
390
- * throws just because a re-send wasn't applicable). Does not reset the
391
- * checkpoint's expiry.
392
- *
393
- * @param accountId - the provisional `account_id` from the 202 response.
387
+ * Hard-disconnect a LinkedIn account; releases the attached seat.
394
388
  */
395
- requestCheckpoint(accountId) {
389
+ disconnect(accountId) {
396
390
  return this.ctx.request({
397
- method: "POST",
398
- path: `/v1/accounts/${accountId}/checkpoint/request`
391
+ method: "DELETE",
392
+ path: `/v1/accounts/${accountId}`
399
393
  });
400
394
  }
395
+ };
396
+
397
+ // src/resources/auth.ts
398
+ var AuthResource = class {
399
+ constructor(ctx) {
400
+ this.ctx = ctx;
401
+ }
401
402
  /**
402
- * Poll for mobile-app approval of a pending checkpoint challenge.
403
+ * Authenticate a LinkedIn account with credentials or a session cookie.
403
404
  *
404
- * The account is addressed by `accountId` (the provisional `account_id`
405
- * from the 202 response). `status` is `pending` until the end user approves
406
- * on their device, then `active` (the account is connected), or
407
- * `expired` / `failed`. Poll until it leaves `pending`.
405
+ * Omit `account_id` to connect a NEW account into an empty seat; include it
406
+ * to re-authenticate an EXISTING account in place. Returns the account on
407
+ * success (200 re-authenticated in place / 201 new), or a checkpoint
408
+ * challenge (202) when LinkedIn requires verification — resolve it with
409
+ * {@link solveCheckpoint} (code) or {@link pollCheckpoint} (mobile-app
410
+ * approval).
408
411
  *
409
- * On `status: "expired"` (the approval timed out), the response carries
410
- * `challenge_type` (`"mobile_app_approval"`) and a human-readable
411
- * `recovery_hint` — the actionable next step — so a client can render the
412
- * right recovery guidance without parsing prose.
412
+ * For `auth_method: "cookie"`, `user_agent` is **required** — connecting by
413
+ * session cookie without one is rejected with `INVALID_REQUEST`. It stays
414
+ * optional for `auth_method: "credentials"`.
413
415
  *
414
- * @param accountId - the provisional `account_id` from the 202 response.
415
- */
416
- pollCheckpoint(accountId) {
417
- return this.ctx.request({
418
- method: "POST",
419
- path: `/v1/accounts/${accountId}/checkpoint/poll`
420
- });
421
- }
422
- /**
423
- * Generate a one-time hosted connection link the end user opens to authorize
424
- * a new LinkedIn connection without credentials transiting the API or LLM
425
- * context. Poll completion with {@link getConnectSession} using the returned
426
- * `session_id`. To re-authorize an existing account, use
427
- * {@link createReconnectLink} instead.
428
- */
429
- createConnectLink(body) {
416
+ * On a 201 account, `recovered` is `true` only when the connect reclaimed a
417
+ * LinkedIn identity already present on the workspace (claiming it into your
418
+ * account) rather than connecting a brand-new one — it is absent on a normal
419
+ * connect.
420
+ *
421
+ * Connection scope (which LinkedIn products are enabled: classic, company,
422
+ * a premium tier) is **seat-derived**: there is no products input on this
423
+ * body, and the recorded scope is readable back as `requested_products` on
424
+ * the account (`accounts.list()` / `accounts.get()`). A reconnect that
425
+ * would change scope must use `auth_method: "credentials"`, since a cookie
426
+ * replay cannot change scope and throws `CurviateError(code:
427
+ * "REAUTH_REQUIRED")` instead. A seat resolving to both individual-Premium
428
+ * tiers at once throws `CurviateError(code: "PREMIUM_CONFLICT")` (LinkedIn
429
+ * permits only one per profile). Pin a managed proxy with the optional
430
+ * `country`/`ip`, or supply `proxy` to override it entirely.
431
+ */
432
+ intent(body) {
430
433
  return this.ctx.request({
431
434
  method: "POST",
432
- path: "/v1/accounts/connect-link",
435
+ path: "/v1/auth/intent",
433
436
  body
434
437
  });
435
438
  }
436
439
  /**
437
- * Generate a one-time hosted **re-authorization** link for an existing
438
- * account that became disconnected — the hosted counterpart of
439
- * {@link reconnect}. The end user opens the returned `url`; poll completion
440
- * with {@link getConnectSession} using the returned `session_id`. The account
441
- * keeps its `account_id` and seat.
440
+ * Solve a checkpoint challenge by submitting an OTP / 2FA code (or the
441
+ * special value `TRY_ANOTHER_WAY` to switch challenge method).
442
442
  *
443
- * @param accountId - the account to re-authorize.
444
- * @param body - optional `expires_in_seconds` and `redirect_url`.
443
+ * Returns the connected account (201) or, when LinkedIn chains a second
444
+ * challenge, another checkpoint (202) — resolve that one with a further
445
+ * `solveCheckpoint` call for the same `accountId`.
446
+ *
447
+ * @param accountId - the (provisional) `account_id` from the 202 response.
448
+ * @param body - the verification `code`.
445
449
  */
446
- createReconnectLink(accountId, body) {
450
+ solveCheckpoint(accountId, body) {
447
451
  return this.ctx.request({
448
452
  method: "POST",
449
- path: `/v1/accounts/${accountId}/reconnect-link`,
450
- ...body !== void 0 ? { body } : {}
453
+ path: "/v1/auth/checkpoint/solve",
454
+ body: { account_id: accountId, ...body }
451
455
  });
452
456
  }
453
457
  /**
454
- * Poll a hosted connect session minted by {@link createConnectLink} or
455
- * {@link createReconnectLink} (its `session_id`).
456
- *
457
- * A pure status read — it makes no external call and does not itself complete
458
- * the connection. `status` is `pending` until the end user finishes the
459
- * hosted flow, then `resolved` (with `account_id`), or `expired` / `failed`.
460
- * Poll until it leaves `pending`.
458
+ * Re-request the pending checkpoint notification (OTP / 2FA / mobile-app
459
+ * push). `resent` is honest: `true` once the notification was actually
460
+ * re-sent, `false` when there was nothing to re-send for that challenge
461
+ * type (e.g. an authenticator-app code) — this call never throws just
462
+ * because a re-send wasn't applicable. Does not reset the checkpoint's
463
+ * expiry.
461
464
  *
462
- * @param sessionId - the `session_id` returned on the connect-link 201 body.
463
- * @returns the session's current `status` and, once `resolved`, `account_id`.
465
+ * @param accountId - the (provisional) `account_id` from the 202 response.
464
466
  */
465
- getConnectSession(sessionId) {
467
+ requestCheckpoint(accountId) {
466
468
  return this.ctx.request({
467
- method: "GET",
468
- path: `/v1/accounts/connect-sessions/${sessionId}`
469
+ method: "POST",
470
+ path: "/v1/auth/checkpoint/request",
471
+ body: { account_id: accountId }
469
472
  });
470
473
  }
471
474
  /**
472
- * Return metadata and current state for one connected account, including the
473
- * central `quotas[]` view for all tracked quota families and `seat_id` (the
474
- * seat this account occupies, `null` for an admin seatless account).
475
+ * Poll for mobile-app approval of a pending checkpoint challenge until it
476
+ * leaves `pending`.
475
477
  *
476
- * This is a stale-while-revalidate read — it always returns immediately
477
- * from the cached row (never blocks on a live substrate call). The cached
478
- * enrichment fields `full_name` and `substrate_created_at` are populated by
479
- * a background enrichment pass; `username`, `premium_id`,
480
- * `public_identifier`, `signatures`, and `groups` are not sourced on the
481
- * current connection surface and read `null`/`[]`.
482
- */
483
- get(accountId) {
484
- return this.ctx.request({
485
- method: "GET",
486
- path: `/v1/accounts/${accountId}`
487
- });
488
- }
489
- /**
490
- * Re-authorize a disconnected account in place (same `account_id`, same
491
- * seat). Returns the reconnected account (200) or a checkpoint challenge
492
- * (202) when LinkedIn requires verification — resolve a 202 with
493
- * {@link solveCheckpoint} / {@link pollCheckpoint}, exactly like {@link link}.
478
+ * On `status: "expired"`, the response carries `challenge_type`
479
+ * (`"mobile_app_approval"`) and a human-readable `recovery_hint`.
494
480
  *
495
- * For `auth_method: "cookie"`, `user_agent` is **required** (as on
496
- * {@link link}). For the hosted re-auth flow, use {@link createReconnectLink}.
481
+ * @param accountId - the (provisional) `account_id` from the 202 response.
497
482
  */
498
- reconnect(accountId, body) {
483
+ pollCheckpoint(accountId) {
499
484
  return this.ctx.request({
500
485
  method: "POST",
501
- path: `/v1/accounts/${accountId}/reconnect`,
502
- body
486
+ path: "/v1/auth/checkpoint/poll",
487
+ body: { account_id: accountId }
503
488
  });
504
489
  }
505
490
  /**
506
- * Update an account's configuration.
491
+ * Poll a credential connect session by its account id — a pure status read
492
+ * that makes no external call and does not itself complete the connection.
493
+ * `status` is `checkpoint_required` until resolved, then `done` (with
494
+ * `account_id`), or `expired` / `failed`.
507
495
  *
508
- * `metadata` is a flat string→string map that **replaces** the account's
509
- * custom-data store wholesale (keys not provided are removed). `proxy` sets a
510
- * custom egress proxy, or clears it (reverting to automatic proxy protection)
511
- * when passed as `null`. The `proxy.password`, if given, is stored securely
512
- * and never returned.
496
+ * @param sessionId - the acc_… id returned by {@link intent} or a checkpoint response.
513
497
  */
514
- update(accountId, body) {
515
- return this.ctx.request({
516
- method: "PATCH",
517
- path: `/v1/accounts/${accountId}`,
518
- body
519
- });
520
- }
521
- /**
522
- * Hard-disconnect a LinkedIn account; releases the attached seat.
523
- */
524
- disconnect(accountId) {
498
+ getSession(sessionId) {
525
499
  return this.ctx.request({
526
- method: "DELETE",
527
- path: `/v1/accounts/${accountId}`
500
+ method: "GET",
501
+ path: `/v1/auth/sessions/${sessionId}`
528
502
  });
529
503
  }
530
504
  };
531
505
 
532
506
  // src/resources/messaging.ts
533
- function buildFormData(scalars, attachments) {
534
- const form = new FormData();
535
- for (const [key, value] of Object.entries(scalars)) {
536
- if (value !== void 0 && value !== null) {
537
- form.append(key, String(value));
538
- }
539
- }
540
- for (const attachment of attachments) {
541
- if (attachment instanceof File) {
542
- form.append("attachments", attachment);
543
- } else {
544
- form.append("attachments", new Blob([attachment]));
545
- }
546
- }
547
- return form;
548
- }
549
507
  var MessagingResource = class {
550
508
  constructor(ctx) {
551
509
  this.ctx = ctx;
552
510
  }
553
- /** List paginated chats for the account. `GET /v1/chats` */
511
+ /** List paginated chats for the account. `GET /v1/{account_id}/chats` */
554
512
  listChats(params) {
555
513
  return this.ctx.request({
556
514
  method: "GET",
557
- path: "/v1/chats",
515
+ path: "/v1/{account_id}/chats",
558
516
  ...params ? { query: params } : {}
559
517
  });
560
518
  }
561
519
  /**
562
- * Start a new chat. Accepts optional `attachments[]` — when present, the
563
- * request is sent as `multipart/form-data`. `POST /v1/chats`
520
+ * Start a new chat with one or more members. `POST /v1/{account_id}/chats`
521
+ * `attachments[]`, when supplied, carry base64-encoded file bytes, always
522
+ * sent as JSON, never multipart.
523
+ *
524
+ * Company pages are reply-only and cannot start a conversation this way.
525
+ * Reply to an existing one instead, with `sendMessage()` using a
526
+ * `COMPANY_` chat id from `inboxes.listChats()`.
564
527
  */
565
528
  startChat(body) {
566
- const { attachments, ...scalars } = body;
567
- if (attachments && attachments.length > 0) {
568
- return this.ctx.request({
569
- method: "POST",
570
- path: "/v1/chats",
571
- body: buildFormData(scalars, attachments),
572
- accountIdIn: "body"
573
- });
574
- }
575
529
  return this.ctx.request({
576
530
  method: "POST",
577
- path: "/v1/chats",
578
- body: scalars,
579
- accountIdIn: "body"
531
+ path: "/v1/{account_id}/chats",
532
+ body
580
533
  });
581
534
  }
582
- /** Get details of a single chat. `GET /v1/chats/{chat_id}` */
535
+ /** Get details of a single chat. `GET /v1/{account_id}/chats/{chat_id}` */
583
536
  getChat(chatId) {
584
537
  return this.ctx.request({
585
538
  method: "GET",
586
- path: `/v1/chats/${chatId}`
539
+ path: `/v1/{account_id}/chats/${chatId}`
540
+ });
541
+ }
542
+ /**
543
+ * Mark a chat read or unread. `PATCH /v1/{account_id}/chats/{chat_id}`
544
+ * Body: `{read}`.
545
+ */
546
+ markChatRead(chatId, body) {
547
+ return this.ctx.request({
548
+ method: "PATCH",
549
+ path: `/v1/{account_id}/chats/${chatId}`,
550
+ body
587
551
  });
588
552
  }
589
- /** List messages in a chat, cursor-paginated. `GET /v1/chats/{chat_id}/messages` */
553
+ /**
554
+ * List messages in a chat, cursor-paginated.
555
+ * `GET /v1/{account_id}/chats/{chat_id}/messages`
556
+ */
590
557
  listMessages(chatId, params) {
591
558
  return this.ctx.request({
592
559
  method: "GET",
593
- path: `/v1/chats/${chatId}/messages`,
560
+ path: `/v1/{account_id}/chats/${chatId}/messages`,
594
561
  ...params ? { query: params } : {}
595
562
  });
596
563
  }
597
564
  /**
598
- * Send a message in a chat. Multipart when `attachments[]` are provided.
599
- * `POST /v1/chats/{chat_id}/messages`
565
+ * Send a message in a chat. `POST /v1/{account_id}/chats/{chat_id}/messages`
566
+ * `attachments[]`, when supplied, carry base64-encoded file bytes, always
567
+ * sent as JSON, never multipart. At least one of `text`/`attachments` is
568
+ * required (enforced server-side).
569
+ *
570
+ * Sending on behalf of a company page: this same method is the reply
571
+ * surface for a company page, there is no separate send-as-page method.
572
+ * Pass a `COMPANY_` chat id (from `inboxes.listChats()`, e.g.
573
+ * `"COMPANY_83734124_2-YTQ3ODU3Njgt"`) and the message sends AS THE PAGE,
574
+ * no extra parameter needed. Any other chat id sends as the connected
575
+ * member. Company pages are reply-only: they can answer an existing
576
+ * conversation this way, but `startChat()` cannot start one on their
577
+ * behalf.
578
+ *
579
+ * The response echoes `sent_as`, the acting identity actually used:
580
+ * `{ kind: "company", company_id, name }` (`company_id` is `null` when
581
+ * the page could not be correlated to a managed page) or
582
+ * `{ kind: "personal" }`. Never infer the acting identity from a
583
+ * message's `sender` field, only from `sent_as`.
584
+ *
585
+ * @example
586
+ * const acc = curviate.account("acc_YOUR_ACCOUNT_ID");
587
+ * const result = await acc.messaging.sendMessage("COMPANY_83734124_2-YTQ3ODU3Njgt", {
588
+ * text: "Thanks for reaching out!",
589
+ * });
590
+ * console.log(result.sent_as); // { kind: "company", company_id: "112013061", name: "Acme Inc" }
600
591
  */
601
592
  sendMessage(chatId, body) {
602
- const { attachments, ...scalars } = body;
603
- if (attachments && attachments.length > 0) {
604
- return this.ctx.request({
605
- method: "POST",
606
- path: `/v1/chats/${chatId}/messages`,
607
- body: buildFormData(scalars, attachments),
608
- accountIdIn: "body"
609
- });
610
- }
611
593
  return this.ctx.request({
612
594
  method: "POST",
613
- path: `/v1/chats/${chatId}/messages`,
614
- body: scalars,
615
- accountIdIn: "body"
616
- });
617
- }
618
- /** Trigger a re-sync of a specific chat's message history. `GET /v1/chats/{chat_id}/sync` */
619
- syncChat(chatId) {
620
- return this.ctx.request({
621
- method: "GET",
622
- path: `/v1/chats/${chatId}/sync`
595
+ path: `/v1/{account_id}/chats/${chatId}/messages`,
596
+ body
623
597
  });
624
598
  }
625
- /** Get a single message by ID. `GET /v1/messages/{message_id}` */
626
- getMessage(messageId) {
599
+ /**
600
+ * Get a single message by ID, re-homed under its chat.
601
+ * `GET /v1/{account_id}/chats/{chat_id}/messages/{message_id}`
602
+ */
603
+ getMessage(chatId, messageId) {
627
604
  return this.ctx.request({
628
605
  method: "GET",
629
- path: `/v1/messages/${messageId}`
606
+ path: `/v1/{account_id}/chats/${chatId}/messages/${messageId}`
630
607
  });
631
608
  }
632
- /** Edit a message within the ~60-minute edit window. `PATCH /v1/messages/{message_id}` */
633
- editMessage(messageId, body) {
609
+ /**
610
+ * Edit a message within the ~60-minute edit window.
611
+ * `PATCH /v1/{account_id}/chats/{chat_id}/messages/{message_id}`
612
+ */
613
+ editMessage(chatId, messageId, body) {
634
614
  return this.ctx.request({
635
615
  method: "PATCH",
636
- path: `/v1/messages/${messageId}`,
616
+ path: `/v1/{account_id}/chats/${chatId}/messages/${messageId}`,
637
617
  body
638
618
  });
639
619
  }
640
- /** Delete a message within the delete window. `DELETE /v1/messages/{message_id}` */
641
- deleteMessage(messageId) {
620
+ /**
621
+ * Delete a message within the delete window (bodyless).
622
+ * `DELETE /v1/{account_id}/chats/{chat_id}/messages/{message_id}`
623
+ */
624
+ deleteMessage(chatId, messageId) {
642
625
  return this.ctx.request({
643
626
  method: "DELETE",
644
- path: `/v1/messages/${messageId}`,
645
- accountIdIn: "none"
646
- // server resolves owning account from message id
627
+ path: `/v1/{account_id}/chats/${chatId}/messages/${messageId}`
647
628
  });
648
629
  }
649
630
  /**
650
- * Download a message attachment. Returns raw binary.
651
- * `GET /v1/messages/{message_id}/attachments/{attachment_id}`
652
- * Returns `ArrayBuffer` — binary response; the SDK does not cache or store it.
631
+ * Add a reaction to a message.
632
+ * `POST /v1/{account_id}/chats/{chat_id}/messages/{message_id}/reactions`
653
633
  */
654
- getAttachment(messageId, attachmentId) {
634
+ addReaction(chatId, messageId, body) {
655
635
  return this.ctx.request({
656
- method: "GET",
657
- path: `/v1/messages/${messageId}/attachments/${attachmentId}`
636
+ method: "POST",
637
+ path: `/v1/{account_id}/chats/${chatId}/messages/${messageId}/reactions`,
638
+ body
658
639
  });
659
640
  }
660
- /** Add a reaction to a message. `POST /v1/messages/{message_id}/reactions` */
661
- addReaction(messageId, body) {
641
+ /**
642
+ * Download a message attachment. Returns raw binary.
643
+ * `GET /v1/{account_id}/chats/{chat_id}/messages/{message_id}/attachments/{attachment_id}`
644
+ * Returns `ArrayBuffer` — binary response; the SDK does not cache or store it.
645
+ */
646
+ getAttachment(chatId, messageId, attachmentId) {
662
647
  return this.ctx.request({
663
- method: "POST",
664
- path: `/v1/messages/${messageId}/reactions`,
665
- body,
666
- accountIdIn: "none"
667
- // server resolves owning account from message id
648
+ method: "GET",
649
+ path: `/v1/{account_id}/chats/${chatId}/messages/${messageId}/attachments/${attachmentId}`
668
650
  });
669
651
  }
670
- /** Send an InMail. `POST /v1/messages/inmail` */
652
+ /** Send an InMail. `POST /v1/{account_id}/messages/inmail` */
671
653
  sendInMail(body) {
672
654
  return this.ctx.request({
673
655
  method: "POST",
674
- path: "/v1/messages/inmail",
675
- body,
676
- accountIdIn: "body"
677
- });
678
- }
679
- /** Get the account's InMail credit balance. `GET /v1/messaging/inmail-balance` */
680
- getInMailBalance(params) {
681
- return this.ctx.request({
682
- method: "GET",
683
- path: "/v1/messaging/inmail-balance",
684
- ...params ? { query: params } : {}
656
+ path: "/v1/{account_id}/messages/inmail",
657
+ body
685
658
  });
686
659
  }
687
- /** Re-sync account message history. `GET /v1/messages/sync` */
688
- syncMessages(params) {
660
+ /**
661
+ * Free-text search the account's own inbox — matches participant names
662
+ * and message content. `GET /v1/{account_id}/chats/search`
663
+ *
664
+ * @param params - `query` (required free-text term) plus `limit` and an
665
+ * opaque `cursor` for pagination.
666
+ */
667
+ searchChats(params) {
689
668
  return this.ctx.request({
690
669
  method: "GET",
691
- path: "/v1/messages/sync",
692
- ...params ? { query: params } : {}
670
+ path: "/v1/{account_id}/chats/search",
671
+ query: params
693
672
  });
694
673
  }
695
674
  };
696
675
 
697
- // src/resources/profiles.ts
698
- var ProfilesResource = class {
676
+ // src/resources/users.ts
677
+ var UsersResource = class {
699
678
  constructor(ctx) {
700
679
  this.ctx = ctx;
701
680
  }
702
681
  /**
703
- * Get the account's own LinkedIn profile. `GET /v1/profiles/me`
704
- * Pass `{ linkedin_sections: ['about', 'experience', ...] }` to request enriched sections.
705
- * The `account_id` is injected by the account-scoped context.
682
+ * Retrieve a user's profile. `GET /v1/{account_id}/users/{user_id}`
683
+ * Pass `userId: "me"` for the caller's own account (folds in the old
684
+ * standalone `getMe()`). Pass `{ linkedin_sections: [...] }` to request
685
+ * enriched sections.
706
686
  */
707
- getMe(params) {
687
+ get(userId, params) {
708
688
  return this.ctx.request({
709
689
  method: "GET",
710
- path: "/v1/profiles/me",
690
+ path: `/v1/{account_id}/users/${userId}`,
711
691
  ...params ? { query: params } : {}
712
692
  });
713
693
  }
714
694
  /**
715
- * Get a LinkedIn member profile by id or public handle.
716
- * `GET /v1/profiles/{profile_id}`
717
- * Pass `{ notify: true }` to signal a profile view.
718
- * The `account_id` is injected by the account-scoped context.
695
+ * Update the caller's own profile. `PATCH /v1/{account_id}/users/{user_id}`
696
+ * Only `first_name`, `last_name`, `bio`, `headline`, `skills`, `picture`,
697
+ * `background_picture` are accepted — there is no `description` key.
698
+ * `description` is not part of the update contract: TypeScript already
699
+ * rejects it at the call site, but an untyped/JS caller (or an `as`-cast)
700
+ * could still smuggle it onto the object, so it is also stripped
701
+ * defensively at runtime before the request goes out — the caller's
702
+ * object is never mutated.
719
703
  */
720
- get(profileId, params) {
704
+ update(userId, body) {
705
+ const { description: _description, ...safeBody } = body;
721
706
  return this.ctx.request({
722
- method: "GET",
723
- path: `/v1/profiles/${profileId}`,
724
- ...params ? { query: params } : {}
707
+ method: "PATCH",
708
+ path: `/v1/{account_id}/users/${userId}`,
709
+ body: safeBody
725
710
  });
726
711
  }
727
- /** List the account's 1st-degree connections. `GET /v1/profiles/relations` */
728
- listConnections(params) {
712
+ /**
713
+ * List the account's 1st-degree connections.
714
+ * `GET /v1/{account_id}/profiles/relations` (served URL keeps `profiles/`).
715
+ * Was `profiles.listConnections`.
716
+ */
717
+ listRelations(params) {
729
718
  return this.ctx.request({
730
719
  method: "GET",
731
- path: "/v1/profiles/relations",
720
+ path: "/v1/{account_id}/profiles/relations",
732
721
  ...params ? { query: params } : {}
733
722
  });
734
723
  }
735
- /** List a profile's followers. `GET /v1/profiles/{profile_id}/followers` */
736
- listFollowers(profileId, params) {
724
+ /** List a user's followers. `GET /v1/{account_id}/users/{user_id}/followers` */
725
+ listFollowers(userId, params) {
737
726
  return this.ctx.request({
738
727
  method: "GET",
739
- path: `/v1/profiles/${profileId}/followers`,
728
+ path: `/v1/{account_id}/users/${userId}/followers`,
740
729
  ...params ? { query: params } : {}
741
730
  });
742
731
  }
743
- /**
744
- * List a profile's posts. `GET /v1/profiles/{profile_id}/posts`
745
- * Pass `{ is_company: true }` for company posts.
746
- */
747
- listPosts(profileId, params) {
732
+ /** List who a user follows. `GET /v1/{account_id}/users/{user_id}/following` */
733
+ listFollowing(userId, params) {
748
734
  return this.ctx.request({
749
735
  method: "GET",
750
- path: `/v1/profiles/${profileId}/posts`,
736
+ path: `/v1/{account_id}/users/${userId}/following`,
751
737
  ...params ? { query: params } : {}
752
738
  });
753
739
  }
754
- /** List a profile's comments. `GET /v1/profiles/{profile_id}/comments` */
755
- listComments(profileId, params) {
740
+ /**
741
+ * Follow a user (bodyless). `POST /v1/{account_id}/users/{user_id}/follow`
742
+ * If the target's profile is private, sends a connect request instead and
743
+ * returns `connect_request_sent`.
744
+ */
745
+ follow(userId) {
756
746
  return this.ctx.request({
757
- method: "GET",
758
- path: `/v1/profiles/${profileId}/comments`,
759
- ...params ? { query: params } : {}
747
+ method: "POST",
748
+ path: `/v1/{account_id}/users/${userId}/follow`
749
+ });
750
+ }
751
+ /**
752
+ * Unfollow a user (bodyless). `DELETE /v1/{account_id}/users/{user_id}/follow`
753
+ * Idempotent — unfollowing a user this account does not currently follow
754
+ * still returns 200.
755
+ */
756
+ unfollow(userId) {
757
+ return this.ctx.request({
758
+ method: "DELETE",
759
+ path: `/v1/{account_id}/users/${userId}/follow`
760
760
  });
761
761
  }
762
- /** List a profile's reactions. `GET /v1/profiles/{profile_id}/reactions` */
763
- listReactions(profileId, params) {
762
+ /**
763
+ * Get remaining InMail credits across subscribed LinkedIn premium products.
764
+ * `GET /v1/{account_id}/inmail-credits`
765
+ * Relocated from `messaging.getInMailBalance`.
766
+ */
767
+ getInMailCredits(params) {
764
768
  return this.ctx.request({
765
769
  method: "GET",
766
- path: `/v1/profiles/${profileId}/reactions`,
770
+ path: "/v1/{account_id}/inmail-credits",
767
771
  ...params ? { query: params } : {}
768
772
  });
769
773
  }
770
- /** Endorse a skill on a profile. `POST /v1/profiles/{profile_id}/endorse` */
771
- endorse(profileId, body) {
774
+ /**
775
+ * Endorse one specific skill on a target member's profile.
776
+ * `POST /v1/{account_id}/users/{user_id}/endorse-skill`
777
+ * Body key is `endorsement_id` (was `skill_endorsement_id` on `profiles.endorse`).
778
+ * Obtain it from `GET /v1/{account_id}/users/{user_id}?linkedin_sections=linkedin_skills`.
779
+ */
780
+ endorseSkill(userId, body) {
772
781
  return this.ctx.request({
773
782
  method: "POST",
774
- path: `/v1/profiles/${profileId}/endorse`,
775
- body,
776
- accountIdIn: "body"
783
+ path: `/v1/{account_id}/users/${userId}/endorse-skill`,
784
+ body
777
785
  });
778
786
  }
779
787
  };
@@ -783,398 +791,876 @@ var InvitesResource = class {
783
791
  constructor(ctx) {
784
792
  this.ctx = ctx;
785
793
  }
786
- /** Send a connection invitation. `POST /v1/invites` */
794
+ /** Send a connect-request. `POST /v1/{account_id}/invites` */
787
795
  send(body) {
788
796
  return this.ctx.request({
789
797
  method: "POST",
790
- path: "/v1/invites",
791
- body,
792
- accountIdIn: "body"
798
+ path: "/v1/{account_id}/invites",
799
+ body
793
800
  });
794
801
  }
795
- /** List sent invitations. `GET /v1/invites/sent` */
802
+ /** List sent connect-requests. `GET /v1/{account_id}/invites/sent` */
796
803
  listSent(params) {
797
804
  return this.ctx.request({
798
805
  method: "GET",
799
- path: "/v1/invites/sent",
806
+ path: "/v1/{account_id}/invites/sent",
800
807
  ...params ? { query: params } : {}
801
808
  });
802
809
  }
803
- /** List received invitations. `GET /v1/invites/received` */
810
+ /** List received connect-requests. `GET /v1/{account_id}/invites/received` */
804
811
  listReceived(params) {
805
812
  return this.ctx.request({
806
813
  method: "GET",
807
- path: "/v1/invites/received",
814
+ path: "/v1/{account_id}/invites/received",
808
815
  ...params ? { query: params } : {}
809
816
  });
810
817
  }
811
818
  /**
812
- * Accept or decline a received invitation.
813
- * `POST /v1/invites/received/{invitation_id}`
819
+ * Accept a received connect-request (bodyless). An unrecognized or
820
+ * non-pending `invitationId` returns `status: "not_found"`, not an error.
821
+ * `POST /v1/{account_id}/invites/received/{invitation_id}/accept`
814
822
  */
815
- respond(invitationId, body) {
823
+ accept(invitationId) {
816
824
  return this.ctx.request({
817
825
  method: "POST",
818
- path: `/v1/invites/received/${invitationId}`,
819
- body,
820
- accountIdIn: "body"
826
+ path: `/v1/{account_id}/invites/received/${invitationId}/accept`
827
+ });
828
+ }
829
+ /**
830
+ * Decline a received connect-request (bodyless). An unrecognized or
831
+ * non-pending `invitationId` returns `status: "not_found"`, not an error.
832
+ * `POST /v1/{account_id}/invites/received/{invitation_id}/decline`
833
+ */
834
+ decline(invitationId) {
835
+ return this.ctx.request({
836
+ method: "POST",
837
+ path: `/v1/{account_id}/invites/received/${invitationId}/decline`
821
838
  });
822
839
  }
823
- /** Cancel a sent invitation. `DELETE /v1/invites/{invitation_id}` */
840
+ /**
841
+ * Withdraw a sent connect-request (bodyless). An already-absent or
842
+ * unrecognized `invitationId` returns `status: "not_found"`, not an error.
843
+ * `DELETE /v1/{account_id}/invites/sent/{invitation_id}`
844
+ */
824
845
  cancel(invitationId) {
825
846
  return this.ctx.request({
826
847
  method: "DELETE",
827
- path: `/v1/invites/${invitationId}`
848
+ path: `/v1/{account_id}/invites/sent/${invitationId}`
828
849
  });
829
850
  }
830
851
  };
831
852
 
832
853
  // src/resources/search.ts
854
+ function splitPagination(merged) {
855
+ const { cursor, limit, offset, ...body } = merged;
856
+ const hasQuery = cursor !== void 0 || limit !== void 0 || offset !== void 0;
857
+ return {
858
+ body,
859
+ ...hasQuery ? {
860
+ query: {
861
+ ...cursor !== void 0 ? { cursor } : {},
862
+ ...limit !== void 0 ? { limit } : {},
863
+ ...offset !== void 0 ? { offset } : {}
864
+ }
865
+ } : {}
866
+ };
867
+ }
833
868
  var SearchResource = class {
834
869
  constructor(ctx) {
835
870
  this.ctx = ctx;
836
871
  }
837
872
  /**
838
873
  * Resolve human-readable terms to opaque filter IDs for structured search.
839
- * `GET /v1/search/parameters`
874
+ * `GET /v1/{account_id}/search/parameters`
840
875
  */
841
876
  getParameters(query) {
842
877
  return this.ctx.request({
843
878
  method: "GET",
844
- path: "/v1/search/parameters",
879
+ path: "/v1/{account_id}/search/parameters",
845
880
  query
846
881
  });
847
882
  }
848
883
  /**
849
- * Search people with structured filters or a pasted URL.
850
- * `POST /v1/search/people`
851
- * Cursor passed via query param (not body) — matches the OpenAPI spec.
884
+ * Search people with structured filters.
885
+ * `POST /v1/{account_id}/search/people`
886
+ * `offset`/`limit`/`cursor` are top-level query params, never in the body.
852
887
  */
853
888
  people(body) {
854
- const { cursor, limit, ...rest } = body;
889
+ const split = splitPagination(body);
855
890
  return this.ctx.request({
856
891
  method: "POST",
857
- path: "/v1/search/people",
858
- body: rest,
859
- ...cursor !== void 0 || limit !== void 0 ? { query: { ...cursor !== void 0 ? { cursor } : {}, ...limit !== void 0 ? { limit } : {} } } : {}
892
+ path: "/v1/{account_id}/search/people",
893
+ body: split.body,
894
+ ...split.query ? { query: split.query } : {}
860
895
  });
861
896
  }
862
897
  /**
863
- * Search companies.
864
- * `POST /v1/search/companies`
898
+ * Search companies with structured filters.
899
+ * `POST /v1/{account_id}/search/companies`
865
900
  */
866
901
  companies(body) {
867
- const { cursor, limit, ...rest } = body;
902
+ const split = splitPagination(body);
868
903
  return this.ctx.request({
869
904
  method: "POST",
870
- path: "/v1/search/companies",
871
- body: rest,
872
- ...cursor !== void 0 || limit !== void 0 ? { query: { ...cursor !== void 0 ? { cursor } : {}, ...limit !== void 0 ? { limit } : {} } } : {}
905
+ path: "/v1/{account_id}/search/companies",
906
+ body: split.body,
907
+ ...split.query ? { query: split.query } : {}
873
908
  });
874
909
  }
875
910
  /**
876
- * Search posts.
877
- * `POST /v1/search/posts`
911
+ * Search posts with structured filters.
912
+ * `POST /v1/{account_id}/search/posts`
878
913
  */
879
914
  posts(body) {
880
- const { cursor, limit, ...rest } = body;
915
+ const split = splitPagination(body);
881
916
  return this.ctx.request({
882
917
  method: "POST",
883
- path: "/v1/search/posts",
884
- body: rest,
885
- ...cursor !== void 0 || limit !== void 0 ? { query: { ...cursor !== void 0 ? { cursor } : {}, ...limit !== void 0 ? { limit } : {} } } : {}
918
+ path: "/v1/{account_id}/search/posts",
919
+ body: split.body,
920
+ ...split.query ? { query: split.query } : {}
886
921
  });
887
922
  }
888
923
  /**
889
- * Search jobs.
890
- * `POST /v1/search/jobs`
924
+ * Search jobs with structured filters.
925
+ * `POST /v1/{account_id}/search/jobs`
891
926
  */
892
927
  jobs(body) {
893
- const { cursor, limit, ...rest } = body;
928
+ const split = splitPagination(body);
929
+ return this.ctx.request({
930
+ method: "POST",
931
+ path: "/v1/{account_id}/search/jobs",
932
+ body: split.body,
933
+ ...split.query ? { query: split.query } : {}
934
+ });
935
+ }
936
+ /**
937
+ * Run a pasted LinkedIn search/saved-search/lead-list URL directly.
938
+ * `POST /v1/{account_id}/search`
939
+ * `url` is the ONLY accepted body field — the sole home of URL-mode search;
940
+ * the structured endpoints above no longer accept `url`. The response is
941
+ * polymorphic, each item discriminated individually by its own `object`.
942
+ */
943
+ fromUrl(body) {
944
+ const { cursor, limit, offset, url } = body;
945
+ const hasQuery = cursor !== void 0 || limit !== void 0 || offset !== void 0;
894
946
  return this.ctx.request({
895
947
  method: "POST",
896
- path: "/v1/search/jobs",
897
- body: rest,
898
- ...cursor !== void 0 || limit !== void 0 ? { query: { ...cursor !== void 0 ? { cursor } : {}, ...limit !== void 0 ? { limit } : {} } } : {}
948
+ path: "/v1/{account_id}/search",
949
+ body: { url },
950
+ ...hasQuery ? {
951
+ query: {
952
+ ...cursor !== void 0 ? { cursor } : {},
953
+ ...limit !== void 0 ? { limit } : {},
954
+ ...offset !== void 0 ? { offset } : {}
955
+ }
956
+ } : {}
957
+ });
958
+ }
959
+ /**
960
+ * Search LinkedIn groups by keyword.
961
+ * `GET /v1/{account_id}/search/groups`
962
+ */
963
+ groups(query) {
964
+ return this.ctx.request({
965
+ method: "GET",
966
+ path: "/v1/{account_id}/search/groups",
967
+ query
968
+ });
969
+ }
970
+ /**
971
+ * Search Services Marketplace providers with structured filters.
972
+ * `POST /v1/{account_id}/search/services`
973
+ * `limit`/`cursor` are top-level query params, never in the body. Resolve
974
+ * `service_category`/`location` filter values with `getServiceParameters()`
975
+ * first — both take opaque ids, not free text.
976
+ */
977
+ services(body) {
978
+ const split = splitPagination(body);
979
+ return this.ctx.request({
980
+ method: "POST",
981
+ path: "/v1/{account_id}/search/services",
982
+ body: split.body,
983
+ ...split.query ? { query: split.query } : {}
984
+ });
985
+ }
986
+ /**
987
+ * Resolve human-readable service-filter terms into the opaque ids
988
+ * `services()` accepts.
989
+ * `GET /v1/{account_id}/search/services/parameters`
990
+ *
991
+ * @param query - `type` selects `service_category` (default) or `location`;
992
+ * `keywords` is the free-text term to resolve.
993
+ */
994
+ getServiceParameters(query) {
995
+ return this.ctx.request({
996
+ method: "GET",
997
+ path: "/v1/{account_id}/search/services/parameters",
998
+ query
899
999
  });
900
1000
  }
901
1001
  };
902
1002
 
903
1003
  // src/resources/posts.ts
904
- function buildFormData2(scalars, attachments) {
905
- const form = new FormData();
906
- for (const [key, value] of Object.entries(scalars)) {
907
- if (value !== void 0 && value !== null) {
908
- form.append(key, String(value));
909
- }
910
- }
911
- for (const attachment of attachments ?? []) {
912
- if (attachment instanceof File) {
913
- form.append("attachments", attachment);
914
- } else {
915
- form.append("attachments", new Blob([attachment]));
916
- }
917
- }
918
- return form;
919
- }
920
1004
  var PostsResource = class {
921
1005
  constructor(ctx) {
922
1006
  this.ctx = ctx;
923
1007
  }
924
- /** List the account's own posts. `GET /v1/posts` */
925
- list(params) {
1008
+ /** List comments on a post. `GET /v1/{account_id}/posts/{post_id}/comments` */
1009
+ listComments(postId, params) {
926
1010
  return this.ctx.request({
927
1011
  method: "GET",
928
- path: "/v1/posts",
1012
+ path: `/v1/{account_id}/posts/${postId}/comments`,
929
1013
  ...params ? { query: params } : {}
930
1014
  });
931
1015
  }
1016
+ /** Get a single post. `GET /v1/{account_id}/posts/{post_id}` */
1017
+ get(postId) {
1018
+ return this.ctx.request({
1019
+ method: "GET",
1020
+ path: `/v1/{account_id}/posts/${postId}`
1021
+ });
1022
+ }
1023
+ /** Delete a post (bodyless, 204). `DELETE /v1/{account_id}/posts/{post_id}` */
1024
+ delete(postId) {
1025
+ return this.ctx.request({
1026
+ method: "DELETE",
1027
+ path: `/v1/{account_id}/posts/${postId}`
1028
+ });
1029
+ }
932
1030
  /**
933
- * Create a new post. Always sent as multipart/form-data (the API only accepts
934
- * multipart for this endpoint). `POST /v1/posts`
1031
+ * Publish a new post. Always `application/json` — attachments (if any) are
1032
+ * base64-encoded objects, never `FormData`/multipart.
1033
+ * `POST /v1/{account_id}/posts`
935
1034
  */
936
1035
  create(body) {
937
- const { attachments, video_thumbnail, ...scalars } = body;
938
- const form = buildFormData2(scalars, attachments);
939
- if (video_thumbnail) {
940
- if (video_thumbnail instanceof File) {
941
- form.append("video_thumbnail", video_thumbnail);
942
- } else {
943
- form.append("video_thumbnail", new Blob([video_thumbnail]));
944
- }
945
- }
946
1036
  return this.ctx.request({
947
1037
  method: "POST",
948
- path: "/v1/posts",
949
- body: form,
950
- accountIdIn: "body"
1038
+ path: "/v1/{account_id}/posts",
1039
+ body
951
1040
  });
952
1041
  }
953
- /** Get a single post. `GET /v1/posts/{post_id}` */
954
- get(postId) {
1042
+ /**
1043
+ * List a user's own posts. `GET /v1/{account_id}/users/{user_id}/posts`
1044
+ * Was `profiles.listPosts`.
1045
+ */
1046
+ listUserPosts(userId, params) {
955
1047
  return this.ctx.request({
956
1048
  method: "GET",
957
- path: `/v1/posts/${postId}`
1049
+ path: `/v1/{account_id}/users/${userId}/posts`,
1050
+ ...params ? { query: params } : {}
958
1051
  });
959
1052
  }
960
- /** List comments on a post. `GET /v1/posts/{post_id}/comments` */
961
- listComments(postId, params) {
1053
+ /** List reactions on a post. `GET /v1/{account_id}/posts/{post_id}/reactions` */
1054
+ listReactions(postId, params) {
962
1055
  return this.ctx.request({
963
1056
  method: "GET",
964
- path: `/v1/posts/${postId}/comments`,
1057
+ path: `/v1/{account_id}/posts/${postId}/reactions`,
965
1058
  ...params ? { query: params } : {}
966
1059
  });
967
1060
  }
1061
+ /** React to a post. `POST /v1/{account_id}/posts/{post_id}/reactions` */
1062
+ react(postId, body) {
1063
+ return this.ctx.request({
1064
+ method: "POST",
1065
+ path: `/v1/{account_id}/posts/${postId}/reactions`,
1066
+ body
1067
+ });
1068
+ }
1069
+ /**
1070
+ * Remove this account's reaction from a post — a **DELETE-with-body**:
1071
+ * the reaction value to remove travels in the JSON body, not the path.
1072
+ * `DELETE /v1/{account_id}/posts/{post_id}/reactions`
1073
+ */
1074
+ unreact(postId, body) {
1075
+ return this.ctx.request({
1076
+ method: "DELETE",
1077
+ path: `/v1/{account_id}/posts/${postId}/reactions`,
1078
+ body
1079
+ });
1080
+ }
968
1081
  /**
969
- * Comment on a post. Always sent as multipart/form-data. `POST /v1/posts/{post_id}/comments`
1082
+ * List a user's reactions. `GET /v1/{account_id}/users/{user_id}/reactions`
1083
+ * Was `profiles.listReactions`.
970
1084
  */
971
- comment(postId, body) {
972
- const { attachments, ...scalars } = body;
1085
+ listUserReactions(userId, params) {
1086
+ return this.ctx.request({
1087
+ method: "GET",
1088
+ path: `/v1/{account_id}/users/${userId}/reactions`,
1089
+ ...params ? { query: params } : {}
1090
+ });
1091
+ }
1092
+ /**
1093
+ * List the connected account's own saved posts (a private bookmark list)
1094
+ * — newest-saved-first. A self resource: there is no other-member
1095
+ * saved-posts view, so this takes no target param. Each item is a
1096
+ * PREVIEW — `snippet` capped at ≤140 chars, never the full post body.
1097
+ * `GET /v1/{account_id}/saved-posts`
1098
+ *
1099
+ * @param params - optional `limit` (default 20, a page-aligned lower
1100
+ * bound) and an opaque `cursor` from a prior response.
1101
+ */
1102
+ listSaved(params) {
1103
+ return this.ctx.request({
1104
+ method: "GET",
1105
+ path: "/v1/{account_id}/saved-posts",
1106
+ ...params ? { query: params } : {}
1107
+ });
1108
+ }
1109
+ /**
1110
+ * Save a post to the connected account's private bookmark list. Any post
1111
+ * may be saved — it does not notify the author and is never visible to
1112
+ * third parties. Idempotent: saving an already-saved post re-asserts
1113
+ * `saved:true`.
1114
+ * `POST /v1/{account_id}/saved-posts`
1115
+ *
1116
+ * @param postId - the post to save. Accepts `urn:li:activity:<id>` or a
1117
+ * bare numeric `<id>` — both normalize to the same target. Any other
1118
+ * shape (`urn:li:ugcPost:…`, `urn:li:share:…`, a URL) is rejected with
1119
+ * `400 INVALID_REQUEST`.
1120
+ */
1121
+ save(postId) {
1122
+ const body = { post_id: postId };
973
1123
  return this.ctx.request({
974
1124
  method: "POST",
975
- path: `/v1/posts/${postId}/comments`,
976
- body: buildFormData2(scalars, attachments),
977
- accountIdIn: "body"
1125
+ path: "/v1/{account_id}/saved-posts",
1126
+ body
978
1127
  });
979
1128
  }
980
- /** List reactions on a post. `GET /v1/posts/{post_id}/reactions` */
981
- listReactions(postId, params) {
1129
+ /**
1130
+ * Unsave a post — the reverse of {@link save}, over the SAME underlying
1131
+ * save-state call (a distinct product endpoint, one substrate call).
1132
+ * Idempotent: unsaving a not-currently-saved post re-asserts
1133
+ * `saved:false`.
1134
+ * `DELETE /v1/{account_id}/saved-posts/{post_id}`
1135
+ *
1136
+ * @param postId - the post to unsave, same accepted shapes as {@link save}.
1137
+ */
1138
+ unsave(postId) {
1139
+ return this.ctx.request({
1140
+ method: "DELETE",
1141
+ path: `/v1/{account_id}/saved-posts/${postId}`
1142
+ });
1143
+ }
1144
+ };
1145
+
1146
+ // src/resources/comments.ts
1147
+ var CommentsResource = class {
1148
+ constructor(ctx) {
1149
+ this.ctx = ctx;
1150
+ }
1151
+ /**
1152
+ * List comments authored by a user, each embedding the post it was made
1153
+ * on. `GET /v1/{account_id}/users/{user_id}/comments`
1154
+ * Was `profiles.listComments`.
1155
+ */
1156
+ listUserComments(userId, params) {
982
1157
  return this.ctx.request({
983
1158
  method: "GET",
984
- path: `/v1/posts/${postId}/reactions`,
1159
+ path: `/v1/{account_id}/users/${userId}/comments`,
985
1160
  ...params ? { query: params } : {}
986
1161
  });
987
1162
  }
988
- /** React to a post. `POST /v1/posts/{post_id}/reactions` */
989
- react(postId, body) {
1163
+ /**
1164
+ * Publish a comment on a post. `POST /v1/{account_id}/posts/{post_id}/comments`
1165
+ * Was `posts.comment`. Attachments (at most one) travel as a base64
1166
+ * object, never `FormData`.
1167
+ */
1168
+ create(postId, body) {
990
1169
  return this.ctx.request({
991
1170
  method: "POST",
992
- path: `/v1/posts/${postId}/reactions`,
993
- body,
994
- accountIdIn: "body"
1171
+ path: `/v1/{account_id}/posts/${postId}/comments`,
1172
+ body
1173
+ });
1174
+ }
1175
+ /**
1176
+ * Edit the text of this account's own comment.
1177
+ * `PATCH /v1/{account_id}/posts/{post_id}/comments/{comment_id}`
1178
+ */
1179
+ edit(postId, commentId, body) {
1180
+ return this.ctx.request({
1181
+ method: "PATCH",
1182
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}`,
1183
+ body
1184
+ });
1185
+ }
1186
+ /**
1187
+ * Delete this account's own comment (bodyless, 204).
1188
+ * `DELETE /v1/{account_id}/posts/{post_id}/comments/{comment_id}`
1189
+ */
1190
+ delete(postId, commentId) {
1191
+ return this.ctx.request({
1192
+ method: "DELETE",
1193
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}`
1194
+ });
1195
+ }
1196
+ /**
1197
+ * Reply to a comment. Shares its path with `edit` — POST creates a reply
1198
+ * under the target comment, PATCH edits the target comment's own text.
1199
+ * `POST /v1/{account_id}/posts/{post_id}/comments/{comment_id}`
1200
+ */
1201
+ reply(postId, commentId, body) {
1202
+ return this.ctx.request({
1203
+ method: "POST",
1204
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}`,
1205
+ body
1206
+ });
1207
+ }
1208
+ /**
1209
+ * List replies to a comment, most-recent-first.
1210
+ * `GET /v1/{account_id}/posts/{post_id}/comments/{comment_id}/replies`
1211
+ */
1212
+ listReplies(postId, commentId, params) {
1213
+ return this.ctx.request({
1214
+ method: "GET",
1215
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}/replies`,
1216
+ ...params ? { query: params } : {}
1217
+ });
1218
+ }
1219
+ /**
1220
+ * List reactions on a comment.
1221
+ * `GET /v1/{account_id}/posts/{post_id}/comments/{comment_id}/reactions`
1222
+ */
1223
+ listReactions(postId, commentId, params) {
1224
+ return this.ctx.request({
1225
+ method: "GET",
1226
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}/reactions`,
1227
+ ...params ? { query: params } : {}
1228
+ });
1229
+ }
1230
+ /**
1231
+ * React to a comment. `POST /v1/{account_id}/posts/{post_id}/comments/{comment_id}/reactions`
1232
+ */
1233
+ addReaction(postId, commentId, body) {
1234
+ return this.ctx.request({
1235
+ method: "POST",
1236
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}/reactions`,
1237
+ body
1238
+ });
1239
+ }
1240
+ /**
1241
+ * Remove this account's reaction from a comment — a **DELETE-with-body**:
1242
+ * the reaction value to remove travels in the JSON body, not the path.
1243
+ * `DELETE /v1/{account_id}/posts/{post_id}/comments/{comment_id}/reactions`
1244
+ */
1245
+ removeReaction(postId, commentId, body) {
1246
+ return this.ctx.request({
1247
+ method: "DELETE",
1248
+ path: `/v1/{account_id}/posts/${postId}/comments/${commentId}/reactions`,
1249
+ body
995
1250
  });
996
1251
  }
997
1252
  };
998
1253
 
999
- // src/resources/sales-navigator.ts
1000
- function buildFormData3(scalars, attachments, voice_message, video_message) {
1001
- const form = new FormData();
1002
- for (const [key, value] of Object.entries(scalars)) {
1003
- if (value !== void 0 && value !== null) {
1004
- if (Array.isArray(value)) {
1005
- for (const item of value) {
1006
- form.append(key, String(item));
1007
- }
1008
- } else {
1009
- form.append(key, String(value));
1010
- }
1011
- }
1254
+ // src/resources/profile.ts
1255
+ var ProfileResource = class {
1256
+ constructor(ctx) {
1257
+ this.ctx = ctx;
1012
1258
  }
1013
- for (const attachment of attachments ?? []) {
1014
- if (attachment instanceof File) {
1015
- form.append("attachments", attachment);
1016
- } else {
1017
- form.append("attachments", new Blob([attachment]));
1018
- }
1259
+ /**
1260
+ * Read the connected account's premium subscription — its full entitlement
1261
+ * inventory, primary plan title, and LinkedIn management links.
1262
+ * `GET /v1/{account_id}/profile/subscription`
1263
+ *
1264
+ * A free account is a valid, non-error result: `has_premium` is `false`,
1265
+ * `plan_title` is `null`, and `subscriptions` is empty. Read
1266
+ * `subscriptions` for the complete set of active plans; the flat fields
1267
+ * mirror the primary plan only.
1268
+ */
1269
+ subscription() {
1270
+ return this.ctx.request({
1271
+ method: "GET",
1272
+ path: `/v1/{account_id}/profile/subscription`
1273
+ });
1019
1274
  }
1020
- if (voice_message) {
1021
- if (voice_message instanceof File) {
1022
- form.append("voice_message", voice_message);
1023
- } else {
1024
- form.append("voice_message", new Blob([voice_message]));
1025
- }
1275
+ /**
1276
+ * Read the connected account's performance headline metrics — profile
1277
+ * viewers, followers, post impressions, and search appearances.
1278
+ * `GET /v1/{account_id}/profile/analytics`
1279
+ *
1280
+ * Each metric is a headline scalar over a fixed reporting window set by
1281
+ * LinkedIn (viewers 90d, impressions 7d, search last completed week,
1282
+ * followers running total); there is no window selector. A `count` of `0`
1283
+ * is a real zero; a per-metric `null` means that one card was unavailable.
1284
+ */
1285
+ analytics() {
1286
+ return this.ctx.request({
1287
+ method: "GET",
1288
+ path: `/v1/{account_id}/profile/analytics`
1289
+ });
1026
1290
  }
1027
- if (video_message) {
1028
- if (video_message instanceof File) {
1029
- form.append("video_message", video_message);
1030
- } else {
1031
- form.append("video_message", new Blob([video_message]));
1032
- }
1291
+ /**
1292
+ * List the people who recently viewed the connected account's profile,
1293
+ * cursor-paginated and classified by disclosure fidelity.
1294
+ * `GET /v1/{account_id}/profile/visitors`
1295
+ *
1296
+ * Each item's `kind` is `identified`, `semi-anonymous`, or `aggregate` —
1297
+ * a Premium account sees identified viewers, a free account is capped at a
1298
+ * lower fidelity (still a `200`, never a permission error). Page with the
1299
+ * returned `cursor`: a non-null `cursor` means more may exist, even if a
1300
+ * page held zero identified individuals; `cursor: null` means exhausted.
1301
+ *
1302
+ * @param params - `limit` (1–100, default 20) and an opaque `cursor` from
1303
+ * a prior response.
1304
+ */
1305
+ visitors(params) {
1306
+ return this.ctx.request({
1307
+ method: "GET",
1308
+ path: `/v1/{account_id}/profile/visitors`,
1309
+ ...params ? { query: params } : {}
1310
+ });
1033
1311
  }
1034
- return form;
1035
- }
1036
- var SalesNavigatorResource = class {
1312
+ /**
1313
+ * Read the connected account's Social Selling Index — the overall score,
1314
+ * its four pillar breakdowns, and industry/network percentile ranks.
1315
+ * `GET /v1/{account_id}/profile/ssi`
1316
+ *
1317
+ * A low score on a low-activity account is a normal `200`; a genuine
1318
+ * zero-activity account returns all scalars `null` with `active_seat`
1319
+ * `false`. Scores preserve full float precision.
1320
+ */
1321
+ ssi() {
1322
+ return this.ctx.request({
1323
+ method: "GET",
1324
+ path: `/v1/{account_id}/profile/ssi`
1325
+ });
1326
+ }
1327
+ };
1328
+
1329
+ // src/resources/groups.ts
1330
+ var GroupsResource = class {
1037
1331
  constructor(ctx) {
1038
1332
  this.ctx = ctx;
1039
1333
  }
1040
1334
  /**
1041
- * Search LinkedIn members using the full Sales Navigator filter set.
1042
- * `POST /v1/sales-navigator/search/people`
1043
- * Requires tier `sn`. Returns `TIER_NOT_ACTIVE` (403) when the seat lacks it.
1335
+ * List the LinkedIn groups a member belongs to, each enriched to full
1336
+ * group detail. `GET /v1/{account_id}/profile/groups`
1337
+ *
1338
+ * Reads the connected account's own groups by default; pass `{ profile }`
1339
+ * (a member's public identifier) to read another member's group set. The
1340
+ * `id` on each returned group is the same id `members()` consumes.
1341
+ *
1342
+ * @param params - optional `{ profile }` to target another member, plus
1343
+ * `limit` and an opaque `cursor` for pagination.
1044
1344
  */
1045
- searchPeople(body, params) {
1345
+ list(params) {
1046
1346
  return this.ctx.request({
1047
- method: "POST",
1048
- path: "/v1/sales-navigator/search/people",
1049
- body,
1347
+ method: "GET",
1348
+ path: `/v1/{account_id}/profile/groups`,
1050
1349
  ...params ? { query: params } : {}
1051
1350
  });
1052
1351
  }
1053
1352
  /**
1054
- * Search LinkedIn companies using the full Sales Navigator company filter set.
1055
- * `POST /v1/sales-navigator/search/companies`
1353
+ * Retrieve a single LinkedIn group's full detail — its name, description,
1354
+ * member count, membership status, and admin contact.
1355
+ * `GET /v1/{account_id}/groups/{group}`
1356
+ *
1357
+ * @param group - the group's id or LinkedIn group URL.
1056
1358
  */
1057
- searchCompanies(body, params) {
1359
+ get(group) {
1058
1360
  return this.ctx.request({
1059
- method: "POST",
1060
- path: "/v1/sales-navigator/search/companies",
1061
- body,
1361
+ method: "GET",
1362
+ path: `/v1/{account_id}/groups/${group}`
1363
+ });
1364
+ }
1365
+ /**
1366
+ * List a group's members, cursor-paginated, each carrying its profile URL,
1367
+ * name, and headline. `GET /v1/{account_id}/groups/{group}/members`
1368
+ *
1369
+ * Pass `{ name }` to search the roster by member name — the folded-in
1370
+ * member search, not a separate endpoint.
1371
+ *
1372
+ * @param group - the group's id or LinkedIn group URL.
1373
+ * @param params - optional `{ name }` filter plus `limit` and an opaque
1374
+ * `cursor` for pagination.
1375
+ */
1376
+ members(group, params) {
1377
+ return this.ctx.request({
1378
+ method: "GET",
1379
+ path: `/v1/{account_id}/groups/${group}/members`,
1062
1380
  ...params ? { query: params } : {}
1063
1381
  });
1064
1382
  }
1383
+ };
1384
+
1385
+ // src/resources/feed.ts
1386
+ var FeedResource = class {
1387
+ constructor(ctx) {
1388
+ this.ctx = ctx;
1389
+ }
1065
1390
  /**
1066
- * Resolve human-readable terms to opaque Sales Navigator filter IDs.
1067
- * `GET /v1/sales-navigator/search/parameters`
1391
+ * Read the connected account's LinkedIn home feed as agent-actionable posts.
1392
+ * `GET /v1/{account_id}/feed/home`
1393
+ *
1394
+ * Pass `{ sort }` to choose the order: `recent` (reverse-chronological,
1395
+ * the default) or `relevant` (LinkedIn's ranked "top" feed). The feed is an
1396
+ * unbounded, reordering stream with no total count — walk it with the
1397
+ * returned `cursor` until `cursor` is null. When a `cursor` is supplied its
1398
+ * carrier is authoritative and `sort` is ignored.
1399
+ *
1400
+ * @param params - optional `{ sort }`, `limit` (1–100, default 20), and an
1401
+ * opaque `cursor` from a prior response.
1068
1402
  */
1069
- getParameters(params) {
1403
+ home(params) {
1070
1404
  return this.ctx.request({
1071
1405
  method: "GET",
1072
- path: "/v1/sales-navigator/search/parameters",
1073
- query: params
1406
+ path: `/v1/{account_id}/feed/home`,
1407
+ ...params ? { query: params } : {}
1074
1408
  });
1075
1409
  }
1410
+ };
1411
+
1412
+ // src/resources/inboxes.ts
1413
+ var InboxesResource = class {
1414
+ constructor(ctx) {
1415
+ this.ctx = ctx;
1416
+ }
1076
1417
  /**
1077
- * Start a new Sales Navigator chat. Accepts optional `attachments[]`,
1078
- * `voice_message`, and `video_message` — when present, the request is sent
1079
- * as `multipart/form-data`. `POST /v1/sales-navigator/chats`
1418
+ * List the account's inboxes: its own personal inbox, plus, when the
1419
+ * company product is attached, one entry per company page (id like
1420
+ * `"COMPANY_83734124_PRIMARY"`). `GET /v1/{account_id}/inboxes`
1421
+ *
1422
+ * Company inboxes carry `reply_only: true`: reply to an existing
1423
+ * conversation via `messaging.sendMessage()`, never `startChat()`.
1424
+ * `company_id` is resolved by correlating the page name against the
1425
+ * account's managed pages. An uncorrelatable page reports
1426
+ * `company_id: null`, never a fabricated id. When no company inbox
1427
+ * exists, `hint` names the Company Pages reconnect requirement instead of
1428
+ * an empty list.
1429
+ *
1430
+ * Beta.
1431
+ *
1432
+ * @param params - optional `{ kind }` (`"personal"` or `"company"`)
1433
+ * and/or `{ company_id }` filters. Omit to list every inbox.
1434
+ *
1435
+ * @example
1436
+ * const acc = curviate.account("acc_YOUR_ACCOUNT_ID");
1437
+ * const { items, hint } = await acc.inboxes.list({ kind: "company" });
1438
+ * const companyPage = (items ?? []).find((i) => i.kind === "company");
1439
+ * console.log(companyPage?.id, companyPage?.name, hint);
1440
+ * // companyPage?.id is what you pass to listChats(), e.g. "COMPANY_83734124_PRIMARY"
1080
1441
  */
1081
- startChat(body) {
1082
- const { attachments, voice_message, video_message, ...scalars } = body;
1083
- const hasFiles = attachments && attachments.length > 0 || voice_message || video_message;
1084
- if (hasFiles) {
1085
- return this.ctx.request({
1086
- method: "POST",
1087
- path: "/v1/sales-navigator/chats",
1088
- body: buildFormData3(
1089
- scalars,
1090
- attachments,
1091
- voice_message,
1092
- video_message
1093
- ),
1094
- accountIdIn: "body"
1095
- });
1096
- }
1442
+ list(params) {
1443
+ return this.ctx.request({
1444
+ method: "GET",
1445
+ path: `/v1/{account_id}/inboxes`,
1446
+ ...params ? { query: params } : {}
1447
+ });
1448
+ }
1449
+ /**
1450
+ * List an inbox's conversations, newest-activity-first, cursor-paginated.
1451
+ * `GET /v1/{account_id}/inboxes/{inbox_id}/chats`
1452
+ *
1453
+ * Every chat's `id` is already send-ready: pass it directly to
1454
+ * `messaging.sendMessage(chatId, { text })` to reply. A company inbox's
1455
+ * chat id (e.g. `"COMPANY_83734124_2-YTQ3ODU3Njgt"`) replies AS THE PAGE,
1456
+ * no separate parameter needed. This is the full reply-as-a-page flow:
1457
+ *
1458
+ * ```ts
1459
+ * const { items: inboxes } = await acc.inboxes.list({ kind: "company" });
1460
+ * const pageInbox = inboxes[0]!; // e.g. id "COMPANY_83734124_PRIMARY", reply_only: true
1461
+ *
1462
+ * const { items: chats } = await acc.inboxes.listChats(pageInbox.id);
1463
+ * const chatId = chats[0]!.id as string; // e.g. "COMPANY_83734124_2-YTQ3ODU3Njgt"
1464
+ *
1465
+ * const sent = await acc.messaging.sendMessage(chatId, { text: "Thanks for reaching out!" });
1466
+ * console.log(sent.sent_as); // { kind: "company", company_id: "112013061", name: "Acme Inc" }
1467
+ * ```
1468
+ *
1469
+ * Works identically for personal (`CLASSIC_`) inboxes: pass a `CLASSIC_`
1470
+ * chat id and the send goes out as the connected member. An unknown
1471
+ * `inbox_id` returns `RESOURCE_NOT_FOUND` (404). Beta.
1472
+ *
1473
+ * @param inboxId - the inbox id from `list()` (e.g. `"CLASSIC_PRIMARY"`
1474
+ * or `"COMPANY_83734124_PRIMARY"`).
1475
+ * @param params - optional `limit` (1–25, default 20) and an opaque
1476
+ * `cursor` for pagination.
1477
+ */
1478
+ listChats(inboxId, params) {
1479
+ return this.ctx.request({
1480
+ method: "GET",
1481
+ path: `/v1/{account_id}/inboxes/${inboxId}/chats`,
1482
+ ...params ? { query: params } : {}
1483
+ });
1484
+ }
1485
+ };
1486
+
1487
+ // src/resources/notifications.ts
1488
+ var NotificationsResource = class {
1489
+ constructor(ctx) {
1490
+ this.ctx = ctx;
1491
+ }
1492
+ /**
1493
+ * List the connected account's notification cards — newest first, plus
1494
+ * the account-level `unread_count` (the unseen badge — NOT a count of
1495
+ * `items`) and `latest_published_at` (a cheap poll watermark). Injected
1496
+ * promo/suggestion cards are included and flagged (`injected:true`),
1497
+ * never filtered.
1498
+ * `GET /v1/{account_id}/notifications`
1499
+ *
1500
+ * @param params - optional `filter` (7-token enum, default `all`),
1501
+ * `limit` (1-100, default 20), and an opaque `cursor` from a prior
1502
+ * response. Page until the returned `cursor` is `null`.
1503
+ */
1504
+ list(params) {
1505
+ return this.ctx.request({
1506
+ method: "GET",
1507
+ path: "/v1/{account_id}/notifications",
1508
+ ...params ? { query: params } : {}
1509
+ });
1510
+ }
1511
+ /**
1512
+ * Delete one of the connected account's own notification cards by its
1513
+ * card urn. A self-action — no third party is notified. This cannot be
1514
+ * undone.
1515
+ *
1516
+ * **Safe to retry.** Idempotent: deleting a card you already deleted
1517
+ * succeeds with `200`, not an error — a client that retries after a
1518
+ * timeout is never punished for it. Only a `card_urn` that never existed
1519
+ * on this account 404s.
1520
+ *
1521
+ * **Timing.** The removal takes effect within a few seconds; a
1522
+ * {@link list} read immediately after this call may still include the
1523
+ * card for a moment. That is not a failure signal — the `200` is already
1524
+ * confirmation the card was resolved and the removal accepted. Re-read
1525
+ * after a short delay, not instantly, if you need to visually confirm.
1526
+ *
1527
+ * `DELETE /v1/{account_id}/notifications/{card_urn}`
1528
+ *
1529
+ * @param cardUrn - the card's entity urn, raw/unencoded (the `card_urn`
1530
+ * field of a {@link list} item — `urn:li:fsd_notificationCard:(…)`,
1531
+ * NOT `object_urn`, which targets the wrong notification). The SDK
1532
+ * percent-encodes it into the path.
1533
+ */
1534
+ delete(cardUrn) {
1535
+ return this.ctx.request({
1536
+ method: "DELETE",
1537
+ path: `/v1/{account_id}/notifications/${encodeURIComponent(cardUrn)}`
1538
+ });
1539
+ }
1540
+ /**
1541
+ * Apply "show less like this" to the source of one of the connected
1542
+ * account's own notification cards — a self-action, same card handle and
1543
+ * timing/retry contract as {@link delete}. For network-activity cards (a
1544
+ * repost, comment, or reaction by your network) this **removes the
1545
+ * card**, the same effect as `delete` — LinkedIn exposes no separate,
1546
+ * softer signal for these cards; this method exists so the calling
1547
+ * intent reads clearly, not because it behaves differently. This cannot
1548
+ * be undone.
1549
+ *
1550
+ * **Safe to retry**, same timing as {@link delete}: idempotent (a repeat
1551
+ * is a `200`, not an error), effective within a few seconds — a
1552
+ * still-visible card on an immediate {@link list} re-read is not a
1553
+ * failure signal.
1554
+ *
1555
+ * `POST /v1/{account_id}/notifications/{card_urn}/show-less`
1556
+ *
1557
+ * @param cardUrn - the card's entity urn, raw/unencoded (see
1558
+ * {@link delete}). The SDK percent-encodes it into the path.
1559
+ */
1560
+ showLess(cardUrn) {
1097
1561
  return this.ctx.request({
1098
1562
  method: "POST",
1099
- path: "/v1/sales-navigator/chats",
1100
- body: scalars,
1101
- accountIdIn: "body"
1563
+ path: `/v1/{account_id}/notifications/${encodeURIComponent(cardUrn)}/show-less`
1102
1564
  });
1103
1565
  }
1566
+ };
1567
+
1568
+ // src/resources/sales-navigator.ts
1569
+ var SalesNavigatorResource = class {
1570
+ constructor(ctx) {
1571
+ this.ctx = ctx;
1572
+ }
1104
1573
  /**
1105
- * Retrieve a LinkedIn profile with Sales Navigator enrichment.
1106
- * `GET /v1/sales-navigator/profiles/{identifier}`
1574
+ * Search LinkedIn members using the full Sales Navigator filter set.
1575
+ * `POST /v1/{account_id}/sales-navigator/search/people`
1576
+ * Requires tier `sn`. Returns `TIER_NOT_ACTIVE` (403) when the seat lacks it.
1577
+ */
1578
+ searchPeople(body, query) {
1579
+ return this.ctx.request({
1580
+ method: "POST",
1581
+ path: "/v1/{account_id}/sales-navigator/search/people",
1582
+ body,
1583
+ ...query ? { query } : {}
1584
+ });
1585
+ }
1586
+ /**
1587
+ * Search LinkedIn companies using the full Sales Navigator company filter set.
1588
+ * `POST /v1/{account_id}/sales-navigator/search/companies`
1589
+ */
1590
+ searchCompanies(body, query) {
1591
+ return this.ctx.request({
1592
+ method: "POST",
1593
+ path: "/v1/{account_id}/sales-navigator/search/companies",
1594
+ body,
1595
+ ...query ? { query } : {}
1596
+ });
1597
+ }
1598
+ /**
1599
+ * Resolve human-readable terms to opaque Sales Navigator filter IDs.
1600
+ * `GET /v1/{account_id}/sales-navigator/search/parameters`
1107
1601
  */
1108
- getProfile(identifier, params) {
1602
+ getParameters(query) {
1109
1603
  return this.ctx.request({
1110
1604
  method: "GET",
1111
- path: `/v1/sales-navigator/profiles/${identifier}`,
1112
- // cast needed: linkedin_sections is string[] but transport encodes arrays as repeated params
1113
- ...params ? { query: params } : {}
1605
+ path: "/v1/{account_id}/sales-navigator/search/parameters",
1606
+ query
1114
1607
  });
1115
1608
  }
1116
1609
  /**
1117
- * Save a Sales Navigator member into a lead list.
1118
- * `POST /v1/sales-navigator/lead-lists/{list_id}/save`
1119
- *
1120
- * **BREAKING (2026-07-04):** replaces the retired v1
1121
- * `saveLead(userId, { account_id, list_id? })` — there is no alias. The
1122
- * list is now mandatory and addresses the path; the member id travels in
1123
- * the body alongside `account_id` (the save endpoints carry no query
1124
- * params, so `account_id` has nowhere else to go).
1610
+ * Start a new Sales Navigator chat. `attachments[]`, when supplied, carry
1611
+ * base64-encoded file bytes — always sent as JSON, never multipart.
1612
+ * `POST /v1/{account_id}/sales-navigator/chats`
1125
1613
  */
1126
- saveLead(input) {
1127
- const { list_id, ...body } = input;
1614
+ startChat(body) {
1128
1615
  return this.ctx.request({
1129
1616
  method: "POST",
1130
- path: `/v1/sales-navigator/lead-lists/${list_id}/save`,
1131
- body,
1132
- accountIdIn: "body"
1617
+ path: "/v1/{account_id}/sales-navigator/chats",
1618
+ body
1133
1619
  });
1134
1620
  }
1135
1621
  /**
1136
- * Trigger a re-sync of the account's Sales Navigator message history.
1137
- * `GET /v1/sales-navigator/messages/sync`
1622
+ * Retrieve a LinkedIn profile with Sales Navigator enrichment.
1623
+ * `GET /v1/{account_id}/sales-navigator/profiles/{identifier}`
1138
1624
  */
1139
- syncMessages(params) {
1625
+ getProfile(identifier, query) {
1140
1626
  return this.ctx.request({
1141
1627
  method: "GET",
1142
- path: "/v1/sales-navigator/messages/sync",
1143
- query: params
1628
+ path: `/v1/{account_id}/sales-navigator/profiles/${identifier}`,
1629
+ // cast needed: with_sections is string[] but transport encodes arrays as repeated params
1630
+ ...query ? { query } : {}
1144
1631
  });
1145
1632
  }
1146
- // ─── v2 list surface ──────────────────────────────────────────────────────
1147
1633
  /**
1148
1634
  * List the saved-account (company) lists on the operator's Sales Navigator seat.
1149
- * `GET /v1/sales-navigator/account-lists`
1635
+ * `GET /v1/{account_id}/sales-navigator/account-lists`
1150
1636
  */
1151
1637
  accountLists(query) {
1152
1638
  return this.ctx.request({
1153
1639
  method: "GET",
1154
- path: "/v1/sales-navigator/account-lists",
1640
+ path: "/v1/{account_id}/sales-navigator/account-lists",
1155
1641
  ...query ? { query } : {}
1156
1642
  });
1157
1643
  }
1158
1644
  /**
1159
1645
  * List the saved-lead (member) lists on the operator's Sales Navigator seat.
1160
- * `GET /v1/sales-navigator/lead-lists`
1646
+ * `GET /v1/{account_id}/sales-navigator/lead-lists`
1161
1647
  */
1162
1648
  leadLists(query) {
1163
1649
  return this.ctx.request({
1164
1650
  method: "GET",
1165
- path: "/v1/sales-navigator/lead-lists",
1651
+ path: "/v1/{account_id}/sales-navigator/lead-lists",
1166
1652
  ...query ? { query } : {}
1167
1653
  });
1168
1654
  }
1169
1655
  /**
1170
1656
  * Browse the saved accounts (companies) in one account list. Pass optional
1171
1657
  * `persona` / `filter` / `sort_by` / `sort_order` filters in the body.
1172
- * `POST /v1/sales-navigator/account-lists/{list_id}`
1658
+ * `POST /v1/{account_id}/sales-navigator/account-lists/{list_id}`
1173
1659
  */
1174
1660
  browseAccountList(listId, body, query) {
1175
1661
  return this.ctx.request({
1176
1662
  method: "POST",
1177
- path: `/v1/sales-navigator/account-lists/${listId}`,
1663
+ path: `/v1/{account_id}/sales-navigator/account-lists/${listId}`,
1178
1664
  body: body ?? {},
1179
1665
  ...query ? { query } : {}
1180
1666
  });
@@ -1182,19 +1668,20 @@ var SalesNavigatorResource = class {
1182
1668
  /**
1183
1669
  * Browse the saved leads (members) in one lead list. Pass optional
1184
1670
  * `spotlight` / `sort_by` / `sort_order` filters in the body.
1185
- * `POST /v1/sales-navigator/lead-lists/{list_id}`
1671
+ * `POST /v1/{account_id}/sales-navigator/lead-lists/{list_id}`
1186
1672
  */
1187
1673
  browseLeadList(listId, body, query) {
1188
1674
  return this.ctx.request({
1189
1675
  method: "POST",
1190
- path: `/v1/sales-navigator/lead-lists/${listId}`,
1676
+ path: `/v1/{account_id}/sales-navigator/lead-lists/${listId}`,
1191
1677
  body: body ?? {},
1192
1678
  ...query ? { query } : {}
1193
1679
  });
1194
1680
  }
1195
1681
  /**
1196
- * Save a LinkedIn company into an account list.
1197
- * `POST /v1/sales-navigator/account-lists/{list_id}/save`
1682
+ * Save a LinkedIn company into an account list. Body is just `{company_id}`
1683
+ * — `account_id` lives in the path, not the body.
1684
+ * `POST /v1/{account_id}/sales-navigator/account-lists/{list_id}/save`
1198
1685
  *
1199
1686
  * No `saved` boolean is invented — a `2xx` response body is the success
1200
1687
  * signal (the substrate returns no success flag).
@@ -1203,9 +1690,35 @@ var SalesNavigatorResource = class {
1203
1690
  const { list_id, ...body } = input;
1204
1691
  return this.ctx.request({
1205
1692
  method: "POST",
1206
- path: `/v1/sales-navigator/account-lists/${list_id}/save`,
1693
+ path: `/v1/{account_id}/sales-navigator/account-lists/${list_id}/save`,
1694
+ body
1695
+ });
1696
+ }
1697
+ /**
1698
+ * Save a Sales Navigator member into a lead list. Body is just `{user_id}`
1699
+ * — `account_id` lives in the path, not the body.
1700
+ * `POST /v1/{account_id}/sales-navigator/lead-lists/{list_id}/save`
1701
+ */
1702
+ saveLead(input) {
1703
+ const { list_id, ...body } = input;
1704
+ return this.ctx.request({
1705
+ method: "POST",
1706
+ path: `/v1/{account_id}/sales-navigator/lead-lists/${list_id}/save`,
1707
+ body
1708
+ });
1709
+ }
1710
+ /**
1711
+ * Run a pasted Sales Navigator search/list URL directly.
1712
+ * `POST /v1/{account_id}/sales-navigator/search`
1713
+ * `url` is the only accepted body field. Response items are polymorphic,
1714
+ * each discriminated individually by its own `object`.
1715
+ */
1716
+ searchFromUrl(body, query) {
1717
+ return this.ctx.request({
1718
+ method: "POST",
1719
+ path: "/v1/{account_id}/sales-navigator/search",
1207
1720
  body,
1208
- accountIdIn: "body"
1721
+ ...query ? { query } : {}
1209
1722
  });
1210
1723
  }
1211
1724
  };
@@ -1226,268 +1739,295 @@ function resolveJobId(idOrUrl) {
1226
1739
  }
1227
1740
 
1228
1741
  // src/resources/recruiter.ts
1229
- function buildFormData4(scalars, attachments, voice_message, video_message) {
1230
- const form = new FormData();
1231
- for (const [key, value] of Object.entries(scalars)) {
1232
- if (value !== void 0 && value !== null) {
1233
- if (Array.isArray(value)) {
1234
- for (const item of value) {
1235
- form.append(key, String(item));
1236
- }
1237
- } else {
1238
- form.append(key, String(value));
1239
- }
1240
- }
1241
- }
1242
- for (const attachment of attachments ?? []) {
1243
- if (attachment instanceof File) {
1244
- form.append("attachments", attachment);
1245
- } else {
1246
- form.append("attachments", new Blob([attachment]));
1247
- }
1248
- }
1249
- if (voice_message) {
1250
- if (voice_message instanceof File) {
1251
- form.append("voice_message", voice_message);
1252
- } else {
1253
- form.append("voice_message", new Blob([voice_message]));
1254
- }
1255
- }
1256
- if (video_message) {
1257
- if (video_message instanceof File) {
1258
- form.append("video_message", video_message);
1259
- } else {
1260
- form.append("video_message", new Blob([video_message]));
1261
- }
1262
- }
1263
- return form;
1264
- }
1265
1742
  var RecruiterResource = class {
1266
1743
  constructor(ctx) {
1267
1744
  this.ctx = ctx;
1268
1745
  }
1269
1746
  /**
1270
- * Trigger a re-sync of the account's Recruiter message history.
1271
- * `GET /v1/recruiter/messages/sync`
1747
+ * Retrieve a Recruiter-enriched profile. `{identifier}` is `'me'`, the
1748
+ * target's public identifier, or a member id. Request specific profile
1749
+ * sections via `with_sections` (repeatable query param).
1750
+ * `GET /v1/{account_id}/recruiter/profiles/{user_id}`
1272
1751
  */
1273
- syncMessages(params) {
1752
+ getProfile(identifier, params) {
1274
1753
  return this.ctx.request({
1275
1754
  method: "GET",
1276
- path: "/v1/recruiter/messages/sync",
1277
- query: params
1755
+ path: `/v1/{account_id}/recruiter/profiles/${identifier}`,
1756
+ ...params ? { query: params } : {}
1278
1757
  });
1279
1758
  }
1280
1759
  /**
1281
- * Start a Recruiter chat (InMail). Accepts optional `attachments[]`,
1282
- * `voice_message`, and `video_message` — when present, the request is sent
1283
- * as `multipart/form-data`. `POST /v1/recruiter/chats`
1760
+ * Start a Recruiter chat (InMail). JSON-only — `subject` and `signature`
1761
+ * are REQUIRED; optional attachments ride the body as base64-encoded
1762
+ * objects (no multipart). `POST /v1/{account_id}/recruiter/chats`
1284
1763
  */
1285
1764
  startChat(body) {
1286
- const { attachments, voice_message, video_message, ...scalars } = body;
1287
- const hasFiles = attachments && attachments.length > 0 || voice_message || video_message;
1288
- if (hasFiles) {
1289
- return this.ctx.request({
1290
- method: "POST",
1291
- path: "/v1/recruiter/chats",
1292
- body: buildFormData4(
1293
- scalars,
1294
- attachments,
1295
- voice_message,
1296
- video_message
1297
- ),
1298
- accountIdIn: "body"
1299
- });
1300
- }
1301
1765
  return this.ctx.request({
1302
1766
  method: "POST",
1303
- path: "/v1/recruiter/chats",
1304
- body: scalars,
1305
- accountIdIn: "body"
1767
+ path: "/v1/{account_id}/recruiter/chats",
1768
+ body
1306
1769
  });
1307
1770
  }
1308
1771
  /**
1309
- * Retrieve a LinkedIn profile with Recruiter enrichment.
1310
- * `GET /v1/recruiter/profiles/{identifier}`
1772
+ * Search LinkedIn members using Recruiter filters. `limit`/`cursor` stay
1773
+ * TOP-LEVEL query params. `POST /v1/{account_id}/recruiter/search/people`
1311
1774
  */
1312
- getProfile(identifier, params) {
1775
+ searchPeople(body, params) {
1313
1776
  return this.ctx.request({
1314
- method: "GET",
1315
- path: `/v1/recruiter/profiles/${identifier}`,
1316
- // cast needed: linkedin_sections is string[] but transport encodes arrays as repeated params
1777
+ method: "POST",
1778
+ path: "/v1/{account_id}/recruiter/search/people",
1779
+ body,
1317
1780
  ...params ? { query: params } : {}
1318
1781
  });
1319
1782
  }
1320
1783
  /**
1321
- * Search LinkedIn members using Recruiter filters.
1322
- * `POST /v1/recruiter/search/people`
1784
+ * Resolve human-readable terms to opaque Recruiter filter ids. POST (was
1785
+ * GET): the body is a `source`-discriminated oneOf — `APPLICANTS`/`PIPELINE`
1786
+ * require `project_id`. `POST /v1/{account_id}/recruiter/search/parameters`
1323
1787
  */
1324
- searchPeople(body, params) {
1788
+ searchParameters(body, params) {
1325
1789
  return this.ctx.request({
1326
1790
  method: "POST",
1327
- path: "/v1/recruiter/search/people",
1791
+ path: "/v1/{account_id}/recruiter/search/parameters",
1328
1792
  body,
1329
1793
  ...params ? { query: params } : {}
1330
1794
  });
1331
1795
  }
1332
1796
  /**
1333
- * Resolve human-readable terms to opaque Recruiter filter IDs.
1334
- * `GET /v1/recruiter/search/parameters`
1797
+ * Search a project's talent pool. `channel_id` (the project's own
1798
+ * `RECRUITER_SEARCH` talent-pool channel) is REQUIRED. `limit`/`cursor` stay
1799
+ * TOP-LEVEL query params.
1800
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/talent-pool/search`
1335
1801
  */
1336
- getParameters(params) {
1802
+ searchTalentPool(projectId, body, params) {
1337
1803
  return this.ctx.request({
1338
- method: "GET",
1339
- path: "/v1/recruiter/search/parameters",
1340
- query: params
1804
+ method: "POST",
1805
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/talent-pool/search`,
1806
+ body,
1807
+ ...params ? { query: params } : {}
1808
+ });
1809
+ }
1810
+ /**
1811
+ * Search from a pasted Recruiter search / talent-pool / applicant URL —
1812
+ * nothing else in the body. The response is a 3-way oneOf (people-search,
1813
+ * job-applicant list, or pipeline-candidate list) keyed by the URL kind.
1814
+ * `POST /v1/{account_id}/recruiter/search`
1815
+ */
1816
+ searchFromUrl(body, params) {
1817
+ return this.ctx.request({
1818
+ method: "POST",
1819
+ path: "/v1/{account_id}/recruiter/search",
1820
+ body,
1821
+ ...params ? { query: params } : {}
1341
1822
  });
1342
1823
  }
1343
1824
  /**
1344
- * List Recruiter hiring projects. `GET /v1/recruiter/projects`
1825
+ * List Recruiter hiring projects visible to the account.
1826
+ * `GET /v1/{account_id}/recruiter/projects`
1345
1827
  */
1346
1828
  listProjects(params) {
1347
1829
  return this.ctx.request({
1348
1830
  method: "GET",
1349
- path: "/v1/recruiter/projects",
1831
+ path: "/v1/{account_id}/recruiter/projects",
1350
1832
  ...params ? { query: params } : {}
1351
1833
  });
1352
1834
  }
1353
1835
  /**
1354
- * Get a single Recruiter hiring project. `GET /v1/recruiter/projects/{project_id}`
1836
+ * Get a single Recruiter hiring project (owner, metadata, talent_pool,
1837
+ * pipeline). `GET /v1/{account_id}/recruiter/projects/{project_id}`
1355
1838
  */
1356
1839
  getProject(projectId) {
1357
1840
  return this.ctx.request({
1358
1841
  method: "GET",
1359
- path: `/v1/recruiter/projects/${projectId}`
1842
+ path: `/v1/{account_id}/recruiter/projects/${projectId}`
1360
1843
  });
1361
1844
  }
1362
1845
  /**
1363
- * Add a member to a hiring project pipeline as a candidate.
1364
- * `POST /v1/recruiter/projects/candidates/{user_id}`
1846
+ * Edit a Recruiter project's config — all fields optional; omitted fields
1847
+ * are left unchanged. Returns a thin acknowledgement only.
1848
+ * `PATCH /v1/{account_id}/recruiter/projects/{project_id}`
1365
1849
  */
1366
- addCandidate(userId, body) {
1850
+ updateProject(projectId, body) {
1367
1851
  return this.ctx.request({
1368
- method: "POST",
1369
- path: `/v1/recruiter/projects/candidates/${userId}`,
1370
- body,
1371
- accountIdIn: "body"
1852
+ method: "PATCH",
1853
+ path: `/v1/{account_id}/recruiter/projects/${projectId}`,
1854
+ body
1372
1855
  });
1373
1856
  }
1374
1857
  /**
1375
- * Add a member to a hiring project pipeline as an applicant.
1376
- * `POST /v1/recruiter/projects/applicants/{user_id}`
1858
+ * List candidates in a project's pipeline (POST-as-list; the body carries
1859
+ * an all-optional filter set, no state mutates). `limit`/`cursor` stay
1860
+ * TOP-LEVEL query params.
1861
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/pipeline`
1377
1862
  */
1378
- addApplicant(userId, body) {
1863
+ listPipeline(projectId, body = {}, params) {
1379
1864
  return this.ctx.request({
1380
1865
  method: "POST",
1381
- path: `/v1/recruiter/projects/applicants/${userId}`,
1866
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/pipeline`,
1382
1867
  body,
1383
- accountIdIn: "body"
1868
+ ...params ? { query: params } : {}
1384
1869
  });
1385
1870
  }
1386
1871
  /**
1387
- * Reject an applicant from a hiring project.
1388
- * `POST /v1/recruiter/projects/applicants/{user_id}/reject`
1872
+ * Get the single job posting attached to a Recruiter project (the surface
1873
+ * returns one posting, not a list — a project with no attached job 404s).
1874
+ * `GET /v1/{account_id}/recruiter/projects/{project_id}/jobs`
1389
1875
  */
1390
- rejectApplicant(userId, body) {
1876
+ getProjectJob(projectId) {
1391
1877
  return this.ctx.request({
1392
- method: "POST",
1393
- path: `/v1/recruiter/projects/applicants/${userId}/reject`,
1394
- body,
1395
- accountIdIn: "body"
1878
+ method: "GET",
1879
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs`
1396
1880
  });
1397
1881
  }
1398
1882
  /**
1399
- * List Recruiter job postings. `GET /v1/recruiter/jobs`
1883
+ * Create a job-posting DRAFT attached to an EXISTING project — `project_name`
1884
+ * is not accepted (the project comes from the path). Never publishes, never
1885
+ * spends money. `POST /v1/{account_id}/recruiter/projects/{project_id}/jobs`
1400
1886
  */
1401
- listJobs(params) {
1887
+ createProjectJob(projectId, body) {
1402
1888
  return this.ctx.request({
1403
- method: "GET",
1404
- path: "/v1/recruiter/jobs",
1405
- ...params ? { query: params } : {}
1889
+ method: "POST",
1890
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs`,
1891
+ body
1406
1892
  });
1407
1893
  }
1408
1894
  /**
1409
- * Create a Recruiter job posting draft. `POST /v1/recruiter/jobs`
1895
+ * Get pricing to publish a project's job posting — price a publish before
1896
+ * committing any money.
1897
+ * `GET /v1/{account_id}/recruiter/projects/{project_id}/jobs/{job_id}/budget`
1410
1898
  */
1411
- createJob(body) {
1899
+ getProjectJobBudget(projectId, jobId) {
1412
1900
  return this.ctx.request({
1413
- method: "POST",
1414
- path: "/v1/recruiter/jobs",
1415
- body,
1416
- accountIdIn: "body"
1901
+ method: "GET",
1902
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs/${jobId}/budget`
1417
1903
  });
1418
1904
  }
1419
1905
  /**
1420
- * Publish a Recruiter job posting draft.
1421
- * `POST /v1/recruiter/jobs/{job_id}/publish`
1906
+ * Create a job-posting DRAFT together with a brand-new hiring project —
1907
+ * `project_name` is REQUIRED; `description` must be ≥ 200 characters. Never
1908
+ * publishes, never spends money. `POST /v1/{account_id}/recruiter/jobs`
1422
1909
  */
1423
- publishJob(jobId, body) {
1910
+ createJob(body) {
1424
1911
  return this.ctx.request({
1425
1912
  method: "POST",
1426
- path: `/v1/recruiter/jobs/${jobId}/publish`,
1427
- body,
1428
- accountIdIn: "body"
1913
+ path: "/v1/{account_id}/recruiter/jobs",
1914
+ body
1429
1915
  });
1430
1916
  }
1431
1917
  /**
1432
- * Solve a publish verification checkpoint.
1433
- * `POST /v1/recruiter/jobs/{job_id}/checkpoint`
1918
+ * List Recruiter job postings. `limit`/`cursor` stay TOP-LEVEL query params.
1919
+ * `GET /v1/{account_id}/recruiter/jobs`
1434
1920
  */
1435
- solveJobCheckpoint(jobId, body) {
1921
+ listJobs(params) {
1436
1922
  return this.ctx.request({
1437
- method: "POST",
1438
- path: `/v1/recruiter/jobs/${jobId}/checkpoint`,
1439
- body,
1440
- accountIdIn: "body"
1923
+ method: "GET",
1924
+ path: "/v1/{account_id}/recruiter/jobs",
1925
+ ...params ? { query: params } : {}
1441
1926
  });
1442
1927
  }
1443
1928
  /**
1444
- * Get a job posting via the Recruiter lens (any public posting, not only
1445
- * the operator's own). `GET /v1/recruiter/jobs/{job_id}`
1929
+ * Get one Recruiter job posting's full detail (the recruiter-specific
1930
+ * `recruiter_job_posting` shape, carrying `project_id`).
1931
+ * `GET /v1/{account_id}/recruiter/jobs/{job_id}`
1446
1932
  *
1447
- * Accepts a bare numeric job id or a full job URL
1933
+ * Accepts a bare numeric job id or a full LinkedIn job URL
1448
1934
  * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
1449
- * client-side, mirroring `jobs.get()`. Throws
1935
+ * client-side; the wire request always carries the numeric id. Throws
1450
1936
  * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
1451
- * form can be recognized. Returns the same `JobPosting` shape as
1452
- * `jobs.get()`.
1937
+ * form can be recognized.
1453
1938
  */
1454
1939
  getJob(jobIdOrUrl) {
1455
1940
  const jobId = resolveJobId(jobIdOrUrl);
1456
1941
  return this.ctx.request({
1457
1942
  method: "GET",
1458
- path: `/v1/recruiter/jobs/${jobId}`
1943
+ path: `/v1/{account_id}/recruiter/jobs/${jobId}`
1459
1944
  });
1460
1945
  }
1461
1946
  /**
1462
- * List applicants for a job posting.
1463
- * `GET /v1/recruiter/jobs/{job_id}/applicants`
1947
+ * Apply a partial update to a project's job posting — only included fields
1948
+ * change. MONEY WARNING: editing an already-published (LISTED) posting
1949
+ * mutates a live, money-spending listing.
1950
+ * `PATCH /v1/{account_id}/recruiter/projects/{project_id}/jobs/{job_id}`
1464
1951
  */
1465
- listApplicants(jobId, params) {
1952
+ updateProjectJob(projectId, jobId, body) {
1466
1953
  return this.ctx.request({
1467
- method: "GET",
1468
- path: `/v1/recruiter/jobs/${jobId}/applicants`,
1469
- // cast needed: include_degree / exclude_degree can be string[] but transport encodes arrays as repeated params
1954
+ method: "PATCH",
1955
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs/${jobId}`,
1956
+ body
1957
+ });
1958
+ }
1959
+ /**
1960
+ * Publish a project's job posting draft. `mode` is a discriminated oneOf:
1961
+ * `FREE` (requires free-posting eligibility, spends nothing) or
1962
+ * `PROMOTED`/`PROMOTED_PLUS` (requires `budget` — THIS SPENDS REAL MONEY on
1963
+ * the connected account's LinkedIn payment method; supplying `budget` IS the
1964
+ * explicit opt-in).
1965
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/jobs/{job_id}/publish`
1966
+ */
1967
+ publishJob(projectId, jobId, body) {
1968
+ return this.ctx.request({
1969
+ method: "POST",
1970
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs/${jobId}/publish`,
1971
+ body
1972
+ });
1973
+ }
1974
+ /**
1975
+ * Close a project's job posting so it stops accepting applications
1976
+ * (bodyless). Closing an already-published (LISTED) posting cannot be
1977
+ * undone — there is no re-open operation.
1978
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/jobs/{job_id}/close`
1979
+ */
1980
+ closeJob(projectId, jobId) {
1981
+ return this.ctx.request({
1982
+ method: "POST",
1983
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/jobs/${jobId}/close`
1984
+ });
1985
+ }
1986
+ /**
1987
+ * Save a candidate (or user profile) to a project's pipeline at the given
1988
+ * stage — the sole surviving pipeline write.
1989
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/pipeline/candidate/save`
1990
+ */
1991
+ saveCandidate(projectId, body) {
1992
+ return this.ctx.request({
1993
+ method: "POST",
1994
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/pipeline/candidate/save`,
1995
+ body
1996
+ });
1997
+ }
1998
+ /**
1999
+ * List applicants in a project's talent pool (POST-as-list; the body carries
2000
+ * filters, no state mutates). `channel_id` (a `JOB_POSTING` talent-pool
2001
+ * channel) is REQUIRED. `limit`/`cursor` stay TOP-LEVEL query params.
2002
+ * `POST /v1/{account_id}/recruiter/projects/{project_id}/talent-pool/applicants`
2003
+ */
2004
+ listApplicants(projectId, body, params) {
2005
+ return this.ctx.request({
2006
+ method: "POST",
2007
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/talent-pool/applicants`,
2008
+ body,
1470
2009
  ...params ? { query: params } : {}
1471
2010
  });
1472
2011
  }
1473
2012
  /**
1474
- * Get one job applicant. `GET /v1/recruiter/jobs/applicants/{applicant_id}`
2013
+ * Get full detail for one talent-pool applicant (profile guaranteed full,
2014
+ * includes PII). `GET /v1/{account_id}/recruiter/projects/{project_id}/talent-pool/applicants/{applicant_id}`
1475
2015
  */
1476
- getApplicant(applicantId) {
2016
+ getApplicant(projectId, applicantId) {
1477
2017
  return this.ctx.request({
1478
2018
  method: "GET",
1479
- path: `/v1/recruiter/jobs/applicants/${applicantId}`
2019
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/talent-pool/applicants/${applicantId}`
1480
2020
  });
1481
2021
  }
1482
2022
  /**
1483
- * Download an applicant's resume as raw binary.
1484
- * `GET /v1/recruiter/jobs/applicants/{applicant_id}/resume`
1485
- * Returns `ArrayBuffer` — binary response; the SDK does not cache or store it.
2023
+ * Download a talent-pool applicant's résumé as raw binary.
2024
+ * `GET /v1/{account_id}/recruiter/projects/{project_id}/talent-pool/applicants/{applicant_id}/resume`
2025
+ * Returns `ArrayBuffer` — the SDK does not cache or store it.
1486
2026
  */
1487
- downloadResume(applicantId) {
2027
+ downloadResume(projectId, applicantId) {
1488
2028
  return this.ctx.request({
1489
2029
  method: "GET",
1490
- path: `/v1/recruiter/jobs/applicants/${applicantId}/resume`
2030
+ path: `/v1/{account_id}/recruiter/projects/${projectId}/talent-pool/applicants/${applicantId}/resume`
1491
2031
  });
1492
2032
  }
1493
2033
  };
@@ -1498,24 +2038,137 @@ var JobsResource = class {
1498
2038
  this.ctx = ctx;
1499
2039
  }
1500
2040
  /**
1501
- * Retrieve one public LinkedIn job posting's full detail.
1502
- * `GET /v1/jobs/{job_id}`
2041
+ * List the connected account's own classic job postings.
2042
+ * `GET /v1/{account_id}/jobs`
2043
+ * `state` is REQUIRED — filtering is LinkedIn-side and best-effort (see
2044
+ * the generated type's own field description).
2045
+ */
2046
+ list(params) {
2047
+ return this.ctx.request({
2048
+ method: "GET",
2049
+ path: "/v1/{account_id}/jobs",
2050
+ query: params
2051
+ });
2052
+ }
2053
+ /**
2054
+ * Create a classic job posting DRAFT. Never publishes, never spends money.
2055
+ * `POST /v1/{account_id}/jobs`
2056
+ */
2057
+ create(body) {
2058
+ return this.ctx.request({
2059
+ method: "POST",
2060
+ path: "/v1/{account_id}/jobs",
2061
+ body
2062
+ });
2063
+ }
2064
+ /**
2065
+ * Retrieve one classic LinkedIn job posting's full detail.
2066
+ * `GET /v1/{account_id}/jobs/{job_id}`
1503
2067
  *
1504
2068
  * Accepts a bare numeric job id or a full job URL
1505
2069
  * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
1506
2070
  * client-side; the wire request always carries the numeric id. Throws
1507
2071
  * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
1508
2072
  * form can be recognized.
1509
- * The `account_id` is injected by the account-scoped context.
1510
2073
  */
1511
2074
  get(jobIdOrUrl, params) {
1512
2075
  const jobId = resolveJobId(jobIdOrUrl);
1513
2076
  return this.ctx.request({
1514
2077
  method: "GET",
1515
- path: `/v1/jobs/${jobId}`,
2078
+ path: `/v1/{account_id}/jobs/${jobId}`,
1516
2079
  ...params ? { query: params } : {}
1517
2080
  });
1518
2081
  }
2082
+ /**
2083
+ * Apply a partial update to a job posting this account owns — only
2084
+ * included fields change. Can affect real money on an already-published
2085
+ * (LISTED) posting. `PATCH /v1/{account_id}/jobs/{job_id}`
2086
+ */
2087
+ update(jobId, body) {
2088
+ return this.ctx.request({
2089
+ method: "PATCH",
2090
+ path: `/v1/{account_id}/jobs/${jobId}`,
2091
+ body
2092
+ });
2093
+ }
2094
+ /**
2095
+ * Get pricing to publish a job posting — price a publish before
2096
+ * committing any money. `GET /v1/{account_id}/jobs/{job_id}/budget`
2097
+ */
2098
+ getBudget(jobId) {
2099
+ return this.ctx.request({
2100
+ method: "GET",
2101
+ path: `/v1/{account_id}/jobs/${jobId}/budget`
2102
+ });
2103
+ }
2104
+ /**
2105
+ * Publish a classic job posting draft. `mode` is a discriminated oneOf:
2106
+ * `FREE` (requires free-posting eligibility, spends nothing) or
2107
+ * `PROMOTED`/`PROMOTED_PLUS` (requires `budget` — THIS SPENDS REAL MONEY
2108
+ * on the connected account's LinkedIn payment method; supplying `budget`
2109
+ * IS the explicit opt-in). `POST /v1/{account_id}/jobs/{job_id}/publish`
2110
+ */
2111
+ publish(jobId, body) {
2112
+ return this.ctx.request({
2113
+ method: "POST",
2114
+ path: `/v1/{account_id}/jobs/${jobId}/publish`,
2115
+ body
2116
+ });
2117
+ }
2118
+ /**
2119
+ * Close a job posting so it stops accepting applications (bodyless).
2120
+ * Closing an already-published (LISTED) posting cannot be undone.
2121
+ * `POST /v1/{account_id}/jobs/{job_id}/close`
2122
+ */
2123
+ close(jobId) {
2124
+ return this.ctx.request({
2125
+ method: "POST",
2126
+ path: `/v1/{account_id}/jobs/${jobId}/close`
2127
+ });
2128
+ }
2129
+ /**
2130
+ * List applicants to a job posting this account owns — a read; POST
2131
+ * carries the filter body, matching the `/search/*` convention. Omit
2132
+ * `ratings` to see the full applicant funnel. `limit`/`cursor` stay
2133
+ * TOP-LEVEL query params, never in the body.
2134
+ * `POST /v1/{account_id}/jobs/{job_id}/applicants`
2135
+ */
2136
+ listApplicants(jobId, params = {}) {
2137
+ const { cursor, limit, ...body } = params;
2138
+ const hasQuery = cursor !== void 0 || limit !== void 0;
2139
+ return this.ctx.request({
2140
+ method: "POST",
2141
+ path: `/v1/{account_id}/jobs/${jobId}/applicants`,
2142
+ body,
2143
+ ...hasQuery ? {
2144
+ query: {
2145
+ ...cursor !== void 0 ? { cursor } : {},
2146
+ ...limit !== void 0 ? { limit } : {}
2147
+ }
2148
+ } : {}
2149
+ });
2150
+ }
2151
+ /**
2152
+ * Get full detail for one applicant, including contact information.
2153
+ * `GET /v1/{account_id}/jobs/{job_id}/applicants/{applicant_id}`
2154
+ */
2155
+ getApplicant(jobId, applicantId) {
2156
+ return this.ctx.request({
2157
+ method: "GET",
2158
+ path: `/v1/{account_id}/jobs/${jobId}/applicants/${applicantId}`
2159
+ });
2160
+ }
2161
+ /**
2162
+ * Download an applicant's résumé. Returns raw binary.
2163
+ * `GET /v1/{account_id}/jobs/{job_id}/applicants/{applicant_id}/resume`
2164
+ * Returns `ArrayBuffer` — the SDK does not cache or store it.
2165
+ */
2166
+ downloadResume(jobId, applicantId) {
2167
+ return this.ctx.request({
2168
+ method: "GET",
2169
+ path: `/v1/{account_id}/jobs/${jobId}/applicants/${applicantId}/resume`
2170
+ });
2171
+ }
1519
2172
  };
1520
2173
 
1521
2174
  // src/resources/companies.ts
@@ -1525,21 +2178,20 @@ var CompaniesResource = class {
1525
2178
  }
1526
2179
  /**
1527
2180
  * Retrieve a company's full LinkedIn profile.
1528
- * `GET /v1/companies/{identifier}`
2181
+ * `GET /v1/{account_id}/companies/{identifier}`
1529
2182
  *
1530
2183
  * Accepts a public handle (the slug in `linkedin.com/company/<handle>`,
1531
2184
  * e.g. `t-systems`) or a numeric id (e.g. `1234567`). A URN is not accepted.
1532
- * The `account_id` is injected by the account-scoped context.
1533
2185
  */
1534
2186
  get(identifier) {
1535
2187
  return this.ctx.request({
1536
2188
  method: "GET",
1537
- path: `/v1/companies/${identifier}`
2189
+ path: `/v1/{account_id}/companies/${identifier}`
1538
2190
  });
1539
2191
  }
1540
2192
  /**
1541
2193
  * List people who currently work at the company.
1542
- * `GET /v1/companies/{identifier}/employees`
2194
+ * `GET /v1/{account_id}/companies/{identifier}/employees`
1543
2195
  *
1544
2196
  * A facade over people search with the company filter applied — filter
1545
2197
  * further with `keywords` or `location`. `identifier` must be the
@@ -1549,13 +2201,13 @@ var CompaniesResource = class {
1549
2201
  employees(identifier, params) {
1550
2202
  return this.ctx.request({
1551
2203
  method: "GET",
1552
- path: `/v1/companies/${identifier}/employees`,
2204
+ path: `/v1/{account_id}/companies/${identifier}/employees`,
1553
2205
  ...params ? { query: params } : {}
1554
2206
  });
1555
2207
  }
1556
2208
  /**
1557
2209
  * List the company's posts.
1558
- * `GET /v1/companies/{identifier}/posts`
2210
+ * `GET /v1/{account_id}/companies/{identifier}/posts`
1559
2211
  *
1560
2212
  * A facade over post search with the company filter applied. `identifier`
1561
2213
  * must be the company's numeric provider_id.
@@ -1563,13 +2215,13 @@ var CompaniesResource = class {
1563
2215
  posts(identifier, params) {
1564
2216
  return this.ctx.request({
1565
2217
  method: "GET",
1566
- path: `/v1/companies/${identifier}/posts`,
2218
+ path: `/v1/{account_id}/companies/${identifier}/posts`,
1567
2219
  ...params ? { query: params } : {}
1568
2220
  });
1569
2221
  }
1570
2222
  /**
1571
2223
  * List the company's open job postings.
1572
- * `GET /v1/companies/{identifier}/jobs`
2224
+ * `GET /v1/{account_id}/companies/{identifier}/jobs`
1573
2225
  *
1574
2226
  * A facade over job search with the company filter applied — filter
1575
2227
  * further with `keywords`. An empty `items[]` is a valid result (the
@@ -1579,22 +2231,123 @@ var CompaniesResource = class {
1579
2231
  jobs(identifier, params) {
1580
2232
  return this.ctx.request({
1581
2233
  method: "GET",
1582
- path: `/v1/companies/${identifier}/jobs`,
2234
+ path: `/v1/{account_id}/companies/${identifier}/jobs`,
2235
+ ...params ? { query: params } : {}
2236
+ });
2237
+ }
2238
+ /**
2239
+ * List the pages the connected account administers.
2240
+ * `GET /v1/{account_id}/companies/managed`
2241
+ *
2242
+ * The `id` on each returned page is the numeric provider_id `followers()`,
2243
+ * `invitableFollowers()`, `employees()`, `posts()`, and `jobs()` consume.
2244
+ * An empty `items[]` is valid — the account administers no pages.
2245
+ */
2246
+ managed(params) {
2247
+ return this.ctx.request({
2248
+ method: "GET",
2249
+ path: `/v1/{account_id}/companies/managed`,
1583
2250
  ...params ? { query: params } : {}
1584
2251
  });
1585
2252
  }
1586
2253
  /**
1587
- * List the company's followers.
1588
- * `GET /v1/companies/{identifier}/followers`
2254
+ * List a company page's followers, newest first.
2255
+ * `GET /v1/{account_id}/companies/{identifier}/followers`
1589
2256
  *
1590
- * Native — reuses the same seam that backs `profiles.listFollowers()`.
1591
- * You must be an administrator of the target company page.
2257
+ * The connected account must administer the page (see `managed()`).
1592
2258
  * `identifier` must be the company's numeric provider_id.
1593
2259
  */
1594
2260
  followers(identifier, params) {
1595
2261
  return this.ctx.request({
1596
2262
  method: "GET",
1597
- path: `/v1/companies/${identifier}/followers`,
2263
+ path: `/v1/{account_id}/companies/${identifier}/followers`,
2264
+ ...params ? { query: params } : {}
2265
+ });
2266
+ }
2267
+ /**
2268
+ * List the connected account's connections who are invitable to follow the
2269
+ * company page. `GET /v1/{account_id}/companies/{identifier}/invitable-followers`
2270
+ *
2271
+ * The connected account must administer the page. `identifier` must be the
2272
+ * company's numeric provider_id. An empty `items[]` is valid — nobody is
2273
+ * currently invitable.
2274
+ */
2275
+ invitableFollowers(identifier, params) {
2276
+ return this.ctx.request({
2277
+ method: "GET",
2278
+ path: `/v1/{account_id}/companies/${identifier}/invitable-followers`,
2279
+ ...params ? { query: params } : {}
2280
+ });
2281
+ }
2282
+ /**
2283
+ * List the conversations in a company page's admin message inbox,
2284
+ * newest-activity-first.
2285
+ * `GET /v1/{account_id}/companies/{identifier}/chats`
2286
+ *
2287
+ * The connected account must administer the page. `identifier` must be the
2288
+ * company's numeric provider_id. Content passes through verbatim and is
2289
+ * never stored. Beta — single-page listing and termination are verified;
2290
+ * deep pagination (many pages / large cursor round-trips) is provisional
2291
+ * until validated against a busier inbox.
2292
+ */
2293
+ chats(identifier, params) {
2294
+ return this.ctx.request({
2295
+ method: "GET",
2296
+ path: `/v1/{account_id}/companies/${identifier}/chats`,
2297
+ ...params ? { query: params } : {}
2298
+ });
2299
+ }
2300
+ /**
2301
+ * Retrieve one conversation from a company page's admin inbox.
2302
+ * `GET /v1/{account_id}/companies/{identifier}/chats/{chat_id}`
2303
+ *
2304
+ * The connected account must administer the page. Beta — see `chats()`.
2305
+ */
2306
+ chat(identifier, chatId) {
2307
+ return this.ctx.request({
2308
+ method: "GET",
2309
+ path: `/v1/{account_id}/companies/${identifier}/chats/${chatId}`
2310
+ });
2311
+ }
2312
+ /**
2313
+ * List a company-inbox conversation's messages, newest first.
2314
+ * `GET /v1/{account_id}/companies/{identifier}/chats/{chat_id}/messages`
2315
+ *
2316
+ * The connected account must administer the page. Content passes through
2317
+ * verbatim and is never stored. Beta — see `chats()`.
2318
+ */
2319
+ messages(identifier, chatId, params) {
2320
+ return this.ctx.request({
2321
+ method: "GET",
2322
+ path: `/v1/{account_id}/companies/${identifier}/chats/${chatId}/messages`,
2323
+ ...params ? { query: params } : {}
2324
+ });
2325
+ }
2326
+ /**
2327
+ * Retrieve one message from a company-inbox conversation.
2328
+ * `GET /v1/{account_id}/companies/{identifier}/chats/{chat_id}/messages/{message_id}`
2329
+ *
2330
+ * The connected account must administer the page. Beta — see `chats()`.
2331
+ */
2332
+ message(identifier, chatId, messageId) {
2333
+ return this.ctx.request({
2334
+ method: "GET",
2335
+ path: `/v1/{account_id}/companies/${identifier}/chats/${chatId}/messages/${messageId}`
2336
+ });
2337
+ }
2338
+ /**
2339
+ * Search or filter a company page's admin inbox.
2340
+ * `GET /v1/{account_id}/companies/{identifier}/chats/search`
2341
+ *
2342
+ * Exactly one mode per call: free-text `query` (matches participant names
2343
+ * and message content), a `topic` card, or `unread`-only — the three are
2344
+ * mutually exclusive and enforced server-side. The connected account must
2345
+ * administer the page. Beta — see `chats()`.
2346
+ */
2347
+ searchChats(identifier, params) {
2348
+ return this.ctx.request({
2349
+ method: "GET",
2350
+ path: `/v1/{account_id}/companies/${identifier}/chats/search`,
1598
2351
  ...params ? { query: params } : {}
1599
2352
  });
1600
2353
  }
@@ -1671,29 +2424,24 @@ var WebhooksResource = class {
1671
2424
  path: `/v1/webhooks/${id}`
1672
2425
  });
1673
2426
  }
1674
- /**
1675
- * Return the set of changes since the last known version for a connected account.
1676
- * Enables event-driven state sync without polling the account endpoint.
1677
- * `GET /v1/accounts/{id}/state-diff`
1678
- */
1679
- getStateDiff(accountId, params) {
1680
- return this.ctx.request({
1681
- method: "GET",
1682
- path: `/v1/accounts/${accountId}/state-diff`,
1683
- ...params ? { query: params } : {}
1684
- });
1685
- }
1686
2427
  };
1687
2428
 
1688
2429
  // src/resources/index.ts
1689
2430
  function buildNamespaces(ctx) {
1690
2431
  return {
1691
2432
  accounts: new AccountsResource(ctx),
2433
+ auth: new AuthResource(ctx),
1692
2434
  messaging: new MessagingResource(ctx),
1693
- profiles: new ProfilesResource(ctx),
2435
+ users: new UsersResource(ctx),
1694
2436
  invites: new InvitesResource(ctx),
1695
2437
  search: new SearchResource(ctx),
1696
2438
  posts: new PostsResource(ctx),
2439
+ comments: new CommentsResource(ctx),
2440
+ profile: new ProfileResource(ctx),
2441
+ groups: new GroupsResource(ctx),
2442
+ feed: new FeedResource(ctx),
2443
+ inboxes: new InboxesResource(ctx),
2444
+ notifications: new NotificationsResource(ctx),
1697
2445
  salesNavigator: new SalesNavigatorResource(ctx),
1698
2446
  recruiter: new RecruiterResource(ctx),
1699
2447
  jobs: new JobsResource(ctx),
@@ -1701,9 +2449,23 @@ function buildNamespaces(ctx) {
1701
2449
  webhooks: new WebhooksResource(ctx)
1702
2450
  };
1703
2451
  }
2452
+ function buildRootNamespaces(ctx) {
2453
+ return {
2454
+ accounts: new AccountsResource(ctx),
2455
+ auth: new AuthResource(ctx),
2456
+ webhooks: new WebhooksResource(ctx)
2457
+ };
2458
+ }
1704
2459
  function buildAccountScopedNamespaces(ctx) {
1705
- const { accounts: _accounts, ...rest } = buildNamespaces(ctx);
2460
+ const {
2461
+ accounts: _accounts,
2462
+ auth: _auth,
2463
+ webhooks: _webhooks,
2464
+ ...rest
2465
+ } = buildNamespaces(ctx);
1706
2466
  void _accounts;
2467
+ void _auth;
2468
+ void _webhooks;
1707
2469
  return rest;
1708
2470
  }
1709
2471
 
@@ -1712,17 +2474,9 @@ var Curviate = class {
1712
2474
  constructor(config) {
1713
2475
  this.config = resolveConfig(config);
1714
2476
  const ctx = createContext(this.config);
1715
- const ns = buildNamespaces(ctx);
2477
+ const ns = buildRootNamespaces(ctx);
1716
2478
  this.accounts = ns.accounts;
1717
- this.messaging = ns.messaging;
1718
- this.profiles = ns.profiles;
1719
- this.invites = ns.invites;
1720
- this.search = ns.search;
1721
- this.posts = ns.posts;
1722
- this.salesNavigator = ns.salesNavigator;
1723
- this.recruiter = ns.recruiter;
1724
- this.jobs = ns.jobs;
1725
- this.companies = ns.companies;
2479
+ this.auth = ns.auth;
1726
2480
  this.webhooks = ns.webhooks;
1727
2481
  }
1728
2482
  /**
@@ -1753,15 +2507,15 @@ var Curviate = class {
1753
2507
  * Calls `fn` repeatedly, injecting the `cursor` from each response into the
1754
2508
  * next call, yielding individual items until `cursor` is null.
1755
2509
  *
1756
- * @param fn - a bound resource method (e.g. `profiles.listConnections.bind(profiles)`)
2510
+ * @param fn - a bound resource method (e.g. `users.listRelations.bind(users)`)
1757
2511
  * @param params - the initial params (minus cursor)
1758
2512
  *
1759
2513
  * @example
1760
- * for await (const profile of curviate.paginate(
1761
- * curviate.account('acc_123').profiles.listConnections,
2514
+ * for await (const relation of curviate.paginate(
2515
+ * curviate.account('acc_123').users.listRelations,
1762
2516
  * { limit: 50 }
1763
2517
  * )) {
1764
- * console.log(profile);
2518
+ * console.log(relation);
1765
2519
  * }
1766
2520
  */
1767
2521
  paginate(fn, params) {