@curviate/sdk 0.24.3 → 0.25.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.d.ts CHANGED
@@ -982,6 +982,8 @@ interface paths {
982
982
  /**
983
983
  * Search chats
984
984
  * @description Free-text search of the connected account's own inbox. Matches both participant names and message content. A no-match term returns an empty list, not an error.
985
+ *
986
+ * Served from the chats and messages already retrieved for this account, so it costs no platform call and draws down no budget. A chat that has never been retrieved cannot match. The `coverage` block on the response says how complete the searched corpus is.
985
987
  */
986
988
  get: operations["getV1AccountIdChatsSearch"];
987
989
  put?: never;
@@ -2377,7 +2379,7 @@ interface paths {
2377
2379
  };
2378
2380
  /**
2379
2381
  * Get a linked LinkedIn account
2380
- * @description Return the metadata and current state for one connected account. Status is mapped to the stable whitelist: active | reconnect_needed | restricted | connecting.
2382
+ * @description Return the metadata and current state for one connected account. Status is mapped to the stable whitelist: active | reconnect_needed | restricted | connecting | disconnected.
2381
2383
  */
2382
2384
  get: operations["getV1AccountsAccountId"];
2383
2385
  put?: never;
@@ -2496,6 +2498,86 @@ interface paths {
2496
2498
  patch?: never;
2497
2499
  trace?: never;
2498
2500
  };
2501
+ "/v1/{account_id}/events": {
2502
+ parameters: {
2503
+ query?: never;
2504
+ header?: never;
2505
+ path?: never;
2506
+ cookie?: never;
2507
+ };
2508
+ /**
2509
+ * List the events received for an account
2510
+ * @description Returns the events Curviate received for this account in the last 24 hours, newest received first, with the exact payload a registered webhook would have been delivered. Events are retained whether or not any webhook is subscribed to them, so this works with no listener running. Retention is 24 hours from received_at and is absolute, and a live claim does not extend it. Disconnecting an account removes its events from this response immediately; the rows are not otherwise deleted early. Events can arrive out of order, so treat the ordering as arrival order, not causal order. Each item carries its queue state, and `state` filters on it: `done` is what your agents have already handled, `unread` is what is still waiting, `claimed` is what someone holds right now. This read claims nothing and is safe to poll. Page until cursor is null.
2511
+ */
2512
+ get: operations["getV1AccountIdEvents"];
2513
+ put?: never;
2514
+ post?: never;
2515
+ delete?: never;
2516
+ options?: never;
2517
+ head?: never;
2518
+ patch?: never;
2519
+ trace?: never;
2520
+ };
2521
+ "/v1/{account_id}/events/claim": {
2522
+ parameters: {
2523
+ query?: never;
2524
+ header?: never;
2525
+ path?: never;
2526
+ cookie?: never;
2527
+ };
2528
+ get?: never;
2529
+ put?: never;
2530
+ /**
2531
+ * Claim events to work on
2532
+ * @description Leases up to `limit` unread events for this account and returns them, oldest `occurred_at` first, in one atomic step. Two agents claiming at the same instant never receive the same event, which a plain read plus a read flag cannot promise. Claiming when nothing is available returns an empty list and a 200. `consumer` is free text that identifies your worker. It is recorded on each event and never verified, so the lease is cooperative: it keeps your own agents from duplicating work, and it is not access control. Finish an event with the done call, or hand it back with release. If you do neither, the lease expires after `lease_seconds` (default 300, max 3600) and the event becomes claimable again. `limit` defaults to 10 and caps at 100. Ordering is arrival order, not causal order: events can arrive out of sequence, so establish causality from the payload if you need it. Retention is still 24 hours from `received_at` and a lease does not extend it, so an event can disappear while you hold it.
2533
+ */
2534
+ post: operations["postV1AccountIdEventsClaim"];
2535
+ delete?: never;
2536
+ options?: never;
2537
+ head?: never;
2538
+ patch?: never;
2539
+ trace?: never;
2540
+ };
2541
+ "/v1/{account_id}/events/{event_id}/release": {
2542
+ parameters: {
2543
+ query?: never;
2544
+ header?: never;
2545
+ path?: never;
2546
+ cookie?: never;
2547
+ };
2548
+ get?: never;
2549
+ put?: never;
2550
+ /**
2551
+ * Release a claimed event
2552
+ * @description Hands a leased event back so it can be claimed again immediately, by you or by anyone else. Use it when your handler fails: returning the work is better than holding it for the rest of the lease. No request body. The caller is not checked against whoever claimed it, because the lease is cooperative rather than access control. Releasing an event that is already done changes nothing and returns state `done`.
2553
+ */
2554
+ post: operations["postV1AccountIdEventsEventIdRelease"];
2555
+ delete?: never;
2556
+ options?: never;
2557
+ head?: never;
2558
+ patch?: never;
2559
+ trace?: never;
2560
+ };
2561
+ "/v1/{account_id}/events/{event_id}/done": {
2562
+ parameters: {
2563
+ query?: never;
2564
+ header?: never;
2565
+ path?: never;
2566
+ cookie?: never;
2567
+ };
2568
+ get?: never;
2569
+ put?: never;
2570
+ /**
2571
+ * Mark an event done
2572
+ * @description Marks an event handled. This is the only terminal state there is, and it is shared across every consumer under this tenant: a done event leaves the unread set for all of them, so it is the signal that the work is finished rather than a per-agent bookmark. There is no failed state. An event nothing can handle needs no special marking, it expires with the rest at 24 hours. No request body, safe to call twice, and it keeps the first completion time.
2573
+ */
2574
+ post: operations["postV1AccountIdEventsEventIdDone"];
2575
+ delete?: never;
2576
+ options?: never;
2577
+ head?: never;
2578
+ patch?: never;
2579
+ trace?: never;
2580
+ };
2499
2581
  "/v1/webhooks": {
2500
2582
  parameters: {
2501
2583
  query?: never;
@@ -2529,7 +2611,7 @@ interface paths {
2529
2611
  };
2530
2612
  /**
2531
2613
  * List webhook event types
2532
- * @description Returns the complete canonical event catalogue (28 events) grouped by source: messaging (8), user (2), account_status (15), plus 3 tier-gated events. No quota consumed.
2614
+ * @description Returns the complete canonical event catalogue (28 events) grouped by source: messaging (8), user (2), account_status (15), plus 3 tier-gated events. A local catalogue read: no platform call is made.
2533
2615
  */
2534
2616
  get: operations["getV1WebhooksEvents"];
2535
2617
  put?: never;
@@ -2588,6 +2670,50 @@ interface paths {
2588
2670
  patch: operations["patchV1WebhooksId"];
2589
2671
  trace?: never;
2590
2672
  };
2673
+ "/v1/{account_id}/safety-policy": {
2674
+ parameters: {
2675
+ query?: never;
2676
+ header?: never;
2677
+ path?: never;
2678
+ cookie?: never;
2679
+ };
2680
+ /**
2681
+ * Read an account's safety policy
2682
+ * @description Returns every configurable safety field for every budget row of one account, in a single response: the ceiling, the green and amber bands, the counting window, the enforcement posture and where it was inherited from, the activity window, warm-up state, the per-row character caps and search per-query cap, and how well calibrated each row's number is. Every value is seeded configuration and every one of them is settable. Where a configured value is above the Curviate default, the row carries an `over_default` entry naming the value, the default and the source class of that default. The response also carries `tenant_default_posture`, the workspace-wide default every account inherits, separately from this account's own effective `posture`, and `limit_profile`, which says which of the four Curviate default sets (`basic`, `premium`, `sales_navigator`, `recruiter`) every row below was resolved from, detected from the platform when the account connected. When the Curviate seat attached to this account disagrees with that, `seat_tier_mismatch` names the seat tier; the detected product wins the numbers either way.
2683
+ */
2684
+ get: operations["getV1AccountIdSafetyPolicy"];
2685
+ put?: never;
2686
+ post?: never;
2687
+ delete?: never;
2688
+ options?: never;
2689
+ head?: never;
2690
+ /**
2691
+ * Update an account's safety policy
2692
+ * @description The single safety-policy write. Send only the fields you are changing: everything you omit is left byte-identical, and sending `null` for a field clears your override and restores the Curviate default. Any limit may be set to any value: Curviate does not clamp, cap or refuse a change on the grounds that the number is unsafe. A value above the Curviate default is reported back in the response, and on every later read, with the default and its source class attached; there is no way to acknowledge it away, and setting the value back to the default removes it. A value that is not a value, such as a negative ceiling or an unknown row name, is a 400. Every field whose value CHANGES is appended to the account's action ledger with the previous value, the new value, the Curviate default, the actor and the time; a field you send whose value already equals its current effective value is not ledgered and is not stored as an override, so reading the policy and sending the document back changes nothing. Four fields on the read are derived rather than configured: `effective_ceiling`, `posture_source`, `over_default` and each row's `warm_up_state`. Send them and they are accepted and ignored, never stored and never ledgered, so the document you read can go straight back. To pin a row's ramp, set `warm_up_factor`. `activity_window` is not accepted: send its settable subfields instead, which for the zone is the top-level `timezone`. And read `changes nothing` as `changes no limit`: the `posture` fields are OVERRIDES at every scope, so writing back a posture a scope merely inherits PINS it there and appends one ledger entry per pinned scope. That is what stops a later account- or tenant-wide change from moving it. A pin already in place is a no-op. An agent-authenticated write is accepted on exactly the same footing as an operator's. There is no bulk import, file upload or policy template: configuring many rows or many accounts is a loop over this operation. One field is not account-scoped: `tenant_default_posture` sets the default for EVERY account in the tenant, not just the one in the path, which is what makes it a one-call change for a whole workspace of personas. It is reported back at the top of every policy response, so a change to it is visible even on an account that overrides it. One other field is not row-scoped: `limit_profile` selects which of the four Curviate default sets this account's rows resolve from, so it moves the default under every row at once while leaving every value you have configured explicitly exactly as you set it. It is the platform product active on the account, not your Curviate seat entitlement, and the two can disagree: a lapsed subscription leaves the seat untouched. Curviate detects it when the account connects and re-detects it on every reconnect; setting it here is an operator override of that result, and it holds until the next connect. When the seat and the detected product disagree, the detected product wins the numbers and the read reports the disagreement as `seat_tier_mismatch`.
2693
+ */
2694
+ patch: operations["patchV1AccountIdSafetyPolicy"];
2695
+ trace?: never;
2696
+ };
2697
+ "/v1/{account_id}/safety-events": {
2698
+ parameters: {
2699
+ query?: never;
2700
+ header?: never;
2701
+ path?: never;
2702
+ cookie?: never;
2703
+ };
2704
+ /**
2705
+ * List what Curviate refused or warned about
2706
+ * @description Returns this account's safety events, newest first: every action Curviate refused, and every action it let through while flagging that a limit had been crossed. Under the default posture nothing is refused, so everything here is a warning and `blocked` is how you tell the two apart. Each event names the budget row that breached, why, the operation that hit it and when. Filter by `budget_row` for one action type and by `since` / `until` for a period, and page until `cursor` is null. This is a read of what already happened, so it never changes a limit or a count: to change what is refused, update the safety policy. The account resource is the other half of the picture, reporting where each row stands right now rather than what has already been flagged.
2707
+ */
2708
+ get: operations["getV1AccountIdSafetyEvents"];
2709
+ put?: never;
2710
+ post?: never;
2711
+ delete?: never;
2712
+ options?: never;
2713
+ head?: never;
2714
+ patch?: never;
2715
+ trace?: never;
2716
+ };
2591
2717
  }
2592
2718
  interface components {
2593
2719
  schemas: {
@@ -2621,6 +2747,26 @@ interface components {
2621
2747
  * @enum {string}
2622
2748
  */
2623
2749
  required_tier?: "core" | "sales_nav" | "recruiter";
2750
+ /** @description Present on a PLATFORM_RATE_LIMIT raised because one account-safety budget row is paused: LinkedIn refused a recent call on that row, so this one was refused locally without reaching LinkedIn. Names the paused row (for example profile_views, connection_requests_no_note). The pause is scoped to this row on this account; every other row keeps working. Absent on every other rate limit. Also present on BUDGET_EXHAUSTED, where it names the row that hit its CEILING rather than one LinkedIn paused: read the code to tell the two apart, because the recovery differs. */
2751
+ row?: string;
2752
+ /** @description Present on BUDGET_EXHAUSTED: when this row's budget frees up, as an absolute instant rather than a duration, so it stays true however long you hold it. Null for the pending_invites row, whose backlog falls when invitations are accepted or withdrawn rather than at any window boundary, so there is no instant to name. */
2753
+ reset_at?: string | null;
2754
+ /** @description Present on BUDGET_EXHAUSTED: the exact setting to change, as a field rather than as prose. parameter is addressable on PATCH /v1/{account_id}/safety-policy, so an agent can decide between waiting until reset_at, escalating to a human, and reconfiguring, without parsing the message. */
2755
+ hint?: {
2756
+ /** @description The settable parameter, for example profile_views.ceiling or posture. */
2757
+ parameter: string;
2758
+ /** @description One paragraph naming the condition and what changing that parameter would do. */
2759
+ message: string;
2760
+ };
2761
+ /**
2762
+ * @description Present on BUDGET_EXHAUSTED: which rule refused. ceiling means the row's configured limit is spent, and you wait until reset_at or raise it. activity_window means the account is outside the hours it works in, and reset_at is when the window next opens. The two need different fixes, so branch on this rather than on the message; hint.parameter already names the right setting for each.
2763
+ * @enum {string}
2764
+ */
2765
+ reason?: "ceiling" | "activity_window";
2766
+ /** @description Present on BUDGET_EXHAUSTED, always true: the action did not happen and spent nothing. The same payload with blocked false rides the SUCCESS body of an account on the default warn posture, under safety_warning, and is otherwise field-identical, so one branch of your code handles both postures and moving an account to enforce is not a breaking change. */
2767
+ blocked?: boolean;
2768
+ /** @description Present alongside row: whole seconds until that row is usable again. This response carries no Retry-After header and no delay retry_hint, deliberately: a paused row can be paused for an hour, and a client that sleeps that inside the call hangs. Do not retry this row before the wait elapses; switch to other work instead. Seconds, not milliseconds. */
2769
+ retry_after?: number;
2624
2770
  /** @description Present when a structured search filter value matched several filter options and one has to be picked (422 FILTER_CANDIDATES_REQUIRED). One entry per offending value, so a single retry can fix them all. A value that matched no option at all is not listed here and is not an error: it is sent on as an id and reported in notices[] on the 200. */
2625
2771
  unresolved?: {
2626
2772
  /** @description The request body field the value came from. Dotted for nested filters, for example author.company. */
@@ -2652,6 +2798,38 @@ interface components {
2652
2798
  value?: string;
2653
2799
  }[];
2654
2800
  };
2801
+ /**
2802
+ * @description Present on a successful response when this account crossed an account-safety ceiling while the affected budget row is on the default warn posture. The action still happened. Field-identical to the BUDGET_EXHAUSTED error body except that blocked is false, so one branch handles both postures and switching the account to enforce is a configuration change rather than a client rewrite.
2803
+ * @example {
2804
+ * "row": "profile_views",
2805
+ * "reset_at": "2026-09-05T00:00:00.000Z",
2806
+ * "hint": {
2807
+ * "parameter": "profile_views.ceiling",
2808
+ * "message": "This account is at its configured ceiling for profile_views."
2809
+ * },
2810
+ * "reason": "ceiling",
2811
+ * "blocked": false
2812
+ * }
2813
+ */
2814
+ SafetyWarning: {
2815
+ /** @description The budget row that breached, for example profile_views or pending_invites. */
2816
+ row: string;
2817
+ /** @description When the row frees up, as an absolute instant. Null for the pending_invites gauge. */
2818
+ reset_at: string | null;
2819
+ hint: {
2820
+ /** @description The settable parameter on PATCH /v1/{account_id}/safety-policy. */
2821
+ parameter: string;
2822
+ /** @description What the condition is and what changing that parameter would do. */
2823
+ message: string;
2824
+ };
2825
+ /**
2826
+ * @description Which rule was crossed. ceiling means the row's configured limit is spent; activity_window means the account is outside the hours it works in, which is a different fact with a different remedy. hint.parameter names the right setting for each.
2827
+ * @enum {string}
2828
+ */
2829
+ reason: "ceiling" | "activity_window";
2830
+ /** @description Always false here. True on the BUDGET_EXHAUSTED refusal carrying the same payload. */
2831
+ blocked: boolean;
2832
+ };
2655
2833
  /** @description A message. `quoted` carries the message this one quotes, when it quotes one; a quoted message has the same shape and may itself carry a `quoted`. */
2656
2834
  Message: {
2657
2835
  /**
@@ -2740,6 +2918,10 @@ interface operations {
2740
2918
  query?: {
2741
2919
  /** @description Which profile sections to fetch (repeatable, flat). Pass one or more of: linkedin_experience, linkedin_education, linkedin_languages, linkedin_skills, linkedin_certifications, linkedin_volunteer_experience, linkedin_projects, linkedin_recommendations, linkedin_interests (or linkedin_* for all), plus each value's _preview variant. Omit for base fields only. */
2742
2920
  linkedin_sections?: string[];
2921
+ /** @description How willing this read is to reach LinkedIn. `auto` (default) serves a stored copy while it is within the resource's freshness threshold; `live` always fetches; `refill` serves a stored copy at any age and fetches once when this read has no stored copy yet; `cache_only` never fetches and returns NOT_STORED when nothing is stored. `cache_only` cannot be combined with max_age. A 502 under `cache_only` means the store could not be read, not that nothing is stored: no request was made to LinkedIn, and the same read is worth retrying. */
2922
+ mode?: "live" | "auto" | "refill" | "cache_only";
2923
+ /** @description Maximum age, in seconds, of a stored copy this read will accept. Overrides the `auto`, `live` and `refill` presets in both directions; `0` is the same as `mode=live`. Not accepted with `mode=cache_only`, whose guarantee is not a freshness threshold. */
2924
+ max_age?: number;
2743
2925
  };
2744
2926
  header?: never;
2745
2927
  path: {
@@ -2757,6 +2939,8 @@ interface operations {
2757
2939
  headers: {
2758
2940
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
2759
2941
  RateLimit: components["headers"]["RateLimit"];
2942
+ /** @description Seconds since this data was observed on LinkedIn. Present only when source is `store`; a live fetch sets no Age header at all. */
2943
+ Age?: number;
2760
2944
  [name: string]: unknown;
2761
2945
  };
2762
2946
  content: {
@@ -3247,10 +3431,22 @@ interface operations {
3247
3431
  /** @description Section names that were requested but throttled by LinkedIn. */
3248
3432
  throttled_sections?: string[];
3249
3433
  };
3434
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
3435
+ observed_at: string;
3436
+ /**
3437
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
3438
+ * @enum {string}
3439
+ */
3440
+ source: "store" | "live";
3441
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
3442
+ withdrawn: boolean;
3443
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
3444
+ withdrawn_at?: string;
3445
+ safety_warning?: components["schemas"]["SafetyWarning"];
3250
3446
  };
3251
3447
  };
3252
3448
  };
3253
- /** @description Malformed linkedin_sections value or invalid query param. */
3449
+ /** @description Either a query param failed validation (the error details name the field), or the platform rejected the request for this target profile: The request was rejected as invalid. In the latter case the linkedin_sections value is not at fault, and the same request may succeed on another profile. INVALID_REQUEST also covers `mode=cache_only` sent together with `max_age`: that mode never reaches LinkedIn at any age, so a freshness threshold cannot change its answer, and the pair is refused rather than one of the two being ignored. Drop `max_age`, or use `mode=refill` (stored at any age, fetch only when nothing is stored). */
3254
3450
  400: {
3255
3451
  headers: {
3256
3452
  [name: string]: unknown;
@@ -3286,7 +3482,7 @@ interface operations {
3286
3482
  "application/json": components["schemas"]["Error"];
3287
3483
  };
3288
3484
  };
3289
- /** @description The account is restricted and cannot be queried. */
3485
+ /** @description The account is restricted and cannot be queried (ACCOUNT_RESTRICTED). Nothing is stored for this resource and `mode=cache_only` never fetches (NOT_STORED). Not the same as a 404: the resource may exist, and this API holds no copy of it. Re-read with `mode=refill` or `mode=auto` to fetch it. */
3290
3486
  422: {
3291
3487
  headers: {
3292
3488
  [name: string]: unknown;
@@ -3316,7 +3512,7 @@ interface operations {
3316
3512
  "application/json": components["schemas"]["Error"];
3317
3513
  };
3318
3514
  };
3319
- /** @description A temporary error occurred. Please try again. */
3515
+ /** @description A temporary error occurred. Please try again. Under `mode=cache_only` this specifically means the stored copy could not be read: no request was made to LinkedIn, and the same read is worth retrying. */
3320
3516
  502: {
3321
3517
  headers: {
3322
3518
  [name: string]: unknown;
@@ -3411,6 +3607,7 @@ interface operations {
3411
3607
  * @enum {string}
3412
3608
  */
3413
3609
  object: "user_updated";
3610
+ safety_warning?: components["schemas"]["SafetyWarning"];
3414
3611
  } & {
3415
3612
  [key: string]: unknown;
3416
3613
  };
@@ -3582,6 +3779,7 @@ interface operations {
3582
3779
  }[];
3583
3780
  /** @description Next-page cursor; null on the last page. */
3584
3781
  cursor?: string | null;
3782
+ safety_warning?: components["schemas"]["SafetyWarning"];
3585
3783
  };
3586
3784
  };
3587
3785
  };
@@ -3708,6 +3906,7 @@ interface operations {
3708
3906
  /** @description Remaining InMail credits on a Sales Navigator seat, or null if not subscribed. */
3709
3907
  sales_navigator: number | null;
3710
3908
  };
3909
+ safety_warning?: components["schemas"]["SafetyWarning"];
3711
3910
  };
3712
3911
  };
3713
3912
  };
@@ -3823,6 +4022,7 @@ interface operations {
3823
4022
  * @enum {string}
3824
4023
  */
3825
4024
  object: "skill_endorsed";
4025
+ safety_warning?: components["schemas"]["SafetyWarning"];
3826
4026
  };
3827
4027
  };
3828
4028
  };
@@ -4019,6 +4219,7 @@ interface operations {
4019
4219
  };
4020
4220
  /** @description Always null for this resource; the full administered set returns in one page. */
4021
4221
  cursor: string | null;
4222
+ safety_warning?: components["schemas"]["SafetyWarning"];
4022
4223
  };
4023
4224
  };
4024
4225
  };
@@ -4181,6 +4382,7 @@ interface operations {
4181
4382
  actions: {
4182
4383
  [key: string]: string;
4183
4384
  };
4385
+ safety_warning?: components["schemas"]["SafetyWarning"];
4184
4386
  };
4185
4387
  };
4186
4388
  };
@@ -4323,6 +4525,7 @@ interface operations {
4323
4525
  /** @description Trend magnitude in percent (direction is a client-rendered icon absent from the source, so magnitude only). */
4324
4526
  change_pct_magnitude: number | null;
4325
4527
  };
4528
+ safety_warning?: components["schemas"]["SafetyWarning"];
4326
4529
  };
4327
4530
  };
4328
4531
  };
@@ -4465,6 +4668,7 @@ interface operations {
4465
4668
  }[];
4466
4669
  /** @description Opaque cursor for the next page; null when the list is known-exhausted. A non-null cursor means more may exist, keep paging (zero individuals on a page is not exhaustion). There is no total (the upstream total is unreliable). */
4467
4670
  cursor: string | null;
4671
+ safety_warning?: components["schemas"]["SafetyWarning"];
4468
4672
  };
4469
4673
  };
4470
4674
  };
@@ -4594,6 +4798,7 @@ interface operations {
4594
4798
  active_seat: boolean;
4595
4799
  /** @description Freshness watermark (epoch ms). Treat 0/null as "no watermark / computed on read", not a 1970 date. */
4596
4800
  calculated_at: number | null;
4801
+ safety_warning?: components["schemas"]["SafetyWarning"];
4597
4802
  };
4598
4803
  };
4599
4804
  };
@@ -4747,6 +4952,7 @@ interface operations {
4747
4952
  }[];
4748
4953
  /** @description Next-page cursor; null on the last page. */
4749
4954
  cursor?: string | null;
4955
+ safety_warning?: components["schemas"]["SafetyWarning"];
4750
4956
  };
4751
4957
  };
4752
4958
  };
@@ -4900,6 +5106,7 @@ interface operations {
4900
5106
  }[];
4901
5107
  /** @description Next-page cursor; null on the last page. */
4902
5108
  cursor?: string | null;
5109
+ safety_warning?: components["schemas"]["SafetyWarning"];
4903
5110
  };
4904
5111
  };
4905
5112
  };
@@ -5174,6 +5381,7 @@ interface operations {
5174
5381
  * @enum {string}
5175
5382
  */
5176
5383
  object: "user_unfollowed";
5384
+ safety_warning?: components["schemas"]["SafetyWarning"];
5177
5385
  };
5178
5386
  };
5179
5387
  };
@@ -5368,6 +5576,18 @@ interface operations {
5368
5576
  /** @description Average employee tenure, in years. */
5369
5577
  average_tenure?: number;
5370
5578
  };
5579
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
5580
+ observed_at: string;
5581
+ /**
5582
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
5583
+ * @enum {string}
5584
+ */
5585
+ source: "store" | "live";
5586
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
5587
+ withdrawn: boolean;
5588
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
5589
+ withdrawn_at?: string;
5590
+ safety_warning?: components["schemas"]["SafetyWarning"];
5371
5591
  };
5372
5592
  };
5373
5593
  };
@@ -5558,6 +5778,7 @@ interface operations {
5558
5778
  /** @description The offending value. */
5559
5779
  value?: string;
5560
5780
  }[];
5781
+ safety_warning?: components["schemas"]["SafetyWarning"];
5561
5782
  };
5562
5783
  };
5563
5784
  };
@@ -5732,11 +5953,26 @@ interface operations {
5732
5953
  repost_count: number;
5733
5954
  }[];
5734
5955
  paging: {
5735
- /** @description Total number of matching posts across all pages, or null when the platform does not report one. Count the items rather than relying on this. */
5956
+ /** @description Always null. This list runs on the post search, which no longer reports how many posts match, so there is no total to page toward; walk the pages with cursor until it comes back null. */
5736
5957
  total_count: number | null;
5737
5958
  };
5738
5959
  /** @description Opaque cursor for the next page, or null when there are no more results. */
5739
5960
  cursor: string | null;
