@curviate/sdk 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -55,10 +55,11 @@ var ERROR_CODES = [
55
55
  "PLATFORM_RATE_LIMIT",
56
56
  // Curviate's OWN account-safety ceiling, not a request-rate limit and not
57
57
  // LinkedIn refusing. A 429 that never reached LinkedIn and spent nothing:
58
- // the budget row named by `budgetRow` is at the ceiling configured on
59
- // `PATCH /v1/{account_id}/safety-policy`, or the account is outside its
60
- // activity window. Deliberately absent from RETRYABLE_CODES: a monthly
61
- // row's reset can be weeks out, so backing off is the wrong recovery.
58
+ // the budget row named by `budgetRow` is at its ceiling, set by the account's
59
+ // safety policy or else the tenant default (`GET`/`PATCH /v1/safety-policy`),
60
+ // or the account is outside its activity window. Deliberately absent from
61
+ // RETRYABLE_CODES: a monthly row's reset can be weeks out, so backing off
62
+ // is the wrong recovery.
62
63
  // Read `resetAt`, `safetyReason` and `safetyHint` instead.
63
64
  "BUDGET_EXHAUSTED",
64
65
  // Platform errors
@@ -511,6 +512,28 @@ var AccountsResource = class {
511
512
  ...params !== void 0 ? { query: params } : {}
512
513
  });
513
514
  }
