@curviate/sdk 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ Versioning: semantic — minor for additive changes, patch for bug fixes; no sta
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.10.0] — 2026-07-03
11
+
12
+ ### Added
13
+
14
+ - **New `jobs` namespace.** `jobs.get(jobIdOrUrl)` retrieves one public LinkedIn job posting's full detail — title, company, location, description, applicant count, and more. Accepts either a bare numeric job id (e.g. `"4428113858"`) or a full job URL (`"https://www.linkedin.com/jobs/view/4428113858"`) — the SDK extracts the numeric id client-side, so both forms issue the identical request. Passing a value with no extractable numeric id throws `CurviateError({ code: "INVALID_REQUEST" })` synchronously, before any network call. Type: `JobPosting`.
15
+ - `recruiter.getJob(jobIdOrUrl)` — the Recruiter-lens sibling, retrieving any public job posting (not only the operator's own postings). Accepts the same bare-id-or-URL forms as `jobs.get()` and returns the identical `JobPosting` type (no separate response shape to learn). Extends the Recruiter surface to 18 methods.
16
+
10
17
  ## [0.9.0] — 2026-07-03
11
18
 
12
19
  ### Added
package/dist/index.d.ts CHANGED
@@ -2520,6 +2520,176 @@ interface paths {
2520
2520
  patch?: never;
2521
2521
  trace?: never;
2522
2522
  };
2523
+ "/v1/jobs/{job_id}": {
2524
+ parameters: {
2525
+ query?: never;
2526
+ header?: never;
2527
+ path?: never;
2528
+ cookie?: never;
2529
+ };
2530
+ /**
2531
+ * Get job posting
2532
+ * @description Returns one public LinkedIn job posting's full detail — title, company, location, description, and applicant count.
2533
+ */
2534
+ get: {
2535
+ parameters: {
2536
+ query: {
2537
+ /** @description The connected LinkedIn account to retrieve the job posting on behalf of. */
2538
+ account_id: string;
2539
+ };
2540
+ header?: never;
2541
+ path: {
2542
+ /** @description The numeric id of the job posting, e.g. 4428113858. This is the number in a LinkedIn job URL (https://www.linkedin.com/jobs/view/4428113858) and the job_urn value in a POST /v1/search/jobs result. Pass the number only — a full URL is not accepted by the API (the SDK and CLI extract the id from a URL for you). */
2543
+ job_id: string;
2544
+ };
2545
+ cookie?: never;
2546
+ };
2547
+ requestBody?: never;
2548
+ responses: {
2549
+ /** @description The job posting's full detail. Content fields (title, description) are returned verbatim and are never stored. */
2550
+ 200: {
2551
+ headers: {
2552
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
2553
+ RateLimit: components["headers"]["RateLimit"];
2554
+ [name: string]: unknown;
2555
+ };
2556
+ content: {
2557
+ "application/json": {
2558
+ /**
2559
+ * @description Response type discriminator.
2560
+ * @enum {string}
2561
+ */
2562
+ object: "job_posting";
2563
+ /** @description The job posting's numeric identifier. */
2564
+ id: string;
2565
+ /** @description Job title (content pass-through — never stored). */
2566
+ title: string;
2567
+ /** @description Hiring company's display name. */
2568
+ company: string;
2569
+ /** @description The hiring company's id — pass it to GET /v1/profiles/companies/{company_id}. */
2570
+ company_id: string;
2571
+ /** @description Posting lifecycle state (e.g. active). */
2572
+ state: string;
2573
+ /** @description Job location, or null when not set. */
2574
+ location: string | null;
2575
+ /** @description Posting cost, in the currency the account bills in. */
2576
+ cost: number;
2577
+ /** @description Number of applicants so far. */
2578
+ applicants_counter: number;
2579
+ /** @description Full job description text (content pass-through — never stored). */
2580
+ description: string;
2581
+ /** @description ISO-8601 UTC timestamp the posting was created, or null when not set. */
2582
+ created_at: string | null;
2583
+ /** @description ISO-8601 UTC timestamp the posting was published, or null when not set. */
2584
+ published_at: string | null;
2585
+ /** @description Hiring team members associated with this posting, when the posting exposes any. */
2586
+ hiring_team: unknown[];
2587
+ };
2588
+ };
2589
+ };
2590
+ /** @description Missing or empty account_id. */
2591
+ 400: {
2592
+ headers: {
2593
+ [name: string]: unknown;
2594
+ };
2595
+ content: {
2596
+ "application/json": components["schemas"]["Error"];
2597
+ };
2598
+ };
2599
+ /** @description Missing or invalid API key. */
2600
+ 401: {
2601
+ headers: {
2602
+ [name: string]: unknown;
2603
+ };
2604
+ content: {
2605
+ "application/json": components["schemas"]["Error"];
2606
+ };
2607
+ };
2608
+ /** @description The account does not have the required Core seat. */
2609
+ 403: {
2610
+ headers: {
2611
+ [name: string]: unknown;
2612
+ };
2613
+ content: {
2614
+ "application/json": components["schemas"]["Error"];
2615
+ };
2616
+ };
2617
+ /** @description The account_id does not belong to this tenant, or the job posting was not found. */
2618
+ 404: {
2619
+ headers: {
2620
+ [name: string]: unknown;
2621
+ };
2622
+ content: {
2623
+ "application/json": components["schemas"]["Error"];
2624
+ };
2625
+ };
2626
+ /** @description The account is restricted and cannot be queried. */
2627
+ 422: {
2628
+ headers: {
2629
+ [name: string]: unknown;
2630
+ };
2631
+ content: {
2632
+ "application/json": components["schemas"]["Error"];
2633
+ };
2634
+ };
2635
+ /** @description Rate limited — slow down and retry after the hinted delay. */
2636
+ 429: {
2637
+ headers: {
2638
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
2639
+ RateLimit: components["headers"]["RateLimit"];
2640
+ "Retry-After": components["headers"]["Retry-After"];
2641
+ [name: string]: unknown;
2642
+ };
2643
+ content: {
2644
+ "application/json": components["schemas"]["Error"];
2645
+ };
2646
+ };
2647
+ /** @description Internal error. */
2648
+ 500: {
2649
+ headers: {
2650
+ [name: string]: unknown;
2651
+ };
2652
+ content: {
2653
+ "application/json": components["schemas"]["Error"];
2654
+ };
2655
+ };
2656
+ /** @description A temporary error occurred. Please try again. */
2657
+ 502: {
2658
+ headers: {
2659
+ [name: string]: unknown;
2660
+ };
2661
+ content: {
2662
+ "application/json": components["schemas"]["Error"];
2663
+ };
2664
+ };
2665
+ /** @description Service unavailable. */
2666
+ 503: {
2667
+ headers: {
2668
+ [name: string]: unknown;
2669
+ };
2670
+ content: {
2671
+ "application/json": components["schemas"]["Error"];
2672
+ };
2673
+ };
2674
+ /** @description Gateway timeout. */
2675
+ 504: {
2676
+ headers: {
2677
+ [name: string]: unknown;
2678
+ };
2679
+ content: {
2680
+ "application/json": components["schemas"]["Error"];
2681
+ };
2682
+ };
2683
+ };
2684
+ };
2685
+ put?: never;
2686
+ post?: never;
2687
+ delete?: never;
2688
+ options?: never;
2689
+ head?: never;
2690
+ patch?: never;
2691
+ trace?: never;
2692
+ };
2523
2693
  "/v1/chats": {
2524
2694
  parameters: {
2525
2695
  query?: never;
@@ -11212,6 +11382,176 @@ interface paths {
11212
11382
  patch?: never;
11213
11383
  trace?: never;
11214
11384
  };
11385
+ "/v1/recruiter/jobs/{job_id}": {
11386
+ parameters: {
11387
+ query?: never;
11388
+ header?: never;
11389
+ path?: never;
11390
+ cookie?: never;
11391
+ };
11392
+ /**
11393
+ * Get job posting
11394
+ * @description Returns one public LinkedIn job posting's full detail (RECRUITER lens) — title, company, location, description, and applicant count. Requires a Recruiter seat.
11395
+ */
11396
+ get: {
11397
+ parameters: {
11398
+ query: {
11399
+ /** @description The connected LinkedIn account to retrieve the job posting on behalf of. */
11400
+ account_id: string;
11401
+ };
11402
+ header?: never;
11403
+ path: {
11404
+ /** @description The numeric id of the job posting, e.g. 4428113858. This is the number in a LinkedIn job URL (https://www.linkedin.com/jobs/view/4428113858) and the job_urn value in a POST /v1/search/jobs result. Pass the number only — a full URL is not accepted by the API (the SDK and CLI extract the id from a URL for you). */
11405
+ job_id: string;
11406
+ };
11407
+ cookie?: never;
11408
+ };
11409
+ requestBody?: never;
11410
+ responses: {
11411
+ /** @description The job posting's full detail (RECRUITER lens — the same job_posting shape as the Core retrieve; content fields are returned verbatim and never stored). Requires a Recruiter seat. */
11412
+ 200: {
11413
+ headers: {
11414
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
11415
+ RateLimit: components["headers"]["RateLimit"];
11416
+ [name: string]: unknown;
11417
+ };
11418
+ content: {
11419
+ "application/json": {
11420
+ /**
11421
+ * @description Response type discriminator.
11422
+ * @enum {string}
11423
+ */
11424
+ object: "job_posting";
11425
+ /** @description The job posting's numeric identifier. */
11426
+ id: string;
11427
+ /** @description Job title (content pass-through — never stored). */
11428
+ title: string;
11429
+ /** @description Hiring company's display name. */
11430
+ company: string;
11431
+ /** @description The hiring company's id — pass it to GET /v1/profiles/companies/{company_id}. */
11432
+ company_id: string;
11433
+ /** @description Posting lifecycle state (e.g. active). */
11434
+ state: string;
11435
+ /** @description Job location, or null when not set. */
11436
+ location: string | null;
11437
+ /** @description Posting cost, in the currency the account bills in. */
11438
+ cost: number;
11439
+ /** @description Number of applicants so far. */
11440
+ applicants_counter: number;
11441
+ /** @description Full job description text (content pass-through — never stored). */
11442
+ description: string;
11443
+ /** @description ISO-8601 UTC timestamp the posting was created, or null when not set. */
11444
+ created_at: string | null;
11445
+ /** @description ISO-8601 UTC timestamp the posting was published, or null when not set. */
11446
+ published_at: string | null;
11447
+ /** @description Hiring team members associated with this posting, when the posting exposes any. */
11448
+ hiring_team: unknown[];
11449
+ };
11450
+ };
11451
+ };
11452
+ /** @description Missing or empty account_id. */
11453
+ 400: {
11454
+ headers: {
11455
+ [name: string]: unknown;
11456
+ };
11457
+ content: {
11458
+ "application/json": components["schemas"]["Error"];
11459
+ };
11460
+ };
11461
+ /** @description Missing or invalid API key, or LinkedIn authentication failed. */
11462
+ 401: {
11463
+ headers: {
11464
+ [name: string]: unknown;
11465
+ };
11466
+ content: {
11467
+ "application/json": components["schemas"]["Error"];
11468
+ };
11469
+ };
11470
+ /** @description The account does not have the required Recruiter seat. */
11471
+ 403: {
11472
+ headers: {
11473
+ [name: string]: unknown;
11474
+ };
11475
+ content: {
11476
+ "application/json": components["schemas"]["Error"];
11477
+ };
11478
+ };
11479
+ /** @description The account_id does not belong to this tenant, or the job posting was not found. */
11480
+ 404: {
11481
+ headers: {
11482
+ [name: string]: unknown;
11483
+ };
11484
+ content: {
11485
+ "application/json": components["schemas"]["Error"];
11486
+ };
11487
+ };
11488
+ /** @description The account is restricted and cannot be queried. */
11489
+ 422: {
11490
+ headers: {
11491
+ [name: string]: unknown;
11492
+ };
11493
+ content: {
11494
+ "application/json": components["schemas"]["Error"];
11495
+ };
11496
+ };
11497
+ /** @description Rate limited — slow down and retry after the hinted delay. */
11498
+ 429: {
11499
+ headers: {
11500
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
11501
+ RateLimit: components["headers"]["RateLimit"];
11502
+ "Retry-After": components["headers"]["Retry-After"];
11503
+ [name: string]: unknown;
11504
+ };
11505
+ content: {
11506
+ "application/json": components["schemas"]["Error"];
11507
+ };
11508
+ };
11509
+ /** @description Internal error. */
11510
+ 500: {
11511
+ headers: {
11512
+ [name: string]: unknown;
11513
+ };
11514
+ content: {
11515
+ "application/json": components["schemas"]["Error"];
11516
+ };
11517
+ };
11518
+ /** @description A temporary error occurred. Please try again. */
11519
+ 502: {
11520
+ headers: {
11521
+ [name: string]: unknown;
11522
+ };
11523
+ content: {
11524
+ "application/json": components["schemas"]["Error"];
11525
+ };
11526
+ };
11527
+ /** @description Service unavailable. */
11528
+ 503: {
11529
+ headers: {
11530
+ [name: string]: unknown;
11531
+ };
11532
+ content: {
11533
+ "application/json": components["schemas"]["Error"];
11534
+ };
11535
+ };
11536
+ /** @description Gateway timeout. */
11537
+ 504: {
11538
+ headers: {
11539
+ [name: string]: unknown;
11540
+ };
11541
+ content: {
11542
+ "application/json": components["schemas"]["Error"];
11543
+ };
11544
+ };
11545
+ };
11546
+ };
11547
+ put?: never;
11548
+ post?: never;
11549
+ delete?: never;
11550
+ options?: never;
11551
+ head?: never;
11552
+ patch?: never;
11553
+ trace?: never;
11554
+ };
11215
11555
  "/v1/recruiter/jobs/{job_id}/applicants": {
11216
11556
  parameters: {
11217
11557
  query?: never;
@@ -15246,23 +15586,23 @@ declare class MessagingResource {
15246
15586
  */
15247
15587
 
15248
15588
  /** Strip `account_id` from a query-param type (injected by the account-scoped context). */
15249
- type WithoutAccountId<T> = Omit<T, "account_id">;
15589
+ type WithoutAccountId$1<T> = Omit<T, "account_id">;
15250
15590
  type ProfileDetail = paths["/v1/profiles/me"]["get"]["responses"]["200"]["content"]["application/json"];
15251
15591
  /** Caller-facing query params for `profiles.getMe()` — `account_id` is injected by context. */
15252
- type ProfileGetMeParams = WithoutAccountId<paths["/v1/profiles/me"]["get"]["parameters"]["query"]>;
15592
+ type ProfileGetMeParams = WithoutAccountId$1<paths["/v1/profiles/me"]["get"]["parameters"]["query"]>;
15253
15593
  type ProfileGetResult = paths["/v1/profiles/{profile_id}"]["get"]["responses"]["200"]["content"]["application/json"];
15254
15594
  /** Caller-facing query params for `profiles.get()` — `account_id` is injected by context. */
15255
- type ProfileGetParams = WithoutAccountId<paths["/v1/profiles/{profile_id}"]["get"]["parameters"]["query"]>;
15595
+ type ProfileGetParams = WithoutAccountId$1<paths["/v1/profiles/{profile_id}"]["get"]["parameters"]["query"]>;
15256
15596
  type ConnectionListPage = paths["/v1/profiles/relations"]["get"]["responses"]["200"]["content"]["application/json"];
15257
- type ConnectionListParams = WithoutAccountId<paths["/v1/profiles/relations"]["get"]["parameters"]["query"]>;
15597
+ type ConnectionListParams = WithoutAccountId$1<paths["/v1/profiles/relations"]["get"]["parameters"]["query"]>;
15258
15598
  type FollowerListPage = paths["/v1/profiles/{profile_id}/followers"]["get"]["responses"]["200"]["content"]["application/json"];
15259
- type FollowerListParams = WithoutAccountId<NonNullable<paths["/v1/profiles/{profile_id}/followers"]["get"]["parameters"]["query"]>>;
15599
+ type FollowerListParams = WithoutAccountId$1<NonNullable<paths["/v1/profiles/{profile_id}/followers"]["get"]["parameters"]["query"]>>;
15260
15600
  type ProfilePostListPage = paths["/v1/profiles/{profile_id}/posts"]["get"]["responses"]["200"]["content"]["application/json"];
15261
- type ProfilePostListParams = WithoutAccountId<NonNullable<paths["/v1/profiles/{profile_id}/posts"]["get"]["parameters"]["query"]>>;
15601
+ type ProfilePostListParams = WithoutAccountId$1<NonNullable<paths["/v1/profiles/{profile_id}/posts"]["get"]["parameters"]["query"]>>;
15262
15602
  type ProfileCommentListPage = paths["/v1/profiles/{profile_id}/comments"]["get"]["responses"]["200"]["content"]["application/json"];
15263
- type ProfileCommentListParams = WithoutAccountId<NonNullable<paths["/v1/profiles/{profile_id}/comments"]["get"]["parameters"]["query"]>>;
15603
+ type ProfileCommentListParams = WithoutAccountId$1<NonNullable<paths["/v1/profiles/{profile_id}/comments"]["get"]["parameters"]["query"]>>;
15264
15604
  type ProfileReactionListPage = paths["/v1/profiles/{profile_id}/reactions"]["get"]["responses"]["200"]["content"]["application/json"];
15265
- type ProfileReactionListParams = WithoutAccountId<NonNullable<paths["/v1/profiles/{profile_id}/reactions"]["get"]["parameters"]["query"]>>;
15605
+ type ProfileReactionListParams = WithoutAccountId$1<NonNullable<paths["/v1/profiles/{profile_id}/reactions"]["get"]["parameters"]["query"]>>;
15266
15606
  type CompanyProfile = paths["/v1/profiles/companies/{company_id}"]["get"]["responses"]["200"]["content"]["application/json"];
15267
15607
  type EndorseBody = paths["/v1/profiles/{profile_id}/endorse"]["post"]["requestBody"]["content"]["application/json"];
15268
15608
  type EndorseResult = paths["/v1/profiles/{profile_id}/endorse"]["post"]["responses"]["200"]["content"]["application/json"];
@@ -15538,12 +15878,48 @@ declare class SalesNavigatorResource {
15538
15878
  }
15539
15879
 
15540
15880
  /**
15541
- * Recruiter resource — 17 methods (tier: recruiter).
15881
+ * Jobs resource — 1 method.
15882
+ *
15883
+ * Account-scoped: the bound context injects `account_id` into every request.
15884
+ */
15885
+
15886
+ /** Strip `account_id` from a query-param type (injected by the account-scoped context). */
15887
+ type WithoutAccountId<T> = Omit<T, "account_id">;
15888
+ /**
15889
+ * The job-posting shape returned by `jobs.get()` and `recruiter.getJob()` —
15890
+ * the two endpoints return the same shape (Core vs. Recruiter lens), so this
15891
+ * is the single canonical type; `recruiter.ts` reuses it rather than
15892
+ * deriving a duplicate from its own path.
15893
+ */
15894
+ type JobPosting = paths["/v1/jobs/{job_id}"]["get"]["responses"]["200"]["content"]["application/json"];
15895
+ /** Caller-facing query params for `jobs.get()` — `account_id` is injected by context. */
15896
+ type JobGetParams = WithoutAccountId<paths["/v1/jobs/{job_id}"]["get"]["parameters"]["query"]>;
15897
+ declare class JobsResource {
15898
+ protected readonly ctx: RequestContext;
15899
+ constructor(ctx: RequestContext);
15900
+ /**
15901
+ * Retrieve one public LinkedIn job posting's full detail.
15902
+ * `GET /v1/jobs/{job_id}`
15903
+ *
15904
+ * Accepts a bare numeric job id or a full job URL
15905
+ * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
15906
+ * client-side; the wire request always carries the numeric id. Throws
15907
+ * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
15908
+ * form can be recognized.
15909
+ * The `account_id` is injected by the account-scoped context.
15910
+ */
15911
+ get(jobIdOrUrl: string, params?: JobGetParams): Promise<JobPosting>;
15912
+ }
15913
+
15914
+ /**
15915
+ * Recruiter resource — 18 methods (tier: recruiter).
15542
15916
  *
15543
15917
  * Account-scoped: the bound context injects `account_id` into every request.
15544
15918
  * `startChat` accepts optional `attachments[]`, `voice_message`, and
15545
15919
  * `video_message`; the SDK builds the `FormData` body automatically.
15546
15920
  * `downloadResume` returns `Promise<ArrayBuffer>` (binary response).
15921
+ * `getJob` returns the same `JobPosting` shape as `jobs.get()` — reused, not
15922
+ * redefined, since both endpoints share the underlying job-posting retrieve.
15547
15923
  */
15548
15924
 
15549
15925
  type RecruiterSyncMessagesParams = NonNullable<paths["/v1/recruiter/messages/sync"]["get"]["parameters"]["query"]>;
@@ -15659,6 +16035,18 @@ declare class RecruiterResource {
15659
16035
  * `POST /v1/recruiter/jobs/{job_id}/checkpoint`
15660
16036
  */
15661
16037
  solveJobCheckpoint(jobId: string, body: RecruiterSolveJobCheckpointBody): Promise<RecruiterSolveJobCheckpointResult>;
16038
+ /**
16039
+ * Get a job posting via the Recruiter lens (any public posting, not only
16040
+ * the operator's own). `GET /v1/recruiter/jobs/{job_id}`
16041
+ *
16042
+ * Accepts a bare numeric job id or a full job URL
16043
+ * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
16044
+ * client-side, mirroring `jobs.get()`. Throws
16045
+ * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
16046
+ * form can be recognized. Returns the same `JobPosting` shape as
16047
+ * `jobs.get()`.
16048
+ */
16049
+ getJob(jobIdOrUrl: string): Promise<JobPosting>;
15662
16050
  /**
15663
16051
  * List applicants for a job posting.
15664
16052
  * `GET /v1/recruiter/jobs/{job_id}/applicants`
@@ -15737,9 +16125,9 @@ declare class WebhooksResource {
15737
16125
  * Resource namespace assembly.
15738
16126
  *
15739
16127
  * The root client and every `account(id)` accessor expose the same set of
15740
- * resource namespaces. All 9 namespaces are fully implemented:
16128
+ * resource namespaces. All 10 namespaces are fully implemented:
15741
16129
  * accounts, messaging, profiles, invites, search, posts,
15742
- * salesNavigator, recruiter, and webhooks.
16130
+ * salesNavigator, recruiter, jobs, and webhooks.
15743
16131
  */
15744
16132
 
15745
16133
  /** The full namespace surface, shared by the root client and account scopes. */
@@ -15752,6 +16140,7 @@ interface ResourceNamespaces {
15752
16140
  posts: PostsResource;
15753
16141
  salesNavigator: SalesNavigatorResource;
15754
16142
  recruiter: RecruiterResource;
16143
+ jobs: JobsResource;
15755
16144
  webhooks: WebhooksResource;
15756
16145
  }
15757
16146
  /**
@@ -15785,6 +16174,7 @@ declare class Curviate {
15785
16174
  readonly posts: PostsResource;
15786
16175
  readonly salesNavigator: SalesNavigatorResource;
15787
16176
  readonly recruiter: RecruiterResource;
16177
+ readonly jobs: JobsResource;
15788
16178
  readonly webhooks: WebhooksResource;
15789
16179
  constructor(config: CurviateConfig);
15790
16180
  /**
package/dist/index.js CHANGED
@@ -1043,6 +1043,21 @@ var SalesNavigatorResource = class {
1043
1043
  }
1044
1044
  };
1045
1045
 
1046
+ // src/internal/job-id.ts
1047
+ var JOB_URL_ID_PATTERN = /\/jobs\/view\/(\d+)/;
1048
+ function resolveJobId(idOrUrl) {
1049
+ const trimmed = idOrUrl.trim();
1050
+ if (/^\d+$/.test(trimmed)) return trimmed;
1051
+ const match = trimmed.match(JOB_URL_ID_PATTERN);
1052
+ if (match) return match[1];
1053
+ throw new CurviateError({
1054
+ code: "INVALID_REQUEST",
1055
+ message: `Could not extract a numeric job id from "${idOrUrl}". Pass a bare numeric id (e.g. "4428113858") or a LinkedIn job URL (e.g. "https://www.linkedin.com/jobs/view/4428113858").`,
1056
+ userFixable: true,
1057
+ retryLikelyToSucceed: false
1058
+ });
1059
+ }
1060
+
1046
1061
  // src/resources/recruiter.ts
1047
1062
  function buildFormData4(scalars, attachments, voice_message, video_message) {
1048
1063
  const form = new FormData();
@@ -1258,6 +1273,24 @@ var RecruiterResource = class {
1258
1273
  accountIdIn: "body"
1259
1274
  });
1260
1275
  }
1276
+ /**
1277
+ * Get a job posting via the Recruiter lens (any public posting, not only
1278
+ * the operator's own). `GET /v1/recruiter/jobs/{job_id}`
1279
+ *
1280
+ * Accepts a bare numeric job id or a full job URL
1281
+ * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
1282
+ * client-side, mirroring `jobs.get()`. Throws
1283
+ * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
1284
+ * form can be recognized. Returns the same `JobPosting` shape as
1285
+ * `jobs.get()`.
1286
+ */
1287
+ getJob(jobIdOrUrl) {
1288
+ const jobId = resolveJobId(jobIdOrUrl);
1289
+ return this.ctx.request({
1290
+ method: "GET",
1291
+ path: `/v1/recruiter/jobs/${jobId}`
1292
+ });
1293
+ }
1261
1294
  /**
1262
1295
  * List applicants for a job posting.
1263
1296
  * `GET /v1/recruiter/jobs/{job_id}/applicants`
@@ -1292,6 +1325,32 @@ var RecruiterResource = class {
1292
1325
  }
1293
1326
  };
1294
1327
 
1328
+ // src/resources/jobs.ts
1329
+ var JobsResource = class {
1330
+ constructor(ctx) {
1331
+ this.ctx = ctx;
1332
+ }
1333
+ /**
1334
+ * Retrieve one public LinkedIn job posting's full detail.
1335
+ * `GET /v1/jobs/{job_id}`
1336
+ *
1337
+ * Accepts a bare numeric job id or a full job URL
1338
+ * (`https://www.linkedin.com/jobs/view/{id}`) — the id is extracted
1339
+ * client-side; the wire request always carries the numeric id. Throws
1340
+ * `CurviateError({ code: 'INVALID_REQUEST' })` synchronously if neither
1341
+ * form can be recognized.
1342
+ * The `account_id` is injected by the account-scoped context.
1343
+ */
1344
+ get(jobIdOrUrl, params) {
1345
+ const jobId = resolveJobId(jobIdOrUrl);
1346
+ return this.ctx.request({
1347
+ method: "GET",
1348
+ path: `/v1/jobs/${jobId}`,
1349
+ ...params ? { query: params } : {}
1350
+ });
1351
+ }
1352
+ };
1353
+
1295
1354
  // src/resources/webhooks.ts
1296
1355
  var WebhooksResource = class {
1297
1356
  constructor(ctx) {
@@ -1377,6 +1436,7 @@ function buildNamespaces(ctx) {
1377
1436
  posts: new PostsResource(ctx),
1378
1437
  salesNavigator: new SalesNavigatorResource(ctx),
1379
1438
  recruiter: new RecruiterResource(ctx),
1439
+ jobs: new JobsResource(ctx),
1380
1440
  webhooks: new WebhooksResource(ctx)
1381
1441
  };
1382
1442
  }
@@ -1400,6 +1460,7 @@ var Curviate = class {
1400
1460
  this.posts = ns.posts;
1401
1461
  this.salesNavigator = ns.salesNavigator;
1402
1462
  this.recruiter = ns.recruiter;
1463
+ this.jobs = ns.jobs;
1403
1464
  this.webhooks = ns.webhooks;
1404
1465
  }
1405
1466
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curviate/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Curviate API.",
6
6
  "license": "MIT",
@@ -2411,6 +2411,176 @@ export interface paths {
2411
2411
  patch?: never;
2412
2412
  trace?: never;
2413
2413
  };
2414
+ "/v1/jobs/{job_id}": {
2415
+ parameters: {
2416
+ query?: never;
2417
+ header?: never;
2418
+ path?: never;
2419
+ cookie?: never;
2420
+ };
2421
+ /**
2422
+ * Get job posting
2423
+ * @description Returns one public LinkedIn job posting's full detail — title, company, location, description, and applicant count.
2424
+ */
2425
+ get: {
2426
+ parameters: {
2427
+ query: {
2428
+ /** @description The connected LinkedIn account to retrieve the job posting on behalf of. */
2429
+ account_id: string;
2430
+ };
2431
+ header?: never;
2432
+ path: {
2433
+ /** @description The numeric id of the job posting, e.g. 4428113858. This is the number in a LinkedIn job URL (https://www.linkedin.com/jobs/view/4428113858) and the job_urn value in a POST /v1/search/jobs result. Pass the number only — a full URL is not accepted by the API (the SDK and CLI extract the id from a URL for you). */
2434
+ job_id: string;
2435
+ };
2436
+ cookie?: never;
2437
+ };
2438
+ requestBody?: never;
2439
+ responses: {
2440
+ /** @description The job posting's full detail. Content fields (title, description) are returned verbatim and are never stored. */
2441
+ 200: {
2442
+ headers: {
2443
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
2444
+ RateLimit: components["headers"]["RateLimit"];
2445
+ [name: string]: unknown;
2446
+ };
2447
+ content: {
2448
+ "application/json": {
2449
+ /**
2450
+ * @description Response type discriminator.
2451
+ * @enum {string}
2452
+ */
2453
+ object: "job_posting";
2454
+ /** @description The job posting's numeric identifier. */
2455
+ id: string;
2456
+ /** @description Job title (content pass-through — never stored). */
2457
+ title: string;
2458
+ /** @description Hiring company's display name. */
2459
+ company: string;
2460
+ /** @description The hiring company's id — pass it to GET /v1/profiles/companies/{company_id}. */
2461
+ company_id: string;
2462
+ /** @description Posting lifecycle state (e.g. active). */
2463
+ state: string;
2464
+ /** @description Job location, or null when not set. */
2465
+ location: string | null;
2466
+ /** @description Posting cost, in the currency the account bills in. */
2467
+ cost: number;
2468
+ /** @description Number of applicants so far. */
2469
+ applicants_counter: number;
2470
+ /** @description Full job description text (content pass-through — never stored). */
2471
+ description: string;
2472
+ /** @description ISO-8601 UTC timestamp the posting was created, or null when not set. */
2473
+ created_at: string | null;
2474
+ /** @description ISO-8601 UTC timestamp the posting was published, or null when not set. */
2475
+ published_at: string | null;
2476
+ /** @description Hiring team members associated with this posting, when the posting exposes any. */
2477
+ hiring_team: unknown[];
2478
+ };
2479
+ };
2480
+ };
2481
+ /** @description Missing or empty account_id. */
2482
+ 400: {
2483
+ headers: {
2484
+ [name: string]: unknown;
2485
+ };
2486
+ content: {
2487
+ "application/json": components["schemas"]["Error"];
2488
+ };
2489
+ };
2490
+ /** @description Missing or invalid API key. */
2491
+ 401: {
2492
+ headers: {
2493
+ [name: string]: unknown;
2494
+ };
2495
+ content: {
2496
+ "application/json": components["schemas"]["Error"];
2497
+ };
2498
+ };
2499
+ /** @description The account does not have the required Core seat. */
2500
+ 403: {
2501
+ headers: {
2502
+ [name: string]: unknown;
2503
+ };
2504
+ content: {
2505
+ "application/json": components["schemas"]["Error"];
2506
+ };
2507
+ };
2508
+ /** @description The account_id does not belong to this tenant, or the job posting was not found. */
2509
+ 404: {
2510
+ headers: {
2511
+ [name: string]: unknown;
2512
+ };
2513
+ content: {
2514
+ "application/json": components["schemas"]["Error"];
2515
+ };
2516
+ };
2517
+ /** @description The account is restricted and cannot be queried. */
2518
+ 422: {
2519
+ headers: {
2520
+ [name: string]: unknown;
2521
+ };
2522
+ content: {
2523
+ "application/json": components["schemas"]["Error"];
2524
+ };
2525
+ };
2526
+ /** @description Rate limited — slow down and retry after the hinted delay. */
2527
+ 429: {
2528
+ headers: {
2529
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
2530
+ RateLimit: components["headers"]["RateLimit"];
2531
+ "Retry-After": components["headers"]["Retry-After"];
2532
+ [name: string]: unknown;
2533
+ };
2534
+ content: {
2535
+ "application/json": components["schemas"]["Error"];
2536
+ };
2537
+ };
2538
+ /** @description Internal error. */
2539
+ 500: {
2540
+ headers: {
2541
+ [name: string]: unknown;
2542
+ };
2543
+ content: {
2544
+ "application/json": components["schemas"]["Error"];
2545
+ };
2546
+ };
2547
+ /** @description A temporary error occurred. Please try again. */
2548
+ 502: {
2549
+ headers: {
2550
+ [name: string]: unknown;
2551
+ };
2552
+ content: {
2553
+ "application/json": components["schemas"]["Error"];
2554
+ };
2555
+ };
2556
+ /** @description Service unavailable. */
2557
+ 503: {
2558
+ headers: {
2559
+ [name: string]: unknown;
2560
+ };
2561
+ content: {
2562
+ "application/json": components["schemas"]["Error"];
2563
+ };
2564
+ };
2565
+ /** @description Gateway timeout. */
2566
+ 504: {
2567
+ headers: {
2568
+ [name: string]: unknown;
2569
+ };
2570
+ content: {
2571
+ "application/json": components["schemas"]["Error"];
2572
+ };
2573
+ };
2574
+ };
2575
+ };
2576
+ put?: never;
2577
+ post?: never;
2578
+ delete?: never;
2579
+ options?: never;
2580
+ head?: never;
2581
+ patch?: never;
2582
+ trace?: never;
2583
+ };
2414
2584
  "/v1/chats": {
2415
2585
  parameters: {
2416
2586
  query?: never;
@@ -11103,6 +11273,176 @@ export interface paths {
11103
11273
  patch?: never;
11104
11274
  trace?: never;
11105
11275
  };
11276
+ "/v1/recruiter/jobs/{job_id}": {
11277
+ parameters: {
11278
+ query?: never;
11279
+ header?: never;
11280
+ path?: never;
11281
+ cookie?: never;
11282
+ };
11283
+ /**
11284
+ * Get job posting
11285
+ * @description Returns one public LinkedIn job posting's full detail (RECRUITER lens) — title, company, location, description, and applicant count. Requires a Recruiter seat.
11286
+ */
11287
+ get: {
11288
+ parameters: {
11289
+ query: {
11290
+ /** @description The connected LinkedIn account to retrieve the job posting on behalf of. */
11291
+ account_id: string;
11292
+ };
11293
+ header?: never;
11294
+ path: {
11295
+ /** @description The numeric id of the job posting, e.g. 4428113858. This is the number in a LinkedIn job URL (https://www.linkedin.com/jobs/view/4428113858) and the job_urn value in a POST /v1/search/jobs result. Pass the number only — a full URL is not accepted by the API (the SDK and CLI extract the id from a URL for you). */
11296
+ job_id: string;
11297
+ };
11298
+ cookie?: never;
11299
+ };
11300
+ requestBody?: never;
11301
+ responses: {
11302
+ /** @description The job posting's full detail (RECRUITER lens — the same job_posting shape as the Core retrieve; content fields are returned verbatim and never stored). Requires a Recruiter seat. */
11303
+ 200: {
11304
+ headers: {
11305
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
11306
+ RateLimit: components["headers"]["RateLimit"];
11307
+ [name: string]: unknown;
11308
+ };
11309
+ content: {
11310
+ "application/json": {
11311
+ /**
11312
+ * @description Response type discriminator.
11313
+ * @enum {string}
11314
+ */
11315
+ object: "job_posting";
11316
+ /** @description The job posting's numeric identifier. */
11317
+ id: string;
11318
+ /** @description Job title (content pass-through — never stored). */
11319
+ title: string;
11320
+ /** @description Hiring company's display name. */
11321
+ company: string;
11322
+ /** @description The hiring company's id — pass it to GET /v1/profiles/companies/{company_id}. */
11323
+ company_id: string;
11324
+ /** @description Posting lifecycle state (e.g. active). */
11325
+ state: string;
11326
+ /** @description Job location, or null when not set. */
11327
+ location: string | null;
11328
+ /** @description Posting cost, in the currency the account bills in. */
11329
+ cost: number;
11330
+ /** @description Number of applicants so far. */
11331
+ applicants_counter: number;
11332
+ /** @description Full job description text (content pass-through — never stored). */
11333
+ description: string;
11334
+ /** @description ISO-8601 UTC timestamp the posting was created, or null when not set. */
11335
+ created_at: string | null;
11336
+ /** @description ISO-8601 UTC timestamp the posting was published, or null when not set. */
11337
+ published_at: string | null;
11338
+ /** @description Hiring team members associated with this posting, when the posting exposes any. */
11339
+ hiring_team: unknown[];
11340
+ };
11341
+ };
11342
+ };
11343
+ /** @description Missing or empty account_id. */
11344
+ 400: {
11345
+ headers: {
11346
+ [name: string]: unknown;
11347
+ };
11348
+ content: {
11349
+ "application/json": components["schemas"]["Error"];
11350
+ };
11351
+ };
11352
+ /** @description Missing or invalid API key, or LinkedIn authentication failed. */
11353
+ 401: {
11354
+ headers: {
11355
+ [name: string]: unknown;
11356
+ };
11357
+ content: {
11358
+ "application/json": components["schemas"]["Error"];
11359
+ };
11360
+ };
11361
+ /** @description The account does not have the required Recruiter seat. */
11362
+ 403: {
11363
+ headers: {
11364
+ [name: string]: unknown;
11365
+ };
11366
+ content: {
11367
+ "application/json": components["schemas"]["Error"];
11368
+ };
11369
+ };
11370
+ /** @description The account_id does not belong to this tenant, or the job posting was not found. */
11371
+ 404: {
11372
+ headers: {
11373
+ [name: string]: unknown;
11374
+ };
11375
+ content: {
11376
+ "application/json": components["schemas"]["Error"];
11377
+ };
11378
+ };
11379
+ /** @description The account is restricted and cannot be queried. */
11380
+ 422: {
11381
+ headers: {
11382
+ [name: string]: unknown;
11383
+ };
11384
+ content: {
11385
+ "application/json": components["schemas"]["Error"];
11386
+ };
11387
+ };
11388
+ /** @description Rate limited — slow down and retry after the hinted delay. */
11389
+ 429: {
11390
+ headers: {
11391
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
11392
+ RateLimit: components["headers"]["RateLimit"];
11393
+ "Retry-After": components["headers"]["Retry-After"];
11394
+ [name: string]: unknown;
11395
+ };
11396
+ content: {
11397
+ "application/json": components["schemas"]["Error"];
11398
+ };
11399
+ };
11400
+ /** @description Internal error. */
11401
+ 500: {
11402
+ headers: {
11403
+ [name: string]: unknown;
11404
+ };
11405
+ content: {
11406
+ "application/json": components["schemas"]["Error"];
11407
+ };
11408
+ };
11409
+ /** @description A temporary error occurred. Please try again. */
11410
+ 502: {
11411
+ headers: {
11412
+ [name: string]: unknown;
11413
+ };
11414
+ content: {
11415
+ "application/json": components["schemas"]["Error"];
11416
+ };
11417
+ };
11418
+ /** @description Service unavailable. */
11419
+ 503: {
11420
+ headers: {
11421
+ [name: string]: unknown;
11422
+ };
11423
+ content: {
11424
+ "application/json": components["schemas"]["Error"];
11425
+ };
11426
+ };
11427
+ /** @description Gateway timeout. */
11428
+ 504: {
11429
+ headers: {
11430
+ [name: string]: unknown;
11431
+ };
11432
+ content: {
11433
+ "application/json": components["schemas"]["Error"];
11434
+ };
11435
+ };
11436
+ };
11437
+ };
11438
+ put?: never;
11439
+ post?: never;
11440
+ delete?: never;
11441
+ options?: never;
11442
+ head?: never;
11443
+ patch?: never;
11444
+ trace?: never;
11445
+ };
11106
11446
  "/v1/recruiter/jobs/{job_id}/applicants": {
11107
11447
  parameters: {
11108
11448
  query?: never;