5961
+ /** @description Present only when there is something to report about this page, for example that this page is short because the walk stopped fetching upstream pages after hitting the page cap, not because the results ran out; more results wait behind the cursor (a null cursor is the only reliable end-of-results signal). Absent when there is nothing to report. */
5962
+ notices?: {
5963
+ /**
5964
+ * @description FILTER_VALUE_UNRESOLVED: the value matched no known filter option, so it was sent on as an id we could not check. FILTER_VALUE_UNCHECKED: the value already looked like an id, so it was never looked up, and this page came back empty. SOME_RESULTS_HIDDEN: some people on this page were not disclosed to the connected account, so those entries cannot be read back. ALL_RESULTS_HIDDEN: none of the people on this page were disclosed, so the page yielded nothing you can act on, even when more pages follow. PAGE_TRUNCATED: this page is short because the search stopped fetching upstream pages, not because the results ran out; follow cursor, and treat only a null cursor as the end. Branch on this rather than on message.
5965
+ * @enum {string}
5966
+ */
5967
+ code: "FILTER_VALUE_UNRESOLVED" | "FILTER_VALUE_UNCHECKED" | "SOME_RESULTS_HIDDEN" | "ALL_RESULTS_HIDDEN" | "PAGE_TRUNCATED";
5968
+ /** @description One sentence explaining the condition and how to resolve it. */
5969
+ message: string;
5970
+ /** @description The request field the notice is about, dotted for nested filters. */
5971
+ field?: string;
5972
+ /** @description The offending value. */
5973
+ value?: string;
5974
+ }[];
5975
+ safety_warning?: components["schemas"]["SafetyWarning"];
5740
5976
  };
5741
5977
  };
5742
5978
  };
@@ -5916,6 +6152,7 @@ interface operations {
5916
6152
  };
5917
6153
  /** @description Opaque cursor for the next page, or null when there are no more results. */
5918
6154
  cursor: string | null;
6155
+ safety_warning?: components["schemas"]["SafetyWarning"];
5919
6156
  };
5920
6157
  };
5921
6158
  };
@@ -6073,6 +6310,7 @@ interface operations {
6073
6310
  message?: string;
6074
6311
  } | null;
6075
6312
  }[];
6313
+ safety_warning?: components["schemas"]["SafetyWarning"];
6076
6314
  };
6077
6315
  };
6078
6316
  };
@@ -6239,6 +6477,7 @@ interface operations {
6239
6477
  }[];
6240
6478
  /** @description Opaque cursor for the next page, or null at end-of-list. */
6241
6479
  cursor: string | null;
6480
+ safety_warning?: components["schemas"]["SafetyWarning"];
6242
6481
  };
6243
6482
  };
6244
6483
  };
@@ -6386,6 +6625,7 @@ interface operations {
6386
6625
  }[];
6387
6626
  /** @description Opaque cursor for the next page, or null when exhausted. */
6388
6627
  cursor: string | null;
6628
+ safety_warning?: components["schemas"]["SafetyWarning"];
6389
6629
  };
6390
6630
  };
6391
6631
  };
@@ -6580,6 +6820,7 @@ interface operations {
6580
6820
  /** @description The expansion the notice is about, here public_identifier. */
6581
6821
  value?: string;
6582
6822
  }[];
6823
+ safety_warning?: components["schemas"]["SafetyWarning"];
6583
6824
  };
6584
6825
  };
6585
6826
  };
@@ -6762,6 +7003,7 @@ interface operations {
6762
7003
  /** @description The expansion the notice is about, here public_identifier. */
6763
7004
  value?: string;
6764
7005
  }[];
7006
+ safety_warning?: components["schemas"]["SafetyWarning"];
6765
7007
  };
6766
7008
  };
6767
7009
  };
@@ -6937,6 +7179,7 @@ interface operations {
6937
7179
  /** @description The expansion the notice is about, here public_identifier. */
6938
7180
  value?: string;
6939
7181
  }[];
7182
+ safety_warning?: components["schemas"]["SafetyWarning"];
6940
7183
  };
6941
7184
  };
6942
7185
  };
@@ -7082,11 +7325,11 @@ interface operations {
7082
7325
  * @description Response type discriminator.
7083
7326
  * @enum {string}
7084
7327
  */
7085
- object?: "message_sent";
7086
- /** @description The sent message's identifier. */
7087
- message_id?: string;
7328
+ object: "message_sent";
7329
+ /** @description The sent message's identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. */
7330
+ message_id: string | string[] | null;
7088
7331
  /** @description The identity this message was actually sent as. */
7089
- sent_as?: {
7332
+ sent_as: {
7090
7333
  /**
7091
7334
  * @description Which identity sent the message.
7092
7335
  * @enum {string}
@@ -7097,6 +7340,7 @@ interface operations {
7097
7340
  /** @description Company inboxes only. The page's display name, or null in the rare case the page itself could not be resolved at all. */
7098
7341
  name?: string | null;
7099
7342
  };
7343
+ safety_warning?: components["schemas"]["SafetyWarning"];
7100
7344
  };
7101
7345
  };
7102
7346
  };
@@ -7278,6 +7522,7 @@ interface operations {
7278
7522
  /** @description The expansion the notice is about, here public_identifier. */
7279
7523
  value?: string;
7280
7524
  }[];
7525
+ safety_warning?: components["schemas"]["SafetyWarning"];
7281
7526
  };
7282
7527
  };
7283
7528
  };
@@ -7483,6 +7728,7 @@ interface operations {
7483
7728
  /** @description The expansion the notice is about, here public_identifier. */
7484
7729
  value?: string;
7485
7730
  }[];
7731
+ safety_warning?: components["schemas"]["SafetyWarning"];
7486
7732
  };
7487
7733
  };
7488
7734
  };
@@ -7675,6 +7921,7 @@ interface operations {
7675
7921
  };
7676
7922
  /** @description Opaque cursor for the next page; null when the enumeration is exhausted. */
7677
7923
  cursor: string | null;
7924
+ safety_warning?: components["schemas"]["SafetyWarning"];
7678
7925
  };
7679
7926
  };
7680
7927
  };
@@ -7839,6 +8086,18 @@ interface operations {
7839
8086
  invitation_level: string | null;
7840
8087
  /** @description Up to ~12 sampled member ids, a partial sample, possibly empty. */
7841
8088
  sample_past_members: string[];
8089
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
8090
+ observed_at: string;
8091
+ /**
8092
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
8093
+ * @enum {string}
8094
+ */
8095
+ source: "store" | "live";
8096
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
8097
+ withdrawn: boolean;
8098
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
8099
+ withdrawn_at?: string;
8100
+ safety_warning?: components["schemas"]["SafetyWarning"];
7842
8101
  };
7843
8102
  };
7844
8103
  };
@@ -8001,6 +8260,7 @@ interface operations {
8001
8260
  };
8002
8261
  /** @description Opaque cursor for the next page; null on the last page. */
8003
8262
  cursor: string | null;
8263
+ safety_warning?: components["schemas"]["SafetyWarning"];
8004
8264
  };
8005
8265
  };
8006
8266
  };
@@ -8134,6 +8394,7 @@ interface operations {
8134
8394
  }[];
8135
8395
  /** @description Opaque pagination cursor for the next page, or null when this is the last page. */
8136
8396
  cursor: string | null;
8397
+ safety_warning?: components["schemas"]["SafetyWarning"];
8137
8398
  };
8138
8399
  };
8139
8400
  };
@@ -8354,6 +8615,7 @@ interface operations {
8354
8615
  /** @description The offending value. */
8355
8616
  value?: string;
8356
8617
  }[];
8618
+ safety_warning?: components["schemas"]["SafetyWarning"];
8357
8619
  };
8358
8620
  };
8359
8621
  };
@@ -8532,7 +8794,7 @@ interface operations {
8532
8794
  }[];
8533
8795
  /** @description Paging metadata. */
8534
8796
  paging: {
8535
- /** @description Total matching result count. Large counts are a >=1000 ceiling, not an exact total. */
8797
+ /** @description Always null on this search. LinkedIn stopped reporting how many results match, so there is no total to page toward; walk the pages with cursor until it comes back null. A short or empty page is not the end: only a null cursor is. */
8536
8798
  total_count: number | null;
8537
8799
  };
8538
8800
  /** @description Opaque next-page cursor; null on the last page. */
@@ -8551,6 +8813,7 @@ interface operations {
8551
8813
  /** @description The offending value. */
8552
8814
  value?: string;
8553
8815
  }[];
8816
+ safety_warning?: components["schemas"]["SafetyWarning"];
8554
8817
  };
8555
8818
  };
8556
8819
  };
@@ -8777,7 +9040,7 @@ interface operations {
8777
9040
  }[];
8778
9041
  /** @description Paging metadata. */
8779
9042
  paging: {
8780
- /** @description Total matching result count. */
9043
+ /** @description Always null on this search. LinkedIn stopped reporting how many results match, so there is no total to page toward; walk the pages with cursor until it comes back null. A short or empty page is not the end: only a null cursor is. */
8781
9044
  total_count: number | null;
8782
9045
  };
8783
9046
  /** @description Opaque next-page cursor; null on the last page. */
@@ -8796,6 +9059,7 @@ interface operations {
8796
9059
  /** @description The offending value. */
8797
9060
  value?: string;
8798
9061
  }[];
9062
+ safety_warning?: components["schemas"]["SafetyWarning"];
8799
9063
  };
8800
9064
  };
8801
9065
  };
@@ -9060,6 +9324,7 @@ interface operations {
9060
9324
  /** @description The offending value. */
9061
9325
  value?: string;
9062
9326
  }[];
9327
+ safety_warning?: components["schemas"]["SafetyWarning"];
9063
9328
  };
9064
9329
  };
9065
9330
  };
@@ -9344,11 +9609,12 @@ interface operations {
9344
9609
  })[];
9345
9610
  /** @description Paging metadata. */
9346
9611
  paging: {
9347
- /** @description Total matching result count. Always null when the pasted URL is a people search, because LinkedIn does not report a total for those; page with cursor instead. For company, post, and job URLs a large count is a >=1000 ceiling, not an exact total. */
9612
+ /** @description Present only when LinkedIn reports a total for the kind of search the pasted URL names, and null whenever it does not. Never treat it as the has-more signal or as an exact figure; page with cursor until that comes back null. */
9348
9613
  total_count: number | null;
9349
9614
  };
9350
9615
  /** @description Opaque next-page cursor; null on the last page. */
9351
9616
  cursor: string | null;
9617
+ safety_warning?: components["schemas"]["SafetyWarning"];
9352
9618
  };
9353
9619
  };
9354
9620
  };
@@ -9503,6 +9769,7 @@ interface operations {
9503
9769
  }[];
9504
9770
  /** @description Opaque next-page cursor; null on the last page. */
9505
9771
  cursor: string | null;
9772
+ safety_warning?: components["schemas"]["SafetyWarning"];
9506
9773
  };
9507
9774
  };
9508
9775
  };
@@ -9690,6 +9957,7 @@ interface operations {
9690
9957
  /** @description The offending value. */
9691
9958
  value?: string;
9692
9959
  }[];
9960
+ safety_warning?: components["schemas"]["SafetyWarning"];
9693
9961
  };
9694
9962
  };
9695
9963
  };
@@ -9839,6 +10107,7 @@ interface operations {
9839
10107
  }[];
9840
10108
  /** @description Opaque pagination cursor for the next page, or null when this is the last page. */
9841
10109
  cursor: string | null;
10110
+ safety_warning?: components["schemas"]["SafetyWarning"];
9842
10111
  };
9843
10112
  };
9844
10113
  };
@@ -10106,6 +10375,7 @@ interface operations {
10106
10375
  /** @description The expansion the notice is about, here public_identifier. */
10107
10376
  value?: string;
10108
10377
  }[];
10378
+ safety_warning?: components["schemas"]["SafetyWarning"];
10109
10379
  };
10110
10380
  };
10111
10381
  };
@@ -10251,11 +10521,12 @@ interface operations {
10251
10521
  * @description Response type discriminator.
10252
10522
  * @enum {string}
10253
10523
  */
10254
- object?: "chat_started";
10524
+ object: "chat_started";
10255
10525
  /** @description The chat's identifier (existing thread reused when messaging a known 1:1 contact). */
10256
- chat_id?: string;
10257
- /** @description The opening message identifier. */
10258
- message_id?: string;
10526
+ chat_id: string;
10527
+ /** @description The opening message identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. Confirm the send with chat_id, which is always present. */
10528
+ message_id: string | string[] | null;
10529
+ safety_warning?: components["schemas"]["SafetyWarning"];
10259
10530
  };
10260
10531
  };
10261
10532
  };
@@ -10368,6 +10639,10 @@ interface operations {
10368
10639
  query?: {
10369
10640
  /** @description Additional response fields to resolve, comma-separated in a single value (for example expand=public_identifier). Accepted values: public_identifier. When requested, the field is present on every user object in the response, carrying either a value or null. Omit for the base fields only. Pass the parameter once; repeating it drops values. At most 25 people per request need an extra lookup to resolve; anyone beyond that carries null, so ask for a smaller page to resolve them all. */
10370
10641
  expand?: string;
10642
+ /** @description How willing this read is to reach LinkedIn. `auto` (default) serves a stored copy while it is within the resource's freshness threshold; `live` always fetches; `refill` serves a stored copy at any age and fetches once when this read has no stored copy yet; `cache_only` never fetches and returns NOT_STORED when nothing is stored. `cache_only` cannot be combined with max_age. A 502 under `cache_only` means the store could not be read, not that nothing is stored: no request was made to LinkedIn, and the same read is worth retrying. */
10643
+ mode?: "live" | "auto" | "refill" | "cache_only";
10644
+ /** @description Maximum age, in seconds, of a stored copy this read will accept. Overrides the `auto`, `live` and `refill` presets in both directions; `0` is the same as `mode=live`. Not accepted with `mode=cache_only`, whose guarantee is not a freshness threshold. */
10645
+ max_age?: number;
10371
10646
  };
10372
10647
  header?: never;
10373
10648
  path: {
@@ -10380,11 +10655,13 @@ interface operations {
10380
10655
  };
10381
10656
  requestBody?: never;
10382
10657
  responses: {
10383
- /** @description The chat's metadata and most-recent message. */
10658
+ /** @description The chat's metadata and most-recent message. `source` says whether it came from Curviate's stored copy or from a fresh fetch, and `observed_at` says when it was seen; a stored answer also carries an `Age` header. */
10384
10659
  200: {
10385
10660
  headers: {
10386
10661
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
10387
10662
  RateLimit: components["headers"]["RateLimit"];
10663
+ /** @description Seconds since this data was observed on LinkedIn. Present only when source is `store`; a live fetch sets no Age header at all. */
10664
+ Age?: number;
10388
10665
  [name: string]: unknown;
10389
10666
  };
10390
10667
  content: {
@@ -10520,9 +10797,30 @@ interface operations {
10520
10797
  /** @description The expansion the notice is about, here public_identifier. */
10521
10798
  value?: string;
10522
10799
  }[];
10800
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
10801
+ observed_at: string;
10802
+ /**
10803
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
10804
+ * @enum {string}
10805
+ */
10806
+ source: "store" | "live";
10807
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
10808
+ withdrawn: boolean;
10809
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
10810
+ withdrawn_at?: string;
10811
+ safety_warning?: components["schemas"]["SafetyWarning"];
10523
10812
  };
10524
10813
  };
10525
10814
  };
10815
+ /** @description A query parameter failed validation. INVALID_REQUEST also covers `mode=cache_only` sent together with `max_age`: that mode never reaches LinkedIn at any age, so a freshness threshold cannot change its answer, and the pair is refused rather than one of the two being ignored. Either parameter sent more than once is refused for the same reason, in either order. */
10816
+ 400: {
10817
+ headers: {
10818
+ [name: string]: unknown;
10819
+ };
10820
+ content: {
10821
+ "application/json": components["schemas"]["Error"];
10822
+ };
10823
+ };
10526
10824
  /** @description Missing or invalid API key. */
10527
10825
  401: {
10528
10826
  headers: {
@@ -10541,6 +10839,15 @@ interface operations {
10541
10839
  "application/json": components["schemas"]["Error"];
10542
10840
  };
10543
10841
  };
10842
+ /** @description Nothing is stored for this chat and `mode=cache_only` never fetches (NOT_STORED). Not the same as a 404: the chat may exist, and this API holds no copy of it. Re-read with `mode=refill` or `mode=auto` to fetch it. */
10843
+ 422: {
10844
+ headers: {
10845
+ [name: string]: unknown;
10846
+ };
10847
+ content: {
10848
+ "application/json": components["schemas"]["Error"];
10849
+ };
10850
+ };
10544
10851
  /** @description Rate limited. */
10545
10852
  429: {
10546
10853
  headers: {
@@ -10562,7 +10869,7 @@ interface operations {
10562
10869
  "application/json": components["schemas"]["Error"];
10563
10870
  };
10564
10871
  };
10565
- /** @description A temporary error occurred. Please try again. */
10872
+ /** @description A temporary error occurred. Please try again. Under `mode=cache_only` this specifically means the stored copy could not be read: no request was made to LinkedIn, and the same read is worth retrying. */
10566
10873
  502: {
10567
10874
  headers: {
10568
10875
  [name: string]: unknown;
@@ -10630,6 +10937,7 @@ interface operations {
10630
10937
  chat_id?: string;
10631
10938
  /** @description The applied read status. */
10632
10939
  read?: boolean;
10940
+ safety_warning?: components["schemas"]["SafetyWarning"];
10633
10941
  };
10634
10942
  };
10635
10943
  };
@@ -10722,6 +11030,10 @@ interface operations {
10722
11030
  getV1AccountIdChatsChatIdMessages: {
10723
11031
  parameters: {
10724
11032
  query?: {
11033
+ /** @description How willing this read is to reach LinkedIn. `auto` (default) serves a stored copy while it is within the resource's freshness threshold; `live` always fetches; `refill` serves a stored copy at any age and fetches once when this read has no stored copy yet; `cache_only` never fetches and returns NOT_STORED when nothing is stored. `cache_only` cannot be combined with max_age. A 502 under `cache_only` means the store could not be read, not that nothing is stored: no request was made to LinkedIn, and the same read is worth retrying. */
11034
+ mode?: "live" | "auto" | "refill" | "cache_only";
11035
+ /** @description Maximum age, in seconds, of a stored copy this read will accept. Overrides the `auto`, `live` and `refill` presets in both directions; `0` is the same as `mode=live`. Not accepted with `mode=cache_only`, whose guarantee is not a freshness threshold. */
11036
+ max_age?: number;
10725
11037
  /** @description Optional filter: only return messages from this sender ID. */
10726
11038
  user_id?: string;
10727
11039
  /** @description ISO-8601 UTC datetime (exclusive upper bound). Only entries before this time are returned. */
@@ -10746,11 +11058,13 @@ interface operations {
10746
11058
  };
10747
11059
  requestBody?: never;
10748
11060
  responses: {
10749
- /** @description A page of messages from the chat. Full message text is returned verbatim (content pass-through). */
11061
+ /** @description A page of messages from the chat, newest first. Full message text is returned verbatim (content pass-through). `source` says whether the page came from Curviate's stored copy or from a fresh fetch, and `observed_at` says when the chat was last read end to end; a stored answer also carries an `Age` header and always sets `cursor` to null, because a page is served from the store only when it is the whole answer. Narrowing the request with `user_id`, `before`, `after` or a `cursor` always fetches. */
10750
11062
  200: {
10751
11063
  headers: {
10752
11064
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
10753
11065
  RateLimit: components["headers"]["RateLimit"];
11066
+ /** @description Seconds since this data was observed on LinkedIn. Present only when source is `store`; a live fetch sets no Age header at all. */
11067
+ Age?: number;
10754
11068
  [name: string]: unknown;
10755
11069
  };
10756
11070
  content: {
@@ -10843,10 +11157,22 @@ interface operations {
10843
11157
  /** @description The expansion the notice is about, here public_identifier. */
10844
11158
  value?: string;
10845
11159
  }[];
11160
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
11161
+ observed_at: string;
11162
+ /**
11163
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
11164
+ * @enum {string}
11165
+ */
11166
+ source: "store" | "live";
11167
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
11168
+ withdrawn: boolean;
11169
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
11170
+ withdrawn_at?: string;
11171
+ safety_warning?: components["schemas"]["SafetyWarning"];
10846
11172
  };
10847
11173
  };
10848
11174
  };
10849
- /** @description limit out of range (1-25), malformed cursor, or invalid before/after datetime. */
11175
+ /** @description limit out of range (1-25), malformed cursor, or invalid before/after datetime. INVALID_REQUEST also covers `mode=cache_only` sent together with `max_age`, and either parameter sent more than once, in either order. */
10850
11176
  400: {
10851
11177
  headers: {
10852
11178
  [name: string]: unknown;
@@ -10873,6 +11199,15 @@ interface operations {
10873
11199
  "application/json": components["schemas"]["Error"];
10874
11200
  };
10875
11201
  };
11202
+ /** @description `mode=cache_only` and this chat's messages cannot be answered from the stored copy (NOT_STORED): either no complete read of the chat has been observed, or the request narrows the page in a way the stored copy cannot reproduce. Not a 404: the chat may exist. Re-read with `mode=refill`, `auto` or `live`, without the narrowing parameters. */
11203
+ 422: {
11204
+ headers: {
11205
+ [name: string]: unknown;
11206
+ };
11207
+ content: {
11208
+ "application/json": components["schemas"]["Error"];
11209
+ };
11210
+ };
10876
11211
  /** @description Rate limited. */
10877
11212
  429: {
10878
11213
  headers: {
@@ -10894,7 +11229,7 @@ interface operations {
10894
11229
  "application/json": components["schemas"]["Error"];
10895
11230
  };
10896
11231
  };
10897
- /** @description A temporary error occurred. Please try again. */
11232
+ /** @description A temporary error occurred. Please try again. Under `mode=cache_only` this specifically means the stored copy could not be read: no request was made to LinkedIn, and the same read is worth retrying. */
10898
11233
  502: {
10899
11234
  headers: {
10900
11235
  [name: string]: unknown;
@@ -10968,11 +11303,11 @@ interface operations {
10968
11303
  * @description Response type discriminator.
10969
11304
  * @enum {string}
10970
11305
  */
10971
- object?: "message_sent";
10972
- /** @description The sent message's identifier. */
10973
- message_id?: string;
11306
+ object: "message_sent";
11307
+ /** @description The sent message's identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. */
11308
+ message_id: string | string[] | null;
10974
11309
  /** @description The identity this message was actually sent as. */
10975
- sent_as?: {
11310
+ sent_as: {
10976
11311
  /**
10977
11312
  * @description Which identity sent the message.
10978
11313
  * @enum {string}
@@ -10983,6 +11318,7 @@ interface operations {
10983
11318
  /** @description Company inboxes only. The page's display name, or null in the rare case the page itself could not be resolved at all. */
10984
11319
  name?: string | null;
10985
11320
  };
11321
+ safety_warning?: components["schemas"]["SafetyWarning"];
10986
11322
  };
10987
11323
  };
10988
11324
  };
@@ -11197,6 +11533,7 @@ interface operations {
11197
11533
  /** @description The expansion the notice is about, here public_identifier. */
11198
11534
  value?: string;
11199
11535
  }[];
11536
+ safety_warning?: components["schemas"]["SafetyWarning"];
11200
11537
  };
11201
11538
  };
11202
11539
  };
@@ -11300,6 +11637,7 @@ interface operations {
11300
11637
  object?: "message_deleted";
11301
11638
  /** @description The deleted message's identifier. */
11302
11639
  message_id?: string;
11640
+ safety_warning?: components["schemas"]["SafetyWarning"];
11303
11641
  };
11304
11642
  };
11305
11643
  };
@@ -11419,6 +11757,7 @@ interface operations {
11419
11757
  object?: "message_edited";
11420
11758
  /** @description The edited message's identifier. */
11421
11759
  message_id?: string;
11760
+ safety_warning?: components["schemas"]["SafetyWarning"];
11422
11761
  };
11423
11762
  };
11424
11763
  };
@@ -11558,6 +11897,7 @@ interface operations {
11558
11897
  message_id?: string;
11559
11898
  /** @description The emoji reaction value. */
11560
11899
  reaction?: string;
11900
+ safety_warning?: components["schemas"]["SafetyWarning"];
11561
11901
  };
11562
11902
  };
11563
11903
  };
@@ -11772,7 +12112,7 @@ interface operations {
11772
12112
  };
11773
12113
  requestBody?: never;
11774
12114
  responses: {
11775
- /** @description A page of chats matching the search term (participant names and/or message content), plus the next-page cursor. */
12115
+ /** @description A page of chats matching the search term (participant names and/or message content), the next-page cursor, and how much of the stored inbox the search could see. */
11776
12116
  200: {
11777
12117
  headers: {
11778
12118
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -11799,17 +12139,17 @@ interface operations {
11799
12139
  /** @description Chat display name, or null. */
11800
12140
  name?: string | null;
11801
12141
  /**
11802
- * @description Conversation type (search results are never channels).
12142
+ * @description Conversation type.
11803
12143
  * @enum {string}
11804
12144
  */
11805
- type?: "1to1" | "group";
12145
+ type?: "1to1" | "group" | "channel";
11806
12146
  is_group?: boolean;
11807
12147
  is_1to1?: boolean;
11808
12148
  /** @description Number of unread messages in this chat. */
11809
12149
  unread_count?: number;
11810
12150
  /** @description Identifier of the 1:1 counterpart, when the match is a 1:1 chat. */
11811
12151
  user_id?: string;
11812
- /** @description The most recent message in the chat, when the search carrier returned one. */
12152
+ /** @description The most recent stored message in the chat, when one is stored. */
11813
12153
  last_message?: {
11814
12154
  /**
11815
12155
  * @description Response type discriminator.
@@ -11824,7 +12164,7 @@ interface operations {
11824
12164
  text?: string | null;
11825
12165
  /** @description Identifier of the sender. */
11826
12166
  sender_id?: string;
11827
- /** @description Always empty; the search carrier does not surface attachment descriptors. */
12167
+ /** @description Attachment descriptors on the message, without the file bytes. Empty when the message has none. */
11828
12168
  attachments?: {
11829
12169
  /** @description Attachment identifier. */
11830
12170
  id?: string;
@@ -11843,6 +12183,16 @@ interface operations {
11843
12183
  }[];
11844
12184
  /** @description Opaque next-page cursor; null on the last page. */
11845
12185
  cursor?: string | null;
12186
+ /** @description How complete the searched corpus is. This searches the chats and messages already retrieved for this account, so a chat never retrieved cannot match. Retrieve a chat's messages (GET .../chats/{chat_id}/messages) to widen it. */
12187
+ coverage?: {
12188
+ /** @description True when every stored chat has a complete message walk that no later invalidation has withdrawn; false when any chat is unwalked, mid-walk or withdrawn. */
12189
+ complete?: boolean;
12190
+ /** @description Chats held for this account. */
12191
+ chats_stored?: number;
12192
+ /** @description Of those, how many have messages that were never fully enumerated. */
12193
+ chats_incomplete?: number;
12194
+ };
12195
+ safety_warning?: components["schemas"]["SafetyWarning"];
11846
12196
  };
11847
12197
  };
11848
12198
  };
@@ -11968,11 +12318,12 @@ interface operations {
11968
12318
  * @description Response type discriminator.
11969
12319
  * @enum {string}
11970
12320
  */
11971
- object?: "inmail_sent";
11972
- /** @description The InMail message identifier. */
11973
- message_id?: string;
12321
+ object: "inmail_sent";
12322
+ /** @description The InMail message identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. Confirm the send with chat_id, which is always present. */
12323
+ message_id: string | string[] | null;
11974
12324
  /** @description The conversation thread identifier. */
11975
- chat_id?: string;
12325
+ chat_id: string;
12326
+ safety_warning?: components["schemas"]["SafetyWarning"];
11976
12327
  };
11977
12328
  };
11978
12329
  };
@@ -12137,6 +12488,7 @@ interface operations {
12137
12488
  }[];
12138
12489
  /** @description Present only when no company inbox exists. Names the reconnect requirement. */
12139
12490
  hint?: string;
12491
+ safety_warning?: components["schemas"]["SafetyWarning"];
12140
12492
  };
12141
12493
  };
12142
12494
  };