515
+ /**
516
+ * List the workspace's live seats: each `seat_id`, whether an account
517
+ * `occupied` it, and that `account_id` (`null` when free). Not paginated.
518
+ *
519
+ * Only live seats are listed, and an empty seat is listed only when
520
+ * connecting an account to it would be accepted right now. So an empty seat
521
+ * that is provisional, cancelling, on an ended trial, or blocked by billing
522
+ * does not appear, and `items` can be `[]` even though the workspace has
523
+ * seats. Any seat with `occupied: false` is a valid `seat_id` for connecting
524
+ * a new account with `auth.intent`. Carries no billing fields.
525
+ *
526
+ * @returns `{ object: "seat_list", items: { seat_id, occupied, account_id }[] }`.
527
+ *
528
+ * @example
529
+ * const { items } = await curviate.accounts.listSeats();
530
+ * const free = items.find((seat) => !seat.occupied);
531
+ * if (!free) throw new Error("No seat is free to connect to right now (add a seat, or check billing).");
532
+ * await curviate.auth.intent({ seat_id: free.seat_id, auth_method: "cookie", cookie: { li_at }, user_agent });
533
+ */
534
+ listSeats() {
535
+ return this.ctx.request({ method: "GET", path: "/v1/accounts/seats" });
536
+ }
514
537
  /**
515
538
  * Return metadata and current state for one connected account, including the
516
539
  * central `quotas[]` account-safety view, `account_states[]` and `seat_id`
@@ -587,7 +610,8 @@ var AuthResource = class {
587
610
  /**
588
611
  * Authenticate a LinkedIn account with credentials or a session cookie.
589
612
  *
590
- * Omit `account_id` to connect a NEW account into an empty seat; include it
613
+ * Omit `account_id` to connect a NEW account into an empty seat (find one
614
+ * with `accounts.listSeats()`: any seat with `occupied: false`); include it
591
615
  * to re-authenticate an EXISTING account in place. Returns the account on
592
616
  * success (200 re-authenticated in place / 201 new), or a checkpoint
593
617
  * challenge (202) when LinkedIn requires verification; resolve it with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curviate/sdk",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Curviate API.",
6
6
  "license": "MIT",
@@ -2300,6 +2300,26 @@ export interface paths {
2300
2300
  patch: operations["patchV1AccountsAccountId"];
2301
2301
  trace?: never;
2302
2302
  };
2303
+ "/v1/accounts/seats": {
2304
+ parameters: {
2305
+ query?: never;
2306
+ header?: never;
2307
+ path?: never;
2308
+ cookie?: never;
2309
+ };
2310
+ /**
2311
+ * List seats
2312
+ * @description List the workspace's seats: each `seat_id`, whether an account occupies it, and that `account_id`. Pass an unoccupied `seat_id` to POST /v1/auth/intent to connect a new account. Every unoccupied seat listed is a valid connect target. Not paginated.
2313
+ */
2314
+ get: operations["getV1AccountsSeats"];
2315
+ put?: never;
2316
+ post?: never;
2317
+ delete?: never;
2318
+ options?: never;
2319
+ head?: never;
2320
+ patch?: never;
2321
+ trace?: never;
2322
+ };
2303
2323
  "/v1/auth/intent": {
2304
2324
  parameters: {
2305
2325
  query?: never;
@@ -2311,7 +2331,7 @@ export interface paths {
2311
2331
  put?: never;
2312
2332
  /**
2313
2333
  * Start a credential/cookie authentication
2314
- * @description Authenticate a LinkedIn account directly with credentials or a session cookie. The credential fields are NESTED, not top level: `auth_method: "credentials"` reads `credentials: { email, password }`, and `auth_method: "cookie"` reads `cookie: { li_at }` plus a top-level `user_agent`. Omit `account_id` to connect a NEW account into an empty `seat_id` (every seat id is listed with click-to-copy in the seat table on your dashboard); include `account_id` (in the body) to re-authenticate an EXISTING account in place. Returns the account on success (201 new / 200 reconnect), or a checkpoint challenge (202) carrying the account_id when LinkedIn requires verification. Complete the challenge with POST /v1/auth/checkpoint/solve (codes) or POST /v1/auth/checkpoint/poll (mobile-app approval). Connection scope (which LinkedIn products are enabled) is not something you list: the connection asks for every product and LinkedIn activates the ones the account actually has. The recorded scope is readable as `requested_products` on the account. One LinkedIn account can hold only one of the two premium surfaces, and when both are asked for Sales Navigator takes precedence, so pass the optional `linkedin_premium` (`sales_navigator` or `recruiter`) if the account holds both and you want the other one. Omit it and nothing is narrowed. It applies per connection and is not remembered, so state it on every connect and reconnect where Recruiter must win. A reconnect that changes scope must use credentials (a saved cookie cannot change scope), which includes any reconnect of an account connected before the full product set became the default. Pin a managed proxy with the optional `country`/`ip` or supply a `proxy` to override it.
2334
+ * @description Authenticate a LinkedIn account directly with credentials or a session cookie. The credential fields are NESTED, not top level: `auth_method: "credentials"` reads `credentials: { email, password }`, and `auth_method: "cookie"` reads `cookie: { li_at }` plus a top-level `user_agent`. Omit `account_id` to connect a NEW account into an empty `seat_id` (list your seats with GET /v1/accounts/seats, or copy a seat id from the seat table on your dashboard); include `account_id` (in the body) to re-authenticate an EXISTING account in place. Returns the account on success (201 new / 200 reconnect), or a checkpoint challenge (202) carrying the account_id when LinkedIn requires verification. Complete the challenge with POST /v1/auth/checkpoint/solve (codes) or POST /v1/auth/checkpoint/poll (mobile-app approval). Connection scope (which LinkedIn products are enabled) is not something you list: the connection asks for every product and LinkedIn activates the ones the account actually has. The recorded scope is readable as `requested_products` on the account. One LinkedIn account can hold only one of the two premium surfaces, and when both are asked for Sales Navigator takes precedence, so pass the optional `linkedin_premium` (`sales_navigator` or `recruiter`) if the account holds both and you want the other one. Omit it and nothing is narrowed. It applies per connection and is not remembered, so state it on every connect and reconnect where Recruiter must win. A reconnect that changes scope must use credentials (a saved cookie cannot change scope), which includes any reconnect of an account connected before the full product set became the default. Pin a managed proxy with the optional `country`/`ip` or supply a `proxy` to override it.
2315
2335
  */
2316
2336
  post: operations["postV1AuthIntent"];
2317
2337
  delete?: never;
@@ -2581,7 +2601,7 @@ export interface paths {
2581
2601
  };
2582
2602
  /**
2583
2603
  * Read an account's safety policy
2584
- * @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.
2604
+ * @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. Each value is this account's own where it set one, otherwise the tenant default for its `limit_profile` (`GET /v1/safety-policy`), otherwise the Curviate default. 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.
2585
2605
  */
2586
2606
  get: operations["getV1AccountIdSafetyPolicy"];
2587
2607
  put?: never;
@@ -2591,7 +2611,7 @@ export interface paths {
2591
2611
  head?: never;
2592
2612
  /**
2593
2613
  * Update an account's safety policy
2594
- * @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. Six fields on the read are derived rather than configured: `effective_ceiling`, `interface_ceiling`, `effective_interface_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, the top-level `timezone` for the zone, and per row `activity_window_start`, `activity_window_end`, `activity_window_timezone` and `activity_window_applies_to`. The 400 this produces names those subfields, not just the refused object. 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. One field on this operation does not configure anything: `clear_halts` lifts the platform pause on the action types you name. When the platform refuses a call on an account, Curviate pauses that action type for the platform's own retry-after or a seeded cooldown, and every later call on it is refused locally without reaching the platform; the account resource reports the live pauses as `quotas[].halt`. Naming an action type here lifts its pause immediately, so the next call goes out. An action type with no active pause is accepted and does nothing, which makes it safe to send for a set you have not checked, and only the ones actually lifted are ledgered, each with the expiry it cut short, the actor and the time, readable afterwards on `GET /v1/{account_id}/safety-events` with `reason: "halt_cleared"`. Clearing disarms nothing: if the platform refuses the account again the pause returns, and a 429 or a refused write returns it for the full cooldown. Clear one when you have reason to believe the refusal was a transient upstream fault rather than the platform pushing back on this account.
2614
+ * @description The account-level safety-policy write (tenant-wide defaults are set on `PATCH /v1/safety-policy`). 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 tenant default where one is set, the Curviate default otherwise. 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. Six fields on the read are derived rather than configured: `effective_ceiling`, `interface_ceiling`, `effective_interface_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, the top-level `timezone` for the zone, and per row `activity_window_start`, `activity_window_end`, `activity_window_timezone` and `activity_window_applies_to`. The 400 this produces names those subfields, not just the refused object. 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. One field on this operation does not configure anything: `clear_halts` lifts the platform pause on the action types you name. When the platform refuses a call on an account, Curviate pauses that action type for the platform's own retry-after or a seeded cooldown, and every later call on it is refused locally without reaching the platform; the account resource reports the live pauses as `quotas[].halt`. Naming an action type here lifts its pause immediately, so the next call goes out. An action type with no active pause is accepted and does nothing, which makes it safe to send for a set you have not checked, and only the ones actually lifted are ledgered, each with the expiry it cut short, the actor and the time, readable afterwards on `GET /v1/{account_id}/safety-events` with `reason: "halt_cleared"`. Clearing disarms nothing: if the platform refuses the account again the pause returns, and a 429 or a refused write returns it for the full cooldown. Clear one when you have reason to believe the refusal was a transient upstream fault rather than the platform pushing back on this account.
2595
2615
  */
2596
2616
  patch: operations["patchV1AccountIdSafetyPolicy"];
2597
2617
  trace?: never;
@@ -2616,6 +2636,30 @@ export interface paths {
2616
2636
  patch?: never;
2617
2637
  trace?: never;
2618
2638
  };
2639
+ "/v1/safety-policy": {
2640
+ parameters: {
2641
+ query?: never;
2642
+ header?: never;
2643
+ path?: never;
2644
+ cookie?: never;
2645
+ };
2646
+ /**
2647
+ * Read the tenant safety defaults
2648
+ * @description Returns the tenant-wide safety defaults, per limit profile and per budget row: the values every account on that profile resolves from unless it sets its own. A value nobody has set here is the Curviate default, and a value set above the Curviate default carries an `over_default` entry naming the value, the default and its source class. Also carries `tenant_default_posture`. Works with no accounts connected, so defaults can be in place before the first one is.
2649
+ */
2650
+ get: operations["getV1SafetyPolicy"];
2651
+ put?: never;
2652
+ post?: never;
2653
+ delete?: never;
2654
+ options?: never;
2655
+ head?: never;
2656
+ /**
2657
+ * Update the tenant safety defaults
2658
+ * @description The tenant-level safety-policy write. Sets defaults per limit profile and per budget row; every account on that profile resolves from them unless it overrides the same field itself, including accounts connected later. THIS CHANGES EVERY ACCOUNT IN THE TENANT THAT HAS NOT OVERRIDDEN THE FIELD, and it applies immediately: under `enforce` posture, lowering a default can start refusing actions across the tenant in one request. The response carries `impact`: how many accounts had a ceiling change, and how many moved into `over`, measured on those accounts before and after the write. Send only what you are changing; `null` clears a default back to the Curviate default. A value equal to the one already in force is not stored and not recorded. Any limit may be set to any value, and a value above the Curviate default is reported rather than refused. Every change is appended to the ledger with the previous value, the new value, the Curviate default, the actor and the time. Per-row posture is not a tenant default: use `tenant_default_posture` here, or `posture` on an account's own policy. Per-account values are set on `PATCH /v1/{account_id}/safety-policy`.
2659
+ */
2660
+ patch: operations["patchV1SafetyPolicy"];
2661
+ trace?: never;
2662
+ };
2619
2663
  }
2620
2664
  export type webhooks = Record<string, never>;
2621
2665
  export interface components {
@@ -28237,6 +28281,91 @@ export interface operations {
28237
28281
  };
28238
28282
  };
28239
28283
  };
28284
+ getV1AccountsSeats: {
28285
+ parameters: {
28286
+ query?: never;
28287
+ header?: never;
28288
+ path?: never;
28289
+ cookie?: never;
28290
+ };
28291
+ requestBody?: never;
28292
+ responses: {
28293
+ /** @description Every live seat in the workspace with its occupancy. Empty list when the workspace has no seats. */
28294
+ 200: {
28295
+ headers: {
28296
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
28297
+ RateLimit: components["headers"]["RateLimit"];
28298
+ [name: string]: unknown;
28299
+ };
28300
+ content: {
28301
+ "application/json": {
28302
+ /**
28303
+ * @description Response type discriminator.
28304
+ * @enum {string}
28305
+ */
28306
+ object: "seat_list";
28307
+ items: {
28308
+ /** @description The seat ID (`seat_...`). Pass an unoccupied one as `seat_id` when connecting a new account. */
28309
+ seat_id: string;
28310
+ /** @description True when an account is connected on this seat. */
28311
+ occupied: boolean;
28312
+ /** @description The account on this seat, or null when unoccupied. */
28313
+ account_id: string | null;
28314
+ }[];
28315
+ safety_warning?: components["schemas"]["SafetyWarning"];
28316
+ };
28317
+ };
28318
+ };
28319
+ /** @description Missing or invalid API key. */
28320
+ 401: {
28321
+ headers: {
28322
+ [name: string]: unknown;
28323
+ };
28324
+ content: {
28325
+ "application/json": components["schemas"]["Error"];
28326
+ };
28327
+ };
28328
+ /** @description Rate limited, slow down and retry after the hinted delay. */
28329
+ 429: {
28330
+ headers: {
28331
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
28332
+ RateLimit: components["headers"]["RateLimit"];
28333
+ "Retry-After": components["headers"]["Retry-After"];
28334
+ [name: string]: unknown;
28335
+ };
28336
+ content: {
28337
+ "application/json": components["schemas"]["Error"];
28338
+ };
28339
+ };
28340
+ /** @description Internal error. */
28341
+ 500: {
28342
+ headers: {
28343
+ [name: string]: unknown;
28344
+ };
28345
+ content: {
28346
+ "application/json": components["schemas"]["Error"];
28347
+ };
28348
+ };
28349
+ /** @description Service unavailable. */
28350
+ 503: {
28351
+ headers: {
28352
+ [name: string]: unknown;
28353
+ };
28354
+ content: {
28355
+ "application/json": components["schemas"]["Error"];
28356
+ };
28357
+ };
28358
+ /** @description Gateway timeout. */
28359
+ 504: {
28360
+ headers: {
28361
+ [name: string]: unknown;
28362
+ };
28363
+ content: {
28364
+ "application/json": components["schemas"]["Error"];
28365
+ };
28366
+ };
28367
+ };
28368
+ };
28240
28369
  postV1AuthIntent: {
28241
28370
  parameters: {
28242
28371
  query?: never;
@@ -28247,7 +28376,7 @@ export interface operations {
28247
28376
  requestBody: {
28248
28377
  content: {
28249
28378
  "application/json": {
28250
- /** @description The empty seat this NEW account will occupy. Required on a new connect; omit it and the call returns 400 INVALID_REQUEST with 'seat_id is required'. Seat ids look like seat_01H..., and every seat you own is listed with click-to-copy in the seat table on your dashboard; any seat showing no attached account is free to use. Ignored when account_id is present, because a reconnect keeps the account on its current seat. */
28379
+ /** @description The empty seat this NEW account will occupy. Required on a new connect; omit it and the call returns 400 INVALID_REQUEST with 'seat_id is required'. Seat ids look like seat_01H..., and every seat you own is listed by GET /v1/accounts/seats (any item with occupied false is free to use) and with click-to-copy in the seat table on your dashboard. Ignored when account_id is present, because a reconnect keeps the account on its current seat. */
28251
28380
  seat_id?: string;
28252
28381
  /**
28253
28382
  * @description How to authenticate. 'credentials' reads the nested `credentials` object (email + password). 'cookie' reads the nested `cookie` object (li_at) and also requires a top-level `user_agent`. Neither set of fields is accepted at the top level.
@@ -30934,4 +31063,336 @@ export interface operations {
30934
31063
  };
30935
31064
  };
30936
31065
  };
31066
+ getV1SafetyPolicy: {
31067
+ parameters: {
31068
+ query?: never;
31069
+ header?: never;
31070
+ path?: never;
31071
+ cookie?: never;
31072
+ };
31073
+ requestBody?: never;
31074
+ responses: {
31075
+ /** @description The tenant safety defaults, every limit profile and every budget row. */
31076
+ 200: {
31077
+ headers: {
31078
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
31079
+ RateLimit: components["headers"]["RateLimit"];
31080
+ [name: string]: unknown;
31081
+ };
31082
+ content: {
31083
+ "application/json": {
31084
+ /**
31085
+ * @description Response type discriminator.
31086
+ * @enum {string}
31087
+ */
31088
+ object?: "tenant_safety_policy";
31089
+ /**
31090
+ * @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.
31091
+ * @enum {string|null}
31092
+ */
31093
+ tenant_default_posture?: "warn" | "enforce" | null;
31094
+ /** @description One entry per limit profile, always all of them. */
31095
+ profiles?: {
31096
+ /**
31097
+ * @description The limit profile these defaults apply to. An account resolves from its own profile's defaults.
31098
+ * @enum {string}
31099
+ */
31100
+ limit_profile?: "basic" | "premium" | "sales_navigator" | "recruiter";
31101
+ /** @description Every budget row, always: the tenant default where one is set, the Curviate default otherwise. */
31102
+ rows?: {
31103
+ /** @description The counted-action row this policy governs. */
31104
+ budget_row?: string;
31105
+ /** @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. */
31106
+ ceiling?: number | null;
31107
+ /** @description Routine band. */
31108
+ green?: number | null;
31109
+ /** @description Warn-louder band. */
31110
+ amber?: number | null;
31111
+ /**
31112
+ * @description The calendar grain this row is counted in.
31113
+ * @enum {string}
31114
+ */
31115
+ window_kind?: "day" | "week" | "month";
31116
+ /** @description How many grain units one counting window spans. */
31117
+ window_span?: number;
31118
+ /** @description IANA zone name the counting window is aligned to. Null counts in UTC. */
31119
+ window_timezone?: string | null;
31120
+ /** @description Local HH:MM the activity window opens. */
31121
+ activity_window_start?: string;
31122
+ /** @description Local HH:MM the activity window closes. */
31123
+ activity_window_end?: string;
31124
+ /** @description IANA zone name the activity window is read in. Null disables the window entirely. */
31125
+ activity_window_timezone?: string | null;
31126
+ /**
31127
+ * @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. A read Curviate answers from its own store never reaches LinkedIn, so it is never subject to the window under either setting, at any hour; the same read IS covered when the store cannot answer it, or when you ask for it live. Inbound events are never subject to it either. One action set to `all` reaches the whole account's live reads that spend no budget of their own (notifications, profile analytics, SSI, profile visitors, the invitation and saved-list listings): those calls debit no action type, so they have none to read this setting from and take the account-wide answer instead. Reads that DO debit an action type read that action type's own setting.
31128
+ * @enum {string}
31129
+ */
31130
+ activity_window_applies_to?: "writes" | "all";
31131
+ /** @description The tenant default multiplier for this row, or null when none is set. Null means each account follows its own derived warm-up ramp; 1 pins every account on this profile out of it. */
31132
+ warm_up_factor?: number | null;
31133
+ /** @description Length cap on the row's free-text field, in characters. */
31134
+ character_cap?: number | null;
31135
+ /** @description Length cap on the row's subject line, in characters. */
31136
+ subject_character_cap?: number | null;
31137
+ /** @description Bound on a single query's result lines. Search rows only. */
31138
+ per_query_cap?: number | null;
31139
+ /**
31140
+ * @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`.
31141
+ * @enum {string}
31142
+ */
31143
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
31144
+ /** @description What a call through the matching ELEVATED interface is held to on this row: the `/v1/{account_id}/sales-navigator/...` and `/v1/{account_id}/recruiter/...` operations. Null when this row and limit profile have no such figure, which is every row on `basic` and `premium` and most rows everywhere. Everything else on this row (`ceiling`, `effective_ceiling`, `band` and `over_default`) is the STANDARD-interface figure, because one counter serves both interfaces and the standard side is the conservative one to band against. The consequence is worth reading: on a `sales_navigator` or `recruiter` account a row can report `band: "over"` with `posture: "enforce"` while a call through the elevated interface still succeeds; the refusal the band predicts is the one a standard call gets. */
31145
+ interface_ceiling?: number | null;
31146
+ /** @description Empty when nothing on this row is above default. Computed against the STANDARD-interface default, like `band` and for the same reason. */
31147
+ over_default?: {
31148
+ /**
31149
+ * @description The field whose configured value is above default.
31150
+ * @enum {string}
31151
+ */
31152
+ field?: "ceiling" | "green" | "amber" | "character_cap" | "subject_character_cap" | "per_query_cap";
31153
+ /** @description The configured value. */
31154
+ value?: number;
31155
+ /** @description The Curviate default it exceeds. */
31156
+ default?: number;
31157
+ /**
31158
+ * @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.
31159
+ * @enum {string}
31160
+ */
31161
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
31162
+ }[];
31163
+ }[];
31164
+ }[];
31165
+ safety_warning?: components["schemas"]["SafetyWarning"];
31166
+ };
31167
+ };
31168
+ };
31169
+ /** @description Rate limited, slow down and retry after the hinted delay. */
31170
+ 429: {
31171
+ headers: {
31172
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
31173
+ RateLimit: components["headers"]["RateLimit"];
31174
+ "Retry-After": components["headers"]["Retry-After"];
31175
+ [name: string]: unknown;
31176
+ };
31177
+ content: {
31178
+ "application/json": components["schemas"]["Error"];
31179
+ };
31180
+ };
31181
+ };
31182
+ };
31183
+ patchV1SafetyPolicy: {
31184
+ parameters: {
31185
+ query?: never;
31186
+ header?: never;
31187
+ path?: never;
31188
+ cookie?: never;
31189
+ };
31190
+ requestBody: {
31191
+ content: {
31192
+ "application/json": {
31193
+ /**
31194
+ * @description The tenant-wide posture default every account inherits unless it overrides it. Null clears it back to `warn`.
31195
+ * @enum {string|null}
31196
+ */
31197
+ tenant_default_posture?: "warn" | "enforce" | null;
31198
+ /** @description Tenant defaults, per limit profile. */
31199
+ profiles?: {
31200
+ /**
31201
+ * @description The limit profile these defaults apply to. An account resolves from the defaults of its own `limit_profile` only.
31202
+ * @enum {string}
31203
+ */
31204
+ limit_profile: "basic" | "premium" | "sales_navigator" | "recruiter";
31205
+ /** @description Per-row defaults for this profile. Omit a field to leave it; `null` clears the tenant default back to the Curviate default. A row read back from this document may be sent unchanged. */
31206
+ rows: {
31207
+ /**
31208
+ * @description The budget row this patch configures.
31209
+ * @enum {string}
31210
+ */
31211
+ 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";
31212
+ /** @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. */
31213
+ ceiling?: number | null;
31214
+ /** @description Routine band. Activity at or below it is unremarkable. */
31215
+ green?: number | null;
31216
+ /** @description Warn-louder band, between green and the ceiling. */
31217
+ amber?: number | null;
31218
+ /**
31219
+ * @description The calendar grain this row is counted in.
31220
+ * @enum {string|null}
31221
+ */
31222
+ window_kind?: "day" | "week" | "month" | null;
31223
+ /** @description How many `window_kind` units one counting window spans. */
31224
+ window_span?: number | null;
31225
+ /** @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. */
31226
+ window_timezone?: string | null;
31227
+ /** @description Local time the activity window opens, HH:MM. Outside the window, live write operations are reported or refused per posture. */
31228
+ activity_window_start?: string | null;
31229
+ /** @description Local time the activity window closes, HH:MM. */
31230
+ activity_window_end?: string | null;
31231
+ /** @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. */
31232
+ activity_window_timezone?: string | null;
31233
+ /**
31234
+ * @description What the activity window governs. `writes` (the default) covers live writes to LinkedIn; `all` widens it to live reads. A read Curviate answers from its own store never reaches LinkedIn and so is never covered; the same read IS covered when the store cannot answer it, or when you ask for it live. Stored reads and inbound events are never subject to the window under either setting, at any hour. One action set to `all` reaches the whole account's live reads that spend no budget of their own (notifications, profile analytics, SSI, profile visitors, the invitation and saved-list listings): those calls debit no action type, so they have none to read this setting from and take the account-wide answer instead. Reads that DO debit an action type read that action type's own setting.
31235
+ * @enum {string|null}
31236
+ */
31237
+ activity_window_applies_to?: "writes" | "all" | null;
31238
+ /** @description Multiplier applied to this row's ceiling while ramping. 1 is not ramping. */
31239
+ warm_up_factor?: number | null;
31240
+ /** @description Length cap on this row's free-text field, in characters (an invitation note, an InMail body). */
31241
+ character_cap?: number | null;
31242
+ /** @description Length cap on this row's subject line, in characters. */
31243
+ subject_character_cap?: number | null;
31244
+ /** @description Bound on a single query's result lines. Search rows only. */
31245
+ per_query_cap?: number | null;
31246
+ /**
31247
+ * @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.
31248
+ * @enum {string|null}
31249
+ */
31250
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded" | null;
31251
+ /** @description READ-ONLY. What a call through the matching elevated interface (`/v1/{account_id}/sales-navigator/...`, `/v1/{account_id}/recruiter/...`) is held to on this row, or null when this row and limit profile have no such figure. `ceiling`, `effective_ceiling`, `band` and `over_default` are all the STANDARD-interface figure, so on a `sales_navigator` or `recruiter` account a row can report `over` while a call through the elevated interface still succeeds. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
31252
+ interface_ceiling?: unknown;
31253
+ /** @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. */
31254
+ over_default?: unknown;
31255
+ }[];
31256
+ }[];
31257
+ /** @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. */
31258
+ object?: unknown;
31259
+ /** @description READ-ONLY. What the last write changed across the tenant's accounts. Sent back on a write it is accepted and ignored, so the whole document round-trips; it is never stored and never ledgered. */
31260
+ impact?: unknown;
31261
+ };
31262
+ };
31263
+ };
31264
+ responses: {
31265
+ /** @description The tenant safety defaults as they now read, plus the `impact` of this write. */
31266
+ 200: {
31267
+ headers: {
31268
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
31269
+ RateLimit: components["headers"]["RateLimit"];
31270
+ [name: string]: unknown;
31271
+ };
31272
+ content: {
31273
+ "application/json": {
31274
+ /**
31275
+ * @description Response type discriminator.
31276
+ * @enum {string}
31277
+ */
31278
+ object?: "tenant_safety_policy";
31279
+ /**
31280
+ * @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.
31281
+ * @enum {string|null}
31282
+ */
31283
+ tenant_default_posture?: "warn" | "enforce" | null;
31284
+ /** @description One entry per limit profile, always all of them. */
31285
+ profiles?: {
31286
+ /**
31287
+ * @description The limit profile these defaults apply to. An account resolves from its own profile's defaults.
31288
+ * @enum {string}
31289
+ */
31290
+ limit_profile?: "basic" | "premium" | "sales_navigator" | "recruiter";
31291
+ /** @description Every budget row, always: the tenant default where one is set, the Curviate default otherwise. */
31292
+ rows?: {
31293
+ /** @description The counted-action row this policy governs. */
31294
+ budget_row?: string;
31295
+ /** @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. */
31296
+ ceiling?: number | null;
31297
+ /** @description Routine band. */
31298
+ green?: number | null;
31299
+ /** @description Warn-louder band. */
31300
+ amber?: number | null;
31301
+ /**
31302
+ * @description The calendar grain this row is counted in.
31303
+ * @enum {string}
31304
+ */
31305
+ window_kind?: "day" | "week" | "month";
31306
+ /** @description How many grain units one counting window spans. */
31307
+ window_span?: number;
31308
+ /** @description IANA zone name the counting window is aligned to. Null counts in UTC. */
31309
+ window_timezone?: string | null;
31310
+ /** @description Local HH:MM the activity window opens. */
31311
+ activity_window_start?: string;
31312
+ /** @description Local HH:MM the activity window closes. */
31313
+ activity_window_end?: string;
31314
+ /** @description IANA zone name the activity window is read in. Null disables the window entirely. */
31315
+ activity_window_timezone?: string | null;
31316
+ /**
31317
+ * @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. A read Curviate answers from its own store never reaches LinkedIn, so it is never subject to the window under either setting, at any hour; the same read IS covered when the store cannot answer it, or when you ask for it live. Inbound events are never subject to it either. One action set to `all` reaches the whole account's live reads that spend no budget of their own (notifications, profile analytics, SSI, profile visitors, the invitation and saved-list listings): those calls debit no action type, so they have none to read this setting from and take the account-wide answer instead. Reads that DO debit an action type read that action type's own setting.
31318
+ * @enum {string}
31319
+ */
31320
+ activity_window_applies_to?: "writes" | "all";
31321
+ /** @description The tenant default multiplier for this row, or null when none is set. Null means each account follows its own derived warm-up ramp; 1 pins every account on this profile out of it. */
31322
+ warm_up_factor?: number | null;
31323
+ /** @description Length cap on the row's free-text field, in characters. */
31324
+ character_cap?: number | null;
31325
+ /** @description Length cap on the row's subject line, in characters. */
31326
+ subject_character_cap?: number | null;
31327
+ /** @description Bound on a single query's result lines. Search rows only. */
31328
+ per_query_cap?: number | null;
31329
+ /**
31330
+ * @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`.
31331
+ * @enum {string}
31332
+ */
31333
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
31334
+ /** @description What a call through the matching ELEVATED interface is held to on this row: the `/v1/{account_id}/sales-navigator/...` and `/v1/{account_id}/recruiter/...` operations. Null when this row and limit profile have no such figure, which is every row on `basic` and `premium` and most rows everywhere. Everything else on this row (`ceiling`, `effective_ceiling`, `band` and `over_default`) is the STANDARD-interface figure, because one counter serves both interfaces and the standard side is the conservative one to band against. The consequence is worth reading: on a `sales_navigator` or `recruiter` account a row can report `band: "over"` with `posture: "enforce"` while a call through the elevated interface still succeeds; the refusal the band predicts is the one a standard call gets. */
31335
+ interface_ceiling?: number | null;
31336
+ /** @description Empty when nothing on this row is above default. Computed against the STANDARD-interface default, like `band` and for the same reason. */
31337
+ over_default?: {
31338
+ /**
31339
+ * @description The field whose configured value is above default.
31340
+ * @enum {string}
31341
+ */
31342
+ field?: "ceiling" | "green" | "amber" | "character_cap" | "subject_character_cap" | "per_query_cap";
31343
+ /** @description The configured value. */
31344
+ value?: number;
31345
+ /** @description The Curviate default it exceeds. */
31346
+ default?: number;
31347
+ /**
31348
+ * @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.
31349
+ * @enum {string}
31350
+ */
31351
+ source_class?: "substrate" | "linkedin_official" | "practitioner" | "inferred" | "unseeded";
31352
+ }[];
31353
+ }[];
31354
+ }[];
31355
+ /** @description What this write changed across the tenant's accounts, measured on them before and after it applied. */
31356
+ impact?: {
31357
+ /** @description Accounts on which at least one budget row's resolved ceiling changed. */
31358
+ accounts_affected?: number;
31359
+ /** @description Accounts on which at least one budget row moved into `band: "over"`. */
31360
+ accounts_moved_into_over?: number;
31361
+ };
31362
+ safety_warning?: components["schemas"]["SafetyWarning"];
31363
+ };
31364
+ };
31365
+ };
31366
+ /** @description INVALID_REQUEST. An unknown limit profile, row name or field, or a value that is not a value (a negative ceiling). A value is never rejected for being unsafe. */
31367
+ 400: {
31368
+ headers: {
31369
+ [name: string]: unknown;
31370
+ };
31371
+ content: {
31372
+ "application/json": components["schemas"]["Error"];
31373
+ };
31374
+ };
31375
+ /** @description Payload too large, the request body exceeds the size limit. */
31376
+ 413: {
31377
+ headers: {
31378
+ [name: string]: unknown;
31379
+ };
31380
+ content: {
31381
+ "application/json": components["schemas"]["Error"];
31382
+ };
31383
+ };
31384
+ /** @description Rate limited, slow down and retry after the hinted delay. */
31385
+ 429: {
31386
+ headers: {
31387
+ "RateLimit-Policy": components["headers"]["RateLimit-Policy"];
31388
+ RateLimit: components["headers"]["RateLimit"];
31389
+ "Retry-After": components["headers"]["Retry-After"];
31390
+ [name: string]: unknown;
31391
+ };
31392
+ content: {
31393
+ "application/json": components["schemas"]["Error"];
31394
+ };
31395
+ };
31396
+ };
31397
+ };
30937
31398
  }