Trillboards OpenRTB 2.6 Extension Schema Reference
version 2026-03-v1
Schema reference for Trillboards' proprietary OpenRTB 2.6 bid request extensions. This is NOT a bid endpoint specification — it documents the SIGNALS injected into every bid request sent to DSPs so engineers can build bidding algorithms that leverage Trillboards' unique audience intelligence.
How to Use This Document
When Trillboards sends a bid request to your DSP, it includes standard OpenRTB 2.6 fields plus the proprietary extensions documented here. Your bidding algorithm can use these signals to bid higher on high-value inventory.
Extension Namespaces
| Namespace | What It Contains | Freshness |
|---|---|---|
ext.trillboards | Real-time audience signals from FEIN edge AI | 10-30 seconds |
ext.trb.vas | Verified Attention Seconds (historical quality) | 7-day rolling |
ext.trb.intent | Purchase intent from speech analysis | 10-30 seconds |
ext.trb.weather | Contextual weather conditions | 60-minute cache |
ext.trb.foot_traffic | Venue occupancy and traffic | 10-30 seconds |
ext.trillboards_contract | Signal quality assurance | Per-request |
user.data[] | IAB Audience Taxonomy 1.1 segments + seller-defined | 10-30 seconds |
Signal Sources
- FEIN Edge AI: On-device ML Kit (face detection, gaze tracking) + YAMNet TFLite (audio classification) on the tablet, which sends Trillboards a record for each detected person and sends selected camera frames to a cloud model.
- VAS Engine: Verified Attention Seconds computed from historical FEIN measurements, stored in PostgreSQL attention_ledger.
- Intent Graph: Speech-derived purchase intent from YAMNet audio scene classification + keyword detection on the tablet. The tablet also sends Trillboards the speech transcript, which a cloud model reads for purchase intent and brands mentioned.
- Weather Service: WeatherAPI.com data cached in Redis with 60-minute TTL.
- Census ACS: US Census American Community Survey neighborhood demographics by geohash.
Data behind the signals
- Bid requests carry the screen's IP address, its location (its live GPS position where the screen reports one) and its live audience measurements.
- The tablet keeps a record for each person its camera detects: estimated age range, gender, dominant emotion, dwell time and the time spent looking at the screen. On tablets running the full sensing profile it also computes a face-identity template, which Trillboards uses to recognise the same person across screens and days.
- Selected camera frames go to a cloud model (Google Vertex AI) that estimates ethnicity, age band, gender, social composition, purchase-intent stage, activity, attire, carried items, lifestyle segments and mood. Trillboards keeps those frames for 400 days; the frames captured between 5 May and 21 June 2026 are kept as a model-evaluation set with no scheduled deletion.
- Audio: the tablet classifies ambient sound and transcribes speech on the device, and Trillboards stores the transcript. No audio recording is stored or transmitted.
- Radio: the tablet sends Trillboards the Bluetooth, Wi-Fi and local-network identifiers nearby phones broadcast. Trillboards stores them and links device keys to the person seen with them and to mobile advertising IDs.
- Trillboards sells and shares this audience measurement and location data with its data partners, including 375ai and Intuizi. Each category of data partner and what it receives: https://trillboards.com/legal/data-partners
Schemas
30 schemas. Every one is derived from docs/openapi/openrtb-extensions.yaml at request time.
AudienceAttentionPrediction · AudienceSocialAttention · BidRequestExt · CompletedImpressions · ContractExt · DOOHBidRequest · DOOHObject · DemandPartners · Device · EarnerScreens · EventExt · FootTrafficExt · IABSegmentReference · Impression · IntentExt · OpenrtbRevenueScreenHourly · PartnerReconciliationReports · PartnerVariants · PartnerZoneCpmDaily · Regs · Segment · SellerDefinedSegmentReference · Source · TrbNamespace · TrillboardsAudienceExt · User · UserData · VASExt · VenueExt · WeatherExt
AudienceAttentionPrediction
Backend-derived predictive attention signal + buyer-grade attentive-seconds. 1:1 with audience_metrics. Never null by contract; placeholder on row 1.
| Property | Type | Required | Description |
|---|---|---|---|
predicted_attention_next_30s | string — one of high, medium, low, none | yes | predicted_attention_next_30s enum {high,medium,low,none}. 'none' is below 'low'; fires when rolling history converges to zero or on a trend down from 'low'. |
predicted_disengagement_imminent | boolean | yes | predicted_disengagement_imminent needs elevated dwell AND a falling trend, so it is unreachable whenever the history vector is empty. A 0% fire rate is a HISTORY-PLUMBING alarm, not a quiet fleet. |
prediction_evidence | string | yes | |
attentive_seconds_observed | number | yes | attentive_seconds_observed = SUM(per_face[].gaze_seconds >= 1.0), OpenOOH 2024 4.2. NEVER NULL by DDL, so 0.000 means 'no viewer met threshold' ONLY when per_face_observations[] was present. NUMERIC(6,3). |
attentive_face_count | integer | yes | attentive_face_count SMALLINT: distinct viewers meeting the >=1s threshold; the buyer-side CPMA denominator. NEVER NULL. |
attentive_seconds_predicted_15s | number | yes | attentive_seconds_predicted_15s: expected next-15s attentive seconds from per-screen history x attention-weight; bootstrap = linear extrapolation. No global tau. NUMERIC(6,3). |
next_state_attention_forecast | number, nullable | ||
next_state_confidence | number, nullable | ||
next_state_method | string, nullable | ||
next_state_horizon_minutes | integer, nullable | ||
next_state_sample_size | integer, nullable | ||
next_state_forecast_at | string, nullable | ||
next_state_attention_realized | number, nullable | ||
next_state_abs_error | number, nullable | ||
next_state_graded_at | string, nullable | ||
next_state_persist_abs_error | number, nullable | ||
next_state_xscreen_abs_error | number, nullable | ||
next_state_xscreen_sample_size | integer, nullable | ||
next_state_xscreen_method | string, nullable | ||
next_state_xsv_abs_error | number, nullable | ||
next_state_xsv_sample_size | integer, nullable |
AudienceSocialAttention
Buyer-grade social amplification signal (5 typed columns). Cloud Vertex Gemini producer. 1 row per audience observation when cascade is observable.
| Property | Type | Required | Description |
|---|---|---|---|
social_amplification_factor | number | yes | NUMERIC(4,3). |
cascade_depth | integer | yes | |
viral_attention_score | number | yes | viral_attention_score is the headline number Viant/T-Vision license. Composite of social_amplification_factor × cascade_depth × emotional engagement. NUMERIC(4,3). |
primary_attention_anchor_face_index | integer, nullable | All 5 columns NOT NULL except primary_attention_anchor_face_index (only legit NULL: no anchor or solo scene). | |
amplification_evidence | string | yes | |
data_projection_version | integer | yes | |
data_projection_xid | string | yes |
BidRequestExt
Top-level bid request extensions.
⚠️ Both Trillboards namespaces are STRIPPED on curated (registered-audience) deals
When a bid request is served under a curation plan with registeredAudienceOnly — the shape behind our curated PMP / registered audience deals — the server deletes ext.trillboards and ext.trb outright before the request leaves us (services/programmatic/bidstreamSignalCuration.js). If ext is left with no other keys, ext itself is removed.
This is deliberate: on a curated deal the audience is carried by the registered segment in user.data, and the raw sensing signals are withheld.
The consequence for a bidder is the trap worth stating plainly: every signal described on this page disappears on exactly the deals we most want you buying. A bidder whose valuation depends on ext.trb.vas, ext.trillboards.attention_score, or any other field here will price curated inventory as though the screen had no audience at all.
Build for this explicitly:
- Treat both namespaces as absent-by-default and gate every read on their presence. Do not assume a partially-populated
ext. - Do not infer "no audience" or a zero-valued audience from their absence.
- On a curated deal, read the audience from
user.data(the registered segment) instead. That is where the targeting signal lives for these deals. - The stripping is all-or-nothing per request: you will never see
ext.trbsurvive whileext.trillboardsis removed, or vice versa.
| Property | Type | Required | Description |
|---|---|---|---|
protocol | string | ||
trillboards | TrillboardsAudienceExt | ||
trb | TrbNamespace | ||
trillboards_contract | ContractExt |
CompletedImpressions
Completed-play observations from VAST complete and proof-of-play paths. Deduped by issued request/impression key for live revenue and daily earnings.
| Property | Type | Required | Description |
|---|---|---|---|
id | integer, nullable | JustBaat dedupes issued request keys; Adipolo/VidVerto/others use rendered impression_id with request fallback. Readers/rollups dedupe overlapping vast_event, proof_of_play and approved partner_sdk. | |
completed_at | string | yes | |
screen_mongo_id | string | yes | |
device_id | string, nullable | ||
ad_id | string | yes | |
creative_id | string, nullable | ||
impression_id | string, nullable | ||
source_provider | string | yes | ImpressionController drops rows with unknown source_provider (PR #2924); earnings use partner billing policy, not raw counts. Exclude pi_e2e_*, admin-allocation and is_demo_data from revenue. |
variant_name | string, nullable | ||
actual_cpm | number, nullable | NUMERIC(18,6). | |
actual_gross_revenue | number, nullable | NUMERIC(18,6). | |
pricing_source | string | yes | PG is the canonical completion write; completed_impressions_dist is its CH reporting mirror. pricing_source: 'placement_purchase'=ad_price/goal per play; '_overdelivered'/'_zero'=$0; 'config_defa... |
currency | string | yes | |
ingestion_path | string | yes | |
request_id | string, nullable | owned_direct = signed src or verified SDK. VAST impid, request_id and SDK suffix share one nonce/PG row; completion, exact debit and eligible billing commit together. First authorized write owns CH... | |
bid_request_id | string, nullable | ||
play_id | string, nullable | ||
duration_seconds | number, nullable | NUMERIC(10,2). | |
givt_passed | boolean, nullable | ||
signature | string, nullable | ||
display_mode | string, nullable | ||
display_area_ratio | number, nullable | NUMERIC(5,4). | |
metadata | object | yes | SDK missing-price is server-pending until signed native pricing; directbillable_ is not authority. Client debit/mirror/pending claims are stripped. Legacy finalization: recorded/native nonnull zero... |
created_at | string | yes | chEventParityCron: exact-key repair; created_at/oldest authorized floor; original completed_at CH key; same writer/DLQ before ACK; no delivery debit. All leaf pending indexes must be valid; failed/... |
screen_id | integer, nullable | ||
demand_partner_id | integer, nullable | ||
variant_id | integer, nullable | ||
content_sha256 | string, nullable | content_sha256 is DECLARED to propagate from vast_requests via request_id but lands on ~1% of rows: PG vast_requests is empty (vastRequestsWriteBuffer defaultDisabled since 2026-06-26), so that lin... | |
serving_tenant_id | integer, nullable | ||
impression_multiplier | number | yes | NUMERIC(12,6). |
quantity_source | string, nullable | ||
quantity_source_type | integer, nullable | ||
quantity_vendor | string, nullable | ||
quantity_measured_at | string, nullable | ||
earnings_source | string, nullable | ||
earnings_deal_id | string, nullable | ||
earnings_deal_type | string, nullable | ||
advertiser_domain | string, nullable | ||
buyer_seat | string, nullable | ||
zone_id | string, nullable | ||
raw_client_timestamp | string, nullable | ||
normalized_event_timestamp | string, nullable | ||
event_clock_skew_seconds | number, nullable | ||
event_timestamp_clamped | boolean, nullable | ||
billable_key | string, nullable |
ContractExt
Signal quality assurance contract. Tells DSPs how complete and reliable the signals in this bid request are. If valid=false, some required fields are missing (but the request is still valid OpenRTB).
| Property | Type | Required | Description |
|---|---|---|---|
version | string | Contract schema version | |
valid | boolean | Whether all required fields passed validation | |
required_field_coverage_pct | number | Percentage of required fields that are present and valid | |
error_count | integer | Number of validation errors (missing required fields) | |
warning_count | integer | Number of validation warnings (recommended fields missing) | |
errors | array of object | ||
warnings | array of object |
{
"version": "2026-03-v1",
"valid": true,
"required_field_coverage_pct": 100,
"error_count": 0,
"warning_count": 0,
"errors": [],
"warnings": []
}
DOOHBidRequest
Full OpenRTB 2.6 DOOH bid request with all Trillboards extensions
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Unique bid request ID | |
imp | array of Impression | ||
dooh | DOOHObject | ||
device | Device | ||
user | User | ||
source | Source | ||
regs | Regs | ||
at | integer — one of 1 | Auction type. Always 1 (first price) | |
tmax | integer | Maximum response time in milliseconds | |
cur | array of string | ||
ext | BidRequestExt |
{
"id": "691fced1-040c-4354-b861-ef2496f44f29",
"imp": [
{
"id": "imp_507f1f77bcf86cd799439011",
"tagid": "507f1f77bcf86cd799439011",
"bidfloor": 2,
"bidfloorcur": "USD",
"secure": 1,
"dt": 1788279423607,
"qty": {
"multiplier": 2,
"sourcetype": 2
},
"banner": {
"w": 1920,
"h": 1080,
"format": [
{
"w": 1920,
"h": 1080
}
],
"pos": 1
},
"video": {
"mimes": [
"video/mp4",
"video/webm"
],
"minduration": 5,
"maxduration": 30,
"protocols": [
2,
3,
5,
6
],
"w": 1920,
"h": 1080,
"startdelay": 0,
"linearity": 1,
"skip": 0,
"placement": 4
}
}
],
"dooh": {
"id": "507f1f77bcf86cd799439011",
"name": "Test Screen",
"venuetype": [
2,
202
],
"venuetypetax": 5,
"publisher": {
"id": "trillboards",
"name": "Trillboards",
"domain": "trillboards.com",
"cat": [
"IAB24"
]
},
"domain": "trillboards.com",
"keywords": "Retail,Shopping Mall,New York,NY",
"content": {
"genre": "Entertainment",
"cattax": 1,
"cat": [
"IAB1"
],
"prodq": 1,
"context": 1,
"contentrating": "TV-PG",
"livestream": 1,
"language": "EN",
"channel": {
"name": "Entertainment"
}
}
},
"device": {
"devicetype": 8,
"connectiontype": 2,
"w": 1920,
"h": 1080,
"geo": {
"lat": 40.7128,
"lon": -74.006,
"type": 1,
"country": "US",
"region": "NY",
"city": "New York",
"zip": "10001"
}
},
"source": {
"fd": 0,
"tid": "691fced1-040c-4354-b861-ef2496f44f29",
"schain": {
"complete": 1,
"nodes": [
{
"asi": "trillboards.com",
"sid": "trillboards",
"rid": "691fced1-040c-4354-b861-ef2496f44f29",
"name": "Trillboards",
"domain": "trillboards.com",
"hp": 1
}
],
"ver": "1.0"
}
},
"at": 1,
"tmax": 500,
"cur": [
"USD"
],
"test": 0,
"regs": {
"coppa": 0,
"gdpr": 0
},
"ext": {
"protocol": "OpenRTB 2.6",
"trillboards_contract": {
"version": "2026-03-v1",
"valid": true,
"required_field_coverage_pct": 100,
"error_count": 0,
"warning_count": 0,
"errors": [],
"warnings": []
},
"trillboards": {
"attention_score": 0.87,
"audience_count": 8,
"crowd_density": 12,
"ad_receptivity": 0.75,
"emotional_engagement": 0.82,
"mood": "happy",
"estimated_occupancy": 25,
"avg_dwell_ms": 4500,
"group_composition": {
"solo": 3
},
"screen_engagement": "active",
"audience_reaction": "positive",
"signals_age_ms": 12480
},
"trb": {
"weather": {
"temp_f": 85,
"feels_like_f": 88,
"humidity": 75,
"condition": "Partly cloudy",
"uv_index": 8,
"wind_mph": 12,
"is_raining": false,
"temp_segment": "hot"
},
"foot_traffic": {
"current_audience": 25,
"estimated_occupancy": 50,
"avg_dwell_seconds": 6,
"traffic_tier": "very_high",
"estimated_hourly_impressions": 15000
}
}
}
}
DOOHObject
OpenRTB 2.6 DOOH object (top-level, replaces site/app)
| Property | Type | Required | Description |
|---|---|---|---|
id | string | ||
name | string | ||
venuetype | array of integer | OpenOOH venue-type ids, as INTEGERS, projected down to taxonomy 1.1 — the version venuetypetax declares. Corrected 2026-09-01 against openrtbRequestBuilder.js, which has always emitted integers. This declared string items and "v1.2 IDs"; both were wrong. Our tables implement OpenOOH 1.2, but AdCOM's taxonomy enumeration stops at 5 (= OpenOOH 1.1), so 1.2 cannot be signalled on the wire at all and the ids are projected. The projection is lossless in MEANING, only in specificity — see the WIRE PROJECTION block in config/openoohTaxonomy.js. The array is OMITTED rather than sent empty when classification yields nothing: venuetype: [] would assert "we classified this screen as belonging to no venue type", which is a different and false claim. | |
venuetypetax | integer — one of 5 | AdCOM venue-taxonomy identifier. 5 = OpenOOH 1.1, and 5 is what we send. Corrected 2026-09-01. This declared enum: [1] described as "1 = OpenOOH Venue Taxonomy". AdCOM value 1 is OpenOOH 1.0, so the published contract named the oldest taxonomy in the list while the ids alongside it were not that taxonomy. The builder itself carried the same bug and was fixed; this document was not updated with it, so a DSP resolving our venuetype ids against OpenOOH 1.0 would have mapped them against the wrong version. | |
publisher | object | ||
domain | string | ||
keywords | string |
DemandPartners
Canonical INT-PK identity table for demand-side partners (DSPs/SSPs that Trillboards routes ad requests to). Wave 2 of pristine-state refactor. Replaces partner_key TEXT joins across the billing path.
| Property | Type | Required | Description |
|---|---|---|---|
demand_partner_id | string, nullable | demand_partner_id INT is the canonical FK going forward; partner_key TEXT is kept as the wire-format alternate-key FOREVER (external partner-API auth + admin UI rely on it). Never propose dropping ... | |
partner_key | string | yes | |
public_alias | string | yes | public_alias UNIQUE: this is the obfuscated external alias partner-API consumers see. partnerAlias.js will read from this table after Wave 2 cutover. |
display_name | string | yes | |
partner_type | string | yes | |
archived | boolean | yes | |
created_at | string | yes | |
updated_at | string | yes |
Device
| Property | Type | Required | Description |
|---|---|---|---|
devicetype | integer — one of 8 | 8 = Digital Out of Home (DOOH) per OpenRTB 2.6 | |
ua | string | ||
make | string | ||
model | string | ||
os | string | ||
osv | string | ||
connectiontype | integer | ||
w | integer | ||
h | integer | ||
ifa | string | OS-sanctioned resettable advertising ID in the clear; omitted when unavailable or limited | |
ifa_type | string — one of aaid, rida, afai, idfa, tifa, msai, dpid, ppid, … (10 total) | Legacy compatibility copy; canonical DSP-compatible placement is device.ext.ifa_type | |
ext | object | ||
geo | object | ||
eids | array of object | Extended device identifiers (IAB recommended for DOOH) |
EarnerScreens
User-owned ad screens. Each row is a logical screen that can display ads. Links user to device. PG primary with Mongo legacy mirror.
| Property | Type | Required | Description |
|---|---|---|---|
taxonomy_spec_version | string, nullable | ||
taxonomy_migrated_at | string, nullable | ||
auction_mode | string, nullable | ||
auction_type | string, nullable | ||
auction_enabled_at | string, nullable | ||
auction_cohort | string, nullable | ||
sync_is_master | boolean, nullable | ||
sync_master_screen_id | string, nullable | ||
sync_enabled | boolean, nullable | ||
sync_last_sync_at | string, nullable | ||
sync_group_name | string, nullable | ||
registration_source | string, nullable | ||
last_offline_transition_at | string, nullable | ||
is_standby | boolean | yes | |
blocked_iab_categories | array of string | ||
blocked_advertisers | array of string | ||
pref_allow_tobacco | boolean, nullable | ||
pref_allow_religious | boolean, nullable | ||
pref_auto_approve_known_brands | boolean, nullable | ||
pref_blocked_subjects | array of string | ||
pref_allowed_subjects | array of string | ||
pref_blocked_keywords | array of string | ||
pref_allowed_keywords | array of string | ||
geocode_source | string, nullable | ||
venue_parent | string, nullable | ||
venue_child | string, nullable | ||
venue_grandchild | string, nullable | ||
environment_type | string, nullable | ||
placement_type | string, nullable | ||
venue_state | string, nullable | ||
venue_city | string, nullable | ||
display_mode | string, nullable | ||
ad_interval_seconds | integer, nullable | ||
sound_enabled | boolean, nullable | ||
advertisement_sizing | string, nullable | ||
avg_dwell_time_seconds | integer, nullable | ||
peak_hours | array of string | ||
delivery_mode | string | yes | |
loop_plan_id | integer, nullable | ||
ntp_sync_required | boolean | yes | |
venue_classification_source | string, nullable | enrichment_status: PG GENERATED from (venue_classification_source, enrichment_failed_at); a real classification DOMINATES a stale failure stamp (success wins). Never written directly. Values: pendi... | |
device_platform_override | string, nullable | ||
hb_vast_tag_url | string, nullable | ||
hb_min_interval_seconds | integer, nullable | ||
hb_last_sync_at | string, nullable | ||
waterfall_partners | array of string | yes | waterfall_partners is the ONLY pin store (order + whitelist; screen_partner_settings was never built). Serving reads it only with a screen_header_bidding_config row and ignores it once waterfall_op... |
waterfall_total_timeout_ms | integer, nullable | ||
waterfall_optimized_at | string, nullable | ||
programmatic_enabled | boolean | yes | |
prog_currency | string | yes | |
preferred_deal_ids | array of string | yes | |
last_programmatic_sync_at | string, nullable | ||
mediation_priority | integer, nullable | ||
hive_stack_screen_id | string, nullable | ||
hive_stack_site_id | string, nullable | ||
hive_stack_unit_id | string, nullable | ||
hive_stack_unit_uuid | string, nullable | ||
hive_stack_synced_at | string, nullable | ||
hive_stack_sync_status | string, nullable | ||
taggify_screen_id | string, nullable | ||
gam_ad_unit_id | string, nullable | ||
gam_ad_unit_code | string, nullable | ||
gam_synced_at | string, nullable | ||
gam_sync_status | string, nullable | ||
branding_overlay_enabled | boolean | yes | |
branding_overlay_logo_url | string | yes | |
branding_overlay_logo_transformed_url | string | yes | |
branding_overlay_position | string | yes | |
branding_overlay_size_ratio | number | yes | NUMERIC(5,4). |
branding_overlay_padding_ratio | number | yes | NUMERIC(5,4). |
branding_overlay_opacity | number | yes | NUMERIC(5,4). |
branding_overlay_hide_on_ads | boolean | yes | |
region | string | yes | |
physical_size_set_by | string, nullable | ||
physical_size_set_at | string, nullable | ||
offline_alert_sent_at | string, nullable | ||
back_online_alert_sent_at | string, nullable | ||
enrichment_failed_at | string, nullable | ||
operating_hours | object, nullable | ||
slot_duration_seconds | integer, nullable | ||
state_iso_3166_2 | string, nullable | ||
geocoding_raw_response | object, nullable | ||
geocoded_at | string, nullable | ||
photo_flagged | boolean | yes | |
photo_flagged_at | string, nullable | ||
photo_flagged_by_mongo_id | string, nullable | ||
photo_flag_reason | string, nullable | ||
photo_flag_notes | string, nullable | ||
photo_notification_sent_at | string, nullable | ||
photo_notification_count | integer | yes | |
photo_quality_sent_at | string, nullable | ||
l_bar_position | string, nullable | ||
block_group_geoid | string, nullable | ||
dma_code | string, nullable | ||
dma_name | string, nullable | ||
place_name | string, nullable | ||
neighborhood | string, nullable | ||
branding_overlay_placement | string | yes | |
archetype_photo_url | string, nullable | ||
archetype_photo_key | string, nullable | ||
archetype_photo_synced_at | string, nullable | ||
offline_push_alert_sent_at | string, nullable | ||
is_demo_data | boolean | yes | |
deleted_at | string, nullable | deleted_at IS NULL required — soft-delete nulls earnerscreen_device_id for partial unique index | |
partner_mongo_id | string, nullable | ||
photos | object, nullable | ||
photo_review_status | string, nullable | ||
venue_category | string, nullable | ||
venue_subcategory | string, nullable | ||
venue_child_id | string, nullable | ||
places_rating | number, nullable | ||
places_review_count | integer, nullable | ||
commercial_value_score | number, nullable | ||
foot_traffic_multiplier | number, nullable | ||
floor_price_cpm | number, nullable | NUMERIC(12,6). | |
distributor_id | integer, nullable | ||
billing_mode | string | yes | |
ads_disabled_reasons | array of string | yes | |
cms_payment_status | string | yes | |
ssp_enabled | boolean, nullable | ||
gam_ad_unit_path | string, nullable | ||
l_bar_ads_enabled | boolean, nullable | ||
width_inches | number, nullable | Physical width_inches/height_inches/diagonal_inches are inches. Pixels live on earner_screen_devices; the raw screen has no resolution_width/resolution_height columns. | |
height_inches | number, nullable | ||
diagonal_inches | number, nullable | ||
dynamic_floor_pricing | boolean, nullable | ||
prog_allocation_pct | number, nullable | ||
hb_provider | string, nullable | ||
hb_timeout_ms | integer, nullable | ||
hb_enabled | boolean, nullable | ||
hb_allocation | number, nullable | ||
hb_request_mode | string, nullable | ||
hb_force_request | boolean, nullable | ||
pref_blocked_categories | array of string | ||
pref_allowed_categories | array of string | ||
content_filtering_enabled | boolean, nullable | ||
pref_level | string, nullable | ||
pref_max_age_rating | string, nullable | ||
mobile_distance_threshold_m | integer, nullable | ||
mobile_update_interval_s | integer, nullable | ||
mobile_vehicle_type | string, nullable | ||
latitude | number, nullable | ||
longitude | number, nullable | ||
brand_substitution_mode | string | yes | |
brand_substitution_consent_at | string, nullable | ||
brand_substitution_consent_by | string, nullable | ||
serving_tenant_id | integer, nullable | ||
enrichment_attempts | integer | yes | |
last_enrichment_attempt_at | string, nullable | ||
media_capability | string — one of image_video, image_only, video_only | ||
screen_placement | string, nullable |
EventExt
Large-event proximity for the screen. The whole event sub-namespace is present ONLY when a nearby event was detected — its absence means "no nearby event", so nearby is always true when the object exists.
| Property | Type | Required | Description |
|---|---|---|---|
nearby | boolean | Always true when this object is present. | |
count | integer | Number of nearby events detected | |
max_capacity | integer | Attendee capacity of the largest nearby event | |
category | string | Dominant category across the nearby events | |
audience_spike | boolean | Whether an audience spike is expected from these events |
FootTrafficExt
Real-time venue foot traffic metrics derived from FEIN audience sensing. Higher traffic = more impressions = higher value for advertisers.
| Property | Type | Required | Description |
|---|---|---|---|
current_audience | integer | Current number of people detected (face count, rounded) | |
estimated_occupancy | integer | Estimated total venue occupancy (extrapolated) | |
avg_dwell_seconds | integer | Average dwell time in seconds | |
traffic_tier | string — one of very_low, low, medium, high, very_high | Traffic classification for easy targeting: - very_high: >= 50 people - high: 20-49 people - medium: 8-19 people - low: 3-7 people - very_low: < 3 people | |
estimated_hourly_impressions | integer | Projected impressions per hour based on face turnover rate |
{
"current_audience": 25,
"estimated_occupancy": 50,
"avg_dwell_seconds": 6,
"traffic_tier": "very_high",
"estimated_hourly_impressions": 15000
}
IABSegmentReference
Reference table of IAB Audience Taxonomy 1.1 segment IDs used by Trillboards.
Income Segments
| ID | Name |
|---|---|
| 1001 | High Income |
| 1002 | Upper-Middle Income |
| 1003 | Middle Income |
| 1004 | Lower-Middle Income |
| 1005 | Low Income |
Lifestyle Segments
| ID | Name |
|---|---|
| 2001 | Business Professional |
| 2002 | Student |
| 2003 | Fitness Enthusiast |
| 2004 | Luxury Shopper |
| 2005 | Tech Savvy |
| 2006 | Outdoor Active |
| 2007 | Foodie |
| 2008 | Health Conscious |
| 2009 | Parent |
| 2010 | Commuter |
| 2011 | Tourist |
| 2012 | Nightlife |
| 2013 | Pet Owner |
| 2014 | Gamer |
| 2015 | Reader |
| 2016 | Eco-Conscious |
| 2017 | Fashion Forward |
| 2018 | DIY/Home Improvement |
| 2019 | Music Lover |
| 2020 | Sports Fan |
Purchase Intent Segments
| ID | Name |
|---|---|
| 3001 | High Purchase Intent |
| 3002 | Medium Purchase Intent |
| 3003 | Low Purchase Intent |
Type: object
Impression
| Property | Type | Required | Description |
|---|---|---|---|
id | string | ||
tagid | string | Screen MongoDB ID | |
bidfloor | number | Floor price in CPM | |
bidfloorcur | string | ||
secure | integer | ||
dt | integer | Estimated fulfillment timestamp (epoch seconds) | |
qty | object | DOOH impression quantity/multiplier (OpenRTB 2.6) | |
banner | object | ||
video | object |
IntentExt
Purchase intent signals derived from ambient audio classification. YAMNet TFLite classifies audio SCENES (not speech content) on-device. Combined with visual shopping signals (bag detection) and demographic context to compute a composite intent score.
Higher intent_score = audience is more likely to make a purchase. DSPs can bid higher when purchase_stage is READY_TO_BUY or COMPARING.
| Property | Type | Required | Description |
|---|---|---|---|
score | number | Composite purchase intent score (0-1). Weighted combination of verbal intent, brand mentions, price inquiry, shopping activity, bag detection, income match, emotion, and engagement. | |
purchase_stage | string — one of READY_TO_BUY, COMPARING, CONSIDERING, BROWSING, NONE | Classified purchase funnel stage based on behavioral signals. - READY_TO_BUY: High intent + brand mentions + price inquiry (score >= 0.7) - COMPARING: Active comparison shopping (score 0.5-0.7) - CONSIDERING: Interest shown but no purchase signals (score 0.3-0.5) - BROWSING: Casual browsing, low intent (score 0.1-0.3) - NONE: No purchase intent detected (score < 0.1) | |
categories | array of object | Detected product/service interest categories | |
brand_mentions | boolean | Whether brand names were detected in ambient audio | |
brand_count | integer | Number of distinct brand mentions detected | |
audience_size | integer | Current audience size (face count) when intent was measured | |
should_trigger | boolean | Whether intent score is high enough to trigger premium ad swap |
OpenrtbRevenueScreenHourly
Revenue per screen per hour. Aggregates gross revenue from completed impressions. Refreshed from CH via clickhouseRollupSource. PG only.
| Property | Type | Required | Description |
|---|---|---|---|
hour_bucket | string | yes | |
screen_mongo_id | string | yes | |
impressions | integer | yes | |
priced_impressions | integer | yes | |
estimated_impressions | integer | yes | |
actual_gross_revenue | number | yes | |
estimated_gross_revenue | number | yes | |
gross_revenue | number | yes | |
updated_at | string | yes |
PartnerReconciliationReports
Partner-reported impression and revenue counts for reconciliation against deduped completed_impressions. Ingested from VidVerto SSP API and Adipolo API / JustBaat CSV reports. PG only.
| Property | Type | Required | Description |
|---|---|---|---|
their_requests | integer, nullable | ||
our_requests | integer, nullable | ||
request_variance | number, nullable | NUMERIC(10,4). | |
demand_partner_id | integer, nullable | Wave 2 B added demand_partner_id INT + variant_id INT FKs. Legacy partner_key TEXT kept as wire-format alternate-key. Backfill via follow-up Wave 2 B-backfill PR. | |
variant_id | integer, nullable | ||
id | integer, nullable | VidVerto synced daily via vidvertoRevenueSync before programmatic earnings aggregation. | |
provider_name | string | yes | |
report_date | string | yes | |
report_type | string — one of partner_api, manual_upload, email_extract, dashboard_snapshot | yes | |
their_impressions | integer, nullable | ||
their_revenue | number, nullable | NUMERIC(14,6). | |
their_ecpm | number, nullable | NUMERIC(10,4). | |
their_fill_rate | number, nullable | NUMERIC(6,4). | |
our_impressions | integer, nullable | ||
our_revenue | number, nullable | NUMERIC(14,6). | |
our_ecpm | number, nullable | NUMERIC(10,4). | |
impression_variance | number, nullable | NUMERIC(8,4). | |
revenue_variance | number, nullable | NUMERIC(8,4). | |
status | string — one of pending, matched, flagged, disputed, resolved, accepted | yes | |
resolution_notes | string, nullable | ||
adjustment_id | integer, nullable | ||
raw_data | object, nullable | Adipolo publisher revenue requires a completed vast_requests lifecycle plus latest screens_live source='owned'. ID shape/event alone is insufficient; shared, external, and unjoined bundles are excl... | |
imported_by | string, nullable | ||
reviewed_by | string, nullable | ||
reviewed_at | string, nullable | ||
created_at | string | yes | |
updated_at | string | yes | |
applied_report_hash | string, nullable | applied_report_hash/applied_at (mig 449) gate the hourly pricing re-apply: syncs skip the CI re-stamp + PED rebuild when the applied tuple is unchanged. Written ONLY by partnerPricingApplyGate.reco... | |
applied_at | string, nullable |
PartnerVariants
Canonical INT-PK identity for partner variants (e.g. adipolo_lower_floor_tablet). Wave 2 of pristine-state refactor. variant_name TEXT remains as the wire-format alternate-key indefinitely.
| Property | Type | Required | Description |
|---|---|---|---|
variant_id | string, nullable | feeds_into lists tables that will gain a nullable variant_id INT FK in Wave 2 Stream B. Each is a separate PR — this PR mints the dimension only. | |
demand_partner_id | integer | yes | Backfill is 1:N from demand_partner_config.variant_names[]. Rows with NULL demand_partner_id at apply time are silently skipped — deploy-controller previous-tick guard ensures Wave 2 A1 has populat... |
variant_name | string | yes | variant_name TEXT is the wire-format alternate-key (kept forever) — internal logs and partner integrations rely on it. Never propose dropping variant_name. |
public_alias | string | yes | public_alias is the obfuscation alias for partner-API responses (matches partnerAlias.encodeAlias pattern: 'var_' || sha256(variant_name)[0..5]). |
display_name | string, nullable | vast_tag_variants JSONB on demand_partner_config still co-exists during transition. Backfill UPDATE pulls display_name from the JSONB if present (no-op today; forward-compatible). | |
archived | boolean | yes | |
created_at | string | yes | |
updated_at | string | yes | |
daily_impression_cap | integer, nullable | ||
daily_request_cap | integer, nullable | ||
daily_budget_cap | number, nullable | NUMERIC(10,2). | |
request_pressure_enabled | boolean, nullable | request_pressure_enabled=false fails open for pressure shaping only. It must not be treated as demand-source enablement. | |
request_pressure_base_interval_seconds | integer, nullable | ||
request_pressure_probe_interval_seconds | integer, nullable | ||
request_pressure_defer_seconds | integer, nullable | ||
request_pressure_probe_modulo | integer, nullable | ||
request_pressure_probe_target_per_minute | number, nullable | NUMERIC(10,4). | |
request_pressure_min_pair_requests | integer, nullable | ||
request_pressure_min_source_requests | integer, nullable | ||
request_pressure_completion_rate_floor | number, nullable | NUMERIC(12,8). | |
request_pressure_confidence_level | number, nullable | NUMERIC(6,3). | |
request_pressure_max_multiplier | integer, nullable |
PartnerZoneCpmDaily
Per-zone per-day CPM rates from partner APIs. Used for revenue estimation and settled partner-truth allocation when partner_reconciliation_reports does not provide a newer manual/API row. PG only.
| Property | Type | Required | Description |
|---|---|---|---|
demand_partner_id | integer, nullable | Wave 2 B added demand_partner_id INT + variant_id INT FKs. Legacy partner_key TEXT kept as wire-format alternate-key. Backfill via follow-up Wave 2 B-backfill PR. | |
variant_id | integer, nullable | ||
id | string, nullable | ||
partner_key | string | yes | |
zone_id | string | yes | |
zone_label | string, nullable | ||
period_date | string | yes | |
ecpm | number | yes | NUMERIC(10,4). |
gross_revenue | number, nullable | NUMERIC(14,6). | |
impressions | integer, nullable | ||
requests | integer, nullable | ||
fill_rate | number, nullable | NUMERIC(6,4). | |
source | string | yes | Daily earnings aggregation may use these rows to settle gross revenue, but completed_impressions remains the event-count source for programmatic_earnings_daily.impressions_count. |
fetched_at | string | yes | |
created_at | string | yes | |
updated_at | string | yes |
Regs
| Property | Type | Required | Description |
|---|---|---|---|
coppa | integer | ||
gdpr | integer | Set to 0 by the Trillboards request builder. On demand rails with wire hygiene enabled, the field carries the value from the authenticated publisher or device privacy context and is omitted when that context has none. |
Segment
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Segment identifier (IAB taxonomy ID or seller-defined) | |
name | string | Human-readable segment name | |
value | string |
SellerDefinedSegmentReference
Seller-defined context segments in the trillboards-seller-defined data object. These are included in user.data[] with ext.segtax omitted (seller-defined taxonomy).
Venue Segments (trb_vn_*)
| ID | Name | Matches |
|---|---|---|
| trb_vn_001 | venue:fitness_gym | gym, fitness, workout, crossfit |
| trb_vn_002 | venue:restaurant_casual | casual restaurant, diner |
| trb_vn_003 | venue:restaurant_quick_service | QSR, fast food, takeout |
| trb_vn_004 | venue:bar_nightlife | bar, nightlife, club, pub, lounge |
| trb_vn_005 | venue:salon_beauty | salon, beauty, spa |
| trb_vn_006 | venue:barber_shop | barber |
| trb_vn_007 | venue:retail_general | retail, shopping, mall |
| trb_vn_008 | venue:retail_convenience | convenience, gas station |
| trb_vn_009 | venue:office_lobby | office, cowork, business |
| trb_vn_010 | venue:healthcare_waiting_room | clinic, medical, hospital, pharmacy |
| trb_vn_011 | venue:education_campus | campus, college, university, school |
| trb_vn_012 | venue:hotel_hospitality | hotel, hospitality, resort |
| trb_vn_013 | venue:transit_hub | transit, station, airport, terminal |
| trb_vn_014 | venue:entertainment | cinema, theater, arcade, event |
| trb_vn_015 | venue:residential_common | residential, apartment, condo, lobby |
Geo Segments (trb_geo_*)
| ID | Name |
|---|---|
| trb_geo_001 | geo:country_us |
| trb_geo_002 | geo:state_ca |
| trb_geo_003 | geo:state_ny |
| trb_geo_004 | geo:state_tx |
| trb_geo_005 | geo:city_tier_1 |
Traffic Tier Segments (trb_trf_*)
| ID | Name | Threshold |
|---|---|---|
| trb_trf_001 | traffic:very_low | < 3 people |
| trb_trf_002 | traffic:low | 3-7 people |
| trb_trf_003 | traffic:medium | 8-19 people |
| trb_trf_004 | traffic:high | 20-49 people |
| trb_trf_005 | traffic:very_high | >= 50 people |
Dwell Time Segments (trb_dwl_*)
| ID | Name | Threshold |
|---|---|---|
| trb_dwl_001 | dwell:very_short | < 3 seconds |
| trb_dwl_002 | dwell:short | 3-7 seconds |
| trb_dwl_003 | dwell:medium | 7-15 seconds |
| trb_dwl_004 | dwell:long | > 15 seconds |
Daypart Segments (trb_dpt_*)
| ID | Name | Hours (UTC) |
|---|---|---|
| trb_dpt_001 | daypart:morning | 05:00-10:59 |
| trb_dpt_002 | daypart:midday | 11:00-13:59 |
| trb_dpt_003 | daypart:afternoon | 14:00-17:59 |
| trb_dpt_004 | daypart:evening | 18:00-22:59 |
| trb_dpt_005 | daypart:late_night | 23:00-04:59 |
Quality Flags (trb_qly_*)
| ID | Name | Meaning |
|---|---|---|
| trb_qly_001 | quality:geo_verified | Screen has GPS coordinates |
| trb_qly_002 | quality:edge_sensed_audience | Real-time FEIN audience data present |
| trb_qly_003 | quality:floor_defined | Publisher floor price is set |
Type: object
Source
| Property | Type | Required | Description |
|---|---|---|---|
fd | integer | 1 = exchange is final decision maker | |
tid | string | Transaction/request ID | |
schain | object | Supply chain transparency |
TrbNamespace
Trillboards extension sub-namespaces. Every sub-namespace is OPTIONAL and is omitted entirely when the underlying signal is unavailable for that screen — treat an absent key as "no signal", never as a zero.
This object is pinned to the request builder by tests/unit/openrtbExtNamespaceSpecParity.test.js, which derives the emitted key set from the source and fails on drift in either direction.
| Property | Type | Required | Description |
|---|---|---|---|
vas | VASExt | ||
intent | IntentExt | ||
weather | WeatherExt | ||
foot_traffic | FootTrafficExt | ||
venue | VenueExt | ||
event | EventExt |
TrillboardsAudienceExt
Real-time audience intelligence from FEIN edge AI sensors. Updated every 10-30 seconds. These signals are MEASURED, not estimated. Unlike CTV viewability which uses probabilistic models, Trillboards uses on-device computer vision (ML Kit) and audio classification (YAMNet TFLite) to provide deterministic audience data.
| Property | Type | Required | Description |
|---|---|---|---|
attention_score | number | Fraction of detected faces actively looking at the screen. 0.0 = nobody looking, 1.0 = everyone looking. Measured via gaze detection, NOT self-reported or probabilistic. | |
audience_count | number | Average number of faces detected during current measurement window. Parse audience_count, not live_face_count. A previous revision of this spec documented this field as live_face_count and stated that audience_count "was never actually emitted by the server". That was exactly backwards, and it was a confusion of two different surfaces: - This surface — the outbound bid request's ext.trillboards — has always emitted audience_count. - The inventory discovery response (GET /openrtb/v2/adslots) emits live_face_count inside its fein object. Both names are live; they belong to different documents. live_face_count never appears in a bid request. | |
crowd_density | integer | Peak face count observed in current window (max concurrent viewers) | |
ad_receptivity | number | Audio-derived venue receptivity score. Quiet/focused environments score higher. Based on YAMNet ambient sound classification. | |
emotional_engagement | number | Composite emotional engagement score from facial expression analysis. Higher = more engaged (positive or negative reaction to content). Computed from each detected person's facial expression, classified on the device. | |
mood | string — one of happy, neutral, focused, calm, energetic, social, surprised, sad | Dominant venue emotional state derived from facial expression analysis. Parse mood, not dominant_emotion. Same surface confusion as audience_count above: the bid request emits mood; the GET /openrtb/v2/adslots discovery response emits dominant_emotion. | |
estimated_occupancy | integer | Estimated venue occupancy (extrapolated from visible faces + venue capacity) | |
avg_dwell_ms | number | Average time (milliseconds) a face stays in the camera's field of view | |
group_composition | object | Audience composition breakdown by demographic bucket. Derived from each detected person's estimated age range and gender. | |
screen_engagement | string | Categorical engagement label passed through from the edge reading (e.g. active), NOT a score. Corrected 2026-09-01. This declared type: number with minimum: 0, maximum: 1 and example: 0.65, but _buildTrillboardsExt assigns audience.screenEngagement unchanged and that value is a string everywhere it is produced. A bidder doing arithmetic on this field was getting NaN. The combined 0-1 metrics are emotional_engagement and ad_receptivity. | |
audience_reaction | string — one of positive, neutral, negative, mixed | Dominant audience reaction to current content | |
signals_age_ms | integer | Milliseconds since last FEIN measurement update. Lower = fresher signals. Typical: 10,000-30,000ms. If > 120,000ms, signals may be stale (screen offline or camera occluded). | |
reliability_score | number | Screen reliability score from unified telemetry. Factors: uptime, heartbeat regularity, GPS accuracy, FEIN measurement frequency. | |
audience_quality | number | Audience data quality score. High = rich FEIN signals with high confidence. Low = limited or stale audience data. | |
screen_quality_composite | number | Composite quality score combining reliability + audience quality + geo accuracy | |
speech_contexts | array of string | Shopping contexts inferred from on-device speech classification. The bid request carries the context labels; Trillboards stores the speech transcripts they come from. Omitted entirely when no context was inferred. | |
purchase_journey_stage | string | Where the observed audience sits in a purchase journey, derived from speech context. Omitted when no stage was inferred. | |
purchase_journey_urgency | string | Urgency signal accompanying purchase_journey_stage. Omitted when no stage was inferred. | |
canonical_brand_matches | array of string | De-duplicated canonical brand names matched from speech evidence. Presence of a competitor brand is a conquesting opportunity. Omitted when no brand was matched. | |
signal_derived | object | Camera-less derived audience signals, carried alongside the IAB and Trillboards segment sets. Present only when derived signals exist for the screen AND the request is not a clean curated-wire request. | |
agent_endpoint | string | AI-agent discovery: the Trillboards MCP endpoint, so a DSP's agent can negotiate directly from the bid stream. Constant for all requests. | |
adcp_discovery | string | AI-agent discovery: the AdCP adagents.json document. Constant for all requests. |
{
"attention_score": 0.87,
"audience_count": 8,
"crowd_density": 12,
"ad_receptivity": 0.75,
"emotional_engagement": 0.82,
"mood": "happy",
"estimated_occupancy": 25,
"avg_dwell_ms": 4500,
"group_composition": {
"solo": 3
},
"screen_engagement": "active",
"audience_reaction": "positive",
"signals_age_ms": 12480
}
User
User object carrying the screen's audience segments. The trillboards-audience segments are read from the per-person records the screen keeps for the people its camera detects. Trillboards recognises the same person across screens and days and links people to device keys and mobile advertising IDs.
| Property | Type | Required | Description |
|---|---|---|---|
data | array of UserData |
UserData
IAB Audience Taxonomy 1.1 segments and seller-defined context segments. Three data sources may be present in user.data[]:
- trillboards-audience: Real-time audience segments from FEIN edge AI
- trillboards-census: US Census ACS neighborhood demographics
- trillboards-seller-defined: Seller-defined context segments (venue, traffic, dwell, daypart)
| Property | Type | Required | Description |
|---|---|---|---|
id | string — one of trillboards-audience, trillboards-census, trillboards-seller-defined | Data provider identifier | |
name | string | Human-readable data source name | |
segment | array of Segment | ||
ext | object |
VASExt
Verified Attention Seconds — historical attention quality metrics for this screen. Based on 7-day rolling data from the attention_ledger.
VAS = face_count attention_score dwell_seconds. It captures the TOTAL attention delivered, not just whether someone was present. A screen with 3 attentive viewers for 10 seconds scores higher than 10 distracted viewers for 1 second.
| Property | Type | Required | Description |
|---|---|---|---|
pricing_model | string — one of vas | Always "vas" — indicates attention-based pricing is available | |
predicted_vas | number | Predicted VAS for the next impression based on historical patterns | |
vas_7d | number | Average VAS (Viewable Attention Score) over the past 7 days. (Field renamed 2026-05 from historical_vas_avg to align with the buyer-facing field name on /adslots — partners migrating from a stale spec should rename their parser key.) | |
median_vas | number | Median VAS (more robust to outliers than mean) | |
p90_vas | number | 90th percentile VAS — the quality floor for top 10% of measurements | |
vas_floor | number | Minimum VAS threshold for quality-gated pricing | |
vas_measurements_7d | integer | Number of VAS measurements in the past 7 days (data density) | |
avg_face_count | number | Average face count across measurements | |
avg_attention_score | number | Average attention score across measurements | |
last_measurement | string | Timestamp of most recent VAS measurement |
VenueExt
Venue quality and neighborhood context for the screen's location.
Every field is omitted unless it carries positive value — a zero or unknown is dropped rather than broadcast, because a zero on the bid stream reads as "worthless" and suppresses bids. An absent field means "not known", not "scored zero".
| Property | Type | Required | Description |
|---|---|---|---|
price_level | integer | Venue price tier (0 = cheapest, 4 = most expensive) | |
rating | number | Venue rating on a 5-point scale | |
quality_score | number | Composite venue quality score. Omitted when not positive. | |
neighborhood_class | string | Neighborhood classification for the surrounding area. Omitted when unknown (the literal unknown is never sent). | |
amenity_density | number | Density of nearby amenities (retail, dining, transit) around the screen. Omitted when not positive. |
WeatherExt
Contextual weather data for the screen's location. Cached in Redis with 60-minute TTL from WeatherAPI.com. DSPs use weather for contextual targeting (e.g., ice cream ads when hot, umbrella ads when raining).
| Property | Type | Required | Description |
|---|---|---|---|
temp_f | number | Current temperature in Fahrenheit | |
feels_like_f | number | Feels-like temperature accounting for humidity and wind chill | |
humidity | number | Relative humidity percentage (0-100) | |
condition | string | Weather condition description | |
uv_index | number | UV index (0-11+) | |
wind_mph | number | Wind speed in miles per hour | |
is_raining | boolean | Whether it is currently raining at the screen location | |
temp_segment | string — one of hot, warm, cool, cold | Temperature classification for easy targeting: - hot: >= 85F - warm: 65-84F - cool: 45-64F - cold: < 45F |
{
"temp_f": 85,
"feels_like_f": 88,
"humidity": 75,
"condition": "Partly cloudy",
"uv_index": 8,
"wind_mph": 12,
"is_raining": false,
"temp_segment": "hot"
}