@@ -12387,6 +12739,7 @@ interface operations {
12387
12739
  /** @description The expansion the notice is about, here public_identifier. */
12388
12740
  value?: string;
12389
12741
  }[];
12742
+ safety_warning?: components["schemas"]["SafetyWarning"];
12390
12743
  };
12391
12744
  };
12392
12745
  };
@@ -12571,6 +12924,7 @@ interface operations {
12571
12924
  }[];
12572
12925
  /** @description Opaque cursor for the next page; null when the walk is exhausted. Self-describing of the sort, pass it back verbatim as `cursor`. The relevant cursor expires (~1h). There is no total (an unbounded, reordering stream); page until cursor is null. */
12573
12926
  cursor: string | null;
12927
+ safety_warning?: components["schemas"]["SafetyWarning"];
12574
12928
  };
12575
12929
  };
12576
12930
  };
@@ -12733,6 +13087,7 @@ interface operations {
12733
13087
  unread_count: number;
12734
13088
  /** @description Timestamp (epoch ms) of the newest notification, a poll watermark to detect 'anything new since last poll'. null if unavailable. */
12735
13089
  latest_published_at: number | null;
13090
+ safety_warning?: components["schemas"]["SafetyWarning"];
12736
13091
  };
12737
13092
  };
12738
13093
  };
@@ -12856,6 +13211,7 @@ interface operations {
12856
13211
  object: "notification_deleted";
12857
13212
  /** @description The card urn that was deleted. */
12858
13213
  card_urn: string;
13214
+ safety_warning?: components["schemas"]["SafetyWarning"];
12859
13215
  };
12860
13216
  };
12861
13217
  };
@@ -12979,6 +13335,7 @@ interface operations {
12979
13335
  object: "notification_show_less_applied";
12980
13336
  /** @description The card urn the show-less action was applied to. */
12981
13337
  card_urn: string;
13338
+ safety_warning?: components["schemas"]["SafetyWarning"];
12982
13339
  };
12983
13340
  };
12984
13341
  };
@@ -13209,6 +13566,7 @@ interface operations {
13209
13566
  }[];
13210
13567
  /** @description Next-page cursor; null on the last page. */
13211
13568
  cursor?: string | null;
13569
+ safety_warning?: components["schemas"]["SafetyWarning"];
13212
13570
  };
13213
13571
  };
13214
13572
  };
@@ -13386,6 +13744,7 @@ interface operations {
13386
13744
  }[];
13387
13745
  /** @description Next-page cursor; null on the last page. cursor:null does not confirm completeness when limit is small, the underlying pagination is offset-based. */
13388
13746
  cursor?: string | null;
13747
+ safety_warning?: components["schemas"]["SafetyWarning"];
13389
13748
  };
13390
13749
  };
13391
13750
  };
@@ -13564,6 +13923,7 @@ interface operations {
13564
13923
  /** @description File size in bytes, when known. */
13565
13924
  file_size?: number;
13566
13925
  }[];
13926
+ safety_warning?: components["schemas"]["SafetyWarning"];
13567
13927
  };
13568
13928
  };
13569
13929
  };
@@ -13771,6 +14131,7 @@ interface operations {
13771
14131
  /** @description File size in bytes, when known. */
13772
14132
  file_size?: number;
13773
14133
  }[];
14134
+ safety_warning?: components["schemas"]["SafetyWarning"];
13774
14135
  };
13775
14136
  };
13776
14137
  };
@@ -14078,6 +14439,7 @@ interface operations {
14078
14439
  /** @description File size in bytes, when known. */
14079
14440
  file_size?: number;
14080
14441
  }[];
14442
+ safety_warning?: components["schemas"]["SafetyWarning"];
14081
14443
  };
14082
14444
  };
14083
14445
  };
@@ -14275,6 +14637,7 @@ interface operations {
14275
14637
  cursor?: string | null;
14276
14638
  /** @description Total reply count, when the platform reports it. */
14277
14639
  total_count?: number;
14640
+ safety_warning?: components["schemas"]["SafetyWarning"];
14278
14641
  };
14279
14642
  };
14280
14643
  };
@@ -14420,6 +14783,7 @@ interface operations {
14420
14783
  }[];
14421
14784
  /** @description Next-page cursor; null on the last page. */
14422
14785
  cursor?: string | null;
14786
+ safety_warning?: components["schemas"]["SafetyWarning"];
14423
14787
  };
14424
14788
  };
14425
14789
  };
@@ -14549,6 +14913,7 @@ interface operations {
14549
14913
  * @enum {string}
14550
14914
  */
14551
14915
  reaction?: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
14916
+ safety_warning?: components["schemas"]["SafetyWarning"];
14552
14917
  };
14553
14918
  };
14554
14919
  };
@@ -14694,6 +15059,7 @@ interface operations {
14694
15059
  * @enum {string}
14695
15060
  */
14696
15061
  reaction?: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
15062
+ safety_warning?: components["schemas"]["SafetyWarning"];
14697
15063
  };
14698
15064
  };
14699
15065
  };
@@ -14856,6 +15222,7 @@ interface operations {
14856
15222
  /** @description Public profile picture URL. */
14857
15223
  public_picture_url?: string;
14858
15224
  };
15225
+ safety_warning?: components["schemas"]["SafetyWarning"];
14859
15226
  };
14860
15227
  };
14861
15228
  };
@@ -14934,7 +15301,7 @@ interface operations {
14934
15301
  "application/json": components["schemas"]["Error"];
14935
15302
  };
14936
15303
  };
14937
- /** @description A temporary error occurred while processing the request. */
15304
+ /** @description The platform did not complete the invitation, or is temporarily unavailable. When `retry_hint` is `never`, the invitation may already have been delivered and re-sending would send a second one: read the sent invitations before trying again. The per-response `retry_hint` and `retry_likely_to_succeed` are authoritative, not the general meaning of the code. */
14938
15305
  502: {
14939
15306
  headers: {
14940
15307
  [name: string]: unknown;
@@ -15028,6 +15395,7 @@ interface operations {
15028
15395
  }[];
15029
15396
  /** @description Opaque pagination cursor for the next page, or null if at the end. */
15030
15397
  cursor?: string | null;
15398
+ safety_warning?: components["schemas"]["SafetyWarning"];
15031
15399
  };
15032
15400
  };
15033
15401
  };
@@ -15186,6 +15554,7 @@ interface operations {
15186
15554
  }[];
15187
15555
  /** @description Opaque pagination cursor for the next page, or null if at the end. */
15188
15556
  cursor?: string | null;
15557
+ safety_warning?: components["schemas"]["SafetyWarning"];
15189
15558
  };
15190
15559
  };
15191
15560
  };
@@ -15310,6 +15679,7 @@ interface operations {
15310
15679
  * @enum {string}
15311
15680
  */
15312
15681
  status?: "accepted" | "not_found";
15682
+ safety_warning?: components["schemas"]["SafetyWarning"];
15313
15683
  };
15314
15684
  };
15315
15685
  };
@@ -15434,6 +15804,7 @@ interface operations {
15434
15804
  * @enum {string}
15435
15805
  */
15436
15806
  status?: "declined" | "not_found";
15807
+ safety_warning?: components["schemas"]["SafetyWarning"];
15437
15808
  };
15438
15809
  };
15439
15810
  };
@@ -15558,6 +15929,7 @@ interface operations {
15558
15929
  * @enum {string}
15559
15930
  */
15560
15931
  status?: "withdrawn" | "not_found";
15932
+ safety_warning?: components["schemas"]["SafetyWarning"];
15561
15933
  };
15562
15934
  };
15563
15935
  };
@@ -15705,6 +16077,7 @@ interface operations {
15705
16077
  }[];
15706
16078
  /** @description Opaque pagination cursor for the next page; null when no next page. A string only when a full page was returned, pass it back verbatim to page forward. */
15707
16079
  cursor: string | null;
16080
+ safety_warning?: components["schemas"]["SafetyWarning"];
15708
16081
  };
15709
16082
  };
15710
16083
  };
@@ -15955,6 +16328,7 @@ interface operations {
15955
16328
  listed_at?: string;
15956
16329
  /** @description Hiring team members associated with this posting, when the posting exposes any. */
15957
16330
  hiring_team?: unknown[];
16331
+ safety_warning?: components["schemas"]["SafetyWarning"];
15958
16332
  };
15959
16333
  };
15960
16334
  };
@@ -16168,6 +16542,18 @@ interface operations {
16168
16542
  listed_at?: string;
16169
16543
  /** @description Hiring team members associated with this posting, when the posting exposes any. */
16170
16544
  hiring_team?: unknown[];
16545
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
16546
+ observed_at: string;
16547
+ /**
16548
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
16549
+ * @enum {string}
16550
+ */
16551
+ source: "store" | "live";
16552
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
16553
+ withdrawn: boolean;
16554
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
16555
+ withdrawn_at?: string;
16556
+ safety_warning?: components["schemas"]["SafetyWarning"];
16171
16557
  };
16172
16558
  };
16173
16559
  };
@@ -16420,6 +16806,7 @@ interface operations {
16420
16806
  listed_at?: string;
16421
16807
  /** @description Hiring team members associated with this posting, when the posting exposes any. */
16422
16808
  hiring_team?: unknown[];
16809
+ safety_warning?: components["schemas"]["SafetyWarning"];
16423
16810
  };
16424
16811
  };
16425
16812
  };
@@ -16589,6 +16976,7 @@ interface operations {
16589
16976
  /** @description Estimated daily applicants if published for free. */
16590
16977
  estimated_daily_applicants_count?: number;
16591
16978
  };
16979
+ safety_warning?: components["schemas"]["SafetyWarning"];
16592
16980
  };
16593
16981
  };
16594
16982
  };
@@ -16776,6 +17164,7 @@ interface operations {
16776
17164
  * @enum {string}
16777
17165
  */
16778
17166
  job_state: "DRAFT" | "LISTED" | "CLOSED" | "REVIEW" | "SUSPENDED";
17167
+ safety_warning?: components["schemas"]["SafetyWarning"];
16779
17168
  };
16780
17169
  };
16781
17170
  };
@@ -16911,6 +17300,7 @@ interface operations {
16911
17300
  * @enum {string}
16912
17301
  */
16913
17302
  object: "job_posting_closed";
17303
+ safety_warning?: components["schemas"]["SafetyWarning"];
16914
17304
  };
16915
17305
  };
16916
17306
  };
@@ -17133,6 +17523,7 @@ interface operations {
17133
17523
  };
17134
17524
  /** @description Opaque pagination cursor for the next page. Present only when more applicants remain past this page, omit on the first request, pass back verbatim to page forward. */
17135
17525
  cursor?: string;
17526
+ safety_warning?: components["schemas"]["SafetyWarning"];
17136
17527
  };
17137
17528
  };
17138
17529
  };
@@ -17324,6 +17715,7 @@ interface operations {
17324
17715
  /** @description The applicant's phone number. */
17325
17716
  phone?: string;
17326
17717
  };
17718
+ safety_warning?: components["schemas"]["SafetyWarning"];
17327
17719
  };
17328
17720
  };
17329
17721
  };
@@ -17649,6 +18041,18 @@ interface operations {
17649
18041
  poll?: Record<string, never> | null;
17650
18042
  /** @description Job-posting or article context on the post, present only when applicable. */
17651
18043
  specifics?: Record<string, never> | null;
18044
+ /** @description ISO-8601 timestamp for when this data was observed on LinkedIn. On a live fetch this is the time of the fetch; on a stored answer it is when the data was actually seen, not when you asked for it. */
18045
+ observed_at: string;
18046
+ /**
18047
+ * @description Whether this answer came from Curviate's stored copy or from a fresh LinkedIn fetch. A stored answer costs the connected account nothing and is never surfaced to the person being read. It also carries less: fields Curviate does not retain (message bodies, notes, attachments, third-party contact details) are only ever present on a live fetch. Pass mode=live when you need them.
18048
+ * @enum {string}
18049
+ */
18050
+ source: "store" | "live";
18051
+ /** @description Whether LinkedIn has told us this resource no longer exists, such as a removed profile. When true, the answer above is the copy Curviate still holds and must not be treated as current. Always present; false is the normal case. */
18052
+ withdrawn: boolean;
18053
+ /** @description ISO-8601 timestamp for when a fetch MOST RECENTLY found this resource gone on LinkedIn. Every later fetch that finds it gone again moves it. Present only when withdrawn is true. */
18054
+ withdrawn_at?: string;
18055
+ safety_warning?: components["schemas"]["SafetyWarning"];
17652
18056
  };
17653
18057
  };
17654
18058
  };
@@ -17896,6 +18300,7 @@ interface operations {
17896
18300
  object: "post_created";
17897
18301
  /** @description The newly published post's own identifier. */
17898
18302
  id: string;
18303
+ safety_warning?: components["schemas"]["SafetyWarning"];
17899
18304
  };
17900
18305
  };
17901
18306
  };
@@ -18123,6 +18528,7 @@ interface operations {
18123
18528
  }[];
18124
18529
  /** @description Next-page cursor; null on the last page. */
18125
18530
  cursor?: string | null;
18531
+ safety_warning?: components["schemas"]["SafetyWarning"];
18126
18532
  };
18127
18533
  };
18128
18534
  };
@@ -18271,6 +18677,7 @@ interface operations {
18271
18677
  }[];
18272
18678
  /** @description Next-page cursor; null on the last page. cursor:null does not confirm completeness when limit is small, the underlying pagination is offset-based; use --all / paginate() for exhaustive reads. */
18273
18679
  cursor?: string | null;
18680
+ safety_warning?: components["schemas"]["SafetyWarning"];
18274
18681
  };
18275
18682
  };
18276
18683
  };
@@ -18396,6 +18803,7 @@ interface operations {
18396
18803
  * @enum {string}
18397
18804
  */
18398
18805
  reaction: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
18806
+ safety_warning?: components["schemas"]["SafetyWarning"];
18399
18807
  };
18400
18808
  };
18401
18809
  };
@@ -18537,6 +18945,7 @@ interface operations {
18537
18945
  * @enum {string}
18538
18946
  */
18539
18947
  reaction: "like" | "celebrate" | "support" | "love" | "insightful" | "funny";
18948
+ safety_warning?: components["schemas"]["SafetyWarning"];
18540
18949
  };
18541
18950
  };
18542
18951
  };
@@ -18654,7 +19063,7 @@ interface operations {
18654
19063
  };
18655
19064
  requestBody?: never;
18656
19065
  responses: {
18657
- /** @description Reactions made by this account (user_id="me") or another LinkedIn user. Each item embeds the reacted-on post and whether the queried account is the sender. */
19066
+ /** @description Reactions made by this account (user_id="me") or another LinkedIn user. Each item embeds the reacted-on post and whether the queried account is the sender. No reaction-time field is available on this item, an upstream limitation rather than something this endpoint drops; parent_post.created_at is the post's own creation time, not the reaction's, and is not a usable proxy for it. List order is not documented or guaranteed, so do not rely on it to filter by date; cap results by count (limit) instead. */
18658
19067
  200: {
18659
19068
  headers: {
18660
19069
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -18737,6 +19146,7 @@ interface operations {
18737
19146
  }[];
18738
19147
  /** @description Next-page cursor; null on the last page. */
18739
19148
  cursor?: string | null;
19149
+ safety_warning?: components["schemas"]["SafetyWarning"];
18740
19150
  };
18741
19151
  };
18742
19152
  };
@@ -18893,6 +19303,7 @@ interface operations {
18893
19303
  }[];
18894
19304
  /** @description Next-page cursor; null on the last page. */
18895
19305
  cursor?: string | null;
19306
+ safety_warning?: components["schemas"]["SafetyWarning"];
18896
19307
  };
18897
19308
  };
18898
19309
  };
@@ -19019,6 +19430,7 @@ interface operations {
19019
19430
  saved: boolean;
19020
19431
  /** @description The target post's activity id, stripped of the urn:li:activity: prefix. */
19021
19432
  post_id: string;
19433
+ safety_warning?: components["schemas"]["SafetyWarning"];
19022
19434
  };
19023
19435
  };
19024
19436
  };
@@ -19149,6 +19561,7 @@ interface operations {
19149
19561
  saved: boolean;
19150
19562
  /** @description The target post's activity id, stripped of the urn:li:activity: prefix. */
19151
19563
  post_id: string;
19564
+ safety_warning?: components["schemas"]["SafetyWarning"];
19152
19565
  };
19153
19566
  };
19154
19567
  };
@@ -19595,6 +20008,7 @@ interface operations {
19595
20008
  message_status?: string;
19596
20009
  }[];
19597
20010
  };
20011
+ safety_warning?: components["schemas"]["SafetyWarning"];
19598
20012
  };
19599
20013
  };
19600
20014
  };
@@ -19794,7 +20208,7 @@ interface operations {
19794
20208
  };
19795
20209
  };
19796
20210
  responses: {
19797
- /** @description Recruiter chat started. Returns the object discriminator, the chat_id, and the message_id of the opening message. Message content is never echoed back. The InMail daily quota is consumed; remaining capacity is read from GET /v1/accounts/{account_id}, never echoed here. Requires a Recruiter seat. */
20211
+ /** @description Recruiter chat started. Returns the object discriminator, the chat_id, and the message_id of the opening message. Message content is never echoed back. Requires a Recruiter seat. */
19798
20212
  201: {
19799
20213
  headers: {
19800
20214
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -19810,8 +20224,9 @@ interface operations {
19810
20224
  object: "chat_started";
19811
20225
  /** @description The newly created chat's identifier. */
19812
20226
  chat_id: string;
19813
- /** @description The opening message identifier. */
19814
- message_id: string;
20227
+ /** @description The opening message identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. Confirm the send with chat_id, which is always present. */
20228
+ message_id: string | string[] | null;
20229
+ safety_warning?: components["schemas"]["SafetyWarning"];
19815
20230
  };
19816
20231
  };
19817
20232
  };
@@ -20124,6 +20539,7 @@ interface operations {
20124
20539
  cursor?: string;
20125
20540
  /** @description Total number of matching results, when the platform returns it. */
20126
20541
  total_count?: number;
20542
+ safety_warning?: components["schemas"]["SafetyWarning"];
20127
20543
  };
20128
20544
  };
20129
20545
  };
@@ -20316,6 +20732,7 @@ interface operations {
20316
20732
  cursor?: string;
20317
20733
  /** @description Total number of matching parameters, when the platform returns it. */
20318
20734
  total_count?: number;
20735
+ safety_warning?: components["schemas"]["SafetyWarning"];
20319
20736
  };
20320
20737
  };
20321
20738
  };
@@ -20549,6 +20966,7 @@ interface operations {
20549
20966
  cursor?: string;
20550
20967
  /** @description Total number of projects, when the platform returns it. */
20551
20968
  total_count?: number;
20969
+ safety_warning?: components["schemas"]["SafetyWarning"];
20552
20970
  };
20553
20971
  };
20554
20972
  };
@@ -20752,6 +21170,7 @@ interface operations {
20752
21170
  };
20753
21171
  /** @description ISO 8601 timestamp of the account's last access, when known. */
20754
21172
  last_accessed_at?: string;
21173
+ safety_warning?: components["schemas"]["SafetyWarning"];
20755
21174
  };
20756
21175
  };
20757
21176
  };
@@ -20909,6 +21328,7 @@ interface operations {
20909
21328
  * @enum {string}
20910
21329
  */
20911
21330
  object: "recruiter_project_edited";
21331
+ safety_warning?: components["schemas"]["SafetyWarning"];
20912
21332
  };
20913
21333
  };
20914
21334
  };
@@ -21162,6 +21582,7 @@ interface operations {
21162
21582
  cursor?: string;
21163
21583
  /** @description Total number of matching candidates, when the platform returns it. */
21164
21584
  total_count?: number;
21585
+ safety_warning?: components["schemas"]["SafetyWarning"];
21165
21586
  };
21166
21587
  };
21167
21588
  };
@@ -21469,6 +21890,7 @@ interface operations {
21469
21890
  cursor?: string;
21470
21891
  /** @description Total number of matching results, when the platform returns it. */
21471
21892
  total_count?: number;
21893
+ safety_warning?: components["schemas"]["SafetyWarning"];
21472
21894
  };
21473
21895
  };
21474
21896
  };
@@ -21992,6 +22414,7 @@ interface operations {
21992
22414
  is_eliminatory?: boolean;
21993
22415
  choices?: string[];
21994
22416
  }[];
22417
+ safety_warning?: components["schemas"]["SafetyWarning"];
21995
22418
  };
21996
22419
  };
21997
22420
  };
@@ -22199,6 +22622,7 @@ interface operations {
22199
22622
  object: "recruiter_job_posting_created";
22200
22623
  job_id: string;
22201
22624
  project_id: string;
22625
+ safety_warning?: components["schemas"]["SafetyWarning"];
22202
22626
  };
22203
22627
  };
22204
22628
  };
@@ -22353,6 +22777,7 @@ interface operations {
22353
22777
  free: {
22354
22778
  eligible?: boolean;
22355
22779
  };
22780
+ safety_warning?: components["schemas"]["SafetyWarning"];
22356
22781
  };
22357
22782
  };
22358
22783
  };
@@ -22517,6 +22942,7 @@ interface operations {
22517
22942
  cursor?: string;
22518
22943
  /** @description Total number of matching job postings, when the platform returns it. */
22519
22944
  total_count?: number;
22945
+ safety_warning?: components["schemas"]["SafetyWarning"];
22520
22946
  };
22521
22947
  };
22522
22948
  };
@@ -22724,6 +23150,7 @@ interface operations {
22724
23150
  object: "recruiter_job_posting_created";
22725
23151
  job_id: string;
22726
23152
  project_id: string;
23153
+ safety_warning?: components["schemas"]["SafetyWarning"];
22727
23154
  };
22728
23155
  };
22729
23156
  };
@@ -22903,6 +23330,7 @@ interface operations {
22903
23330
  is_eliminatory?: boolean;
22904
23331
  choices?: string[];
22905
23332
  }[];
23333
+ safety_warning?: components["schemas"]["SafetyWarning"];
22906
23334
  };
22907
23335
  };
22908
23336
  };
@@ -23110,6 +23538,7 @@ interface operations {
23110
23538
  * @enum {string}
23111
23539
  */
23112
23540
  object: "recruiter_job_posting_edited";
23541
+ safety_warning?: components["schemas"]["SafetyWarning"];
23113
23542
  };
23114
23543
  };
23115
23544
  };
@@ -23304,6 +23733,7 @@ interface operations {
23304
23733
  * @enum {string}
23305
23734
  */
23306
23735
  job_state: "DRAFT" | "CLOSED" | "LISTED" | "REVIEW" | "SUSPENDED";
23736
+ safety_warning?: components["schemas"]["SafetyWarning"];
23307
23737
  };
23308
23738
  };
23309
23739
  };
@@ -23441,6 +23871,7 @@ interface operations {
23441
23871
  * @enum {string}
23442
23872
  */
23443
23873
  object: "recruiter_job_posting_closed";
23874
+ safety_warning?: components["schemas"]["SafetyWarning"];
23444
23875
  };
23445
23876
  };
23446
23877
  };
@@ -23585,6 +24016,7 @@ interface operations {
23585
24016
  * @enum {string}
23586
24017
  */
23587
24018
  object: "recruiter_candidate_saved";
24019
+ safety_warning?: components["schemas"]["SafetyWarning"];
23588
24020
  };
23589
24021
  };
23590
24022
  };
@@ -23916,6 +24348,7 @@ interface operations {
23916
24348
  cursor?: string;
23917
24349
  /** @description Total number of matching applicants, when the platform returns it. */
23918
24350
  total_count?: number;
24351
+ safety_warning?: components["schemas"]["SafetyWarning"];
23919
24352
  };
23920
24353
  };
23921
24354
  };
@@ -24123,6 +24556,7 @@ interface operations {
24123
24556
  endorsement_count?: number;
24124
24557
  }[];
24125
24558
  };
24559
+ safety_warning?: components["schemas"]["SafetyWarning"];
24126
24560
  };
24127
24561
  };
24128
24562
  };
@@ -24402,8 +24836,9 @@ interface operations {
24402
24836
  object: "chat_started";
24403
24837
  /** @description The newly created Sales Navigator chat identifier. */
24404
24838
  chat_id: string | null;
24405
- /** @description The opening message identifier. May be an array when attachments are delivered as separate messages. */
24839
+ /** @description The opening message identifier. Null when no message was actually sent, and an array when the message was delivered as several messages, one per attachment. Confirm the send with chat_id, which is always present. */
24406
24840
  message_id: string | string[] | null;
24841
+ safety_warning?: components["schemas"]["SafetyWarning"];
24407
24842
  };
24408
24843
  };
24409
24844
  };
@@ -24718,6 +25153,7 @@ interface operations {
24718
25153
  /** @description Present when available. */
24719
25154
  throttled_sections?: string[];
24720
25155
  };
25156
+ safety_warning?: components["schemas"]["SafetyWarning"];
24721
25157
  };
24722
25158
  };
24723
25159
  };
@@ -24865,6 +25301,7 @@ interface operations {
24865
25301
  };
24866
25302
  /** @description Opaque cursor for the next page, or null when exhausted. */
24867
25303
  cursor: string | null;
25304
+ safety_warning?: components["schemas"]["SafetyWarning"];
24868
25305
  };
24869
25306
  };
24870
25307
  };
