@trillboards/api-client 3.2.6 → 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. */
@@ -1378,7 +1378,7 @@ type EarningsBreakdownResponse = {
1378
1378
  }>;
1379
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. */
1380
1380
  pending_programmatic?: {
1381
- confirmation_date?: string;
1381
+ confirmation_date?: string | null;
1382
1382
  estimated_cents?: number;
1383
1383
  };
1384
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. */
@@ -2407,23 +2407,23 @@ type PartnerDefaultStream = {
2407
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. */
2408
2408
  _id?: string;
2409
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. */
2410
- canva_url?: string;
2410
+ canva_url?: string | null;
2411
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. */
2412
2412
  createdAt?: string;
2413
2413
  /** Falls back to `name`, then to `Background content`; the upload and image routes use `Uploaded video content` / `Image carousel content` instead. */
2414
2414
  description?: string;
2415
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. */
2416
- duration_seconds?: number;
2416
+ duration_seconds?: number | null;
2417
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. */
2418
- embed_url?: string;
2418
+ embed_url?: string | null;
2419
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. */
2420
- generic_iframe_url?: string;
2420
+ generic_iframe_url?: string | null;
2421
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. */
2422
- google_slides_url?: string;
2422
+ google_slides_url?: string | null;
2423
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. */
2424
- iframe_config?: Record<string, unknown>;
2424
+ iframe_config?: Record<string, unknown> | null;
2425
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. */
2426
- image_duration?: number;
2426
+ image_duration?: number | null;
2427
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. */
2428
2428
  images?: Array<string>;
2429
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. */
@@ -2443,13 +2443,13 @@ type PartnerDefaultStream = {
2443
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. */
2444
2444
  stream_type?: string;
2445
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. */
2446
- twitch_url?: string;
2446
+ twitch_url?: string | null;
2447
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. */
2448
2448
  updatedAt?: string;
2449
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. */
2450
- video_url?: string;
2450
+ video_url?: string | null;
2451
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. */
2452
- youtube_url?: string;
2452
+ youtube_url?: string | null;
2453
2453
  };
2454
2454
  type PartnerDefaultStreamCreate = {
2455
2455
  /** Accepted with no required-field check, same as `google_slides_url` — a `canva` create with this omitted succeeds and renders nothing. */
@@ -2888,7 +2888,7 @@ type PartnerLBarContent = {
2888
2888
  };
2889
2889
  type PartnerLBarDisplayPreferences = {
2890
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. */
2891
- ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all";
2891
+ ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all" | null | null;
2892
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. */
2893
2893
  display_mode?: "fullscreen" | "l-bar";
2894
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. */
@@ -2911,8 +2911,20 @@ type PartnerLBarDisplayPreferences = {
2911
2911
  };
2912
2912
  };
2913
2913
  type PartnerLBarSettings = {
2914
- /** 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`. */
2915
- 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
+ }>;
2916
2928
  display_preferences?: PartnerLBarDisplayPreferences;
2917
2929
  l_bar_content?: PartnerLBarContent;
2918
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`. */
@@ -3847,13 +3859,13 @@ type SensingRouteAction = {
3847
3859
  /** E.164 phone number (channel=whatsapp|sms). */
3848
3860
  recipientE164?: string;
3849
3861
  /** Recipient email (channel=email). */
3850
- recipientEmail?: string;
3862
+ recipientEmail?: string | null;
3851
3863
  /** Slack channel id (channel=slack). */
3852
- slackChannelId?: string;
3864
+ slackChannelId?: string | null;
3853
3865
  /** Message template name (managed WhatsApp/SMS). */
3854
3866
  templateName?: string;
3855
3867
  /** Target webhook endpoint id (channel=webhook). */
3856
- webhookId?: string;
3868
+ webhookId?: string | null;
3857
3869
  };
3858
3870
  /**
3859
3871
  * A threshold condition — the route fires when the sensed field crosses the value.
@@ -4982,7 +4994,8 @@ interface getFleetHourlyOperation {
4982
4994
  timeRange?: "today" | "last_7d" | "last_30d";
4983
4995
  };
4984
4996
  response: {
4985
- 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>> | {
4986
4999
  hours?: Array<Record<string, unknown>>;
4987
5000
  timeRange?: string;
4988
5001
  };
@@ -5070,7 +5083,8 @@ interface getNetworkTrendOperation {
5070
5083
  timeRange?: "today" | "last_7d" | "last_30d";
5071
5084
  };
5072
5085
  response: {
5073
- 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>> | {
5074
5088
  interval?: string;
5075
5089
  series?: Array<Record<string, unknown>>;
5076
5090
  timeRange?: string;
@@ -5169,7 +5183,7 @@ interface getSceneIntelligenceOperation {
5169
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. */
5170
5184
  measured: boolean;
5171
5185
  /** Null when `measured` is true. Otherwise names the operational failure. */
5172
- reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null;
5186
+ reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null | null;
5173
5187
  };
5174
5188
  scenes: Array<Record<string, unknown>>;
5175
5189
  };
@@ -5279,7 +5293,8 @@ interface getLiveAudienceOperation {
5279
5293
  mood?: string | null;
5280
5294
  noise_level?: number | null;
5281
5295
  occupancy?: string | null;
5282
- 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;
5283
5298
  } | null;
5284
5299
  last_updated?: string | null;
5285
5300
  /** Present when screen is offline */
@@ -9334,7 +9349,7 @@ interface requestStripePayoutOperation {
9334
9349
  currency?: string;
9335
9350
  description?: string;
9336
9351
  destination?: string;
9337
- /** Estimated arrival (2-3 business days) */
9352
+ /** Estimated arrival date (2-3 business days) */
9338
9353
  estimated_arrival?: string;
9339
9354
  status?: "pending" | "reversed";
9340
9355
  transfer_id?: string;
@@ -9683,6 +9698,7 @@ interface getUsageSummaryOperation {
9683
9698
  avgResponseTime?: number;
9684
9699
  dailyBreakdown?: Array<{
9685
9700
  avg_response_ms?: number;
9701
+ /** The UTC day, sent as the timestamp of its midnight. */
9686
9702
  date?: string;
9687
9703
  total_calls?: number;
9688
9704
  total_errors?: number;
@@ -9719,6 +9735,7 @@ interface getUsageDashboardOperation {
9719
9735
  avg_response_ms?: number;
9720
9736
  daily_breakdown?: Array<{
9721
9737
  avg_response_ms?: number;
9738
+ /** The UTC day, sent as the timestamp of its midnight. */
9722
9739
  date?: string;
9723
9740
  total_calls?: number;
9724
9741
  total_errors?: number;
@@ -10066,7 +10083,7 @@ interface testWebhookOperation {
10066
10083
  query: Record<string, unknown>;
10067
10084
  response: {
10068
10085
  data?: {
10069
- error?: string;
10086
+ error?: string | null;
10070
10087
  response_time_ms?: number;
10071
10088
  status?: number;
10072
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. */
@@ -1378,7 +1378,7 @@ type EarningsBreakdownResponse = {
1378
1378
  }>;
1379
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. */
1380
1380
  pending_programmatic?: {
1381
- confirmation_date?: string;
1381
+ confirmation_date?: string | null;
1382
1382
  estimated_cents?: number;
1383
1383
  };
1384
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. */
@@ -2407,23 +2407,23 @@ type PartnerDefaultStream = {
2407
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. */
2408
2408
  _id?: string;
2409
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. */
2410
- canva_url?: string;
2410
+ canva_url?: string | null;
2411
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. */
2412
2412
  createdAt?: string;
2413
2413
  /** Falls back to `name`, then to `Background content`; the upload and image routes use `Uploaded video content` / `Image carousel content` instead. */
2414
2414
  description?: string;
2415
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. */
2416
- duration_seconds?: number;
2416
+ duration_seconds?: number | null;
2417
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. */
2418
- embed_url?: string;
2418
+ embed_url?: string | null;
2419
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. */
2420
- generic_iframe_url?: string;
2420
+ generic_iframe_url?: string | null;
2421
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. */
2422
- google_slides_url?: string;
2422
+ google_slides_url?: string | null;
2423
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. */
2424
- iframe_config?: Record<string, unknown>;
2424
+ iframe_config?: Record<string, unknown> | null;
2425
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. */
2426
- image_duration?: number;
2426
+ image_duration?: number | null;
2427
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. */
2428
2428
  images?: Array<string>;
2429
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. */
@@ -2443,13 +2443,13 @@ type PartnerDefaultStream = {
2443
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. */
2444
2444
  stream_type?: string;
2445
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. */
2446
- twitch_url?: string;
2446
+ twitch_url?: string | null;
2447
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. */
2448
2448
  updatedAt?: string;
2449
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. */
2450
- video_url?: string;
2450
+ video_url?: string | null;
2451
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. */
2452
- youtube_url?: string;
2452
+ youtube_url?: string | null;
2453
2453
  };
2454
2454
  type PartnerDefaultStreamCreate = {
2455
2455
  /** Accepted with no required-field check, same as `google_slides_url` — a `canva` create with this omitted succeeds and renders nothing. */
@@ -2888,7 +2888,7 @@ type PartnerLBarContent = {
2888
2888
  };
2889
2889
  type PartnerLBarDisplayPreferences = {
2890
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. */
2891
- ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all";
2891
+ ad_surface_mode?: "fullscreen" | "lbar" | "pip" | "all" | null | null;
2892
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. */
2893
2893
  display_mode?: "fullscreen" | "l-bar";
2894
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. */
@@ -2911,8 +2911,20 @@ type PartnerLBarDisplayPreferences = {
2911
2911
  };
2912
2912
  };
2913
2913
  type PartnerLBarSettings = {
2914
- /** 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`. */
2915
- 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
+ }>;
2916
2928
  display_preferences?: PartnerLBarDisplayPreferences;
2917
2929
  l_bar_content?: PartnerLBarContent;
2918
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`. */
@@ -3847,13 +3859,13 @@ type SensingRouteAction = {
3847
3859
  /** E.164 phone number (channel=whatsapp|sms). */
3848
3860
  recipientE164?: string;
3849
3861
  /** Recipient email (channel=email). */
3850
- recipientEmail?: string;
3862
+ recipientEmail?: string | null;
3851
3863
  /** Slack channel id (channel=slack). */
3852
- slackChannelId?: string;
3864
+ slackChannelId?: string | null;
3853
3865
  /** Message template name (managed WhatsApp/SMS). */
3854
3866
  templateName?: string;
3855
3867
  /** Target webhook endpoint id (channel=webhook). */
3856
- webhookId?: string;
3868
+ webhookId?: string | null;
3857
3869
  };
3858
3870
  /**
3859
3871
  * A threshold condition — the route fires when the sensed field crosses the value.
@@ -4982,7 +4994,8 @@ interface getFleetHourlyOperation {
4982
4994
  timeRange?: "today" | "last_7d" | "last_30d";
4983
4995
  };
4984
4996
  response: {
4985
- 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>> | {
4986
4999
  hours?: Array<Record<string, unknown>>;
4987
5000
  timeRange?: string;
4988
5001
  };
@@ -5070,7 +5083,8 @@ interface getNetworkTrendOperation {
5070
5083
  timeRange?: "today" | "last_7d" | "last_30d";
5071
5084
  };
5072
5085
  response: {
5073
- 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>> | {
5074
5088
  interval?: string;
5075
5089
  series?: Array<Record<string, unknown>>;
5076
5090
  timeRange?: string;
@@ -5169,7 +5183,7 @@ interface getSceneIntelligenceOperation {
5169
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. */
5170
5184
  measured: boolean;
5171
5185
  /** Null when `measured` is true. Otherwise names the operational failure. */
5172
- reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null;
5186
+ reason: "timeout" | "error" | "dependency_disabled" | "unknown" | null | null;
5173
5187
  };
5174
5188
  scenes: Array<Record<string, unknown>>;
5175
5189
  };
@@ -5279,7 +5293,8 @@ interface getLiveAudienceOperation {
5279
5293
  mood?: string | null;
5280
5294
  noise_level?: number | null;
5281
5295
  occupancy?: string | null;
5282
- 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;
5283
5298
  } | null;
5284
5299
  last_updated?: string | null;
5285
5300
  /** Present when screen is offline */
@@ -9334,7 +9349,7 @@ interface requestStripePayoutOperation {
9334
9349
  currency?: string;
9335
9350
  description?: string;
9336
9351
  destination?: string;
9337
- /** Estimated arrival (2-3 business days) */
9352
+ /** Estimated arrival date (2-3 business days) */
9338
9353
  estimated_arrival?: string;
9339
9354
  status?: "pending" | "reversed";
9340
9355
  transfer_id?: string;
@@ -9683,6 +9698,7 @@ interface getUsageSummaryOperation {
9683
9698
  avgResponseTime?: number;
9684
9699
  dailyBreakdown?: Array<{
9685
9700
  avg_response_ms?: number;
9701
+ /** The UTC day, sent as the timestamp of its midnight. */
9686
9702
  date?: string;
9687
9703
  total_calls?: number;
9688
9704
  total_errors?: number;
@@ -9719,6 +9735,7 @@ interface getUsageDashboardOperation {
9719
9735
  avg_response_ms?: number;
9720
9736
  daily_breakdown?: Array<{
9721
9737
  avg_response_ms?: number;
9738
+ /** The UTC day, sent as the timestamp of its midnight. */
9722
9739
  date?: string;
9723
9740
  total_calls?: number;
9724
9741
  total_errors?: number;
@@ -10066,7 +10083,7 @@ interface testWebhookOperation {
10066
10083
  query: Record<string, unknown>;
10067
10084
  response: {
10068
10085
  data?: {
10069
- error?: string;
10086
+ error?: string | null;
10070
10087
  response_time_ms?: number;
10071
10088
  status?: number;
10072
10089
  };