@trillboards/api-client 3.2.5 → 3.2.8

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.
@@ -309,7 +309,7 @@ type AudienceSegment = {
309
309
  /** Stored and echoed but **nothing reads it** — `POST /segments/{id}/match` runs a segment regardless, and the list endpoint does not filter on it. Treat it as a caller-owned label, not a gate. */
310
310
  is_active?: boolean;
311
311
  /** When `POST /segments/{id}/match` last ran. The update is fire-and-forget after the response is sent, so a failed write leaves this stale or `null` with no error surfaced to the caller. */
312
- last_matched_at?: string;
312
+ last_matched_at?: string | null;
313
313
  /** Number of SCREENS returned by the most recent `POST /segments/{id}/match`, capped by that call's `limit` (default 50, max 100) — so it is the size of the last result page, not the segment's reach. `null` until the first match call. */
314
314
  last_matched_count?: number;
315
315
  /** Trimmed on write, max 100 characters, and unique per partner — a repeat name is rejected `409 duplicate_segment_name` rather than upserting the existing segment. */
@@ -333,6 +333,8 @@ type AudienceSegment = {
333
333
  type AudienceSocialAttention = {
334
334
  amplification_evidence: string;
335
335
  cascade_depth: number;
336
+ data_projection_version: number;
337
+ data_projection_xid: string;
336
338
  /** All 5 columns NOT NULL except primary_attention_anchor_face_index (only legit NULL: no anchor or solo scene). */
337
339
  primary_attention_anchor_face_index?: number | null;
338
340
  /** NUMERIC(4,3). */
@@ -1376,7 +1378,7 @@ type EarningsBreakdownResponse = {
1376
1378
  }>;
1377
1379
  /** **Both fields are hardcoded** — `estimated_cents` is always `0` and `confirmation_date` always `null`. Read `EarningsResponse.pending_confirmation_cents` for the real Net-60 pending figure. */
1378
1380
  pending_programmatic?: {
1379
- confirmation_date?: string;
1381
+ confirmation_date?: string | null;
1380
1382
  estimated_cents?: number;
1381
1383
  };
1382
1384
  /** `start`/`end` echo the request; with neither supplied the window is the last 30 days ending now. The end bound is EXCLUSIVE in the underlying query (`period_date < end`), so passing today's date omits today. */
@@ -2405,23 +2407,23 @@ type PartnerDefaultStream = {
2405
2407
  /** The stream's 24-hex id, minted server-side with `newObjectId()` at create and stored as `default_streams.mongo_default_stream_id`. It is the value every other default-stream route takes as `{streamId}`; the shape is Mongo's but PostgreSQL is the store of record. */
2406
2408
  _id?: string;
2407
2409
  /** Accepted with no required-field check, same as `google_slides_url`. The derived `canva_design_id` is returned alongside it although this schema does not declare it. */
2408
- canva_url?: string;
2410
+ canva_url?: string | null;
2409
2411
  /** Mapped from `default_streams.created_at`. It is the list endpoint's secondary sort key, so two streams sharing a `playback_order` play oldest-first. */
2410
2412
  createdAt?: string;
2411
2413
  /** Falls back to `name`, then to `Background content`; the upload and image routes use `Uploaded video content` / `Image carousel content` instead. */
2412
2414
  description?: string;
2413
2415
  /** Seconds one pass of this stream occupies. It is computed as `images.length x image_duration` when you send images without it; for every other type it is only ever what you sent — nothing probes a YouTube or uploaded video to fill it in on this path. */
2414
- duration_seconds?: number;
2416
+ duration_seconds?: number | null;
2415
2417
  /** The URL the player actually loads into the iframe. It defaults to `generic_iframe_url` when you do not send it, so on an iframe stream the two are usually equal. */
2416
- embed_url?: string;
2418
+ embed_url?: string | null;
2417
2419
  /** The iframe source for the `generic_iframe` type. It also seeds `embed_url` when that is omitted, so sending only this one populates both fields. */
2418
- generic_iframe_url?: string;
2420
+ generic_iframe_url?: string | null;
2419
2421
  /** Unlike `youtube`/`twitch`/`uploaded`/`images`, the `google_slides` type has NO required-field check — a create naming that type with no URL is accepted, stores null, and the screen then renders an empty frame. */
2420
- google_slides_url?: string;
2422
+ google_slides_url?: string | null;
2421
2423
  /** Only five keys survive: `auto_advance`, `slide_delay_ms`, `refresh_interval_seconds`, `sandbox_flags`, `allow_flags` — anything else is silently discarded and a non-object is discarded whole. On update it is MERGED over the stored config rather than replacing it, so a key cannot be cleared by omitting it. */
2422
- iframe_config?: Record<string, unknown>;
2424
+ iframe_config?: Record<string, unknown> | null;
2423
2425
  /** Seconds each image is held before advancing. The multipart image route defaults it to 10 when omitted and uses it to compute `duration_seconds`; the JSON create leaves it unset unless you send it. */
2424
- image_duration?: number;
2426
+ image_duration?: number | null;
2425
2427
  /** Ordered image URLs, each held for `image_duration` seconds. Required and non-empty when `stream_type` is `images` (400 otherwise), null for every other type; the create accepts a real array, a JSON-encoded array string, or a comma-separated string. */
2426
2428
  images?: Array<string>;
2427
2429
  /** Soft-deactivation flag. The list filters `is_active = true`, so it is always `true` on anything this API returns; the partner DELETE removes the row outright rather than clearing this flag, which is instead used by the screen-wide deactivate paths. */
@@ -2441,13 +2443,13 @@ type PartnerDefaultStream = {
2441
2443
  /** Which renderer the screen uses. When omitted at create it is DERIVED from whichever content field you sent, in this fixed order: `images`, `youtube_url`, `twitch_url`, `google_slides_url`, `canva_url`, `social_wall_url`, `generic_iframe_url`/`embed_url`, `video_url` (`uploaded`), `local_file_path` (`local`) — and the create is rejected 400 if none resolves. Only `youtube`, `uploaded`, `images` and `twitch` are ever content-analyzed; the four embed types deliberately keep `gemini_analyzed_at` NULL. */
2442
2444
  stream_type?: string;
2443
2445
  /** Required when `stream_type` is `twitch` (400 otherwise). The response also carries the undeclared `twitch_embed_url`, `twitch_content_type` and `twitch_id` companions that the PG reshape returns. */
2444
- twitch_url?: string;
2446
+ twitch_url?: string | null;
2445
2447
  /** Mapped from `default_streams.updated_at`. Playback bookkeeping writes it — marking a stream current, clearing a stale current flag, or stamping `last_played_at` — so it moves without anyone editing the stream content. */
2446
2448
  updatedAt?: string;
2447
2449
  /** Required when `stream_type` is `uploaded`. The `/default-streams/upload` route sets it for you to the stored object URL of the file you posted (`req.file.location`), so you never send it on that path. */
2448
- video_url?: string;
2450
+ video_url?: string | null;
2449
2451
  /** Required when `stream_type` is `youtube` — the create is rejected 400 (`youtube_url is required for youtube streams`) without it. Null on every other stream type. */
2450
- youtube_url?: string;
2452
+ youtube_url?: string | null;
2451
2453
  };
2452
2454
  type PartnerDefaultStreamCreate = {
2453
2455
  /** Accepted with no required-field check, same as `google_slides_url` — a `canva` create with this omitted succeeds and renders nothing. */
@@ -2886,7 +2888,7 @@ type PartnerLBarContent = {
2886
2888
  };
2887
2889
  type PartnerLBarDisplayPreferences = {
2888
2890
  /** Which surfaces ads may occupy; `null` by default, in which case it is derived from `display_mode` + `l_bar_ads_enabled`. Setting `lbar` also flips `display_mode` to `l-bar` when the same request does not set it, and narrows the fullscreen surface to `self_promo` only — paid and programmatic demand stops running full screen. */
2889
- ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all";
2891
+ ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all" | null | null;
2890
2892
  /** Default `fullscreen`. Writing it has side effects: `fullscreen` also forces `l_bar_ads_enabled` and `l_bar_content.enabled` to `false`, and `l-bar` forces both to `true` unless the same request sets them explicitly. A change also emits a `refresh` control event so the kiosk reloads and swaps surfaces. */
2891
2893
  display_mode?: "fullscreen" | "l-bar";
2892
2894
  /** Seconds one L-bar ad is held; default 8, accepted range 3-30. It must be strictly LESS than `l_bar_ad_interval`, and the check falls back to the stored interval when this request does not send one. */
@@ -2909,8 +2911,20 @@ type PartnerLBarDisplayPreferences = {
2909
2911
  };
2910
2912
  };
2911
2913
  type PartnerLBarSettings = {
2912
- /** The resolved renderable ad surfaces. Two caveats: it is present ONLY on the PUT response (the GET does not compute it), and despite the declared `object` type it is an ARRAY — one entry per surface (`fullscreen`, `lbar_slot_1..N`, `pip`) carrying `id`, `surface_type`, `enabled`, `display_area_ratio`, `dimensions` and `allowed_ad_types`. */
2913
- ad_surfaces?: Record<string, unknown>;
2914
+ /** The resolved renderable ad surfaces, one entry per surface (`fullscreen`, `lbar_slot_1..N`, `pip`). Present ONLY on the PUT response; the GET does not compute it. */
2915
+ ad_surfaces?: Array<{
2916
+ allowed_ad_types?: Array<string>;
2917
+ dimensions?: {
2918
+ height?: number;
2919
+ width?: number;
2920
+ };
2921
+ display_area_ratio?: number;
2922
+ display_mode?: string;
2923
+ enabled?: boolean;
2924
+ id?: string;
2925
+ surface_group?: string;
2926
+ surface_type?: string;
2927
+ }>;
2914
2928
  display_preferences?: PartnerLBarDisplayPreferences;
2915
2929
  l_bar_content?: PartnerLBarContent;
2916
2930
  /** The screen's resolved pacing contract, present only on the PUT response. `policy_version` is `v1` and every interval is in MILLISECONDS (`paid_interval_ms`, `self_promo_interval_ms`, `image_ad_total_ms`, `image_ad_min_ms`); it also carries `max_paid_share` (null unless a loop share cap is enabled), `surface_policy`, `schedule_policy`, `pip_settings` and `lbar_settings`. */
@@ -3845,13 +3859,13 @@ type SensingRouteAction = {
3845
3859
  /** E.164 phone number (channel=whatsapp|sms). */
3846
3860
  recipientE164?: string;
3847
3861
  /** Recipient email (channel=email). */
3848
- recipientEmail?: string;
3862
+ recipientEmail?: string | null;
3849
3863
  /** Slack channel id (channel=slack). */
3850
- slackChannelId?: string;
3864
+ slackChannelId?: string | null;
3851
3865
  /** Message template name (managed WhatsApp/SMS). */
3852
3866
  templateName?: string;
3853
3867
  /** Target webhook endpoint id (channel=webhook). */
3854
- webhookId?: string;
3868
+ webhookId?: string | null;
3855
3869
  };
3856
3870
  /**
3857
3871
  * A threshold condition — the route fires when the sensed field crosses the value.
@@ -4980,7 +4994,8 @@ interface getFleetHourlyOperation {
4980
4994
  timeRange?: "today" | "last_7d" | "last_30d";
4981
4995
  };
4982
4996
  response: {
4983
- data?: {
4997
+ /** One entry per hour of the day. A partner with no screens gets the empty form `{ hours: [], timeRange }` instead of an array. */
4998
+ data?: Array<Record<string, unknown>> | {
4984
4999
  hours?: Array<Record<string, unknown>>;
4985
5000
  timeRange?: string;
4986
5001
  };
@@ -5068,7 +5083,8 @@ interface getNetworkTrendOperation {
5068
5083
  timeRange?: "today" | "last_7d" | "last_30d";
5069
5084
  };
5070
5085
  response: {
5071
- data?: {
5086
+ /** One bucket per `interval`. A partner with no screens gets the empty form `{ series: [], interval, timeRange }` instead of an array. */
5087
+ data?: Array<Record<string, unknown>> | {
5072
5088
  interval?: string;
5073
5089
  series?: Array<Record<string, unknown>>;
5074
5090
  timeRange?: string;
@@ -5167,7 +5183,7 @@ interface getSceneIntelligenceOperation {
5167
5183
  /** True when the scene read completed and `scenes` reflects the fleet. False when it did not run to completion — `scenes` is then empty for want of an answer, not because the fleet is quiet. */
5168
5184
  measured: boolean;
5169
5185
  /** Null when `measured` is true. Otherwise names the operational failure. */
5170
- reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null;
5186
+ reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null | null;
5171
5187
  };
5172
5188
  scenes: Array<Record<string, unknown>>;
5173
5189
  };
@@ -5277,7 +5293,8 @@ interface getLiveAudienceOperation {
5277
5293
  mood?: string | null;
5278
5294
  noise_level?: number | null;
5279
5295
  occupancy?: string | null;
5280
- purchase_intent?: "NONE" | "BROWSING" | "CONSIDERING" | "COMPARING" | "READY_TO_BUY" | null;
5296
+ /** The speech classifier's reading when the screen reported one (`BROWSING`, `RESEARCHING`, `COMPARING`, `READY_TO_BUY`, `POST_PURCHASE`, `COMPLAINING`, `NONE`), otherwise the segment the purchase-journey stage maps to (`high`, `medium`, `low`), otherwise null. */
5297
+ purchase_intent?: "BROWSING" | "RESEARCHING" | "COMPARING" | "READY_TO_BUY" | "POST_PURCHASE" | "COMPLAINING" | "NONE" | "high" | "medium" | "low" | null | null;
5281
5298
  } | null;
5282
5299
  last_updated?: string | null;
5283
5300
  /** Present when screen is offline */
@@ -9332,7 +9349,7 @@ interface requestStripePayoutOperation {
9332
9349
  currency?: string;
9333
9350
  description?: string;
9334
9351
  destination?: string;
9335
- /** Estimated arrival (2-3 business days) */
9352
+ /** Estimated arrival date (2-3 business days) */
9336
9353
  estimated_arrival?: string;
9337
9354
  status?: "pending" | "reversed";
9338
9355
  transfer_id?: string;
@@ -9681,6 +9698,7 @@ interface getUsageSummaryOperation {
9681
9698
  avgResponseTime?: number;
9682
9699
  dailyBreakdown?: Array<{
9683
9700
  avg_response_ms?: number;
9701
+ /** The UTC day, sent as the timestamp of its midnight. */
9684
9702
  date?: string;
9685
9703
  total_calls?: number;
9686
9704
  total_errors?: number;
@@ -9717,6 +9735,7 @@ interface getUsageDashboardOperation {
9717
9735
  avg_response_ms?: number;
9718
9736
  daily_breakdown?: Array<{
9719
9737
  avg_response_ms?: number;
9738
+ /** The UTC day, sent as the timestamp of its midnight. */
9720
9739
  date?: string;
9721
9740
  total_calls?: number;
9722
9741
  total_errors?: number;
@@ -10064,7 +10083,7 @@ interface testWebhookOperation {
10064
10083
  query: Record<string, unknown>;
10065
10084
  response: {
10066
10085
  data?: {
10067
- error?: string;
10086
+ error?: string | null;
10068
10087
  response_time_ms?: number;
10069
10088
  status?: number;
10070
10089
  };
@@ -309,7 +309,7 @@ type AudienceSegment = {
309
309
  /** Stored and echoed but **nothing reads it** — `POST /segments/{id}/match` runs a segment regardless, and the list endpoint does not filter on it. Treat it as a caller-owned label, not a gate. */
310
310
  is_active?: boolean;
311
311
  /** When `POST /segments/{id}/match` last ran. The update is fire-and-forget after the response is sent, so a failed write leaves this stale or `null` with no error surfaced to the caller. */
312
- last_matched_at?: string;
312
+ last_matched_at?: string | null;
313
313
  /** Number of SCREENS returned by the most recent `POST /segments/{id}/match`, capped by that call's `limit` (default 50, max 100) — so it is the size of the last result page, not the segment's reach. `null` until the first match call. */
314
314
  last_matched_count?: number;
315
315
  /** Trimmed on write, max 100 characters, and unique per partner — a repeat name is rejected `409 duplicate_segment_name` rather than upserting the existing segment. */
@@ -333,6 +333,8 @@ type AudienceSegment = {
333
333
  type AudienceSocialAttention = {
334
334
  amplification_evidence: string;
335
335
  cascade_depth: number;
336
+ data_projection_version: number;
337
+ data_projection_xid: string;
336
338
  /** All 5 columns NOT NULL except primary_attention_anchor_face_index (only legit NULL: no anchor or solo scene). */
337
339
  primary_attention_anchor_face_index?: number | null;
338
340
  /** NUMERIC(4,3). */
@@ -1376,7 +1378,7 @@ type EarningsBreakdownResponse = {
1376
1378
  }>;
1377
1379
  /** **Both fields are hardcoded** — `estimated_cents` is always `0` and `confirmation_date` always `null`. Read `EarningsResponse.pending_confirmation_cents` for the real Net-60 pending figure. */
1378
1380
  pending_programmatic?: {
1379
- confirmation_date?: string;
1381
+ confirmation_date?: string | null;
1380
1382
  estimated_cents?: number;
1381
1383
  };
1382
1384
  /** `start`/`end` echo the request; with neither supplied the window is the last 30 days ending now. The end bound is EXCLUSIVE in the underlying query (`period_date < end`), so passing today's date omits today. */
@@ -2405,23 +2407,23 @@ type PartnerDefaultStream = {
2405
2407
  /** The stream's 24-hex id, minted server-side with `newObjectId()` at create and stored as `default_streams.mongo_default_stream_id`. It is the value every other default-stream route takes as `{streamId}`; the shape is Mongo's but PostgreSQL is the store of record. */
2406
2408
  _id?: string;
2407
2409
  /** Accepted with no required-field check, same as `google_slides_url`. The derived `canva_design_id` is returned alongside it although this schema does not declare it. */
2408
- canva_url?: string;
2410
+ canva_url?: string | null;
2409
2411
  /** Mapped from `default_streams.created_at`. It is the list endpoint's secondary sort key, so two streams sharing a `playback_order` play oldest-first. */
2410
2412
  createdAt?: string;
2411
2413
  /** Falls back to `name`, then to `Background content`; the upload and image routes use `Uploaded video content` / `Image carousel content` instead. */
2412
2414
  description?: string;
2413
2415
  /** Seconds one pass of this stream occupies. It is computed as `images.length x image_duration` when you send images without it; for every other type it is only ever what you sent — nothing probes a YouTube or uploaded video to fill it in on this path. */
2414
- duration_seconds?: number;
2416
+ duration_seconds?: number | null;
2415
2417
  /** The URL the player actually loads into the iframe. It defaults to `generic_iframe_url` when you do not send it, so on an iframe stream the two are usually equal. */
2416
- embed_url?: string;
2418
+ embed_url?: string | null;
2417
2419
  /** The iframe source for the `generic_iframe` type. It also seeds `embed_url` when that is omitted, so sending only this one populates both fields. */
2418
- generic_iframe_url?: string;
2420
+ generic_iframe_url?: string | null;
2419
2421
  /** Unlike `youtube`/`twitch`/`uploaded`/`images`, the `google_slides` type has NO required-field check — a create naming that type with no URL is accepted, stores null, and the screen then renders an empty frame. */
2420
- google_slides_url?: string;
2422
+ google_slides_url?: string | null;
2421
2423
  /** Only five keys survive: `auto_advance`, `slide_delay_ms`, `refresh_interval_seconds`, `sandbox_flags`, `allow_flags` — anything else is silently discarded and a non-object is discarded whole. On update it is MERGED over the stored config rather than replacing it, so a key cannot be cleared by omitting it. */
2422
- iframe_config?: Record<string, unknown>;
2424
+ iframe_config?: Record<string, unknown> | null;
2423
2425
  /** Seconds each image is held before advancing. The multipart image route defaults it to 10 when omitted and uses it to compute `duration_seconds`; the JSON create leaves it unset unless you send it. */
2424
- image_duration?: number;
2426
+ image_duration?: number | null;
2425
2427
  /** Ordered image URLs, each held for `image_duration` seconds. Required and non-empty when `stream_type` is `images` (400 otherwise), null for every other type; the create accepts a real array, a JSON-encoded array string, or a comma-separated string. */
2426
2428
  images?: Array<string>;
2427
2429
  /** Soft-deactivation flag. The list filters `is_active = true`, so it is always `true` on anything this API returns; the partner DELETE removes the row outright rather than clearing this flag, which is instead used by the screen-wide deactivate paths. */
@@ -2441,13 +2443,13 @@ type PartnerDefaultStream = {
2441
2443
  /** Which renderer the screen uses. When omitted at create it is DERIVED from whichever content field you sent, in this fixed order: `images`, `youtube_url`, `twitch_url`, `google_slides_url`, `canva_url`, `social_wall_url`, `generic_iframe_url`/`embed_url`, `video_url` (`uploaded`), `local_file_path` (`local`) — and the create is rejected 400 if none resolves. Only `youtube`, `uploaded`, `images` and `twitch` are ever content-analyzed; the four embed types deliberately keep `gemini_analyzed_at` NULL. */
2442
2444
  stream_type?: string;
2443
2445
  /** Required when `stream_type` is `twitch` (400 otherwise). The response also carries the undeclared `twitch_embed_url`, `twitch_content_type` and `twitch_id` companions that the PG reshape returns. */
2444
- twitch_url?: string;
2446
+ twitch_url?: string | null;
2445
2447
  /** Mapped from `default_streams.updated_at`. Playback bookkeeping writes it — marking a stream current, clearing a stale current flag, or stamping `last_played_at` — so it moves without anyone editing the stream content. */
2446
2448
  updatedAt?: string;
2447
2449
  /** Required when `stream_type` is `uploaded`. The `/default-streams/upload` route sets it for you to the stored object URL of the file you posted (`req.file.location`), so you never send it on that path. */
2448
- video_url?: string;
2450
+ video_url?: string | null;
2449
2451
  /** Required when `stream_type` is `youtube` — the create is rejected 400 (`youtube_url is required for youtube streams`) without it. Null on every other stream type. */
2450
- youtube_url?: string;
2452
+ youtube_url?: string | null;
2451
2453
  };
2452
2454
  type PartnerDefaultStreamCreate = {
2453
2455
  /** Accepted with no required-field check, same as `google_slides_url` — a `canva` create with this omitted succeeds and renders nothing. */
@@ -2886,7 +2888,7 @@ type PartnerLBarContent = {
2886
2888
  };
2887
2889
  type PartnerLBarDisplayPreferences = {
2888
2890
  /** Which surfaces ads may occupy; `null` by default, in which case it is derived from `display_mode` + `l_bar_ads_enabled`. Setting `lbar` also flips `display_mode` to `l-bar` when the same request does not set it, and narrows the fullscreen surface to `self_promo` only — paid and programmatic demand stops running full screen. */
2889
- ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all";
2891
+ ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all" | null | null;
2890
2892
  /** Default `fullscreen`. Writing it has side effects: `fullscreen` also forces `l_bar_ads_enabled` and `l_bar_content.enabled` to `false`, and `l-bar` forces both to `true` unless the same request sets them explicitly. A change also emits a `refresh` control event so the kiosk reloads and swaps surfaces. */
2891
2893
  display_mode?: "fullscreen" | "l-bar";
2892
2894
  /** Seconds one L-bar ad is held; default 8, accepted range 3-30. It must be strictly LESS than `l_bar_ad_interval`, and the check falls back to the stored interval when this request does not send one. */
@@ -2909,8 +2911,20 @@ type PartnerLBarDisplayPreferences = {
2909
2911
  };
2910
2912
  };
2911
2913
  type PartnerLBarSettings = {
2912
- /** The resolved renderable ad surfaces. Two caveats: it is present ONLY on the PUT response (the GET does not compute it), and despite the declared `object` type it is an ARRAY — one entry per surface (`fullscreen`, `lbar_slot_1..N`, `pip`) carrying `id`, `surface_type`, `enabled`, `display_area_ratio`, `dimensions` and `allowed_ad_types`. */
2913
- ad_surfaces?: Record<string, unknown>;
2914
+ /** The resolved renderable ad surfaces, one entry per surface (`fullscreen`, `lbar_slot_1..N`, `pip`). Present ONLY on the PUT response; the GET does not compute it. */
2915
+ ad_surfaces?: Array<{
2916
+ allowed_ad_types?: Array<string>;
2917
+ dimensions?: {
2918
+ height?: number;
2919
+ width?: number;
2920
+ };
2921
+ display_area_ratio?: number;
2922
+ display_mode?: string;
2923
+ enabled?: boolean;
2924
+ id?: string;
2925
+ surface_group?: string;
2926
+ surface_type?: string;
2927
+ }>;
2914
2928
  display_preferences?: PartnerLBarDisplayPreferences;
2915
2929
  l_bar_content?: PartnerLBarContent;
2916
2930
  /** The screen's resolved pacing contract, present only on the PUT response. `policy_version` is `v1` and every interval is in MILLISECONDS (`paid_interval_ms`, `self_promo_interval_ms`, `image_ad_total_ms`, `image_ad_min_ms`); it also carries `max_paid_share` (null unless a loop share cap is enabled), `surface_policy`, `schedule_policy`, `pip_settings` and `lbar_settings`. */
@@ -3845,13 +3859,13 @@ type SensingRouteAction = {
3845
3859
  /** E.164 phone number (channel=whatsapp|sms). */
3846
3860
  recipientE164?: string;
3847
3861
  /** Recipient email (channel=email). */
3848
- recipientEmail?: string;
3862
+ recipientEmail?: string | null;
3849
3863
  /** Slack channel id (channel=slack). */
3850
- slackChannelId?: string;
3864
+ slackChannelId?: string | null;
3851
3865
  /** Message template name (managed WhatsApp/SMS). */
3852
3866
  templateName?: string;
3853
3867
  /** Target webhook endpoint id (channel=webhook). */
3854
- webhookId?: string;
3868
+ webhookId?: string | null;
3855
3869
  };
3856
3870
  /**
3857
3871
  * A threshold condition — the route fires when the sensed field crosses the value.
@@ -4980,7 +4994,8 @@ interface getFleetHourlyOperation {
4980
4994
  timeRange?: "today" | "last_7d" | "last_30d";
4981
4995
  };
4982
4996
  response: {
4983
- data?: {
4997
+ /** One entry per hour of the day. A partner with no screens gets the empty form `{ hours: [], timeRange }` instead of an array. */
4998
+ data?: Array<Record<string, unknown>> | {
4984
4999
  hours?: Array<Record<string, unknown>>;
4985
5000
  timeRange?: string;
4986
5001
  };
@@ -5068,7 +5083,8 @@ interface getNetworkTrendOperation {
5068
5083
  timeRange?: "today" | "last_7d" | "last_30d";
5069
5084
  };
5070
5085
  response: {
5071
- data?: {
5086
+ /** One bucket per `interval`. A partner with no screens gets the empty form `{ series: [], interval, timeRange }` instead of an array. */
5087
+ data?: Array<Record<string, unknown>> | {
5072
5088
  interval?: string;
5073
5089
  series?: Array<Record<string, unknown>>;
5074
5090
  timeRange?: string;
@@ -5167,7 +5183,7 @@ interface getSceneIntelligenceOperation {
5167
5183
  /** True when the scene read completed and `scenes` reflects the fleet. False when it did not run to completion — `scenes` is then empty for want of an answer, not because the fleet is quiet. */
5168
5184
  measured: boolean;
5169
5185
  /** Null when `measured` is true. Otherwise names the operational failure. */
5170
- reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null;
5186
+ reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null | null;
5171
5187
  };
5172
5188
  scenes: Array<Record<string, unknown>>;
5173
5189
  };
@@ -5277,7 +5293,8 @@ interface getLiveAudienceOperation {
5277
5293
  mood?: string | null;
5278
5294
  noise_level?: number | null;
5279
5295
  occupancy?: string | null;
5280
- purchase_intent?: "NONE" | "BROWSING" | "CONSIDERING" | "COMPARING" | "READY_TO_BUY" | null;
5296
+ /** The speech classifier's reading when the screen reported one (`BROWSING`, `RESEARCHING`, `COMPARING`, `READY_TO_BUY`, `POST_PURCHASE`, `COMPLAINING`, `NONE`), otherwise the segment the purchase-journey stage maps to (`high`, `medium`, `low`), otherwise null. */
5297
+ purchase_intent?: "BROWSING" | "RESEARCHING" | "COMPARING" | "READY_TO_BUY" | "POST_PURCHASE" | "COMPLAINING" | "NONE" | "high" | "medium" | "low" | null | null;
5281
5298
  } | null;
5282
5299
  last_updated?: string | null;
5283
5300
  /** Present when screen is offline */
@@ -9332,7 +9349,7 @@ interface requestStripePayoutOperation {
9332
9349
  currency?: string;
9333
9350
  description?: string;
9334
9351
  destination?: string;
9335
- /** Estimated arrival (2-3 business days) */
9352
+ /** Estimated arrival date (2-3 business days) */
9336
9353
  estimated_arrival?: string;
9337
9354
  status?: "pending" | "reversed";
9338
9355
  transfer_id?: string;
@@ -9681,6 +9698,7 @@ interface getUsageSummaryOperation {
9681
9698
  avgResponseTime?: number;
9682
9699
  dailyBreakdown?: Array<{
9683
9700
  avg_response_ms?: number;
9701
+ /** The UTC day, sent as the timestamp of its midnight. */
9684
9702
  date?: string;
9685
9703
  total_calls?: number;
9686
9704
  total_errors?: number;
@@ -9717,6 +9735,7 @@ interface getUsageDashboardOperation {
9717
9735
  avg_response_ms?: number;
9718
9736
  daily_breakdown?: Array<{
9719
9737
  avg_response_ms?: number;
9738
+ /** The UTC day, sent as the timestamp of its midnight. */
9720
9739
  date?: string;
9721
9740
  total_calls?: number;
9722
9741
  total_errors?: number;
@@ -10064,7 +10083,7 @@ interface testWebhookOperation {
10064
10083
  query: Record<string, unknown>;
10065
10084
  response: {
10066
10085
  data?: {
10067
- error?: string;
10086
+ error?: string | null;
10068
10087
  response_time_ms?: number;
10069
10088
  status?: number;
10070
10089
  };