@@ -25021,6 +25458,7 @@ interface operations {
25021
25458
  };
25022
25459
  /** @description Opaque cursor for the next page, or null when exhausted. */
25023
25460
  cursor: string | null;
25461
+ safety_warning?: components["schemas"]["SafetyWarning"];
25024
25462
  };
25025
25463
  };
25026
25464
  };
@@ -25232,6 +25670,7 @@ interface operations {
25232
25670
  };
25233
25671
  /** @description Opaque cursor for the next page, or null when exhausted. */
25234
25672
  cursor: string | null;
25673
+ safety_warning?: components["schemas"]["SafetyWarning"];
25235
25674
  };
25236
25675
  };
25237
25676
  };
@@ -25617,6 +26056,7 @@ interface operations {
25617
26056
  };
25618
26057
  /** @description Opaque cursor for the next page, or null when exhausted. */
25619
26058
  cursor: string | null;
26059
+ safety_warning?: components["schemas"]["SafetyWarning"];
25620
26060
  };
25621
26061
  };
25622
26062
  };
@@ -25763,6 +26203,7 @@ interface operations {
25763
26203
  list_id: string;
25764
26204
  /** @description The LinkedIn company id that was saved. */
25765
26205
  company_id: string;
26206
+ safety_warning?: components["schemas"]["SafetyWarning"];
25766
26207
  };
25767
26208
  };
25768
26209
  };
@@ -25909,6 +26350,7 @@ interface operations {
25909
26350
  list_id: string;
25910
26351
  /** @description The LinkedIn member id that was saved. */
25911
26352
  user_id: string;
26353
+ safety_warning?: components["schemas"]["SafetyWarning"];
25912
26354
  };
25913
26355
  };
25914
26356
  };
@@ -26069,6 +26511,7 @@ interface operations {
26069
26511
  };
26070
26512
  /** @description Opaque cursor for the next page, or null when exhausted. */
26071
26513
  cursor: string | null;
26514
+ safety_warning?: components["schemas"]["SafetyWarning"];
26072
26515
  };
26073
26516
  };
26074
26517
  };
@@ -26549,6 +26992,7 @@ interface operations {
26549
26992
  };
26550
26993
  /** @description Opaque pagination cursor. Null when exhausted. */
26551
26994
  cursor: string | null;
26995
+ safety_warning?: components["schemas"]["SafetyWarning"];
26552
26996
  };
26553
26997
  };
26554
26998
  };
@@ -26820,6 +27264,7 @@ interface operations {
26820
27264
  };
26821
27265
  /** @description Opaque pagination cursor. Null when exhausted. */
26822
27266
  cursor: string | null;
27267
+ safety_warning?: components["schemas"]["SafetyWarning"];
26823
27268
  };
26824
27269
  };
26825
27270
  };
@@ -27220,6 +27665,7 @@ interface operations {
27220
27665
  };
27221
27666
  /** @description Opaque pagination cursor. Null when exhausted. */
27222
27667
  cursor: string | null;
27668
+ safety_warning?: components["schemas"]["SafetyWarning"];
27223
27669
  };
27224
27670
  };
27225
27671
  };
@@ -27378,6 +27824,7 @@ interface operations {
27378
27824
  }[];
27379
27825
  /** @description Next-page cursor; null on the last page. */
27380
27826
  cursor?: string | null;
27827
+ safety_warning?: components["schemas"]["SafetyWarning"];
27381
27828
  };
27382
27829
  };
27383
27830
  };
@@ -27452,7 +27899,7 @@ interface operations {
27452
27899
  };
27453
27900
  requestBody?: never;
27454
27901
  responses: {
27455
- /** @description The account's metadata and current state, including the central quotas[] view, one entry per tracked quota family for this account. */
27902
+ /** @description The account's metadata and current state, including the quotas[] budget view (one entry per counted action row for this account, for the current window of that row's grain) account_states[], the platform conditions the account is currently in, and activity_window, the hours in which live writes are reported or refused. */
27456
27903
  200: {
27457
27904
  headers: {
27458
27905
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -27482,22 +27929,73 @@ interface operations {
27482
27929
  requested_products?: ("classic" | "company" | "sales_navigator" | "recruiter")[] | null;
27483
27930
  /** @description ISO-8601 UTC creation timestamp of the underlying LinkedIn account, distinct from connected_at. Null until the first background enrichment lands. */
27484
27931
  substrate_created_at?: string | null;
27485
- /** @description Capacity figures for this account, one entry per tracked family (messages.daily, connection_requests.daily, profile_views.daily, inmail.daily, profile.endorse, account.per_minute). These are advisory only: Curviate never rejects a request because a daily figure is exceeded; only account.per_minute is a binding limit enforced with HTTP 429. Every figure describes what the underlying platform will accept, not a safe sustained rate; pacing is the caller's responsibility. */
27932
+ /** @description Account-safety rows for this account: one entry per counted action for the current window of its grain, plus the derived total and the invitation backlog. Each row reports where the account stands, what it is allowed, which band that puts it in, what happens at the ceiling, whether the ceiling was raised above the Curviate default, and any active pause the platform caused. Whether a breach is refused or merely reported is `posture`, and the default is to report: nothing here refuses until you configure it to. Pacing is still the caller's responsibility, and nothing on this array paces for you. The per-minute REQUEST ceiling is a different thing and is deliberately not in this array: it protects Curviate's own servers rather than your LinkedIn account, it counts requests where these count platform operations, it is always enforced with HTTP 429 whatever your posture, and its envelope is returned on that refusal. Read this array to see where the account stands; read the safety events surface to see what has already been refused or flagged. */
27486
27933
  quotas?: {
27487
- /** @description Quota family (e.g. messages.daily, account.per_minute). */
27488
- quota_name?: string;
27489
- /** @description Remaining units in the current window. */
27490
- remaining?: number;
27491
- /** @description Window capacity for this family. This is a substrate capacity ceiling, not a safe sustained rate; the caller owns its own pacing. */
27492
- total?: number;
27493
- /** @description ISO-8601 UTC time the current window resets. */
27494
- reset_time?: string;
27495
27934
  /**
27496
- * @description Pacing recommendation for this family, advisory only. `none` (under 80% consumed), `slow_down` (80-99% consumed), and `backoff` (100%+ consumed) never block a request. `stop` is reserved exclusively for `account.per_minute`; the sole binding limit, whose breach is enforced with HTTP 429.
27935
+ * @description The safety row. `search` counts result LINES, not queries issued; `content_poll` also debits `profile_views`. Two rows are not counters and say so in `kind`: `total_actions` is a tally of everything this account did in the window, and `pending_invites` is the outstanding invitation backlog read from the platform rather than something this account spends.
27936
+ * @enum {string}
27937
+ */
27938
+ budget_row: "connection_requests_with_note" | "connection_requests_no_note" | "messages_first_degree" | "inmail" | "open_profile_inmail" | "profile_views" | "search" | "comments" | "post_likes" | "follows" | "content_poll" | "job_poll" | "posts" | "endorsements" | "invite_responses" | "withdrawal_sweep" | "total_actions" | "pending_invites";
27939
+ /**
27940
+ * @description Which arithmetic produced `used`. `debit`: actions this account spent, counted as they happen. `tally`: a sum across the debit rows in the window, which is why it has a warn threshold and no ceiling. `gauge`: a level read from the platform rather than counted here, so it falls on its own as invitations are accepted or withdrawn.
27941
+ * @enum {string}
27942
+ */
27943
+ kind: "debit" | "tally" | "gauge";
27944
+ /**
27945
+ * @description Calendar grain this row is counted in. UTC, not account-local. `null` on the gauge, which has no window at all.
27946
+ * @enum {string|null}
27947
+ */
27948
+ window: "day" | "week" | "month" | null;
27949
+ /** @description ISO-8601 UTC start of the current window; `null` on the gauge. */
27950
+ window_start: string | null;
27951
+ /** @description ISO-8601 UTC instant the current window rolls over and `used` returns to 0. `null` on the gauge: a backlog falls when invitations are accepted or withdrawn, not at a window edge, so naming an instant would promise something nothing keeps. */
27952
+ resets_at: string | null;
27953
+ /** @description Where the account stands: actions counted in the current window for a debit row, the summed tally for `total_actions`, the observed backlog for the gauge. A debit row with no activity reports 0, never null. Only the gauge can be `null`, and only before anything has observed it: 0 there would be a backlog nobody looked at. */
27954
+ used: number | null;
27955
+ /** @description What this account is allowed on this row in this window, as configured. `null` where the row deliberately has no ceiling: the tally is reported and never enforced, and an uncalibrated row has no figure at all. Change it on the safety policy; Curviate never clamps what you set. */
27956
+ ceiling: number | null;
27957
+ /** @description What this row is ACTUALLY held to right now: `ceiling` with the account's warm-up ramp applied. Compare `used` against THIS one, and `band` is computed against it too. While an account is new, thinly connected, dormant or recovering from a restriction the two differ, and on a five-day-old account they differ by a factor of about seven. Equal to `ceiling` once the ramp is over, and `null` wherever `ceiling` is null. The account's ramp, its factor and the triggers behind it are on GET /v1/{account_id}/safety-policy as `warm_up`. */
27958
+ effective_ceiling?: number | null;
27959
+ /**
27960
+ * @description Where `used` sits against this row's own bands. `green` routine, `amber` at or past the routine band, `hard` at or past the warn-louder band, `over` at or past the ceiling. `over` is what `posture` acts on: `warn` reports it and lets the action through, `enforce` refuses. A row with no figures reports `green` at every value, because there is nothing to be past.
27961
+ * @enum {string}
27962
+ */
27963
+ band: "green" | "amber" | "hard" | "over";
27964
+ /**
27965
+ * @description What happens when this row is `over`. `warn`: the action goes through and the response carries the breach. `enforce`: the action is refused before it reaches the platform. Set per row, per account or workspace-wide on the safety policy; the default is `warn`. One exception worth knowing: on `pending_invites` this field is not the one that acts. That row blocks invitations rather than being spent itself, and the posture that decides whether a full backlog warns or refuses is the one on the invitation row, so setting `enforce` on invitations is enough and you never have to find a posture on a row nothing spends.
27966
+ * @enum {string}
27967
+ */
27968
+ posture: "warn" | "enforce";
27969
+ /** @description Whether something on this row is configured above Curviate's own default. The safety policy carries the detail: which field, the value, the default and how well calibrated that default is. */
27970
+ over_default: boolean;
27971
+ /** @description An active pause on this row because the platform refused a recent request on it, or `null`. A halt is not a ceiling: no limit you raise lifts it, and it clears itself when it expires. */
27972
+ halt: {
27973
+ /** @description What the platform did: `rate_limited`, `platform_fault`, or `resend_cooldown` (the same invitation was sent too recently). */
27974
+ reason?: string;
27975
+ /** @description Whole seconds until the pause expires. */
27976
+ retry_after_seconds?: number;
27977
+ } | null;
27978
+ /**
27979
+ * Format: date-time
27980
+ * @description When the gauge's level was read from the platform. `null` on every other row, whose numbers are read live from Curviate's own records. Read it before acting on the backlog: the level is refreshed in the background, so it can be up to an hour old.
27981
+ */
27982
+ observed_at: string | null;
27983
+ /**
27984
+ * @description How well calibrated this row is, in descending order of trust: `substrate` (the transport layer's own guidance), `linkedin_official` (platform-published; reliable for mechanics such as credit counts and reset dates, not for safe volume), `practitioner` (corroborated operator experience), `inferred` (Curviate's own reasoning; flagged, never hidden), `unseeded` (no figure at all for this row on this account's limit profile, reported so nothing is silently missing, never presented as a number).
27497
27985
  * @enum {string}
27498
27986
  */
27499
- recommended_throttle_hint?: "none" | "slow_down" | "backoff" | "stop";
27987
+ source: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
27500
27988
  }[];
27989
+ /** @description Platform conditions this LinkedIn account is currently in, additive to `status` and independent of it. `recruiter_session_evicted`: the account is signed in somewhere else and LinkedIn allows one session at a time for it, so a person has to close the other session; reconnecting will not help. `commercial_use_limited`: LinkedIn's commercial-use limit has tripped, so people search returns only a handful of results per query. It is a paywall, not a penalty, and nothing else about the account is affected. `profile_view_limited`: profile views outside this account's own network are being blocked; first-degree views still work. The two limits are detected from their symptom: LinkedIn publishes no threshold for either, exposes no counter, and lifts neither on request, so no number is reported. `commercial_use_limited` is a present-tense reading and a genuinely narrow search can also produce the shape it looks for, so treat it as a reason to check rather than a certainty; the next people search that returns a full page clears it. An account in none of these reports an empty array. */
27990
+ account_states?: ("recruiter_session_evicted" | "commercial_use_limited" | "profile_view_limited")[];
27991
+ /** @description The account's activity window, the hours in which Curviate reports or refuses live writes to LinkedIn. Reads and anything served from Curviate's own store are never affected, at any hour. Configure it on the account's safety policy. */
27992
+ activity_window?: {
27993
+ /** @description The IANA zone the window is read in, or null when none is set. */
27994
+ timezone?: string | null;
27995
+ /** @description True when no zone is set, which DISABLES the activity window. Curviate never guesses a zone: a guess eight hours wrong inverts the window, refusing every daytime write and permitting every nocturnal one. If you configured a window and it never fires, this is why. */
27996
+ timezone_unset?: boolean;
27997
+ };
27998
+ safety_warning?: components["schemas"]["SafetyWarning"];
27501
27999
  };
27502
28000
  };
27503
28001
  };
@@ -27594,6 +28092,7 @@ interface operations {
27594
28092
  * @enum {boolean}
27595
28093
  */
27596
28094
  already_disconnected?: true;
28095
+ safety_warning?: components["schemas"]["SafetyWarning"];
27597
28096
  };
27598
28097
  };
27599
28098
  };
@@ -27717,6 +28216,7 @@ interface operations {
27717
28216
  */
27718
28217
  object?: "account";
27719
28218
  account_id?: string;
28219
+ safety_warning?: components["schemas"]["SafetyWarning"];
27720
28220
  };
27721
28221
  };
27722
28222
  };
@@ -27896,6 +28396,7 @@ interface operations {
27896
28396
  account_id?: string;
27897
28397
  /** @enum {string} */
27898
28398
  status?: "active";
28399
+ safety_warning?: components["schemas"]["SafetyWarning"];
27899
28400
  };
27900
28401
  };
27901
28402
  };
@@ -27929,6 +28430,7 @@ interface operations {
27929
28430
  attached_seat_id?: string | null;
27930
28431
  /** @description Present and true only when this connect reactivated an account you had previously disconnected, instead of opening a brand-new one. Absent on a normal connect. The account keeps its original id, and its status reflects its real observed state, which may need a reconnect. */
27931
28432
  recovered?: boolean;
28433
+ safety_warning?: components["schemas"]["SafetyWarning"];
27932
28434
  };
27933
28435
  };
27934
28436
  };
@@ -27962,6 +28464,7 @@ interface operations {
27962
28464
  }[];
27963
28465
  /** @description ISO-8601 expiry of the challenge. */
27964
28466
  expires_at?: string;
28467
+ safety_warning?: components["schemas"]["SafetyWarning"];
27965
28468
  };
27966
28469
  };
27967
28470
  };
@@ -28122,6 +28625,7 @@ interface operations {
28122
28625
  attached_seat_id?: string | null;
28123
28626
  /** @description Present and true only when this connect reactivated an account you had previously disconnected, instead of opening a brand-new one. Absent on a normal connect. The account keeps its original id, and its status reflects its real observed state, which may need a reconnect. */
28124
28627
  recovered?: boolean;
28628
+ safety_warning?: components["schemas"]["SafetyWarning"];
28125
28629
  };
28126
28630
  };
28127
28631
  };
@@ -28155,6 +28659,7 @@ interface operations {
28155
28659
  }[];
28156
28660
  /** @description ISO-8601 expiry of the challenge. */
28157
28661
  expires_at?: string;
28662
+ safety_warning?: components["schemas"]["SafetyWarning"];
28158
28663
  };
28159
28664
  };
28160
28665
  };
@@ -28285,6 +28790,7 @@ interface operations {
28285
28790
  object?: "checkpoint";
28286
28791
  account_id?: string;
28287
28792
  resent?: boolean;
28793
+ safety_warning?: components["schemas"]["SafetyWarning"];
28288
28794
  };
28289
28795
  };
28290
28796
  };
@@ -28444,6 +28950,7 @@ interface operations {
28444
28950
  }[];
28445
28951
  /** @description Human-readable, actionable next step when the approval timed out. Present on status:"expired". */
28446
28952
  recovery_hint?: string;
28953
+ safety_warning?: components["schemas"]["SafetyWarning"];
28447
28954
  };
28448
28955
  };
28449
28956
  };
@@ -28572,6 +29079,7 @@ interface operations {
28572
29079
  challenge_type?: string;
28573
29080
  /** @description ISO-8601 UTC challenge expiry, when known; null otherwise. */
28574
29081
  expires_at?: string | null;
29082
+ safety_warning?: components["schemas"]["SafetyWarning"];
28575
29083
  };
28576
29084
  };
28577
29085
  };
@@ -28634,21 +29142,26 @@ interface operations {
28634
29142
  };
28635
29143
  };
28636
29144
  };
28637
- getV1Webhooks: {
29145
+ getV1AccountIdEvents: {
28638
29146
  parameters: {
28639
29147
  query?: {
28640
29148
  /** @description Pagination cursor from a previous response; decodable, pass it back verbatim */
28641
29149
  cursor?: string;
28642
- /** @description Max items per page (1-250, default 50) */
29150
+ /** @description Return only events in this queue state. unread: nobody holds it, which includes an event whose lease expired. claimed: leased to a consumer right now. done: already handled, and it will never be handed out again. Omit to return every state. There is no failed state. */
29151
+ state?: "unread" | "claimed" | "done";
29152
+ /** @description How many events to return per page, 1 to 100. Defaults to 50. */
28643
29153
  limit?: number;
28644
29154
  };
28645
29155
  header?: never;
28646
- path?: never;
29156
+ path: {
29157
+ /** @description The connected LinkedIn account whose received events to read. */
29158
+ account_id: string;
29159
+ };
28647
29160
  cookie?: never;
28648
29161
  };
28649
29162
  requestBody?: never;
28650
29163
  responses: {
28651
- /** @description A page of registered webhooks. The plaintext secret is never present on list items. */
29164
+ /** @description The events Curviate received for this account in the last 24 hours, newest received first. Events are retained whether or not any webhook is subscribed to them, which is what makes this readable with no listener running. Retention is 24 hours from received_at, absolute: an event is deleted on that clock and nothing extends it, including a live claim. Disconnecting an account removes its events from this response immediately. Each item carries its queue state, so state=done answers what your agents have already handled and state=unread answers what is still waiting. This read never claims anything. */
28652
29165
  200: {
28653
29166
  headers: {
28654
29167
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -28661,45 +29174,503 @@ interface operations {
28661
29174
  * @description Response type discriminator.
28662
29175
  * @enum {string}
28663
29176
  */
28664
- object?: "webhook_list";
28665
- /** @description Page of registered webhooks. */
28666
- items?: {
29177
+ object: "event_list";
29178
+ /** @description Retained events for this account, newest received first. [] when nothing arrived in the last 24 hours. */
29179
+ items: {
28667
29180
  /**
28668
- * @description Response type discriminator.
29181
+ * @description Item type discriminator.
28669
29182
  * @enum {string}
28670
29183
  */
28671
- object?: "webhook";
28672
- /** @description Webhook id (wh_-prefixed ULID). */
28673
- id?: string;
29184
+ object: "event";
29185
+ /** @description The retained event's id. Also the value encoded in the next page's cursor. */
29186
+ id: string;
29187
+ /** @description The canonical event name, the same name you subscribe a webhook to (for example message.received, account.connected). */
29188
+ event: string;
28674
29189
  /**
28675
- * @description The event source this webhook subscribes to.
28676
- * @enum {string}
29190
+ * Format: date-time
29191
+ * @description When the platform says the event happened. Events can arrive out of order, so do not treat this ordering as causal, establish ordering from the payload if you need it.
28677
29192
  */
28678
- source?: "messaging" | "user" | "account_status";
28679
- /** @description The URL that receives delivery POST requests. */
28680
- request_url?: string;
28681
- name?: string | null;
28682
- account_ids?: string[];
28683
- enabled?: boolean;
28684
- /** @description Custom headers added to each delivery POST, with the value masked. */
28685
- headers?: {
28686
- key?: string;
28687
- /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
28688
- value_prefix?: string;
28689
- }[];
28690
- events?: string[];
28691
- data?: string[];
28692
- /** @description First 8 characters of the secret (never the full secret). */
28693
- secret_prefix?: string;
29193
+ occurred_at: string;
28694
29194
  /**
28695
- * @description Delivery health state. 'degraded' when recent deliveries have repeatedly failed.
29195
+ * Format: date-time
29196
+ * @description When Curviate received the event. Retention is 24 hours from this instant, absolute.
29197
+ */
29198
+ received_at: string;
29199
+ /** @description Exactly the payload a registered webhook would have been delivered for this event, content included. It always carries account_id, event and occurred_at; the remaining fields depend on the event. This is the delivered payload itself, not a summary of it, and not the per-delivery envelope (there is no delivery when nothing is subscribed). */
29200
+ payload: {
29201
+ [key: string]: unknown;
29202
+ };
29203
+ /**
29204
+ * @description Queue state. unread: nobody holds it. claimed: leased to a consumer until lease_until, and an expired lease reads as unread again. done: handled, and it will never be handed out again. There is no failed state. Filter on it with the state query parameter, which is how you ask what your agents have already handled.
28696
29205
  * @enum {string}
28697
29206
  */
28698
- health?: "ok" | "degraded";
28699
- created_at?: string;
29207
+ state: "unread" | "claimed" | "done";
29208
+ /** @description The consumer name recorded by the last claim, or null. Free text, never verified, so treat it as a label rather than an identity. */
29209
+ claimed_by: string | null;
28700
29210
  /**
28701
- * @description ISO-8601 timestamp of the most recent delivery attempt, whether it succeeded or failed. Null when this webhook has never attempted a delivery.
28702
- * @example 2026-06-17T10:05:00Z
29211
+ * Format: date-time
29212
+ * @description When the current lease expires, or null when the event is not leased. A lease never extends the 24 hour retention.
29213
+ */
29214
+ lease_until: string | null;
29215
+ /**
29216
+ * Format: date-time
29217
+ * @description When the event was marked done, or null. Set once and never moved.
29218
+ */
29219
+ done_at: string | null;
29220
+ }[];
29221
+ /** @description Cursor for the next page; null on the terminal page. Pass it back verbatim. Page until cursor is null. */
29222
+ cursor: string | null;
29223
+ safety_warning?: components["schemas"]["SafetyWarning"];
29224
+ };
29225
+ };
29226
+ };
29227
+ /** @description Invalid request, an out-of-range `limit`, an unknown `state`, or a corrupt `cursor`. */
29228
+ 400: {
29229
+ headers: {
29230
+ [name: string]: unknown;
29231
+ };
29232
+ content: {
29233
+ "application/json": components["schemas"]["Error"];
29234
+ };
29235
+ };
29236
+ /** @description The account_id does not belong to this tenant, or the account is unknown. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29237
+ 404: {
29238
+ headers: {
29239
+ [name: string]: unknown;
29240
+ };
29241
+ content: {
29242
+ "application/json": components["schemas"]["Error"];
29243
+ };
29244
+ };
29245
+ /** @description Rate limited, retry after the hinted delay. */
29246
+ 429: {
29247
+ headers: {
29248
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29249
+ RateLimit: components["headers"]["RateLimit"];
29250
+ "Retry-After": components["headers"]["Retry-After"];
29251
+ [name: string]: unknown;
29252
+ };
29253
+ content: {
29254
+ "application/json": components["schemas"]["Error"];
29255
+ };
29256
+ };
29257
+ /** @description Internal error. */
29258
+ 500: {
29259
+ headers: {
29260
+ [name: string]: unknown;
29261
+ };
29262
+ content: {
29263
+ "application/json": components["schemas"]["Error"];
29264
+ };
29265
+ };
29266
+ /** @description Service unavailable. */
29267
+ 503: {
29268
+ headers: {
29269
+ [name: string]: unknown;
29270
+ };
29271
+ content: {
29272
+ "application/json": components["schemas"]["Error"];
29273
+ };
29274
+ };
29275
+ };
29276
+ };
29277
+ postV1AccountIdEventsClaim: {
29278
+ parameters: {
29279
+ query?: never;
29280
+ header?: never;
29281
+ path: {
29282
+ /** @description The connected LinkedIn account whose received events to read. */
29283
+ account_id: string;
29284
+ };
29285
+ cookie?: never;
29286
+ };
29287
+ requestBody: {
29288
+ content: {
29289
+ "application/json": {
29290
+ consumer: string;
29291
+ limit?: number;
29292
+ lease_seconds?: number;
29293
+ };
29294
+ };
29295
+ };
29296
+ responses: {
29297
+ /** @description The events now leased to you, oldest occurred_at first. Claiming when nothing is available returns an empty list and a 200, so an idle poll is not an error. Each event is handed to one caller only: a second claim running at the same instant gets different events, or none. The lease is cooperative and is not access control: it stops your own agents duplicating work, it does not stop anything from acting on an event it did not claim. */
29298
+ 200: {
29299
+ headers: {
29300
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29301
+ RateLimit: components["headers"]["RateLimit"];
29302
+ [name: string]: unknown;
29303
+ };
29304
+ content: {
29305
+ "application/json": {
29306
+ /**
29307
+ * @description Response type discriminator.
29308
+ * @enum {string}
29309
+ */
29310
+ object: "event_claim";
29311
+ /** @description The consumer name you sent, echoed as recorded on the claimed events. Like every echoed input on this API it passes the output filter on the way back, so a value containing a filtered term returns altered. */
29312
+ consumer: string;
29313
+ /**
29314
+ * Format: date-time
29315
+ * @description When this batch's lease expires. After this instant the events become claimable by anyone again, whether or not you finished. null when nothing was claimed.
29316
+ */
29317
+ lease_until: string | null;
29318
+ /** @description The events now leased to you, oldest occurred_at first. [] when nothing was available, which is a success and not an error. */
29319
+ items: {
29320
+ /**
29321
+ * @description Item type discriminator.
29322
+ * @enum {string}
29323
+ */
29324
+ object: "event";
29325
+ /** @description The retained event's id. Also the value encoded in the next page's cursor. */
29326
+ id: string;
29327
+ /** @description The canonical event name, the same name you subscribe a webhook to (for example message.received, account.connected). */
29328
+ event: string;
29329
+ /**
29330
+ * Format: date-time
29331
+ * @description When the platform says the event happened. Events can arrive out of order, so do not treat this ordering as causal, establish ordering from the payload if you need it.
29332
+ */
29333
+ occurred_at: string;
29334
+ /**
29335
+ * Format: date-time
29336
+ * @description When Curviate received the event. Retention is 24 hours from this instant, absolute.
29337
+ */
29338
+ received_at: string;
29339
+ /** @description Exactly the payload a registered webhook would have been delivered for this event, content included. It always carries account_id, event and occurred_at; the remaining fields depend on the event. This is the delivered payload itself, not a summary of it, and not the per-delivery envelope (there is no delivery when nothing is subscribed). */
29340
+ payload: {
29341
+ [key: string]: unknown;
29342
+ };
29343
+ /**
29344
+ * @description Queue state. unread: nobody holds it. claimed: leased to a consumer until lease_until, and an expired lease reads as unread again. done: handled, and it will never be handed out again. There is no failed state. Filter on it with the state query parameter, which is how you ask what your agents have already handled.
29345
+ * @enum {string}
29346
+ */
29347
+ state: "unread" | "claimed" | "done";
29348
+ /** @description The consumer name recorded by the last claim, or null. Free text, never verified, so treat it as a label rather than an identity. */
29349
+ claimed_by: string | null;
29350
+ /**
29351
+ * Format: date-time
29352
+ * @description When the current lease expires, or null when the event is not leased. A lease never extends the 24 hour retention.
29353
+ */
29354
+ lease_until: string | null;
29355
+ /**
29356
+ * Format: date-time
29357
+ * @description When the event was marked done, or null. Set once and never moved.
29358
+ */
29359
+ done_at: string | null;
29360
+ }[];
29361
+ safety_warning?: components["schemas"]["SafetyWarning"];
29362
+ };
29363
+ };
29364
+ };
29365
+ /** @description Invalid request, a missing consumer or an out-of-range limit or lease_seconds. */
29366
+ 400: {
29367
+ headers: {
29368
+ [name: string]: unknown;
29369
+ };
29370
+ content: {
29371
+ "application/json": components["schemas"]["Error"];
29372
+ };
29373
+ };
29374
+ /** @description The account_id does not belong to this tenant, or the account is unknown. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29375
+ 404: {
29376
+ headers: {
29377
+ [name: string]: unknown;
29378
+ };
29379
+ content: {
29380
+ "application/json": components["schemas"]["Error"];
29381
+ };
29382
+ };
29383
+ /** @description Payload too large, the request body exceeds the size limit. */
29384
+ 413: {
29385
+ headers: {
29386
+ [name: string]: unknown;
29387
+ };
29388
+ content: {
29389
+ "application/json": components["schemas"]["Error"];
29390
+ };
29391
+ };
29392
+ /** @description Rate limited, retry after the hinted delay. */
29393
+ 429: {
29394
+ headers: {
29395
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29396
+ RateLimit: components["headers"]["RateLimit"];
29397
+ "Retry-After": components["headers"]["Retry-After"];
29398
+ [name: string]: unknown;
29399
+ };
29400
+ content: {
29401
+ "application/json": components["schemas"]["Error"];
29402
+ };
29403
+ };
29404
+ /** @description Internal error. */
29405
+ 500: {
29406
+ headers: {
29407
+ [name: string]: unknown;
29408
+ };
29409
+ content: {
29410
+ "application/json": components["schemas"]["Error"];
29411
+ };
29412
+ };
29413
+ /** @description Service unavailable. */
29414
+ 503: {
29415
+ headers: {
29416
+ [name: string]: unknown;
29417
+ };
29418
+ content: {
29419
+ "application/json": components["schemas"]["Error"];
29420
+ };
29421
+ };
29422
+ };
29423
+ };
29424
+ postV1AccountIdEventsEventIdRelease: {
29425
+ parameters: {
29426
+ query?: never;
29427
+ header?: never;
29428
+ path: {
29429
+ /** @description The connected LinkedIn account whose received events to read. */
29430
+ account_id: string;
29431
+ /** @description The event's `id`, as returned by the events read or by a claim. */
29432
+ event_id: string;
29433
+ };
29434
+ cookie?: never;
29435
+ };
29436
+ requestBody?: never;
29437
+ responses: {
29438
+ /** @description The event's state after the release. A released event is claimable again immediately, by you or by anyone else. Releasing an event that is already done changes nothing and comes back with state done, because done is final. Release is the right move when your handler fails: hand the work back rather than sit on it for the rest of the lease. */
29439
+ 200: {
29440
+ headers: {
29441
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29442
+ RateLimit: components["headers"]["RateLimit"];
29443
+ [name: string]: unknown;
29444
+ };
29445
+ content: {
29446
+ "application/json": {
29447
+ /**
29448
+ * @description Response type discriminator.
29449
+ * @enum {string}
29450
+ */
29451
+ object: "event_lease";
29452
+ /** @description The event's id. */
29453
+ id: string;
29454
+ /**
29455
+ * @description unread: claimable now. claimed: leased to someone until lease_until. done: handled, and it will never be handed out again. There is no failed state.
29456
+ * @enum {string}
29457
+ */
29458
+ state: "unread" | "claimed" | "done";
29459
+ /** @description The consumer name recorded by the last claim, or null. Free text, never verified, so treat it as a label rather than an identity. */
29460
+ claimed_by: string | null;
29461
+ /**
29462
+ * Format: date-time
29463
+ * @description When the current lease expires, or null when the event is not leased.
29464
+ */
29465
+ lease_until: string | null;
29466
+ /**
29467
+ * Format: date-time
29468
+ * @description When the event was marked done, or null. Set once and never moved.
29469
+ */
29470
+ done_at: string | null;
29471
+ safety_warning?: components["schemas"]["SafetyWarning"];
29472
+ };
29473
+ };
29474
+ };
29475
+ /** @description No such event for this account, or it has already passed its 24 hour retention. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29476
+ 404: {
29477
+ headers: {
29478
+ [name: string]: unknown;
29479
+ };
29480
+ content: {
29481
+ "application/json": components["schemas"]["Error"];
29482
+ };
29483
+ };
29484
+ /** @description Rate limited, retry after the hinted delay. */
29485
+ 429: {
29486
+ headers: {
29487
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29488
+ RateLimit: components["headers"]["RateLimit"];
29489
+ "Retry-After": components["headers"]["Retry-After"];
29490
+ [name: string]: unknown;
29491
+ };
29492
+ content: {
29493
+ "application/json": components["schemas"]["Error"];
29494
+ };
29495
+ };
29496
+ /** @description Internal error. */
29497
+ 500: {
29498
+ headers: {
29499
+ [name: string]: unknown;
29500
+ };
29501
+ content: {
29502
+ "application/json": components["schemas"]["Error"];
29503
+ };
29504
+ };
29505
+ /** @description Service unavailable. */
29506
+ 503: {
29507
+ headers: {
29508
+ [name: string]: unknown;
29509
+ };
29510
+ content: {
29511
+ "application/json": components["schemas"]["Error"];
29512
+ };
29513
+ };
29514
+ };
29515
+ };
29516
+ postV1AccountIdEventsEventIdDone: {
29517
+ parameters: {
29518
+ query?: never;
29519
+ header?: never;
29520
+ path: {
29521
+ /** @description The connected LinkedIn account whose received events to read. */
29522
+ account_id: string;
29523
+ /** @description The event's `id`, as returned by the events read or by a claim. */
29524
+ event_id: string;
29525
+ };
29526
+ cookie?: never;
29527
+ };
29528
+ requestBody?: never;
29529
+ responses: {
29530
+ /** @description The event's state after being marked done. Done is the only terminal state there is, and it is shared: once an event is done it leaves the unread set for every consumer under this tenant, not just for you. There is no failed state, because an event nothing can handle simply expires with the rest at 24 hours. Calling this twice is safe and keeps the first done_at. */
29531
+ 200: {
29532
+ headers: {
29533
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29534
+ RateLimit: components["headers"]["RateLimit"];
29535
+ [name: string]: unknown;
29536
+ };
29537
+ content: {
29538
+ "application/json": {
29539
+ /**
29540
+ * @description Response type discriminator.
29541
+ * @enum {string}
29542
+ */
29543
+ object: "event_lease";
29544
+ /** @description The event's id. */
29545
+ id: string;
29546
+ /**
29547
+ * @description unread: claimable now. claimed: leased to someone until lease_until. done: handled, and it will never be handed out again. There is no failed state.
29548
+ * @enum {string}
29549
+ */
29550
+ state: "unread" | "claimed" | "done";
29551
+ /** @description The consumer name recorded by the last claim, or null. Free text, never verified, so treat it as a label rather than an identity. */
29552
+ claimed_by: string | null;
29553
+ /**
29554
+ * Format: date-time
29555
+ * @description When the current lease expires, or null when the event is not leased.
29556
+ */
29557
+ lease_until: string | null;
29558
+ /**
29559
+ * Format: date-time
29560
+ * @description When the event was marked done, or null. Set once and never moved.
29561
+ */
29562
+ done_at: string | null;
29563
+ safety_warning?: components["schemas"]["SafetyWarning"];
29564
+ };
29565
+ };
29566
+ };
29567
+ /** @description No such event for this account, or it has already passed its 24 hour retention. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29568
+ 404: {
29569
+ headers: {
29570
+ [name: string]: unknown;
29571
+ };
29572
+ content: {
29573
+ "application/json": components["schemas"]["Error"];
29574
+ };
29575
+ };
29576
+ /** @description Rate limited, retry after the hinted delay. */
29577
+ 429: {
29578
+ headers: {
29579
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29580
+ RateLimit: components["headers"]["RateLimit"];
29581
+ "Retry-After": components["headers"]["Retry-After"];
29582
+ [name: string]: unknown;
29583
+ };
29584
+ content: {
29585
+ "application/json": components["schemas"]["Error"];
29586
+ };
29587
+ };
29588
+ /** @description Internal error. */
29589
+ 500: {
29590
+ headers: {
29591
+ [name: string]: unknown;
29592
+ };
29593
+ content: {
29594
+ "application/json": components["schemas"]["Error"];
29595
+ };
29596
+ };
29597
+ /** @description Service unavailable. */
29598
+ 503: {
29599
+ headers: {
29600
+ [name: string]: unknown;
29601
+ };
29602
+ content: {
29603
+ "application/json": components["schemas"]["Error"];
29604
+ };
29605
+ };
29606
+ };
29607
+ };
29608
+ getV1Webhooks: {
29609
+ parameters: {
29610
+ query?: {
29611
+ /** @description Pagination cursor from a previous response; decodable, pass it back verbatim */
29612
+ cursor?: string;
29613
+ /** @description Max items per page (1-250, default 50) */
29614
+ limit?: number;
29615
+ };
29616
+ header?: never;
29617
+ path?: never;
29618
+ cookie?: never;
29619
+ };
29620
+ requestBody?: never;
29621
+ responses: {
29622
+ /** @description A page of registered webhooks. The plaintext secret is never present on list items. */
29623
+ 200: {
29624
+ headers: {
29625
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29626
+ RateLimit: components["headers"]["RateLimit"];
29627
+ [name: string]: unknown;
29628
+ };
29629
+ content: {
29630
+ "application/json": {
29631
+ /**
29632
+ * @description Response type discriminator.
29633
+ * @enum {string}
29634
+ */
29635
+ object?: "webhook_list";
29636
+ /** @description Page of registered webhooks. */
29637
+ items?: {
29638
+ /**
29639
+ * @description Response type discriminator.
29640
+ * @enum {string}
29641
+ */
29642
+ object?: "webhook";
29643
+ /** @description Webhook id (wh_-prefixed ULID). */
29644
+ id?: string;
29645
+ /**
29646
+ * @description The event source this webhook subscribes to.
29647
+ * @enum {string}
29648
+ */
29649
+ source?: "messaging" | "user" | "account_status";
29650
+ /** @description The URL that receives delivery POST requests. */
29651
+ request_url?: string;
29652
+ name?: string | null;
29653
+ account_ids?: string[];
29654
+ enabled?: boolean;
29655
+ /** @description Custom headers added to each delivery POST, with the value masked. */
29656
+ headers?: {
29657
+ key?: string;
29658
+ /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
29659
+ value_prefix?: string;
29660
+ }[];
29661
+ events?: string[];
29662
+ data?: string[];
29663
+ /** @description First 8 characters of the secret (never the full secret). */
29664
+ secret_prefix?: string;
29665
+ /**
29666
+ * @description Delivery health state. 'degraded' when recent deliveries have repeatedly failed.
29667
+ * @enum {string}
29668
+ */
29669
+ health?: "ok" | "degraded";
29670
+ created_at?: string;
29671
+ /**
29672
+ * @description ISO-8601 timestamp of the most recent delivery attempt, whether it succeeded or failed. Null when this webhook has never attempted a delivery.
29673
+ * @example 2026-06-17T10:05:00Z
28703
29674
  */
28704
29675
  last_delivery_at?: string | null;
28705
29676
  /**
@@ -28710,18 +29681,303 @@ interface operations {
28710
29681
  }[];
28711
29682
  /** @description Opaque next-page cursor. Null on the last page. */
28712
29683
  cursor?: string | null;
29684
+ safety_warning?: components["schemas"]["SafetyWarning"];
29685
+ };
29686
+ };
29687
+ };
29688
+ /** @description Invalid pagination parameters, limit out of range or malformed cursor. */
29689
+ 400: {
29690
+ headers: {
29691
+ [name: string]: unknown;
29692
+ };
29693
+ content: {
29694
+ "application/json": components["schemas"]["Error"];
29695
+ };
29696
+ };
29697
+ /** @description Missing or invalid API key. */
29698
+ 401: {
29699
+ headers: {
29700
+ [name: string]: unknown;
29701
+ };
29702
+ content: {
29703
+ "application/json": components["schemas"]["Error"];
29704
+ };
29705
+ };
29706
+ /** @description Rate limited, slow down and retry after the hinted delay. */
29707
+ 429: {
29708
+ headers: {
29709
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29710
+ RateLimit: components["headers"]["RateLimit"];
29711
+ "Retry-After": components["headers"]["Retry-After"];
29712
+ [name: string]: unknown;
29713
+ };
29714
+ content: {
29715
+ "application/json": components["schemas"]["Error"];
29716
+ };
29717
+ };
29718
+ };
29719
+ };
29720
+ postV1Webhooks: {
29721
+ parameters: {
29722
+ query?: never;
29723
+ header?: never;
29724
+ path?: never;
29725
+ cookie?: never;
29726
+ };
29727
+ requestBody: {
29728
+ content: {
29729
+ "application/json": {
29730
+ /** @constant */
29731
+ source: "messaging";
29732
+ /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
29733
+ request_url: string;
29734
+ /** @description Human-readable name for this webhook (1-100 chars) */
29735
+ name?: string;
29736
+ /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
29737
+ account_ids: string[];
29738
+ /**
29739
+ * @description A disabled webhook is created but delivers nothing
29740
+ * @default true
29741
+ */
29742
+ enabled?: boolean;
29743
+ /**
29744
+ * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
29745
+ * @default []
29746
+ */
29747
+ headers?: {
29748
+ key: string;
29749
+ value: string;
29750
+ }[];
29751
+ /** @description Messaging events to subscribe to (default: [message.received]) */
29752
+ events?: ("message.received" | "message.delivered" | "message.read" | "message.reaction" | "message.edited" | "message.deleted" | "chat.updated" | "chat.deleted")[];
29753
+ /**
29754
+ * @description Field-remapping keys for the messaging delivery payload
29755
+ * @default []
29756
+ */
29757
+ data?: string[];
29758
+ } | {
29759
+ /** @constant */
29760
+ source: "user";
29761
+ /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
29762
+ request_url: string;
29763
+ /** @description Human-readable name for this webhook (1-100 chars) */
29764
+ name?: string;
29765
+ /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
29766
+ account_ids: string[];
29767
+ /**
29768
+ * @description A disabled webhook is created but delivers nothing
29769
+ * @default true
29770
+ */
29771
+ enabled?: boolean;
29772
+ /**
29773
+ * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
29774
+ * @default []
29775
+ */
29776
+ headers?: {
29777
+ key: string;
29778
+ value: string;
29779
+ }[];
29780
+ /** @description User/relation events to subscribe to (default: [connection.accepted]) */
29781
+ events?: ("connection.accepted" | "connection.new")[];
29782
+ /**
29783
+ * @description Field-remapping keys for the user/relation delivery payload
29784
+ * @default []
29785
+ */
29786
+ data?: ("user_provider_id" | "user_full_name" | "user_public_identifier" | "user_profile_url" | "user_picture_url")[];
29787
+ } | {
29788
+ /** @constant */
29789
+ source: "account_status";
29790
+ /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
29791
+ request_url: string;
29792
+ /** @description Human-readable name for this webhook (1-100 chars) */
29793
+ name?: string;
29794
+ /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
29795
+ account_ids: string[];
29796
+ /**
29797
+ * @description A disabled webhook is created but delivers nothing
29798
+ * @default true
29799
+ */
29800
+ enabled?: boolean;
29801
+ /**
29802
+ * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
29803
+ * @default []
29804
+ */
29805
+ headers?: {
29806
+ key: string;
29807
+ value: string;
29808
+ }[];
29809
+ /** @description Account-status events to subscribe to (default: all 12 lifecycle events) */
29810
+ events?: ("account.created" | "account.connected" | "account.synced" | "account.reconnected" | "account.reconnect_needed" | "account.restricted" | "account.creation_failed" | "account.disconnected" | "account.error" | "account.paused" | "account.connecting" | "account.permission_revoked" | "account.initial_sync.running" | "account.initial_sync.completed" | "account.initial_sync.failed")[];
29811
+ };
29812
+ };
29813
+ };
29814
+ responses: {
29815
+ /** @description Webhook registered. The HMAC signing secret is returned exactly once, store it immediately. */
29816
+ 201: {
29817
+ headers: {
29818
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29819
+ RateLimit: components["headers"]["RateLimit"];
29820
+ [name: string]: unknown;
29821
+ };
29822
+ content: {
29823
+ "application/json": {
29824
+ /**
29825
+ * @description Response type discriminator.
29826
+ * @enum {string}
29827
+ */
29828
+ object?: "webhook";
29829
+ /** @description Webhook id (wh_-prefixed ULID). */
29830
+ id?: string;
29831
+ /**
29832
+ * @description The event source this webhook subscribes to.
29833
+ * @enum {string}
29834
+ */
29835
+ source?: "messaging" | "user" | "account_status";
29836
+ /** @description The URL that receives delivery POST requests. */
29837
+ request_url?: string;
29838
+ /** @description Human-readable label. */
29839
+ name?: string | null;
29840
+ /** @description Accounts targeted by this webhook. Required and non-empty, each id must be owned by the tenant. */
29841
+ account_ids?: string[];
29842
+ /** @description A disabled webhook is created but delivers nothing. */
29843
+ enabled?: boolean;
29844
+ /** @description Custom headers added to each delivery POST. Values are encrypted at rest, so a read shows the name and a masked value only. */
29845
+ headers?: {
29846
+ /** @description The header name, as configured. */
29847
+ key?: string;
29848
+ /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
29849
+ value_prefix?: string;
29850
+ }[];
29851
+ /** @description Canonical event names this webhook subscribes to. */
29852
+ events?: string[];
29853
+ /** @description Field-remapping keys for the delivery payload (messaging and user sources only). */
29854
+ data?: string[];
29855
+ /**
29856
+ * @description HMAC signing secret, 64 hexadecimal characters, returned exactly once at creation. Store it securely.
29857
+ * @example a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
29858
+ */
29859
+ secret?: string;
29860
+ /**
29861
+ * @description The first 8 characters of the secret, for identification without exposing it. Exactly the value the read endpoints return for this webhook.
29862
+ * @example a1b2c3d4
29863
+ */
29864
+ secret_prefix?: string;
29865
+ /** @description ISO-8601 creation timestamp. */
29866
+ created_at?: string;
29867
+ safety_warning?: components["schemas"]["SafetyWarning"];
28713
29868
  };
28714
29869
  };
28715
29870
  };
28716
- /** @description Invalid pagination parameters, limit out of range or malformed cursor. */
28717
- 400: {
29871
+ /** @description Invalid request, malformed URL, empty events array, or event-source mismatch. */
29872
+ 400: {
29873
+ headers: {
29874
+ [name: string]: unknown;
29875
+ };
29876
+ content: {
29877
+ "application/json": components["schemas"]["Error"];
29878
+ };
29879
+ };
29880
+ /** @description Missing or invalid API key. */
29881
+ 401: {
29882
+ headers: {
29883
+ [name: string]: unknown;
29884
+ };
29885
+ content: {
29886
+ "application/json": components["schemas"]["Error"];
29887
+ };
29888
+ };
29889
+ /** @description Payment required, an active subscription is needed. */
29890
+ 402: {
29891
+ headers: {
29892
+ [name: string]: unknown;
29893
+ };
29894
+ content: {
29895
+ "application/json": components["schemas"]["Error"];
29896
+ };
29897
+ };
29898
+ /** @description One or more account_ids not found for this tenant. */
29899
+ 404: {
29900
+ headers: {
29901
+ [name: string]: unknown;
29902
+ };
29903
+ content: {
29904
+ "application/json": components["schemas"]["Error"];
29905
+ };
29906
+ };
29907
+ /** @description Payload too large, the request body exceeds the size limit. */
29908
+ 413: {
29909
+ headers: {
29910
+ [name: string]: unknown;
29911
+ };
29912
+ content: {
29913
+ "application/json": components["schemas"]["Error"];
29914
+ };
29915
+ };
29916
+ /** @description Rate limited, slow down and retry after the hinted delay. */
29917
+ 429: {
28718
29918
  headers: {
29919
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29920
+ RateLimit: components["headers"]["RateLimit"];
29921
+ "Retry-After": components["headers"]["Retry-After"];
28719
29922
  [name: string]: unknown;
28720
29923
  };
28721
29924
  content: {
28722
29925
  "application/json": components["schemas"]["Error"];
28723
29926
  };
28724
29927
  };
29928
+ };
29929
+ };
29930
+ getV1WebhooksEvents: {
29931
+ parameters: {
29932
+ query?: never;
29933
+ header?: never;
29934
+ path?: never;
29935
+ cookie?: never;
29936
+ };
29937
+ requestBody?: never;
29938
+ responses: {
29939
+ /** @description The complete canonical event catalogue, grouped by source. */
29940
+ 200: {
29941
+ headers: {
29942
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29943
+ RateLimit: components["headers"]["RateLimit"];
29944
+ [name: string]: unknown;
29945
+ };
29946
+ content: {
29947
+ "application/json": {
29948
+ /**
29949
+ * @description Response type discriminator.
29950
+ * @enum {string}
29951
+ */
29952
+ object?: "webhook_event_catalogue";
29953
+ /** @description Event types grouped by source. */
29954
+ sources?: {
29955
+ /** @description Event source group (messaging, user, account_status, recruiter, sales_nav). */
29956
+ source?: string;
29957
+ events?: {
29958
+ /** @description Canonical event name. */
29959
+ type?: string;
29960
+ /** @description Event source group. */
29961
+ source?: string;
29962
+ description?: string;
29963
+ /** @description Present and true when the event carries LinkedIn-specific content. */
29964
+ linkedin_only?: boolean;
29965
+ /**
29966
+ * @description Required add-on tier. Present only on tier-gated events.
29967
+ * @enum {string}
29968
+ */
29969
+ tier?: "recruiter" | "sales_nav";
29970
+ /**
29971
+ * @description Delivery timing. Omitted (equivalent to 'realtime') on every live-pushed event. 'no_longer_realtime' means no push exists for this event; 'not_realtime' means delivery is not guaranteed sub-second and may lag the underlying LinkedIn event, typically because it is detected by a poll rather than pushed, though a given event's own reference article may document an additional immediate path.
29972
+ * @enum {string}
29973
+ */
29974
+ availability?: "realtime" | "no_longer_realtime" | "not_realtime";
29975
+ }[];
29976
+ }[];
29977
+ safety_warning?: components["schemas"]["SafetyWarning"];
29978
+ };
29979
+ };
29980
+ };
28725
29981
  /** @description Missing or invalid API key. */
28726
29982
  401: {
28727
29983
  headers: {
@@ -28745,103 +30001,102 @@ interface operations {
28745
30001
  };
28746
30002
  };
28747
30003
  };
28748
- postV1Webhooks: {
30004
+ postV1WebhooksIdTest: {
28749
30005
  parameters: {
28750
30006
  query?: never;
28751
30007
  header?: never;
28752
- path?: never;
30008
+ path: {
30009
+ /** @description Webhook id (wh_-prefixed ULID). */
30010
+ id: string;
30011
+ };
28753
30012
  cookie?: never;
28754
30013
  };
28755
- requestBody: {
28756
- content: {
28757
- "application/json": {
28758
- /** @constant */
28759
- source: "messaging";
28760
- /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
28761
- request_url: string;
28762
- /** @description Human-readable name for this webhook (1-100 chars) */
28763
- name?: string;
28764
- /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
28765
- account_ids: string[];
28766
- /**
28767
- * @description A disabled webhook is created but delivers nothing
28768
- * @default true
28769
- */
28770
- enabled?: boolean;
28771
- /**
28772
- * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
28773
- * @default []
28774
- */
28775
- headers?: {
28776
- key: string;
28777
- value: string;
28778
- }[];
28779
- /** @description Messaging events to subscribe to (default: [message.received]) */
28780
- events?: ("message.received" | "message.delivered" | "message.read" | "message.reaction" | "message.edited" | "message.deleted" | "chat.updated" | "chat.deleted")[];
28781
- /**
28782
- * @description Field-remapping keys for the messaging delivery payload
28783
- * @default []
28784
- */
28785
- data?: string[];
28786
- } | {
28787
- /** @constant */
28788
- source: "user";
28789
- /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
28790
- request_url: string;
28791
- /** @description Human-readable name for this webhook (1-100 chars) */
28792
- name?: string;
28793
- /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
28794
- account_ids: string[];
28795
- /**
28796
- * @description A disabled webhook is created but delivers nothing
28797
- * @default true
28798
- */
28799
- enabled?: boolean;
28800
- /**
28801
- * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
28802
- * @default []
28803
- */
28804
- headers?: {
28805
- key: string;
28806
- value: string;
28807
- }[];
28808
- /** @description User/relation events to subscribe to (default: [connection.accepted]) */
28809
- events?: ("connection.accepted" | "connection.new")[];
28810
- /**
28811
- * @description Field-remapping keys for the user/relation delivery payload
28812
- * @default []
28813
- */
28814
- data?: ("user_provider_id" | "user_full_name" | "user_public_identifier" | "user_profile_url" | "user_picture_url")[];
28815
- } | {
28816
- /** @constant */
28817
- source: "account_status";
28818
- /** @description HTTPS URL that receives webhook deliveries. Must be a publicly reachable host on the default https port; private, loopback and link-local addresses are rejected. */
28819
- request_url: string;
28820
- /** @description Human-readable name for this webhook (1-100 chars) */
28821
- name?: string;
28822
- /** @description Per-account targeting. Required and non-empty; each id must be an acc_-prefixed id owned by the tenant. */
28823
- account_ids: string[];
28824
- /**
28825
- * @description A disabled webhook is created but delivers nothing
28826
- * @default true
28827
- */
28828
- enabled?: boolean;
28829
- /**
28830
- * @description Custom headers added to each delivery POST. Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured.
28831
- * @default []
28832
- */
28833
- headers?: {
28834
- key: string;
28835
- value: string;
28836
- }[];
28837
- /** @description Account-status events to subscribe to (default: all 12 lifecycle events) */
28838
- events?: ("account.created" | "account.connected" | "account.synced" | "account.reconnected" | "account.reconnect_needed" | "account.restricted" | "account.creation_failed" | "account.disconnected" | "account.error" | "account.paused" | "account.connecting" | "account.permission_revoked" | "account.initial_sync.running" | "account.initial_sync.completed" | "account.initial_sync.failed")[];
30014
+ requestBody?: never;
30015
+ responses: {
30016
+ /** @description The test delivery is queued. It travels the same path as a real delivery: the same signature, the same custom headers, the same delivery record, the same retry schedule. Wait for it to arrive at your endpoint, verify the signature, and match data.test_id to the test_id below. */
30017
+ 202: {
30018
+ headers: {
30019
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
30020
+ RateLimit: components["headers"]["RateLimit"];
30021
+ [name: string]: unknown;
30022
+ };
30023
+ content: {
30024
+ "application/json": {
30025
+ /**
30026
+ * @description Response type discriminator.
30027
+ * @enum {string}
30028
+ */
30029
+ object?: "webhook_test";
30030
+ /** @description The webhook the test delivery was queued for. */
30031
+ webhook_id?: string;
30032
+ /** @description Correlation id for this test delivery, echoed as data.test_id in the delivered payload. Match it to confirm that the delivery your receiver observed is the one you asked for. */
30033
+ test_id?: string;
30034
+ /**
30035
+ * @description Always webhook.test. This name is not part of the event catalogue and can never be subscribed to, so it can never be confused with a real event.
30036
+ * @enum {string}
30037
+ */
30038
+ event?: "webhook.test";
30039
+ /** @description When the test delivery was queued (ISO 8601). */
30040
+ queued_at?: string;
30041
+ safety_warning?: components["schemas"]["SafetyWarning"];
30042
+ };
30043
+ };
30044
+ };
30045
+ /** @description Malformed webhook id, or the webhook is disabled or targets no account, in which case nothing could be delivered. */
30046
+ 400: {
30047
+ headers: {
30048
+ [name: string]: unknown;
30049
+ };
30050
+ content: {
30051
+ "application/json": components["schemas"]["Error"];
30052
+ };
30053
+ };
30054
+ /** @description Missing or invalid API key. */
30055
+ 401: {
30056
+ headers: {
30057
+ [name: string]: unknown;
30058
+ };
30059
+ content: {
30060
+ "application/json": components["schemas"]["Error"];
30061
+ };
30062
+ };
30063
+ /** @description Webhook not found or not owned by this tenant. */
30064
+ 404: {
30065
+ headers: {
30066
+ [name: string]: unknown;
30067
+ };
30068
+ content: {
30069
+ "application/json": components["schemas"]["Error"];
30070
+ };
30071
+ };
30072
+ /** @description Too many test deliveries, either for this webhook or across your tenant's webhooks. Wait for the window to reset; the Retry-After header gives the number of seconds. */
30073
+ 429: {
30074
+ headers: {
30075
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
30076
+ RateLimit: components["headers"]["RateLimit"];
30077
+ "Retry-After": components["headers"]["Retry-After"];
30078
+ [name: string]: unknown;
30079
+ };
30080
+ content: {
30081
+ "application/json": components["schemas"]["Error"];
28839
30082
  };
28840
30083
  };
28841
30084
  };
30085
+ };
30086
+ getV1WebhooksId: {
30087
+ parameters: {
30088
+ query?: never;
30089
+ header?: never;
30090
+ path: {
30091
+ /** @description Webhook id (wh_-prefixed ULID). */
30092
+ id: string;
30093
+ };
30094
+ cookie?: never;
30095
+ };
30096
+ requestBody?: never;
28842
30097
  responses: {
28843
- /** @description Webhook registered. The HMAC signing secret is returned exactly once, store it immediately. */
28844
- 201: {
30098
+ /** @description The webhook, if owned by the calling tenant. The signing secret is never returned on a read, only the prefix. */
30099
+ 200: {
28845
30100
  headers: {
28846
30101
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
28847
30102
  RateLimit: components["headers"]["RateLimit"];
@@ -28857,7 +30112,7 @@ interface operations {
28857
30112
  /** @description Webhook id (wh_-prefixed ULID). */
28858
30113
  id?: string;
28859
30114
  /**
28860
- * @description The event source this webhook subscribes to.
30115
+ * @description The event source this webhook subscribes to (immutable).
28861
30116
  * @enum {string}
28862
30117
  */
28863
30118
  source?: "messaging" | "user" | "account_status";
@@ -28865,9 +30120,9 @@ interface operations {
28865
30120
  request_url?: string;
28866
30121
  /** @description Human-readable label. */
28867
30122
  name?: string | null;
28868
- /** @description Accounts targeted by this webhook. Required and non-empty, each id must be owned by the tenant. */
30123
+ /** @description Accounts targeted by this webhook. Non-empty, each id is owned by the tenant. */
28869
30124
  account_ids?: string[];
28870
- /** @description A disabled webhook is created but delivers nothing. */
30125
+ /** @description A disabled webhook is kept but delivers nothing. Disabling takes effect immediately, including for retries that were already queued; those attempts are abandoned, not deferred, so re-enabling delivers new events rather than replaying the ones that were dropped. */
28871
30126
  enabled?: boolean;
28872
30127
  /** @description Custom headers added to each delivery POST. Values are encrypted at rest, so a read shows the name and a masked value only. */
28873
30128
  headers?: {
@@ -28880,22 +30135,15 @@ interface operations {
28880
30135
  events?: string[];
28881
30136
  /** @description Field-remapping keys for the delivery payload (messaging and user sources only). */
28882
30137
  data?: string[];
28883
- /**
28884
- * @description HMAC signing secret, 64 hexadecimal characters, returned exactly once at creation. Store it securely.
28885
- * @example a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
28886
- */
28887
- secret?: string;
28888
- /**
28889
- * @description The first 8 characters of the secret, for identification without exposing it. Exactly the value the read endpoints return for this webhook.
28890
- * @example a1b2c3d4
28891
- */
30138
+ /** @description First 8 characters of the signing secret. The full secret is never returned on edit, it is preserved unchanged. */
28892
30139
  secret_prefix?: string;
28893
30140
  /** @description ISO-8601 creation timestamp. */
28894
30141
  created_at?: string;
30142
+ safety_warning?: components["schemas"]["SafetyWarning"];
28895
30143
  };
28896
30144
  };
28897
30145
  };
28898
- /** @description Invalid request, malformed URL, empty events array, or event-source mismatch. */
30146
+ /** @description Malformed or empty webhook id. */
28899
30147
  400: {
28900
30148
  headers: {
28901
30149
  [name: string]: unknown;
@@ -28913,16 +30161,7 @@ interface operations {
28913
30161
  "application/json": components["schemas"]["Error"];
28914
30162
  };
28915
30163
  };
28916
- /** @description Payment required, an active subscription is needed. */
28917
- 402: {
28918
- headers: {
28919
- [name: string]: unknown;
28920
- };
28921
- content: {
28922
- "application/json": components["schemas"]["Error"];
28923
- };
28924
- };
28925
- /** @description One or more account_ids not found for this tenant. */
30164
+ /** @description Webhook not found or not owned by this tenant. */
28926
30165
  404: {
28927
30166
  headers: {
28928
30167
  [name: string]: unknown;
@@ -28931,15 +30170,6 @@ interface operations {
28931
30170
  "application/json": components["schemas"]["Error"];
28932
30171
  };
28933
30172
  };
28934
- /** @description Payload too large, the request body exceeds the size limit. */
28935
- 413: {
28936
- headers: {
28937
- [name: string]: unknown;
28938
- };
28939
- content: {
28940
- "application/json": components["schemas"]["Error"];
28941
- };
28942
- };
28943
30173
  /** @description Rate limited, slow down and retry after the hinted delay. */
28944
30174
  429: {
28945
30175
  headers: {
@@ -28954,16 +30184,19 @@ interface operations {
28954
30184
  };
28955
30185
  };
28956
30186
  };
28957
- getV1WebhooksEvents: {
30187
+ deleteV1WebhooksId: {
28958
30188
  parameters: {
28959
30189
  query?: never;
28960
30190
  header?: never;
28961
- path?: never;
30191
+ path: {
30192
+ /** @description Webhook id (wh_-prefixed ULID). */
30193
+ id: string;
30194
+ };
28962
30195
  cookie?: never;
28963
30196
  };
28964
30197
  requestBody?: never;
28965
30198
  responses: {
28966
- /** @description The complete canonical event catalogue, grouped by source. */
30199
+ /** @description Webhook removed. Delivery stops immediately, including retries that were already queued. Idempotent, deleting an already-deleted webhook returns the same shape. */
28967
30200
  200: {
28968
30201
  headers: {
28969
30202
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -28976,34 +30209,22 @@ interface operations {
28976
30209
  * @description Response type discriminator.
28977
30210
  * @enum {string}
28978
30211
  */
28979
- object?: "webhook_event_catalogue";
28980
- /** @description Event types grouped by source. */
28981
- sources?: {
28982
- /** @description Event source group (messaging, user, account_status, recruiter, sales_nav). */
28983
- source?: string;
28984
- events?: {
28985
- /** @description Canonical event name. */
28986
- type?: string;
28987
- /** @description Event source group. */
28988
- source?: string;
28989
- description?: string;
28990
- /** @description Present and true when the event carries LinkedIn-specific content. */
28991
- linkedin_only?: boolean;
28992
- /**
28993
- * @description Required add-on tier. Present only on tier-gated events.
28994
- * @enum {string}
28995
- */
28996
- tier?: "recruiter" | "sales_nav";
28997
- /**
28998
- * @description Delivery timing. Omitted (equivalent to 'realtime') on every live-pushed event. 'no_longer_realtime' means no push exists for this event; 'not_realtime' means delivery is not guaranteed sub-second and may lag the underlying LinkedIn event, typically because it is detected by a poll rather than pushed, though a given event's own reference article may document an additional immediate path.
28999
- * @enum {string}
29000
- */
29001
- availability?: "realtime" | "no_longer_realtime" | "not_realtime";
29002
- }[];
29003
- }[];
30212
+ object?: "webhook_deleted";
30213
+ /** @description The id of the deleted webhook. */
30214
+ id?: string;
30215
+ safety_warning?: components["schemas"]["SafetyWarning"];
29004
30216
  };
29005
30217
  };
29006
30218
  };
30219
+ /** @description Malformed or empty webhook id. */
30220
+ 400: {
30221
+ headers: {
30222
+ [name: string]: unknown;
30223
+ };
30224
+ content: {
30225
+ "application/json": components["schemas"]["Error"];
30226
+ };
30227
+ };
29007
30228
  /** @description Missing or invalid API key. */
29008
30229
  401: {
29009
30230
  headers: {
@@ -29013,6 +30234,15 @@ interface operations {
29013
30234
  "application/json": components["schemas"]["Error"];
29014
30235
  };
29015
30236
  };
30237
+ /** @description Webhook not found or not owned by this tenant. */
30238
+ 404: {
30239
+ headers: {
30240
+ [name: string]: unknown;
30241
+ };
30242
+ content: {
30243
+ "application/json": components["schemas"]["Error"];
30244
+ };
30245
+ };
29016
30246
  /** @description Rate limited, slow down and retry after the hinted delay. */
29017
30247
  429: {
29018
30248
  headers: {
@@ -29027,7 +30257,7 @@ interface operations {
29027
30257
  };
29028
30258
  };
29029
30259
  };
29030
- postV1WebhooksIdTest: {
30260
+ patchV1WebhooksId: {
29031
30261
  parameters: {
29032
30262
  query?: never;
29033
30263
  header?: never;
@@ -29037,10 +30267,32 @@ interface operations {
29037
30267
  };
29038
30268
  cookie?: never;
29039
30269
  };
29040
- requestBody?: never;
30270
+ requestBody: {
30271
+ content: {
30272
+ "application/json": {
30273
+ /** @description Replace the subscribed events (whole-array). Every event must be a canonical event whose source matches the webhook's source. */
30274
+ events?: string[];
30275
+ /** @description Replace the delivery URL. Must be a publicly reachable https host on the default https port. */
30276
+ request_url?: string;
30277
+ /** @description Replace the human-readable name (1-100 chars), or null to clear it. */
30278
+ name?: string | null;
30279
+ /** @description Enable or disable the webhook. A disabled webhook delivers nothing. Disabling is a stop, not a pause: it takes effect immediately, including for retries that were already queued, and those attempts are abandoned rather than deferred, so re-enabling delivers new events instead of replaying the ones that were dropped. */
30280
+ enabled?: boolean;
30281
+ /** @description Replace the custom headers added to each delivery POST (whole-array). Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured. */
30282
+ headers?: {
30283
+ key: string;
30284
+ value: string;
30285
+ }[];
30286
+ /** @description Replace the field-remapping keys for the delivery payload (whole-array). */
30287
+ data?: string[];
30288
+ /** @description Replace the targeted accounts (whole-array). Must be non-empty; each id must be an acc_-prefixed id owned by the tenant. */
30289
+ account_ids?: string[];
30290
+ };
30291
+ };
30292
+ };
29041
30293
  responses: {
29042
- /** @description The test delivery is queued. It travels the same path as a real delivery: the same signature, the same custom headers, the same delivery record, the same retry schedule. Wait for it to arrive at your endpoint, verify the signature, and match data.test_id to the test_id below. */
29043
- 202: {
30294
+ /** @description Webhook updated. The signing secret is preserved, only the prefix is shown, never the full secret. */
30295
+ 200: {
29044
30296
  headers: {
29045
30297
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
29046
30298
  RateLimit: components["headers"]["RateLimit"];
@@ -29052,22 +30304,42 @@ interface operations {
29052
30304
  * @description Response type discriminator.
29053
30305
  * @enum {string}
29054
30306
  */
29055
- object?: "webhook_test";
29056
- /** @description The webhook the test delivery was queued for. */
29057
- webhook_id?: string;
29058
- /** @description Correlation id for this test delivery, echoed as data.test_id in the delivered payload. Match it to confirm that the delivery your receiver observed is the one you asked for. */
29059
- test_id?: string;
30307
+ object?: "webhook";
30308
+ /** @description Webhook id (wh_-prefixed ULID). */
30309
+ id?: string;
29060
30310
  /**
29061
- * @description Always webhook.test. This name is not part of the event catalogue and can never be subscribed to, so it can never be confused with a real event.
30311
+ * @description The event source this webhook subscribes to (immutable).
29062
30312
  * @enum {string}
29063
30313
  */
29064
- event?: "webhook.test";
29065
- /** @description When the test delivery was queued (ISO 8601). */
29066
- queued_at?: string;
30314
+ source?: "messaging" | "user" | "account_status";
30315
+ /** @description The URL that receives delivery POST requests. */
30316
+ request_url?: string;
30317
+ /** @description Human-readable label. */
30318
+ name?: string | null;
30319
+ /** @description Accounts targeted by this webhook. Non-empty, each id is owned by the tenant. */
30320
+ account_ids?: string[];
30321
+ /** @description A disabled webhook is kept but delivers nothing. Disabling takes effect immediately, including for retries that were already queued; those attempts are abandoned, not deferred, so re-enabling delivers new events rather than replaying the ones that were dropped. */
30322
+ enabled?: boolean;
30323
+ /** @description Custom headers added to each delivery POST. Values are encrypted at rest, so a read shows the name and a masked value only. */
30324
+ headers?: {
30325
+ /** @description The header name, as configured. */
30326
+ key?: string;
30327
+ /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
30328
+ value_prefix?: string;
30329
+ }[];
30330
+ /** @description Canonical event names this webhook subscribes to. */
30331
+ events?: string[];
30332
+ /** @description Field-remapping keys for the delivery payload (messaging and user sources only). */
30333
+ data?: string[];
30334
+ /** @description First 8 characters of the signing secret. The full secret is never returned on edit, it is preserved unchanged. */
30335
+ secret_prefix?: string;
30336
+ /** @description ISO-8601 creation timestamp. */
30337
+ created_at?: string;
30338
+ safety_warning?: components["schemas"]["SafetyWarning"];
29067
30339
  };
29068
30340
  };
29069
30341
  };
29070
- /** @description Malformed webhook id, or the webhook is disabled or targets no account, in which case nothing could be delivered. */
30342
+ /** @description Invalid request, malformed URL, empty events array, event-source mismatch, a change to the immutable source, or a malformed id. */
29071
30343
  400: {
29072
30344
  headers: {
29073
30345
  [name: string]: unknown;
@@ -29085,7 +30357,16 @@ interface operations {
29085
30357
  "application/json": components["schemas"]["Error"];
29086
30358
  };
29087
30359
  };
29088
- /** @description Webhook not found or not owned by this tenant. */
30360
+ /** @description Payment required, an active subscription is needed. */
30361
+ 402: {
30362
+ headers: {
30363
+ [name: string]: unknown;
30364
+ };
30365
+ content: {
30366
+ "application/json": components["schemas"]["Error"];
30367
+ };
30368
+ };
30369
+ /** @description Webhook not found, or one or more account_ids not found for this tenant. */
29089
30370
  404: {
29090
30371
  headers: {
29091
30372
  [name: string]: unknown;
@@ -29094,7 +30375,16 @@ interface operations {
29094
30375
  "application/json": components["schemas"]["Error"];
29095
30376
  };
29096
30377
  };
29097
- /** @description Too many test deliveries, either for this webhook or across your tenant's webhooks. Wait for the window to reset; the Retry-After header gives the number of seconds. */
30378
+ /** @description Payload too large, the request body exceeds the size limit. */
30379
+ 413: {
30380
+ headers: {
30381
+ [name: string]: unknown;
30382
+ };
30383
+ content: {
30384
+ "application/json": components["schemas"]["Error"];
30385
+ };
30386
+ };
30387
+ /** @description Rate limited, slow down and retry after the hinted delay. */
29098
30388
  429: {
29099
30389
  headers: {
29100
30390
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -29108,19 +30398,19 @@ interface operations {
29108
30398
  };
29109
30399
  };
29110
30400
  };
29111
- getV1WebhooksId: {
30401
+ getV1AccountIdSafetyPolicy: {
29112
30402
  parameters: {
29113
30403
  query?: never;
29114
30404
  header?: never;
29115
30405
  path: {
29116
- /** @description Webhook id (wh_-prefixed ULID). */
29117
- id: string;
30406
+ /** @description The account ID (`acc_...`) whose safety policy to read. */
30407
+ account_id: string;
29118
30408
  };
29119
30409
  cookie?: never;
29120
30410
  };
29121
30411
  requestBody?: never;
29122
30412
  responses: {
29123
- /** @description The webhook, if owned by the calling tenant. The signing secret is never returned on a read, only the prefix. */
30413
+ /** @description The account's full safety policy: every configurable field for every budget row, in one response. Any row whose configured value is above the Curviate default carries an `over_default` entry naming the value, the default and the source class of that default. Two top-level fields are DERIVED rather than configured: `warm_up` says whether Curviate is currently holding this account below its configured ceilings and by how much, and `activity_window` says which timezone the window is read in and whether it is switched on at all. */
29124
30414
  200: {
29125
30415
  headers: {
29126
30416
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -29133,59 +30423,140 @@ interface operations {
29133
30423
  * @description Response type discriminator.
29134
30424
  * @enum {string}
29135
30425
  */
29136
- object?: "webhook";
29137
- /** @description Webhook id (wh_-prefixed ULID). */
29138
- id?: string;
30426
+ object?: "safety_policy";
30427
+ /** @description The account this policy governs. */
30428
+ account_id?: string;
29139
30429
  /**
29140
- * @description The event source this webhook subscribes to (immutable).
30430
+ * @description Which set of Curviate defaults every row below is resolved from. Each profile inherits the one before it and raises a few rows: `premium` adds a daily invitation band and open-profile InMail, `sales_navigator` raises profile views and search, `recruiter` raises profile views again. This is the platform product active on the account, not the Curviate seat entitlement. The two can disagree, and this is the one that governs the numbers. The elevated Sales Navigator and Recruiter figures apply only to calls made through the matching interface, the `/v1/{account_id}/sales-navigator/...` and `/v1/{account_id}/recruiter/...` operations. A standard-interface call still lives under the `basic` or `premium` figure for its row, and Curviate does not yet track that difference: on a non-`basic` profile the numbers here are the headroom of the elevated interface, not of every call, so read them as an upper bound rather than as your budget for a standard call.
29141
30431
  * @enum {string}
29142
30432
  */
29143
- source?: "messaging" | "user" | "account_status";
29144
- /** @description The URL that receives delivery POST requests. */
29145
- request_url?: string;
29146
- /** @description Human-readable label. */
29147
- name?: string | null;
29148
- /** @description Accounts targeted by this webhook. Non-empty, each id is owned by the tenant. */
29149
- account_ids?: string[];
29150
- /** @description A disabled webhook is kept but delivers nothing. Disabling takes effect immediately, including for retries that were already queued; those attempts are abandoned, not deferred, so re-enabling delivers new events rather than replaying the ones that were dropped. */
29151
- enabled?: boolean;
29152
- /** @description Custom headers added to each delivery POST. Values are encrypted at rest, so a read shows the name and a masked value only. */
29153
- headers?: {
29154
- /** @description The header name, as configured. */
29155
- key?: string;
29156
- /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
29157
- value_prefix?: string;
30433
+ limit_profile?: "basic" | "premium" | "sales_navigator" | "recruiter";
30434
+ /**
30435
+ * @description The Curviate seat tier attached to this account when it disagrees with `limit_profile`, and null when the two agree or the account holds no seat. A seat records what you bought from Curviate; `limit_profile` records what LinkedIn currently grants, and a lapsed subscription moves one and not the other. The detected product always wins the numbers, so this is a report, not a refusal: a `recruiter` seat here means you are paying for Recruiter on an account LinkedIn does not currently grant it to. `core` alongside `basic` or `premium` is agreement, not a mismatch: Premium is a LinkedIn product, not a Curviate tier. A free-trial seat never reports here, because a trial is not a purchase, and neither does a seat whose entitlement has not started yet or has already lapsed.
30436
+ * @enum {string|null}
30437
+ */
30438
+ seat_tier_mismatch?: "core" | "sn" | "recruiter" | null;
30439
+ /**
30440
+ * @description The account's default posture, before any per-row override.
30441
+ * @enum {string}
30442
+ */
30443
+ posture?: "warn" | "enforce";
30444
+ /**
30445
+ * @description Whether that default is the account's own, the tenant's, or the built-in `warn`.
30446
+ * @enum {string}
30447
+ */
30448
+ posture_source?: "account" | "tenant" | "default";
30449
+ /**
30450
+ * @description The workspace-wide posture default every account in the tenant inherits, or null when none is set. Always present, and separate from `posture` on purpose: when this account overrides the tenant default, a change to the tenant default does not move `posture`, and this field is how you can still see it took effect.
30451
+ * @enum {string|null}
30452
+ */
30453
+ tenant_default_posture?: "warn" | "enforce" | null;
30454
+ /** @description The account's ACTIVE warm-up ramp, derived on every read. While an account is warming up, every row's effective ceiling is its configured ceiling multiplied by `factor`, so a `factor` of 0.15 means an account is being held to 15 percent of the numbers the rows below report. This is what Curviate currently believes about the account, and it is separate from the per-row `warm_up_state` and `warm_up_factor` fields, which report what is active on that row: set `warm_up_factor` on a row to pin it and ignore the derivation, and read `warm_up_state` as the label for whichever factor won. Every warm-up number is inferred, and none of them is published by LinkedIn. */
30455
+ warm_up?: {
30456
+ /**
30457
+ * @description `none` is an account at full speed. `ramping` is a new, thinly connected or dormant account climbing 0.15 to 0.35 to 0.60 to 0.85 to 1.00 by week. `restriction_recovery` is an account under, or just out of, a LinkedIn restriction: it holds at 0 for seven days after the restriction lifts, resumes at 0.40, then steps up 0.15 every four days.
30458
+ * @enum {string}
30459
+ */
30460
+ state?: "none" | "ramping" | "restriction_recovery";
30461
+ /** @description The multiplier applied to every row's ceiling. 1 is not ramping. */
30462
+ factor?: number;
30463
+ /** @description Which week of the ramp this account is in, counted from whichever is later of its creation and its last return from a dormancy. Null when the post-restriction schedule applies instead, which is not the weekly table. */
30464
+ week?: number | null;
30465
+ /** @description The conditions currently true of this account: `account_age` under 180 days, `connections` under 150, `dormant` for over 30 days, `post_restriction` under or recovering from a LinkedIn restriction. `dormant` stays reported until the ramp it started completes, because that is what explains the factor. */
30466
+ triggers?: ("account_age" | "connections" | "dormant" | "post_restriction")[];
30467
+ /** @description The conditions Curviate could not check because nothing supplies their input. Reported rather than assumed false: `connections` is here on every account today, because the connected-account record LinkedIn returns carries no connection count. An unevaluated trigger is not a trigger that was checked and found false. */
30468
+ unevaluated?: ("account_age" | "connections" | "dormant" | "post_restriction")[];
30469
+ };
30470
+ /** @description The account-level half of the activity window. Outside the window, a live write to LinkedIn is reported under `warn` posture and refused under `enforce`; reads and anything served from Curviate's own store are never affected, at any hour. */
30471
+ activity_window?: {
30472
+ /** @description The IANA zone this account's activity window is read in, or null when none is set. A row may override it with its own `activity_window_timezone`. */
30473
+ timezone?: string | null;
30474
+ /** @description True when no zone is set, which DISABLES the activity window for every row that has not set its own. Curviate never guesses a zone: a guess eight hours wrong inverts the window, refusing every daytime write and permitting every nocturnal one, which is worse than having no window at all. If you configured a window and it never fires, this is why. */
30475
+ timezone_unset?: boolean;
30476
+ };
30477
+ /** @description Every budget row, always. */
30478
+ rows?: {
30479
+ /** @description The counted-action row this policy governs. */
30480
+ budget_row?: string;
30481
+ /** @description What `enforce` refuses at and `warn` reports at, in the row's own unit. Null means the row is uncalibrated: Curviate has no figure for it. */
30482
+ ceiling?: number | null;
30483
+ /** @description Routine band. */
30484
+ green?: number | null;
30485
+ /** @description Warn-louder band. */
30486
+ amber?: number | null;
30487
+ /**
30488
+ * @description The calendar grain this row is counted in.
30489
+ * @enum {string}
30490
+ */
30491
+ window_kind?: "day" | "week" | "month";
30492
+ /** @description How many grain units one counting window spans. */
30493
+ window_span?: number;
30494
+ /** @description IANA zone name the counting window is aligned to. Null counts in UTC. */
30495
+ window_timezone?: string | null;
30496
+ /**
30497
+ * @description The posture in force for this row, after inheritance.
30498
+ * @enum {string}
30499
+ */
30500
+ posture?: "warn" | "enforce";
30501
+ /**
30502
+ * @description Where that posture came from, so an override is visible as one.
30503
+ * @enum {string}
30504
+ */
30505
+ posture_source?: "row" | "account" | "tenant" | "default";
30506
+ /** @description Local HH:MM the activity window opens. */
30507
+ activity_window_start?: string;
30508
+ /** @description Local HH:MM the activity window closes. */
30509
+ activity_window_end?: string;
30510
+ /** @description IANA zone name the activity window is read in. Null disables the window entirely. */
30511
+ activity_window_timezone?: string | null;
30512
+ /**
30513
+ * @description What the activity window governs. `writes` (the default) covers live writes to LinkedIn; `all` widens it to live reads as well, which is the setting for a persona that should not be seen browsing at 03:00. It is broader than it sounds: `all` covers every live read, including the ones about the account itself, so a nightly analytics or invitation-backlog poll is refused at 03:00 too. Stored reads and inbound events are never subject to the window under either setting, at any hour.
30514
+ * @enum {string}
30515
+ */
30516
+ activity_window_applies_to?: "writes" | "all";
30517
+ /**
30518
+ * @description The warm-up state ACTIVE on this row: the label for `warm_up_factor` beside it, so the two always agree. Read-only, so a value sent on a write is accepted and ignored; set `warm_up_factor` to pin this row's ramp.
30519
+ * @enum {string}
30520
+ */
30521
+ warm_up_state?: "none" | "ramping" | "restriction_recovery";
30522
+ /** @description The multiplier ACTIVE on this row, which is what `effective_ceiling` above was computed with. 1 is not ramping. Set it to pin this row and ignore the account's derived ramp; leave it unset to follow the derivation. */
30523
+ warm_up_factor?: number;
30524
+ /** @description Length cap on the row's free-text field, in characters. */
30525
+ character_cap?: number | null;
30526
+ /** @description Length cap on the row's subject line, in characters. */
30527
+ subject_character_cap?: number | null;
30528
+ /** @description Bound on a single query's result lines. Search rows only. */
30529
+ per_query_cap?: number | null;
30530
+ /**
30531
+ * @description How well calibrated this row's number is. `unseeded` means Curviate has no green, amber or ceiling figure for this row on this profile: the row is reported so nothing is silently missing, but nothing has calibrated it, so no value you set can be `over_default`.
30532
+ * @enum {string}
30533
+ */
30534
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
30535
+ /** @description What this row is ACTUALLY held to right now: `ceiling` with the account's warm-up ramp applied. Read this one, not `ceiling`, to know where the next call will be stopped. On an account in warm-up the two differ, and on a five-day-old account they differ by a factor of about seven. Null when the row has no ceiling to ramp. `warm_up_state` and `warm_up_factor` beside it are the values ACTIVE on this row, not the seeded ones. */
30536
+ effective_ceiling?: number | null;
30537
+ /** @description Empty when nothing on this row is above default. */
30538
+ over_default?: {
30539
+ /**
30540
+ * @description The field whose configured value is above default.
30541
+ * @enum {string}
30542
+ */
30543
+ field?: "ceiling" | "green" | "amber" | "character_cap" | "subject_character_cap" | "per_query_cap";
30544
+ /** @description The configured value. */
30545
+ value?: number;
30546
+ /** @description The Curviate default it exceeds. */
30547
+ default?: number;
30548
+ /**
30549
+ * @description How well calibrated the DEFAULT is. `substrate` and `linkedin_official` are published figures; `practitioner` is corroborated operator experience; `inferred` is our own reasoning; `unseeded` means we have no figure at all for that row, in which case there is nothing to be above and no entry appears. Read this before deciding whether being above default matters.
30550
+ * @enum {string}
30551
+ */
30552
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
30553
+ }[];
29158
30554
  }[];
29159
- /** @description Canonical event names this webhook subscribes to. */
29160
- events?: string[];
29161
- /** @description Field-remapping keys for the delivery payload (messaging and user sources only). */
29162
- data?: string[];
29163
- /** @description First 8 characters of the signing secret. The full secret is never returned on edit, it is preserved unchanged. */
29164
- secret_prefix?: string;
29165
- /** @description ISO-8601 creation timestamp. */
29166
- created_at?: string;
30555
+ safety_warning?: components["schemas"]["SafetyWarning"];
29167
30556
  };
29168
30557
  };
29169
30558
  };
29170
- /** @description Malformed or empty webhook id. */
29171
- 400: {
29172
- headers: {
29173
- [name: string]: unknown;
29174
- };
29175
- content: {
29176
- "application/json": components["schemas"]["Error"];
29177
- };
29178
- };
29179
- /** @description Missing or invalid API key. */
29180
- 401: {
29181
- headers: {
29182
- [name: string]: unknown;
29183
- };
29184
- content: {
29185
- "application/json": components["schemas"]["Error"];
29186
- };
29187
- };
29188
- /** @description Webhook not found or not owned by this tenant. */
30559
+ /** @description No such account for this tenant. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29189
30560
  404: {
29190
30561
  headers: {
29191
30562
  [name: string]: unknown;
@@ -29208,19 +30579,114 @@ interface operations {
29208
30579
  };
29209
30580
  };
29210
30581
  };
29211
- deleteV1WebhooksId: {
30582
+ patchV1AccountIdSafetyPolicy: {
29212
30583
  parameters: {
29213
30584
  query?: never;
29214
30585
  header?: never;
29215
30586
  path: {
29216
- /** @description Webhook id (wh_-prefixed ULID). */
29217
- id: string;
30587
+ /** @description The account ID (`acc_...`) whose safety policy to update. */
30588
+ account_id: string;
29218
30589
  };
29219
30590
  cookie?: never;
29220
30591
  };
29221
- requestBody?: never;
30592
+ requestBody: {
30593
+ content: {
30594
+ "application/json": {
30595
+ /**
30596
+ * @description Which set of Curviate defaults this account's rows resolve from: `basic`, `premium`, `sales_navigator` or `recruiter`. Each inherits the one before it and raises a few rows. This is the platform product active on the account, NOT the Curviate seat entitlement, and the two can disagree: a lapsed subscription leaves the seat untouched. Automatic detection at connect is not built yet, so setting this is currently the only way an account leaves `basic`; set it back to `basic` to undo. The change is recorded in the ledger and it moves the defaults under every row at once, so any row you have configured explicitly keeps your value. The elevated Sales Navigator and Recruiter figures apply only to calls made through the matching interface, the `/v1/{account_id}/sales-navigator/...` and `/v1/{account_id}/recruiter/...` operations. A standard-interface call still lives under the `basic` or `premium` figure for its row, and Curviate does not yet track that difference: on a non-`basic` profile the numbers here are the headroom of the elevated interface, not of every call, so read them as an upper bound rather than as your budget for a standard call.
30597
+ * @enum {string}
30598
+ */
30599
+ limit_profile?: "basic" | "premium" | "sales_navigator" | "recruiter";
30600
+ /** @description The IANA zone this account's ACTIVITY window is read in, e.g. `Europe/Berlin`. Must be a real zone; anything else is a 400. Null clears it, which DISABLES the activity window for every row that has not set its own: a guessed zone inverts the window, which is worse than not having one. */
30601
+ timezone?: string | null;
30602
+ /**
30603
+ * @description The tenant-wide posture default, which every account inherits unless it overrides it. Set it once for a whole workspace of personas. Null clears it back to `warn`.
30604
+ * @enum {string|null}
30605
+ */
30606
+ tenant_default_posture?: "warn" | "enforce" | null;
30607
+ /**
30608
+ * @description This account's posture default, overriding the tenant default for this account only. Null clears the override.
30609
+ * @enum {string|null}
30610
+ */
30611
+ posture?: "warn" | "enforce" | null;
30612
+ /** @description Per-row configuration. Each entry names its `budget_row` and the fields to change. A row read back from the policy document may be sent here unchanged: its read-only fields (`effective_ceiling`, `posture_source`, `over_default`, `warm_up_state`) are accepted and ignored. */
30613
+ rows?: {
30614
+ /**
30615
+ * @description The budget row this patch configures.
30616
+ * @enum {string}
30617
+ */
30618
+ budget_row: "connection_requests_with_note" | "connection_requests_no_note" | "messages_first_degree" | "inmail" | "open_profile_inmail" | "profile_views" | "search" | "comments" | "post_likes" | "follows" | "content_poll" | "job_poll" | "posts" | "endorsements" | "invite_responses" | "withdrawal_sweep" | "total_actions" | "pending_invites";
30619
+ /** @description What `enforce` refuses at and `warn` reports at, in this row's own unit. Any value up to 9007199254740991 is accepted and stored verbatim; a value above the Curviate default is reported back, never clamped. */
30620
+ ceiling?: number | null;
30621
+ /** @description Routine band. Activity at or below it is unremarkable. */
30622
+ green?: number | null;
30623
+ /** @description Warn-louder band, between green and the ceiling. */
30624
+ amber?: number | null;
30625
+ /**
30626
+ * @description The calendar grain this row is counted in.
30627
+ * @enum {string|null}
30628
+ */
30629
+ window_kind?: "day" | "week" | "month" | null;
30630
+ /** @description How many `window_kind` units one counting window spans. */
30631
+ window_span?: number | null;
30632
+ /** @description IANA zone name the counting window is aligned to, e.g. `Europe/Berlin` or `UTC`. Must be a real zone; anything else is a 400. Null counts in UTC. */
30633
+ window_timezone?: string | null;
30634
+ /**
30635
+ * @description Posture for this row alone, overriding the account default. Null inherits the account default, which inherits the tenant default.
30636
+ * @enum {string|null}
30637
+ */
30638
+ posture?: "warn" | "enforce" | null;
30639
+ /** @description Local time the activity window opens, HH:MM. Outside the window, live write operations are reported or refused per posture. */
30640
+ activity_window_start?: string | null;
30641
+ /** @description Local time the activity window closes, HH:MM. */
30642
+ activity_window_end?: string | null;
30643
+ /** @description IANA zone name the activity window is read in, e.g. `Europe/Berlin`. Must be a real zone; anything else is a 400. Null DISABLES the activity window: a guessed zone inverts it, which is worse than not having one. */
30644
+ activity_window_timezone?: string | null;
30645
+ /**
30646
+ * @description What the activity window governs. `writes` (the default) covers live writes to LinkedIn; `all` widens it to live reads that Curviate cannot serve from its store; a read it may serve from the store (a profile by key, for one) stays exempt. Stored reads and inbound events are never subject to the window under either setting, at any hour.
30647
+ * @enum {string|null}
30648
+ */
30649
+ activity_window_applies_to?: "writes" | "all" | null;
30650
+ /**
30651
+ * @description READ-ONLY. The warm-up ramp state active on this row: the label for `warm_up_factor` beside it, derived from it, so the two always agree. A value sent here is still checked for being a state at all, and is then accepted and ignored: it is never stored and never recorded in the ledger. Set `warm_up_factor` to pin a row out of the ramp Curviate derives for the account.
30652
+ * @enum {string|null}
30653
+ */
30654
+ warm_up_state?: "none" | "ramping" | "restriction_recovery" | null;
30655
+ /** @description Multiplier applied to this row's ceiling while ramping. 1 is not ramping. */
30656
+ warm_up_factor?: number | null;
30657
+ /** @description Length cap on this row's free-text field, in characters (an invitation note, an InMail body). */
30658
+ character_cap?: number | null;
30659
+ /** @description Length cap on this row's subject line, in characters. */
30660
+ subject_character_cap?: number | null;
30661
+ /** @description Bound on a single query's result lines. Search rows only. */
30662
+ per_query_cap?: number | null;
30663
+ /**
30664
+ * @description How well calibrated this row's configured number is. Overriding it labels your own figure; the over-default warning always reports the class of the Curviate default.
30665
+ * @enum {string|null}
30666
+ */
30667
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded" | null;
30668
+ /** @description READ-ONLY. `ceiling` with the account's warm-up ramp applied, which is what this row is actually held to. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30669
+ effective_ceiling?: unknown;
30670
+ /** @description READ-ONLY. Which scope supplied this row's effective `posture`. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30671
+ posture_source?: unknown;
30672
+ /** @description READ-ONLY. The fields on this row configured above the Curviate default. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30673
+ over_default?: unknown;
30674
+ }[];
30675
+ /** @description READ-ONLY. The document's type discriminator. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30676
+ object?: unknown;
30677
+ /** @description READ-ONLY. The account this document describes; the path names it. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30678
+ account_id?: unknown;
30679
+ /** @description READ-ONLY. The Curviate seat tier that disagrees with `limit_profile`. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30680
+ seat_tier_mismatch?: unknown;
30681
+ /** @description READ-ONLY. Which scope supplied this account's effective `posture`. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30682
+ posture_source?: unknown;
30683
+ /** @description READ-ONLY. The account's DERIVED warm-up ramp, recomputed on every read. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
30684
+ warm_up?: unknown;
30685
+ };
30686
+ };
30687
+ };
29222
30688
  responses: {
29223
- /** @description Webhook removed. Delivery stops immediately, including retries that were already queued. Idempotent, deleting an already-deleted webhook returns the same shape. */
30689
+ /** @description The account's full safety policy: every configurable field for every budget row, in one response. Any row whose configured value is above the Curviate default carries an `over_default` entry naming the value, the default and the source class of that default. Two top-level fields are DERIVED rather than configured: `warm_up` says whether Curviate is currently holding this account below its configured ceilings and by how much, and `activity_window` says which timezone the window is read in and whether it is switched on at all. This is the same body the read returns, so a value set above default is reported on the write that set it. */
29224
30690
  200: {
29225
30691
  headers: {
29226
30692
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -29233,13 +30699,140 @@ interface operations {
29233
30699
  * @description Response type discriminator.
29234
30700
  * @enum {string}
29235
30701
  */
29236
- object?: "webhook_deleted";
29237
- /** @description The id of the deleted webhook. */
29238
- id?: string;
30702
+ object?: "safety_policy";
30703
+ /** @description The account this policy governs. */
30704
+ account_id?: string;
30705
+ /**
30706
+ * @description Which set of Curviate defaults every row below is resolved from. Each profile inherits the one before it and raises a few rows: `premium` adds a daily invitation band and open-profile InMail, `sales_navigator` raises profile views and search, `recruiter` raises profile views again. This is the platform product active on the account, not the Curviate seat entitlement. The two can disagree, and this is the one that governs the numbers. The elevated Sales Navigator and Recruiter figures apply only to calls made through the matching interface, the `/v1/{account_id}/sales-navigator/...` and `/v1/{account_id}/recruiter/...` operations. A standard-interface call still lives under the `basic` or `premium` figure for its row, and Curviate does not yet track that difference: on a non-`basic` profile the numbers here are the headroom of the elevated interface, not of every call, so read them as an upper bound rather than as your budget for a standard call.
30707
+ * @enum {string}
30708
+ */
30709
+ limit_profile?: "basic" | "premium" | "sales_navigator" | "recruiter";
30710
+ /**
30711
+ * @description The Curviate seat tier attached to this account when it disagrees with `limit_profile`, and null when the two agree or the account holds no seat. A seat records what you bought from Curviate; `limit_profile` records what LinkedIn currently grants, and a lapsed subscription moves one and not the other. The detected product always wins the numbers, so this is a report, not a refusal: a `recruiter` seat here means you are paying for Recruiter on an account LinkedIn does not currently grant it to. `core` alongside `basic` or `premium` is agreement, not a mismatch: Premium is a LinkedIn product, not a Curviate tier. A free-trial seat never reports here, because a trial is not a purchase, and neither does a seat whose entitlement has not started yet or has already lapsed.
30712
+ * @enum {string|null}
30713
+ */
30714
+ seat_tier_mismatch?: "core" | "sn" | "recruiter" | null;
30715
+ /**
30716
+ * @description The account's default posture, before any per-row override.
30717
+ * @enum {string}
30718
+ */
30719
+ posture?: "warn" | "enforce";
30720
+ /**
30721
+ * @description Whether that default is the account's own, the tenant's, or the built-in `warn`.
30722
+ * @enum {string}
30723
+ */
30724
+ posture_source?: "account" | "tenant" | "default";
30725
+ /**
30726
+ * @description The workspace-wide posture default every account in the tenant inherits, or null when none is set. Always present, and separate from `posture` on purpose: when this account overrides the tenant default, a change to the tenant default does not move `posture`, and this field is how you can still see it took effect.
30727
+ * @enum {string|null}
30728
+ */
30729
+ tenant_default_posture?: "warn" | "enforce" | null;
30730
+ /** @description The account's ACTIVE warm-up ramp, derived on every read. While an account is warming up, every row's effective ceiling is its configured ceiling multiplied by `factor`, so a `factor` of 0.15 means an account is being held to 15 percent of the numbers the rows below report. This is what Curviate currently believes about the account, and it is separate from the per-row `warm_up_state` and `warm_up_factor` fields, which report what is active on that row: set `warm_up_factor` on a row to pin it and ignore the derivation, and read `warm_up_state` as the label for whichever factor won. Every warm-up number is inferred, and none of them is published by LinkedIn. */
30731
+ warm_up?: {
30732
+ /**
30733
+ * @description `none` is an account at full speed. `ramping` is a new, thinly connected or dormant account climbing 0.15 to 0.35 to 0.60 to 0.85 to 1.00 by week. `restriction_recovery` is an account under, or just out of, a LinkedIn restriction: it holds at 0 for seven days after the restriction lifts, resumes at 0.40, then steps up 0.15 every four days.
30734
+ * @enum {string}
30735
+ */
30736
+ state?: "none" | "ramping" | "restriction_recovery";
30737
+ /** @description The multiplier applied to every row's ceiling. 1 is not ramping. */
30738
+ factor?: number;
30739
+ /** @description Which week of the ramp this account is in, counted from whichever is later of its creation and its last return from a dormancy. Null when the post-restriction schedule applies instead, which is not the weekly table. */
30740
+ week?: number | null;
30741
+ /** @description The conditions currently true of this account: `account_age` under 180 days, `connections` under 150, `dormant` for over 30 days, `post_restriction` under or recovering from a LinkedIn restriction. `dormant` stays reported until the ramp it started completes, because that is what explains the factor. */
30742
+ triggers?: ("account_age" | "connections" | "dormant" | "post_restriction")[];
30743
+ /** @description The conditions Curviate could not check because nothing supplies their input. Reported rather than assumed false: `connections` is here on every account today, because the connected-account record LinkedIn returns carries no connection count. An unevaluated trigger is not a trigger that was checked and found false. */
30744
+ unevaluated?: ("account_age" | "connections" | "dormant" | "post_restriction")[];
30745
+ };
30746
+ /** @description The account-level half of the activity window. Outside the window, a live write to LinkedIn is reported under `warn` posture and refused under `enforce`; reads and anything served from Curviate's own store are never affected, at any hour. */
30747
+ activity_window?: {
30748
+ /** @description The IANA zone this account's activity window is read in, or null when none is set. A row may override it with its own `activity_window_timezone`. */
30749
+ timezone?: string | null;
30750
+ /** @description True when no zone is set, which DISABLES the activity window for every row that has not set its own. Curviate never guesses a zone: a guess eight hours wrong inverts the window, refusing every daytime write and permitting every nocturnal one, which is worse than having no window at all. If you configured a window and it never fires, this is why. */
30751
+ timezone_unset?: boolean;
30752
+ };
30753
+ /** @description Every budget row, always. */
30754
+ rows?: {
30755
+ /** @description The counted-action row this policy governs. */
30756
+ budget_row?: string;
30757
+ /** @description What `enforce` refuses at and `warn` reports at, in the row's own unit. Null means the row is uncalibrated: Curviate has no figure for it. */
30758
+ ceiling?: number | null;
30759
+ /** @description Routine band. */
30760
+ green?: number | null;
30761
+ /** @description Warn-louder band. */
30762
+ amber?: number | null;
30763
+ /**
30764
+ * @description The calendar grain this row is counted in.
30765
+ * @enum {string}
30766
+ */
30767
+ window_kind?: "day" | "week" | "month";
30768
+ /** @description How many grain units one counting window spans. */
30769
+ window_span?: number;
30770
+ /** @description IANA zone name the counting window is aligned to. Null counts in UTC. */
30771
+ window_timezone?: string | null;
30772
+ /**
30773
+ * @description The posture in force for this row, after inheritance.
30774
+ * @enum {string}
30775
+ */
30776
+ posture?: "warn" | "enforce";
30777
+ /**
30778
+ * @description Where that posture came from, so an override is visible as one.
30779
+ * @enum {string}
30780
+ */
30781
+ posture_source?: "row" | "account" | "tenant" | "default";
30782
+ /** @description Local HH:MM the activity window opens. */
30783
+ activity_window_start?: string;
30784
+ /** @description Local HH:MM the activity window closes. */
30785
+ activity_window_end?: string;
30786
+ /** @description IANA zone name the activity window is read in. Null disables the window entirely. */
30787
+ activity_window_timezone?: string | null;
30788
+ /**
30789
+ * @description What the activity window governs. `writes` (the default) covers live writes to LinkedIn; `all` widens it to live reads as well, which is the setting for a persona that should not be seen browsing at 03:00. It is broader than it sounds: `all` covers every live read, including the ones about the account itself, so a nightly analytics or invitation-backlog poll is refused at 03:00 too. Stored reads and inbound events are never subject to the window under either setting, at any hour.
30790
+ * @enum {string}
30791
+ */
30792
+ activity_window_applies_to?: "writes" | "all";
30793
+ /**
30794
+ * @description The warm-up state ACTIVE on this row: the label for `warm_up_factor` beside it, so the two always agree. Read-only, so a value sent on a write is accepted and ignored; set `warm_up_factor` to pin this row's ramp.
30795
+ * @enum {string}
30796
+ */
30797
+ warm_up_state?: "none" | "ramping" | "restriction_recovery";
30798
+ /** @description The multiplier ACTIVE on this row, which is what `effective_ceiling` above was computed with. 1 is not ramping. Set it to pin this row and ignore the account's derived ramp; leave it unset to follow the derivation. */
30799
+ warm_up_factor?: number;
30800
+ /** @description Length cap on the row's free-text field, in characters. */
30801
+ character_cap?: number | null;
30802
+ /** @description Length cap on the row's subject line, in characters. */
30803
+ subject_character_cap?: number | null;
30804
+ /** @description Bound on a single query's result lines. Search rows only. */
30805
+ per_query_cap?: number | null;
30806
+ /**
30807
+ * @description How well calibrated this row's number is. `unseeded` means Curviate has no green, amber or ceiling figure for this row on this profile: the row is reported so nothing is silently missing, but nothing has calibrated it, so no value you set can be `over_default`.
30808
+ * @enum {string}
30809
+ */
30810
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
30811
+ /** @description What this row is ACTUALLY held to right now: `ceiling` with the account's warm-up ramp applied. Read this one, not `ceiling`, to know where the next call will be stopped. On an account in warm-up the two differ, and on a five-day-old account they differ by a factor of about seven. Null when the row has no ceiling to ramp. `warm_up_state` and `warm_up_factor` beside it are the values ACTIVE on this row, not the seeded ones. */
30812
+ effective_ceiling?: number | null;
30813
+ /** @description Empty when nothing on this row is above default. */
30814
+ over_default?: {
30815
+ /**
30816
+ * @description The field whose configured value is above default.
30817
+ * @enum {string}
30818
+ */
30819
+ field?: "ceiling" | "green" | "amber" | "character_cap" | "subject_character_cap" | "per_query_cap";
30820
+ /** @description The configured value. */
30821
+ value?: number;
30822
+ /** @description The Curviate default it exceeds. */
30823
+ default?: number;
30824
+ /**
30825
+ * @description How well calibrated the DEFAULT is. `substrate` and `linkedin_official` are published figures; `practitioner` is corroborated operator experience; `inferred` is our own reasoning; `unseeded` means we have no figure at all for that row, in which case there is nothing to be above and no entry appears. Read this before deciding whether being above default matters.
30826
+ * @enum {string}
30827
+ */
30828
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
30829
+ }[];
30830
+ }[];
30831
+ safety_warning?: components["schemas"]["SafetyWarning"];
29239
30832
  };
29240
30833
  };
29241
30834
  };
29242
- /** @description Malformed or empty webhook id. */
30835
+ /** @description INVALID_REQUEST. The row name, posture or source class is not one of the accepted values, or a number is not a number (a negative ceiling). A value is never rejected for being unsafe: any limit may be set to any value, and a high one is reported rather than refused. */
29243
30836
  400: {
29244
30837
  headers: {
29245
30838
  [name: string]: unknown;
@@ -29248,8 +30841,8 @@ interface operations {
29248
30841
  "application/json": components["schemas"]["Error"];
29249
30842
  };
29250
30843
  };
29251
- /** @description Missing or invalid API key. */
29252
- 401: {
30844
+ /** @description No such account for this tenant. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
30845
+ 404: {
29253
30846
  headers: {
29254
30847
  [name: string]: unknown;
29255
30848
  };
@@ -29257,8 +30850,8 @@ interface operations {
29257
30850
  "application/json": components["schemas"]["Error"];
29258
30851
  };
29259
30852
  };
29260
- /** @description Webhook not found or not owned by this tenant. */
29261
- 404: {
30853
+ /** @description Payload too large, the request body exceeds the size limit. */
30854
+ 413: {
29262
30855
  headers: {
29263
30856
  [name: string]: unknown;
29264
30857
  };
@@ -29280,41 +30873,30 @@ interface operations {
29280
30873
  };
29281
30874
  };
29282
30875
  };
29283
- patchV1WebhooksId: {
30876
+ getV1AccountIdSafetyEvents: {
29284
30877
  parameters: {
29285
- query?: never;
30878
+ query?: {
30879
+ /** @description Pagination cursor from a previous response; decodable, pass it back verbatim. */
30880
+ cursor?: string;
30881
+ /** @description Return only events flagged against this budget row. Omit for every row. */
30882
+ budget_row?: "connection_requests_with_note" | "connection_requests_no_note" | "messages_first_degree" | "inmail" | "open_profile_inmail" | "profile_views" | "search" | "comments" | "post_likes" | "follows" | "content_poll" | "job_poll" | "posts" | "endorsements" | "invite_responses" | "withdrawal_sweep" | "total_actions" | "pending_invites";
30883
+ /** @description Only events at or after this instant (ISO-8601, e.g. 2026-09-01T00:00:00Z). */
30884
+ since?: string;
30885
+ /** @description Only events strictly before this instant (ISO-8601). */
30886
+ until?: string;
30887
+ /** @description How many events to return per page, 1 to 100. Defaults to 50. */
30888
+ limit?: number;
30889
+ };
29286
30890
  header?: never;
29287
30891
  path: {
29288
- /** @description Webhook id (wh_-prefixed ULID). */
29289
- id: string;
30892
+ /** @description The account ID (`acc_...`) whose safety events to read. */
30893
+ account_id: string;
29290
30894
  };
29291
30895
  cookie?: never;
29292
30896
  };
29293
- requestBody: {
29294
- content: {
29295
- "application/json": {
29296
- /** @description Replace the subscribed events (whole-array). Every event must be a canonical event whose source matches the webhook's source. */
29297
- events?: string[];
29298
- /** @description Replace the delivery URL. Must be a publicly reachable https host on the default https port. */
29299
- request_url?: string;
29300
- /** @description Replace the human-readable name (1-100 chars), or null to clear it. */
29301
- name?: string | null;
29302
- /** @description Enable or disable the webhook. A disabled webhook delivers nothing. Disabling is a stop, not a pause: it takes effect immediately, including for retries that were already queued, and those attempts are abandoned rather than deferred, so re-enabling delivers new events instead of replaying the ones that were dropped. */
29303
- enabled?: boolean;
29304
- /** @description Replace the custom headers added to each delivery POST (whole-array). Names must be valid HTTP header names and values must be printable ASCII (no line breaks or control characters). Reserved names are rejected at registration; they include Content-Type, User-Agent, Host, any Curviate-* or Sec-* name, and the hop-by-hop headers. At most 10 headers, 1024 characters per value, 4096 bytes in total. Custom headers are not covered by the delivery signature, so treat them as a hint, not as proof of origin. Values are encrypted at rest and are never returned by a read. A read shows the header name plus a masked value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter, so a short value is never disclosed by its own preview. The header name is what identifies which credential is configured. */
29305
- headers?: {
29306
- key: string;
29307
- value: string;
29308
- }[];
29309
- /** @description Replace the field-remapping keys for the delivery payload (whole-array). */
29310
- data?: string[];
29311
- /** @description Replace the targeted accounts (whole-array). Must be non-empty; each id must be an acc_-prefixed id owned by the tenant. */
29312
- account_ids?: string[];
29313
- };
29314
- };
29315
- };
30897
+ requestBody?: never;
29316
30898
  responses: {
29317
- /** @description Webhook updated. The signing secret is preserved, only the prefix is shown, never the full secret. */
30899
+ /** @description What Curviate refused or warned about on this account, newest first. Filter by `budget_row` to ask about one action type, and by `since` / `until` to ask about a period. */
29318
30900
  200: {
29319
30901
  headers: {
29320
30902
  "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
@@ -29327,41 +30909,43 @@ interface operations {
29327
30909
  * @description Response type discriminator.
29328
30910
  * @enum {string}
29329
30911
  */
29330
- object?: "webhook";
29331
- /** @description Webhook id (wh_-prefixed ULID). */
29332
- id?: string;
29333
- /**
29334
- * @description The event source this webhook subscribes to (immutable).
29335
- * @enum {string}
29336
- */
29337
- source?: "messaging" | "user" | "account_status";
29338
- /** @description The URL that receives delivery POST requests. */
29339
- request_url?: string;
29340
- /** @description Human-readable label. */
29341
- name?: string | null;
29342
- /** @description Accounts targeted by this webhook. Non-empty, each id is owned by the tenant. */
29343
- account_ids?: string[];
29344
- /** @description A disabled webhook is kept but delivers nothing. Disabling takes effect immediately, including for retries that were already queued; those attempts are abandoned, not deferred, so re-enabling delivers new events rather than replaying the ones that were dropped. */
29345
- enabled?: boolean;
29346
- /** @description Custom headers added to each delivery POST. Values are encrypted at rest, so a read shows the name and a masked value only. */
29347
- headers?: {
29348
- /** @description The header name, as configured. */
29349
- key?: string;
29350
- /** @description A masked view of the configured value: the first 8 characters followed by "..." when the value is at least 24 characters long, and a fixed "********" for anything shorter. The full value is never returned; the header name is what identifies which credential is configured. */
29351
- value_prefix?: string;
30912
+ object: "safety_event_list";
30913
+ /** @description Flagged events, newest first. [] when this account has never breached a limit. */
30914
+ items: {
30915
+ /**
30916
+ * @description Item type discriminator.
30917
+ * @enum {string}
30918
+ */
30919
+ object: "safety_event";
30920
+ /** @description The event's id. Also the value encoded in the next page's cursor. */
30921
+ id: string;
30922
+ /**
30923
+ * @description The budget row that breached. It is not always a row the call itself spends: an invitation refused because the outstanding backlog is at its ceiling reports `pending_invites`, which is the thing that breached.
30924
+ * @enum {string}
30925
+ */
30926
+ budget_row: "connection_requests_with_note" | "connection_requests_no_note" | "messages_first_degree" | "inmail" | "open_profile_inmail" | "profile_views" | "search" | "comments" | "post_likes" | "follows" | "content_poll" | "job_poll" | "posts" | "endorsements" | "invite_responses" | "withdrawal_sweep" | "total_actions" | "pending_invites";
30927
+ /**
30928
+ * @description Why it was flagged. `budget_exhausted`: the account reached the ceiling configured for that row, which you can raise on the safety policy. `rate_limited`: the platform itself refused this account on that row recently and Curviate is holding the row until the pause expires, which no configuration change lifts.
30929
+ * @enum {string}
30930
+ */
30931
+ reason: "budget_exhausted" | "rate_limited";
30932
+ /** @description Whether the action was stopped. `false` is a warning: the action went through, and this record is the only lasting trace that it crossed a limit. Under the default `warn` posture every event is a warning, which is what makes reading this surface the point of running it. */
30933
+ blocked: boolean;
30934
+ /** @description The operation the caller was running when it breached. */
30935
+ method: string;
30936
+ /**
30937
+ * Format: date-time
30938
+ * @description When it happened.
30939
+ */
30940
+ occurred_at: string;
29352
30941
  }[];
29353
- /** @description Canonical event names this webhook subscribes to. */
29354
- events?: string[];
29355
- /** @description Field-remapping keys for the delivery payload (messaging and user sources only). */
29356
- data?: string[];
29357
- /** @description First 8 characters of the signing secret. The full secret is never returned on edit, it is preserved unchanged. */
29358
- secret_prefix?: string;
29359
- /** @description ISO-8601 creation timestamp. */
29360
- created_at?: string;
30942
+ /** @description Cursor for the next page; null on the terminal page. Pass it back verbatim. Page until cursor is null. */
30943
+ cursor: string | null;
30944
+ safety_warning?: components["schemas"]["SafetyWarning"];
29361
30945
  };
29362
30946
  };
29363
30947
  };
29364
- /** @description Invalid request, malformed URL, empty events array, event-source mismatch, a change to the immutable source, or a malformed id. */
30948
+ /** @description Invalid request: an out-of-range `limit`, an unknown `budget_row`, a malformed `since` / `until`, or a corrupt `cursor`. */
29365
30949
  400: {
29366
30950
  headers: {
29367
30951
  [name: string]: unknown;
@@ -29370,25 +30954,7 @@ interface operations {
29370
30954
  "application/json": components["schemas"]["Error"];
29371
30955
  };
29372
30956
  };
29373
- /** @description Missing or invalid API key. */
29374
- 401: {
29375
- headers: {
29376
- [name: string]: unknown;
29377
- };
29378
- content: {
29379
- "application/json": components["schemas"]["Error"];
29380
- };
29381
- };
29382
- /** @description Payment required, an active subscription is needed. */
29383
- 402: {
29384
- headers: {
29385
- [name: string]: unknown;
29386
- };
29387
- content: {
29388
- "application/json": components["schemas"]["Error"];
29389
- };
29390
- };
29391
- /** @description Webhook not found, or one or more account_ids not found for this tenant. */
30957
+ /** @description No such account for this tenant. An id whose connection was replaced or removed also stops resolving here, and it is already absent from the list, so re-read `GET /v1/accounts` for the current id. */
29392
30958
  404: {
29393
30959
  headers: {
29394
30960
  [name: string]: unknown;
@@ -29397,15 +30963,6 @@ interface operations {
29397
30963
  "application/json": components["schemas"]["Error"];
29398
30964
  };
29399
30965
  };
29400
- /** @description Payload too large, the request body exceeds the size limit. */
29401
- 413: {
29402
- headers: {
29403
- [name: string]: unknown;
29404
- };
29405
- content: {
29406
- "application/json": components["schemas"]["Error"];
29407
- };
29408
- };
29409
30966
  /** @description Rate limited, slow down and retry after the hinted delay. */
29410
30967
  429: {
29411
30968
  headers: {
@@ -29474,8 +31031,31 @@ declare class AccountsResource {
29474
31031
  list(params?: AccountListParams): Promise<AccountListPage>;
29475
31032
  /**
29476
31033
  * Return metadata and current state for one connected account, including the
29477
- * central `quotas[]` view for all tracked quota families and `seat_id` (the
29478
- * seat this account occupies, `null` for an admin seatless account).
31034
+ * central `quotas[]` account-safety view, `account_states[]` and `seat_id`
31035
+ * (the seat this account occupies, `null` for an admin seatless account).
31036
+ *
31037
+ * `quotas[]` REPLACED its earlier `quota_name` / `remaining` / `total` /
31038
+ * `reset_time` / `recommended_throttle_hint` shape. Each row now reports
31039
+ * `budget_row`, `kind`, `used`, `ceiling`, `effective_ceiling`, `band`,
31040
+ * `posture`, `over_default`, `halt` and the window fields. **Compare `used`
31041
+ * against `effective_ceiling`, not `ceiling`**: the effective one is the
31042
+ * ceiling with the account's warm-up ramp applied, and on a new account the
31043
+ * two differ by roughly a factor of seven. `posture` says what happens at the
31044
+ * ceiling and defaults to `warn`, so nothing here refuses until it is
31045
+ * configured to. Two rows are not counters and say so in `kind`:
31046
+ * `total_actions` (`tally`) and `pending_invites` (`gauge`, whose `used` is
31047
+ * `null` until something has observed it).
31048
+ *
31049
+ * The per-minute REQUEST ceiling is a different thing and is deliberately
31050
+ * NOT in this array: it protects Curviate's own servers rather than the
31051
+ * LinkedIn account, and it is always enforced with `429` whatever the
31052
+ * posture.
31053
+ *
31054
+ * `account_states[]` lists platform conditions the account is in right now,
31055
+ * additive to `status` and independent of it: `recruiter_session_evicted`,
31056
+ * `commercial_use_limited`, `profile_view_limited`. The last two are read on
31057
+ * calls that SUCCEEDED, so check them before concluding thin results are
31058
+ * wrong. Empty for an unaffected account.
29479
31059
  *
29480
31060
  * This is a stale-while-revalidate read; it always returns immediately
29481
31061
  * from the cached row (never blocks on a live substrate call). The cached
@@ -29638,7 +31218,13 @@ declare class AuthResource {
29638
31218
  *
29639
31219
  * `searchChats` free-text searches the account's own inbox (participant
29640
31220
  * names and message content), distinct from `companies.searchChats`, which
29641
- * searches a company page's admin inbox.
31221
+ * searches a company page's admin inbox. It reads Curviate's OWN STORE, not
31222
+ * LinkedIn: see the method's own note.
31223
+ *
31224
+ * `startChat`, `sendMessage` and `sendInMail` return a `message_id` that is
31225
+ * `string | string[] | null`, not a bare string: `null` when no message was
31226
+ * actually sent, and an array when the message was delivered as several, one
31227
+ * per attachment. Confirm a send with `chat_id`, which is always present.
29642
31228
  */
29643
31229
 
29644
31230
  type ChatListPage = paths["/v1/{account_id}/chats"]["get"]["responses"]["200"]["content"]["application/json"];
@@ -29750,6 +31336,16 @@ declare class MessagingResource {
29750
31336
  * Free-text search the account's own inbox; matches participant names
29751
31337
  * and message content. `GET /v1/{account_id}/chats/search`
29752
31338
  *
31339
+ * SERVED FROM CURVIATE'S STORE, not from LinkedIn: it costs no platform call
31340
+ * and draws down no account-safety budget, and a chat that has never been
31341
+ * retrieved for this account cannot match. Read `coverage` on the response to
31342
+ * see how complete the searched corpus is before treating an empty result as
31343
+ * "no such conversation".
31344
+ *
31345
+ * A result's `type` is `1to1`, `group` or `channel`. It used to be documented
31346
+ * as never a channel; that is no longer true, so a caller switching on `type`
31347
+ * needs the third arm.
31348
+ *
29753
31349
  * @param params - `query` (required free-text term) plus `limit` and an
29754
31350
  * opaque `cursor` for pagination.
29755
31351
  */
@@ -31481,7 +33077,7 @@ declare class Curviate {
31481
33077
  * The taxonomy is additive-only: new codes may be appended, existing ones are
31482
33078
  * never removed or renamed.
31483
33079
  */
31484
- declare const ERROR_CODES: readonly ["UNAUTHORIZED", "INVALID_REQUEST", "UNSUPPORTED_MEDIA_TYPE", "PAYLOAD_TOO_LARGE", "ACCOUNT_NOT_FOUND", "ACCOUNT_RESTRICTED", "ACCOUNT_ALREADY_LINKED", "RESOURCE_NOT_FOUND", "RESOURCE_ACCESS_RESTRICTED", "FILTER_CANDIDATES_REQUIRED", "TIER_NOT_ACTIVE", "LINKEDIN_FEATURE_NOT_SUBSCRIBED", "RATE_LIMIT_ACCOUNT", "RATE_LIMIT_TENANT", "PLATFORM_RATE_LIMIT", "RATE_LIMITED", "PLATFORM_ERROR", "PLATFORM_NOT_IMPLEMENTED", "LINKEDIN_OPERATION_NOT_SUPPORTED", "CHECKPOINT_NOT_FOUND", "CHECKPOINT_EXPIRED", "CHECKPOINT_INVALID_CODE", "CHECKPOINT_MAX_ATTEMPTS", "CHECKPOINT_ALREADY_RESOLVED", "CHECKPOINT_UNSUPPORTED", "CONNECTION_IN_PROGRESS", "PREMIUM_CONFLICT", "REAUTH_REQUIRED", "LINKEDIN_AUTH_FAILED", "LINKEDIN_RATE_LIMITED", "LINKEDIN_COOKIE_INVALID", "LINKEDIN_SERVICE_UNAVAILABLE", "MESSAGE_WINDOW_EXPIRED", "RECIPIENT_UNREACHABLE", "CONNECTION_REQUEST_CONFLICT", "PAYMENT_REQUIRED", "PAYMENT_FAILED", "SUBSCRIPTION_BUSY", "SUBSCRIPTION_NOT_FOUND", "SEAT_NOT_FOUND", "SEAT_CANCELLED", "INTERNAL"];
33080
+ declare const ERROR_CODES: readonly ["UNAUTHORIZED", "INVALID_REQUEST", "UNSUPPORTED_MEDIA_TYPE", "PAYLOAD_TOO_LARGE", "ACCOUNT_NOT_FOUND", "ACCOUNT_RESTRICTED", "ACCOUNT_ALREADY_LINKED", "RESOURCE_NOT_FOUND", "RESOURCE_ACCESS_RESTRICTED", "FILTER_CANDIDATES_REQUIRED", "TIER_NOT_ACTIVE", "LINKEDIN_FEATURE_NOT_SUBSCRIBED", "RATE_LIMIT_ACCOUNT", "RATE_LIMIT_TENANT", "PLATFORM_RATE_LIMIT", "RATE_LIMITED", "BUDGET_EXHAUSTED", "PLATFORM_ERROR", "PLATFORM_NOT_IMPLEMENTED", "LINKEDIN_OPERATION_NOT_SUPPORTED", "CHECKPOINT_NOT_FOUND", "CHECKPOINT_EXPIRED", "CHECKPOINT_INVALID_CODE", "CHECKPOINT_MAX_ATTEMPTS", "CHECKPOINT_ALREADY_RESOLVED", "CHECKPOINT_UNSUPPORTED", "CONNECTION_IN_PROGRESS", "PREMIUM_CONFLICT", "REAUTH_REQUIRED", "LINKEDIN_AUTH_FAILED", "LINKEDIN_SESSION_EVICTED", "LINKEDIN_RATE_LIMITED", "LINKEDIN_COOKIE_INVALID", "LINKEDIN_SERVICE_UNAVAILABLE", "MESSAGE_WINDOW_EXPIRED", "RECIPIENT_UNREACHABLE", "CONNECTION_REQUEST_CONFLICT", "PAYMENT_REQUIRED", "PAYMENT_FAILED", "SUBSCRIPTION_BUSY", "SUBSCRIPTION_NOT_FOUND", "SEAT_NOT_FOUND", "SEAT_CANCELLED", "INTERNAL"];
31485
33081
  /**
31486
33082
  * The complete set of error codes observable by API callers.
31487
33083
  *
@@ -31512,6 +33108,31 @@ interface RetryHint {
31512
33108
  kind: "delay" | "backoff" | "never";
31513
33109
  delayMs?: number;
31514
33110
  }
33111
+ /**
33112
+ * Which safety rule refused, on a `BUDGET_EXHAUSTED`. Wire field `reason`.
33113
+ *
33114
+ * `ceiling`: the row's configured limit is spent; wait until
33115
+ * {@link CurviateError.resetAt} or raise the limit.
33116
+ * `activity_window`: the account is outside the hours it works in; `resetAt`
33117
+ * is when the window next opens.
33118
+ *
33119
+ * The two need different fixes, which is why this is a field rather than
33120
+ * something to read out of the message.
33121
+ */
33122
+ type SafetyReason = "ceiling" | "activity_window";
33123
+ /**
33124
+ * The exact setting to change to lift a `BUDGET_EXHAUSTED`, as data rather
33125
+ * than as prose. Wire field `hint`.
33126
+ *
33127
+ * `parameter` is addressable on `PATCH /v1/{account_id}/safety-policy` (for
33128
+ * example `profile_views.ceiling`, or `posture` where no ceiling can be
33129
+ * raised), so an agent can choose between waiting, escalating to a human and
33130
+ * reconfiguring without parsing `message`.
33131
+ */
33132
+ interface SafetyHint {
33133
+ parameter: string;
33134
+ message: string;
33135
+ }
31515
33136
  /** Constructor input for {@link CurviateError}. */
31516
33137
  interface CurviateErrorInit {
31517
33138
  code: ErrorCode;
@@ -31526,6 +33147,66 @@ interface CurviateErrorInit {
31526
33147
  requiredTier?: RequiredTier;
31527
33148
  /** Milliseconds to wait before retry, parsed from the `Retry-After` response header. */
31528
33149
  retryAfterMs?: number;
33150
+ /**
33151
+ * The account-safety budget row this response names. Wire field `row`.
33152
+ *
33153
+ * TWO CODES CARRY IT AND THEY MEAN DIFFERENT THINGS. Read {@link
33154
+ * CurviateErrorInit.code} to tell them apart:
33155
+ *
33156
+ * - `PLATFORM_RATE_LIMIT`: the row is PAUSED. LinkedIn refused a recent call
33157
+ * on it, so this one was refused locally without reaching LinkedIn. The
33158
+ * pause is scoped to this row on this account and every other row keeps
33159
+ * working, so the recovery is to switch work, never to retry this row
33160
+ * before {@link CurviateErrorInit.retryAfterSeconds} elapses, because retrying
33161
+ * into a LinkedIn rate limit is what escalates it into a restriction.
33162
+ * - `BUDGET_EXHAUSTED`: the row hit the CEILING you configured, or the
33163
+ * account is outside its activity window. Nothing reached LinkedIn and
33164
+ * nothing was spent. Wait until {@link CurviateErrorInit.resetAt} or
33165
+ * change {@link CurviateErrorInit.safetyHint}'s parameter.
33166
+ *
33167
+ * Either way the SDK never auto-retries a response carrying this field.
33168
+ */
33169
+ budgetRow?: string;
33170
+ /**
33171
+ * Seconds until the PAUSED {@link CurviateErrorInit.budgetRow} is usable
33172
+ * again, on a `PLATFORM_RATE_LIMIT`. Wire field `retry_after`.
33173
+ *
33174
+ * A SEPARATE FIELD FROM {@link CurviateErrorInit.retryAfterMs}, on purpose.
33175
+ * `retryAfterMs` is the transport's own sleep budget and is acted on
33176
+ * automatically; this one is informational, because a pause can last an hour
33177
+ * and sleeping that inside a call is a hang. Switch to other work and come
33178
+ * back after this many seconds.
33179
+ */
33180
+ retryAfterSeconds?: number;
33181
+ /**
33182
+ * When the exhausted {@link CurviateErrorInit.budgetRow} frees up, on a
33183
+ * `BUDGET_EXHAUSTED`. Wire field `reset_at`.
33184
+ *
33185
+ * An absolute ISO-8601 instant rather than a duration, so it stays true
33186
+ * however long you hold it. It is the window roll on a spent ceiling and the
33187
+ * next window open on an activity-window refusal.
33188
+ *
33189
+ * `null`, present rather than absent, in exactly two cases where no clock
33190
+ * frees the account: the `pending_invites` row, whose backlog falls when
33191
+ * invitations are accepted or withdrawn rather than at any window boundary,
33192
+ * and an InMail CREDIT exhaustion (`row: inmail`), which LinkedIn regrants on
33193
+ * a schedule this product cannot read. Null-check it before scheduling on it.
33194
+ */
33195
+ resetAt?: string | null;
33196
+ /** The settable parameter that would lift a `BUDGET_EXHAUSTED`. Wire field `hint`. */
33197
+ safetyHint?: SafetyHint;
33198
+ /** Which safety rule refused, on a `BUDGET_EXHAUSTED`. Wire field `reason`. */
33199
+ safetyReason?: SafetyReason;
33200
+ /**
33201
+ * Whether the action was refused. Wire field `blocked`; always `true` here,
33202
+ * because an error means it did not happen and spent nothing.
33203
+ *
33204
+ * The same payload with `blocked: false` rides the SUCCESS body of an account
33205
+ * on the default `warn` posture, under `safety_warning`, and is otherwise
33206
+ * field-identical, so one branch of your code handles both postures and
33207
+ * moving an account to `enforce` is not a breaking change.
33208
+ */
33209
+ blocked?: boolean;
31529
33210
  }
31530
33211
  /** Plain-object shape produced by {@link CurviateError.toJSON}. */
31531
33212
  interface CurviateErrorJSON {
@@ -31538,6 +33219,12 @@ interface CurviateErrorJSON {
31538
33219
  retryLikelyToSucceed: boolean;
31539
33220
  requiredTier?: RequiredTier;
31540
33221
  retryAfterMs?: number;
33222
+ budgetRow?: string;
33223
+ retryAfterSeconds?: number;
33224
+ resetAt?: string | null;
33225
+ safetyHint?: SafetyHint;
33226
+ safetyReason?: SafetyReason;
33227
+ blocked?: boolean;
31541
33228
  }
31542
33229
  /**
31543
33230
  * The single thrown type for all Curviate API errors.
@@ -31560,6 +33247,18 @@ declare class CurviateError extends Error {
31560
33247
  readonly retryLikelyToSucceed: boolean;
31561
33248
  readonly requiredTier: RequiredTier | undefined;
31562
33249
  readonly retryAfterMs: number | undefined;
33250
+ /** The budget row this response names. See {@link CurviateErrorInit.budgetRow}. */
33251
+ readonly budgetRow: string | undefined;
33252
+ /** Seconds until a paused row lifts. See {@link CurviateErrorInit.retryAfterSeconds}. */
33253
+ readonly retryAfterSeconds: number | undefined;
33254
+ /** When the refusal lifts; `null` on the gauge and on InMail credits. See {@link CurviateErrorInit.resetAt}. */
33255
+ readonly resetAt: string | null | undefined;
33256
+ /** The settable parameter that would lift the refusal. See {@link CurviateErrorInit.safetyHint}. */
33257
+ readonly safetyHint: SafetyHint | undefined;
33258
+ /** Which safety rule refused. See {@link CurviateErrorInit.safetyReason}. */
33259
+ readonly safetyReason: SafetyReason | undefined;
33260
+ /** Whether the action was refused. See {@link CurviateErrorInit.blocked}. */
33261
+ readonly blocked: boolean | undefined;
31563
33262
  constructor(init: CurviateErrorInit);
31564
33263
  /**
31565
33264
  * Explicit, credential-safe serialization. Only the documented structured
@@ -31855,4 +33554,4 @@ interface ConstructEventOptions {
31855
33554
  */
31856
33555
  declare function constructEvent(rawBody: string | Buffer, signatureHeader: string, secret: string, opts?: ConstructEventOptions): Promise<CurviateEvent>;
31857
33556
 
31858
- export { type AccountListPage, type AccountListParams, type AccountPayload, type AccountScopedNamespaces, type ConnectionPayload, type ConstructEventOptions, Curviate, type CurviateConfig, CurviateError, type CurviateErrorInit, type CurviateErrorJSON, type CurviateEvent, type CurviateEventEnvelope, type ErrorCode, type EventPayloadBase, type MessagePayload, type RequiredTier, type ResolvedConfig, type ResourceNamespaces, type RetryHint, WebhookSignatureError, type components, constructEvent, isCurviateError, type operations, type paths };
33557
+ export { type AccountListPage, type AccountListParams, type AccountPayload, type AccountScopedNamespaces, type ConnectionPayload, type ConstructEventOptions, Curviate, type CurviateConfig, CurviateError, type CurviateErrorInit, type CurviateErrorJSON, type CurviateEvent, type CurviateEventEnvelope, ERROR_CODES, type ErrorCode, type EventPayloadBase, type MessagePayload, type RequiredTier, type ResolvedConfig, type ResourceNamespaces, type RetryHint, type SafetyHint, type SafetyReason, WebhookSignatureError, type components, constructEvent, isCurviateError, type operations, type paths };