OpenAPI YAMLOpenAPI JSONproblems YAMLAPI overview
Schema index
AccessLevel— required: noneread (Read only) < propose < manage. Propose reads, contributes (media, notes, comments, support, feedback, screenshots, ad drafts) and turns playlist and screen changes into reviews (docs/reviews.md).
AccessLimitedBy— required: noneWhy effective_access is lower than the credential's access. project - the project requires review for agent changes. person - the agent's responsible person is a member who cannot approve reviews.
AdslotPageWrite— required: id, type, adslot_idWhole-page advertising slot. At most 16 adslot pages, and at least one ordinary page with no visibility, are server checks and are not expressed here.
AdvertisingAvailability— required: estimated_plays, booked_plays, available_plays, screens, observed_from, observed_to, flight_start, flight_endAn estimate: the slot occurrences the targeted screens produced over the observed window, projected over the flight, less the plays other direct goals still need on the same screens.
AdvertisingAvailabilityQuery— required: flight_start, flight_endAdvertisingCampaign— required: id, revision, name, state, pause_reasons, price_change_pending, daily_cap_mcr, lifetime_cap_mcr, reserved_mcr, spent_mcr, flight_start, flight_end, networks, created_at, updated_atAdvertisingCampaignDraft— required: name, daily_cap_mcr, lifetime_cap_mcr, flight_start, flight_end, networksAdvertisingCampaignList— required: campaignsAdvertisingCampaignNetwork— required: seller_project_id, membership_id, admission_generation, screen_ids, slot_ids, creative_ids, network_cap_mcr, network_reserved_mcr, network_spent_mcr, accepted_pricing_generation, price_change_pending, barrier_generation, enabledAdvertisingCampaignNetworkDraft— required: seller_project_id, screen_ids, slot_ids, creative_idsAdvertisingCategory— required: noneThe advertiser category. Two ads of one category never play in the same ad break on a screen; an ad without a category is never separated.
AdvertisingCreative— required: id, revision, media_id, media_revision, sha256, kind, content_type, duration_ms, width, height, bytes, state, created_atAdvertisingCreativeCreate— required: media_idAdvertisingCreativeList— required: creativesAdvertisingCreativeReview— required: id, seller_project_id, creative_revision, state, created_atAdvertisingDaypart— required: start, endAdvertisingDeliveryReport— required: gross_mcr, fee_mcr, net_mcr, completed, interrupted, unbillable, rows, coverageAdvertisingDeliveryRow— required: screen_id, slot_id, reservation_id, gross_mcr, fee_mcr, net_mcr, completed_atAdvertisingDirectCampaign— required: id, revision, name, advertiser, state, flight_start, flight_end, reserved_plays, delivered_plays, media_ids, image_duration_ms, screen_ids, slot_ids, venue_types, cities, tags, dayparts, overbooked, pace, created_at, updated_atAdvertisingDirectCampaignDraft— required: name, advertiser, flight_start, flight_end, media_idsA direct campaign: the seller's own deal for an outside advertiser on the seller's own ad inventory. It moves no money.
AdvertisingDirectCampaignList— required: direct_campaignsAdvertisingDirectPace— required: status, expected_playsDelivery against the goal, which accrues evenly over the flight.
AdvertisingDirectPrice— required: model, amount, currencyThe deal price the seller recorded. screenRIG never charges it; it values the report.
AdvertisingDirectReport— required: campaign_id, name, advertiser, from, to, delivered_plays, reserved_plays, pace, plays, played_ms, not_played, rowsAdvertisingDirectReportRow— required: day, screen_id, slot_id, media_id, plays, played_msAdvertisingDirectValue— required: model, amount, currencyThe recorded price applied to the delivered plays: per thousand plays for cpm, the whole fee for flat.
AdvertisingFillStats— required: from, to, occurrences, direct, marketplace, vast, unfilled, fill_rate, unfilled_reasons, daysThe seller's ad slot supply for all UTC hours overlapping the requested window: occurrences asked for, fills by source, and the unfilled occurrences with the reason of the first ask. The request is at most 35 days; the effective bounds can span up to 35 days plus one hour.
AdvertisingFillStatsDay— required: day, occurrences, direct, marketplace, vast, unfilled, fill_rateAdvertisingInventory— required: screen_id, revision, ads_enabled, audience_tags, rate_override_mcr_per_15s, rate_revision, created_at, updated_atAdvertisingInventoryList— required: inventoryAdvertisingInventoryWrite— required: ads_enabledAdvertisingJoinedInventory— required: inventory, slotsAdvertisingJoinedNetwork— required: seller_project_id, membership_id, policy, admission_generation, scope, price_change_pending, default_rate_mcr_per_15sAdvertisingJoinedNetworkList— required: networksAdvertisingMembership— required: id, seller_project_id, admission_generation, policy_revision, policy, scope, state, revision, created_at, updated_atAdvertisingMembershipList— required: membershipsAdvertisingMembershipScope— required: policy, screen_ids, slot_idsAdvertisingNetwork— required: id, revision, enabled, rate_revision, pricing_generation, seller_fee_bps, seller_fee_revision, created_at, updated_atAdvertisingNetworkCreate— required: nameAdvertisingQuote— required: id, campaign_id, campaign_revision, revision, state, expires_at, items, pricing_generations, created_atAdvertisingQuoteAccept— required: quote_idAdvertisingQuoteItem— required: seller_project_id, screen_id, slot_id, creative_id, duration_ms, rate_mcr_per_15s, play_price_mcr, pricing_generation, fee_bps, fee_revisionAdvertisingRate— required: rate_mcr_per_15sAdvertisingReportCoverage— required: mode, outcomes_completeCoverage of playback outcome counts. Monetary totals always come from immutable settlements. When outcomes_complete is false, counts are observed lower bounds and missing history is unavailable, not zero.
AdvertisingReview— required: id, creative_id, creative_revision, state, created_atAdvertisingReviewDetail— required: review, creativeAdvertisingReviewList— required: reviewsAdvertisingReviewRejection— required: reasonAdvertisingScope— required: screen_ids, slot_idsAdvertisingSellerCampaign— required: id, buyer_project_id, name, state, flight_start, flight_end, screen_ids, slot_ids, creative_ids, review_state, enabled, price_change_pending, updated_atA submitted buyer campaign projected onto the calling seller's network alone.
AdvertisingSellerCampaignDelivery— required: completed, reserved_mcr, gross_mcr, fee_mcr, net_mcrLifetime settled plays and amounts on the calling seller's network, plus its current held amount.
AdvertisingSellerCampaignDetail— required: campaign, deliveryAdvertisingSellerCampaignList— required: campaignsAdvertisingSlot— required: id, revision, name, enabled, accepted_media, max_image_duration_ms, max_video_duration_ms, muted, rate_override_mcr_per_15s, rate_revision, created_at, updated_atAdvertisingSlotList— required: slotsAdvertisingSlotWrite— required: name, accepted_media, max_image_duration_ms, max_video_duration_msAdvertisingSpendReport— required: campaign_id, spent_mcr, reserved_mcr, completed, unbillable, pending, coverageAgent— required: id, name, agent_type, capabilities, state, authenticated_requests, metered_credits, created_atAgentCapability— required: noneOne agent-credential capability area. Capabilities are fixed when the credential is minted; to change them, connect a new agent and disconnect the old one. Canonical order is this enum order; capability lists never contain duplicates.
AgentConnection— required: connection_id, name, agent_type, status, capabilities, expires_at, created_atAgentDisconnectRequest— required: noneAgentInput— required: noneAgentList— required: itemsAgentScreenScopeUpdate— required: screen_scopeAgentSelfStatus— required: agent, connection_readyAlert— required: id, screen_id, condition, state, since, muted, attentionOne screen condition while it lasts. Readers treat an unknown condition as a condition they cannot name.
AlertAcknowledge— required: noneAlertActor— required: kind, idAlertList— required: items, next_cursor, countsAlertMute— required: id, screen_ids, tags, until, created_atAlertMuteList— required: itemsAlertMuteWrite— required: untilAt least one screen id or tag.
AppStoreNotificationAcknowledgement— required: acknowledgedAppStoreNotificationRequest— required: signedPayloadAppStoreOffer— required: account_token, products, education_eligible, education_eligibility_source, can_purchase, current_plan_id, subscriptionAppStoreProduct— required: product_id, plan_idAppStoreSubscription— required: organization_id, plan_id, product_id, original_transaction_id, environment, expires_at, auto_renews, statusAn organization's App Store subscription.
AppStoreTransactionRequest— required: signed_transactionApplication— required: id, name, revision, created_at, updated_atApplicationEventContext— required: primitive_id, codeApplicationEventReport— required: severity, code, contextApplicationList— required: itemsApplicationPlacement— required: playlist_id, playlist_name, page_id, primitive_id, release_id, release, controller, rect, canvas, screen_countOne application primitive on one page of one playlist.
ApplicationRename— required: nameApplicationUploadCommit— required: sha256, compressed_bytesNames the declared archive again; a commit naming other bytes is refused with invalid_request.
ApplicationUploadDeclaration— required: sha256, compressed_bytes, expanded_bytes, file_countDeclares one CLI-produced tar.gz, with the metadata a streamed upload sends as headers.
ApplicationUploadSession— required: id, application_id, release_id, operation, upload_url, method, headers, expires_atApplicationUsage— required: placements, screensArchivedProjectDevice— required: screen_id, label, hostAuditActor— required: kindAuditActorKind— required: noneAuditClient— required: kindAuditEntry— required: id, scope, at, actor, source, action, resource, changesAuditFieldChange— required: path, before, afterAuditHead— required: scope, seq, hash, floor_seq, floor_hash, unsealed, formatAuditList— required: items, next_cursorAuditProblem— required: seq, code, detailAuditResource— required: typeAuditRevision— required: typeAuditSource— required: noneThe surface a change came through: a dashboard session, the HTTP API, the screenrig CLI, an MCP tool call, a staff member under a write lease, a signed device call, an inbound webhook, or the server itself (a worker applying an approved change).
AuditVerifyResult— required: ok, scope, checked, head, problemsAutoTopUpDisabledSettings— required: enabledDisabling preserves saved amounts and monthly spend. Optional amount fields are ignored, including unfinished drafts.
AutoTopUpEnabledSettings— required: enabled, threshold_cents, amount_cents, monthly_limit_centsAutoTopUpSettings— required: noneAutoTopUpView— required: enabled, threshold_cents, amount_cents, monthly_limit_cents, paused, this_month_centsBIBrowse— required: items, next_cursorGrafana: dashboards (uid, title, folder, tags), or with dashboard set that dashboard's panels (id, title, type) and the dashboard itself. Power BI: workspaces (id, name), or with workspace set that workspace's reports (id, name, embed_url) and the workspace itself. Tableau: views (path as Workbook/View, name).
BICheck— required: name, okBIConnection— required: id, project_id, kind, name, config, embed_origin, secret, last_test, revision, created_at, updated_atA project's access to one BI system. Every connection belongs to one project.
BIConnectionConfig— required: noneBIConnectionList— required: items, next_cursorBIConnectionPatch— required: noneBIConnectionWrite— required: name, kind, configBISecret— required: set, hintHow a stored secret shows. The secret itself is never returned.
BITarget— required: noneWhat a view shows. Grafana with a connection: dashboard_uid (and optionally one panel). Power BI with a connection: workspace_id and report_id, with pages or rotate_all_pages. Tableau with a connection: view (Workbook/View), with rotate_sheets. Web with a connection: url. Without a connection: only public_url (and rotate_sheets for Tableau).
BITestReport— required: at, ok, checksBIView— required: id, project_id, kind, name, target, refresh_s, rotate_s, fit, revision, status, embed_url, created_at, updated_atBIViewError— required: at, code, detailBIViewList— required: items, next_cursorBIViewPatch— required: noneBIViewStatus— required: last_ok_at, last_errorHow the view last fared on glass. It changes only when the outcome changes.
BIViewTest— required: at, ok, checksBIViewWrite— required: nameSet connection_id with exactly one of url and target (for Tableau, url may come with a target that holds only rotate_sheets; a web view takes url and no target), or kind (grafana, powerbi or tableau) with target.public_url alone.
BIWebCookie— required: name, value, domainOne cookie of a jar exported from a signed-in browser.
BIWebSession— required: state, cookies, cookies_updated_at, last_login_at, login_failures, retry_atA web connection's cookies and sign-in. state: idle (nothing yet; the first capture signs in), active, login_needed (the page showed its sign-in page; the next capture signs in), paused (a sign-in did not succeed; the next waits until retry_at), stopped (five refused sign-ins in a row; change the credentials) or expired (a connection with cookies alone whose cookies stopped working; give it a new jar).
BIWebStep— required: actionOne step of a scripted sign-in. goto: url (https, public). fill: selector and value. click and wait_for: selector. press: value, a key name such as Enter or Tab, and optionally selector to focus first. wait_ms: ms.
A value may hold {{username}}, {{password}} and {{otp}}, which the renderer fills from the sign-in; put no password or code in a value, only the placeholder.
BillingBalance— required: wallet_revision, remaining_mcr, reserved_mcr, available_mcr, sources, withdrawalBillingPayer— required: kind, idBillingPaymentMethod— required: brand, last4, exp_month, exp_yearBillingPayoutStatus— required: rails_available, unavailable_reasonBillingPlanSubscription— required: subscription_id, plan, status, entitlement, cancel_at_period_endBillingPortalSession— required: urlBillingSourceBucket— required: remaining_mcr, reserved_mcrBillingSources— required: nonredeemable, purchased, ad_earnedBillingStatement— required: entries, next_cursorBillingStatementEntry— required: sequence, journal_id, kind, amount_mcr, amount_credits, amount_usd, occurred_atBillingUsageAllowance— required: storage_bytes, api_events, storage_mcr_per_gb_monthWhat one payer gets free, as canonical decimal strings. Bandwidth is never charged, so it has no allowance.
BillingUsageAmounts— required: bandwidth_bytes, storage_mcr, api_eventsBillingWithdrawal— required: eligible_earned_mcr, amount_usd_cents, threshold_exclusive_usd_cents, allowed, blockers, rails_availableBrowserLinkClaim— required: session_id, status, screenBrowserLinkClaimRequest— required: codeBrowserLinkClaimScreen— required: id, public_id, state, public_urlBrowserLinkEvent— required: session_id, status, event_id, expires_atBrowserLinkSession— required: session_id, code, display_code, status, expires_atBrowserLinkStatus— required: session_id, code, display_code, status, continuation_path, expires_atBrowserProvisioningCompletion— required: screen, public_urlBrowserProvisioningExchange— required: provisioning_token, exchange_idCLIEnrollment— required: project, agent, connection_ready, token, issuance_id, issuance_expires_at, invitationCLIEnrollmentOAuth— required: project, agent, connection_ready, issuance_id, issuance_expires_at, invitation, access_token, token_type, expires_in, refresh_token, refresh_expires_at, scopeEnrollment in the oauth credential format. The enrollment's grant (client screenrig-cli, Manage on the new project with every capability, identity access Manage) issued its first token pair; an exact retry before issuance_expires_at returns the same pair, later retries return credential_issuance_expired.
CLIEnrollmentRequest— required: client_idCampaign— required: id, name, playlist_id, audience, priority, paused, reserved, revision, created_at, updated_atA content campaign: every screen in audience shows playlist_id while from, until and windows hold in the screen's timezone. Absent from, until and windows mean always.
CampaignAudience— required: noneWhich screens a campaign targets: exactly one of all (every rule matches), any (at least one matches), tag (the screen carries the tag), screen_id (that screen) or folder (the screens in that screen folder and every folder inside it, GET /api/screen-folders; the folder must exist).
Rules nest at most 3 deep and an audience has at most 500 rules. {"all": []} is every screen of the project; elsewhere all and any list at least one rule.
CampaignList— required: itemsCampaignPatch— required: noneCampaignPreviewRequest— required: bodyCampaignPromote— required: revisionCampaignReach— required: published, current, at, empty_groupsHow many active screens the audience matches; a pairing screen is not counted.
It counts audience matches, not where the campaign plays: an active screen without a default playlist matches and plays nothing. published is the count when the campaign was last published (created, or updated with a different audience, playlist_id or paused); current the latest count.
A change to an active screen's tags, folder or state, or a folder move, recounts the campaigns whose audience names what changed about 15 seconds after the changes stop and appends campaign.reach_changed (published, previous, current; republished true from a publish) and, per tag or folder of the audience that starts or stops matching screens, campaign.group_changed (kind, value, matched); a publish that drops an empty tag or folder from the audience appends campaign.group_changed with removed true and matched 0.
Absent on a campaign not published or recounted since reach was introduced.
CampaignRevision— required: revision, created_at, change, current, campaignOne stored revision of a campaign: the campaign as it was after a write. change says what made it: created, edited (an update or a schedule import), restored or promoted (a copy of an earlier revision, named by restored_from), or baseline (the revision the campaign held when its history began, before any write recorded one).
A value this reader does not know reads as edited.
CampaignRevisionList— required: itemsCampaignScreens— required: campaign_id, counts, itemsCampaignStatus— required: campaign_id, screen_id, statusOne campaign's standing on one screen.
On a screen whose Player resolves its own stack (schedule.stack) and receives it, a campaign that holds by time stands where that stack puts it: a strip or main campaign plays in its region and a display lock on its displays; one the stack does not carry is pending or blocked (playlist_unavailable). playing: the screen shows it. pending: it should show and the switch lands within a minute. waiting: outside from, until or windows now (next_start, when it starts within eight days). ended: until has passed. outranked: it holds, but a screen takeover (reason takeover) or another campaign (reason campaign, by) wins. conditional: it holds by time and plays while what only the Player observes holds too (reason interaction: someone is using the screen; data: its data condition). paused. blocked: it cannot show here (reason no_default_playlist, no_timezone, or playlist_unavailable: the playlist is missing, unapproved or does not fit the screen, region_not_laid_out: it is laid out on no display of this screen (its strip fits none of the displays it targets, or its display lock names none of the screen's outputs) and is left out of this screen's stack, or stack_only: the screen's Player does not resolve its own stack, and only one that does honors a region, a display lock, interaction or a data condition). not_in_audience.
CampaignWrite— required: name, playlist_id, audienceCanvasBackground— required: noneSolid canonical uppercase #RRGGBBAA, or a top-to-bottom linear gradient.
CanvasColor— required: noneCanonical uppercase sRGB RRGGBBAA.
CapPull— required: noneCAP sources the platform reads for the trigger, with where the target screens are and which alerts count: the params of a cap-alert card placement (docs/feeds.md "CAP alerts"). Each source URL is fetched once per minute for every trigger and placement that names it.
With no sources the platform's public feeds covering location are read (NWS, Canada NAAD, and MeteoAlarm for a location with area codes). The most severe matching alert takes the target screens over exactly as a pushed message would; when the sources show none (a cancel or an expiry) a pulled alert ends.
A stale or failing source never ends an alert. location is required unless sources are given.
Capabilities— required: api_version, protocol_version, application_compressed_bytes, application_expanded_bytes, application_package_bytes, application_file_count, application_file_bytes, application_path_depth, application_path_bytes, media_image_bytes, media_audio_bytes, media_video_bytes, playlist_max_pages, playlist_max_items_per_page, playlist_max_media_per_selector, transition_max_duration_ms, screens_per_project, project_content_bytes, founding_period_ends_at, featuresCardCatalogue— required: cardsCardCreate— required: type, paramsCardDataEntry— required: key, document, bytes, sha256, revision, created_at, updated_atCardDataList— required: items, next_cursorCardDataSummary— required: key, bytes, sha256, revision, created_at, updated_atCardDataWrite— required: documentCardDetails— required: id, major, version, params_schema, data_schema, feed, tier, layouts, exampleCardFile— required: src, sha256CardInstance— required: id, project_id, type, major, params, created_at, updated_at, revisionCardKVWebhook— required: id, card_id, description, mapping, enabled, signing, url, created_at, updated_atAn incoming webhook that writes a card's data document.
CardKVWebhookCreate— required: noneCardKVWebhookList— required: itemsCardKVWebhookRotate— required: noneCardKVWebhookUpdate— required: noneCardKVWebhookWithSecrets— required: noneCardKVWebhookWriteResult— required: statusCardRequirements— required: cardml, featuresCardSummary— required: id, title, short, category, tags, tier, layouts, majorsComments— required: commentsCommentsWrite— required: commentsConnection— required: id, project_id, provider, mode, name, status, last_error, config, secret, last_test, revision, created_at, updated_atA project's reusable access to Microsoft or Google. Sources, Power BI connections and SharePoint media sync accounts name one in config.connection_id instead of carrying credentials of their own.
ConnectionCheck— required: name, ok, detailConnectionConfig— required: noneWhat a connection names besides its secret; only the members its provider and mode use are present. A secret, a key or a refresh token is never here.
ConnectionConfigWrite— required: noneMicrosoft, mode own_app: both members. Not accepted for a Google connection or for mode screenrig_app.
ConnectionConsent— required: noneA connection answered with its one-time consent link, which only a write that makes one carries: creating a connection in mode screenrig_app and POST /api/connections/{id}/connect. The person who consents opens consent_url before consent_expires_at. It is the link of one consent: send it to that person only. A read of the connection never carries it.
ConnectionError— required: code, message, atWhy a connection is not working. The message is a fixed sentence plus, for Microsoft, the AADSTS code; it never repeats text a caller sent.
ConnectionList— required: items, next_cursorConnectionPatch— required: noneConnectionSecret— required: set, hintHow an answer shows a connection's secret, key or refresh token. The value is never returned.
ConnectionTest— required: at, ok, checksConnectionWrite— required: provider, mode, nameCreatedProject— required: project, organizationCreditPurchase— required: id, status, amount_cents, credits, creditedDashboardAgentAction— required: project_idDashboardApplicationPreviewLaunch— required: release_id, launch_url, expires_atDashboardApplicationSummary— required: countDashboardBalance— required: credit_remaining, credit_included, credit_resetDashboardBoot— required: noneOne request for the dashboard shell. Each member is the body the named route answers for this same session, and is absent when that route would not answer 2xx for it. advertising_network is null when the project has no network.
DashboardDisplayName— required: noneTrimmed printable display name, at most 80 UTF-8 bytes. Rejects controls, URL-looking text, @, www., ://, and credential material. Never authorization.
DashboardEmail— required: email, email_verified, email_revisionThe current user's own email view. The verified address stays effective while a change is pending, and the pending candidate is visible only to its owner.
DashboardEmailVerificationAccepted— required: statusDashboardHealthSummary— required: display_disconnected, hot, crashing, staleActive screens with a health report, counted by condition; a screen can count in several. display_disconnected, hot, and crashing count only online screens whose latest report is current (not stale); stale counts online screens whose report is older than 35 minutes and nothing else.
DashboardMe— required: user_id, display_name, email, password, passkeys, created_atDashboardMediaSummary— required: count, bytesReady media counters for the session's project. bytes counts stored media bytes only; it is not the project used_bytes total, which also covers release trees and key/value entries. image_bytes, video_bytes and audio_bytes split bytes by primitive and always sum to bytes while image, video and audio are the only primitives; if another primitive is ever stored, the three sum to less than bytes.
DashboardMember— required: user_id, display_name, joined_at, passkeys, password, owner, externalDashboardMemberList— required: items, viewer_user_id, viewer_is_ownerDashboardPasskey— required: id, name, credential_id, provider, created_at, last_used_atDashboardPasskeyAssertion— required: ceremony_id, request_optionsDashboardPasskeyAssertionComplete— required: ceremony_id, credentialDashboardPasskeyComplete— required: ceremony_id, credentialDashboardPasskeyList— required: rp_id, user_handle, itemsDashboardPasskeyPatch— required: nameDashboardPasskeyProof— required: type, ceremony_id, credentialDashboardPasskeyRegistration— required: ceremony_id, creation_optionsDashboardPasswordLogin— required: email, passwordDashboardPasswordProof— required: type, passwordDashboardPasswordSet— required: password, proofDashboardPendingEmail— required: email, expires_atDashboardProject— required: status, created_at, email, screen_count, screen_limit, used_bytes, reserved_bytes, content_limit_bytes, event_retention_days, nameStatus, contact address, enforced limits, usage, retention, and public whole-credit state for one project. The internal plan identifier remains unpublished.
DashboardProjectList— required: itemsDashboardProjectListItem— required: id, name, last_used_atDashboardProjectReference— required: id, nameDashboardProjectSwitch— required: project_idDashboardProof— required: noneDashboardProofRequest— required: proofDashboardReauthentication— required: ceremony_id, optionsDashboardReauthenticationRequest— required: kindDashboardSSOProof— required: typeProof by a recent single sign-on. It is valid while the current session signed in through the organization's identity provider at most 10 minutes ago, and carries no ceremony and no secret. Otherwise the route answers reauthentication_required, and the person signs in through their provider again (POST /api/session/sso-starts, intent reauth).
DashboardScreenSummary— required: total, online, pairing_pending, active, archivedScreen counters for the session's project. total is pairing_pending plus active and excludes archived, matching project screen_count. online is derived from live paired runtime event streams exactly as Screen.online is; it is not a stored boolean and not a player heartbeat.
DashboardSession— required: user_id, display_name, email, project, auth_method, authenticated_at, expires_at, dev_auth, staff, shadow, passkey_enrollment_required, email_verification_requiredPerson session. Identifiers are descriptive; the session cookie establishes authority. A person with no projects has project: null.
DashboardSessionOrganization— required: id, name, managedThe person's home organization.
DashboardSessionSSO— required: organization_id, provider_name, linkedThe person's single sign-on context. Present whenever the person's verified address is at a domain whose organization has an identity-provider connection; linked is false until the person signs in through it or links it. An SSO session lasts 24 hours from sign-in and is not extended by use.
DashboardStaffWriteMode— required: request_id, project_id, expires_atDashboardSummary— required: screens, media, applicationsDeletedProject— required: id, screens, paired_screens_active, paired_screens_archived, paired_screens_completed_pending, agents, media, applications, playlists, membershipsDemoRequestInput— required: email, country, screen_range, organization, budget, timezone, source, flow_idDemoResult— required: route, booking, review_requested, video_url, email, country, timezone, flow_id, sourceDemoSlots— required: slots, timezoneDemoSource— required: noneDeviceSessionRequest— required: noneCookie-paired browser mint. The body is optional. Unknown members are ignored (docs/player-compatibility.md §1.1).
DisplayWindow— required: daysDocsPage— required: url, title, markdown, truncated, sections, corpus_commitDocsSearchResponse— required: results, corpus_commitDocsSearchResult— required: url, title, section, snippet, scoreEducationEligibilitySource— required: noneWho decided education_eligible. staff is a staff decision, either way, and is final. school_email is the automatic decision from an admin's verified K-12 school email address (a school domain such as k12.ca.us or sch.uk; never a general education domain such as .edu or .ac.uk). The empty string means nobody decided.
EmailVerifyRequest— required: tokenEmailVerifyResult— required: purpose, statusEmptyPaymentRequest— required: noneEnrollmentInvitation— required: id, status, expires_atEvent— required: cursor, type, severity, message, atEventActor— required: user_id, display_nameDashboard user that caused this mutation. Optional and absent on every event an agent, the CLI, a player, or a worker produced, so the field distinguishes a dashboard mutation rather than labelling all traffic. It is descriptive attribution only and is never authorization. display_name is the name the user chose at registration and is not verified.
EventAgent— required: agent_id, name, agent_typeSafe agent principal attribution present only when the event was directly caused by an authenticated agent bearer. It is never authorization.
EventCommand— required: name, methodThe agent request that caused this event. Optional and absent on every event a dashboard user, a player, or a worker produced. It is descriptive attribution only and is never authorization. Every field is written by the server from the route it matched or from the problem registry.
A command object cannot carry an argument vector, a request body, a header, or any text the caller chose, and there is deliberately no field for one. name and method appear on every event an agent request committed.
The outcome fields appear ONLY on an agent.command event, which is the record for a request that committed no domain event: a committed domain event is itself the proof that the request succeeded.
EventList— required: items, next_cursorFeedDocument— required: schema, fetchedAt, attributionFeedPending— required: key, state, retry_after_secondsFeedbackContext— required: noneClosed diagnostic envelope. Every member is an optional constrained scalar, nesting is structurally impossible, and an unknown member is rejected with invalid_request. The shape exists so a client cannot persist argument values, credentials, or free-form environment data through it.
FeedbackList— required: itemsFeedbackReplyWrite— required: textFeedbackResponse— required: id, text, status, created_atOne immutable staff or agent reply. An absent author identifies a staff reply. Plain text (escape it for any display context); credential-shaped text is replaced with [redacted] before it is stored.
FeedbackStatus— required: noneWhere staff stand on a submission. waiting_on_user asks the submitting agent for information; an agent reply reopens it or a closed report. A "planned" report keeps its status.
FeedbackSubmission— required: id, kind, title, body, created_atOne project-scoped submission. Its own fields (id, kind, title, body, context, created_at) never change after it is written. status is open until staff respond and then the status of the latest response; agent replies reopen reports awaiting input or closed, while a "planned" report keeps its status.
Responses are staff and agent replies, oldest first and at most 20, never edited or removed. Each reply also appends a feedback.responded event to the project's event stream (resource.id is the submission id; details carry response_id, status, fixed_in and author for agent replies, never the text).
The submission id is never authorization: only the owning project reads it.
FeedbackWrite— required: title, bodyFleetLink— required: id, label, enabled, tags, screens_created, revision, created_at, updated_atA Player URL that turns every device opening it into its own screen in this project.
FleetLinkList— required: itemsFleetLinkPatch— required: noneFleetLinkWrite— required: labelGroupBinding— required: binding_id, group_id, group_name, role, created_atGroupBindingCreate— required: group_id, roleGroupBindingGroup— required: id, display_name, member_countGroupBindingList— required: bindings, groupsHLSStreamSource— required: protocol, urlHealthResponse— required: statusHealthTrend— required: items, next_cursorHealthTrendHour— required: hour, screens_reporting, hot, display_disconnected, crashingHostContext— required: platformThe shell and hardware a player runs on, as the player reported it. A HINT that names a device; never a credential, never authorization. Only the Ed25519 key authorizes.
Stored per screen, replaced by every session mint that carries one, deleted with the screen, never written to operation logs, never echoed in problem responses, and never returned to any project other than the owner's. Read-only on the project API; ScreenPatch cannot write it.
HostDevice— required: noneOptional hardware identifiers. duid and serial are the only two the recovery hint consults; a failed read on the player omits the field.
IceServer— required: urlsAn RTCIceServer. Port 53 URLs are removed because browsers block them and nothing trickles.
IdentityProvider— required: connection_id, kind, preset, name, jit, service_provider, ready_to_require, policy, revision, created_at, updated_atThe organization's single sign-on connection. It holds no secret.
IdentityProviderCertificate— required: sha256, subject, not_afterIdentityProviderOIDC— required: issuer, client_id, client_secret_set, scopesIdentityProviderOIDCWrite— required: client_idIdentityProviderSAML— required: idp_entity_id, idp_sso_url, certificatesIdentityProviderSAMLWrite— required: noneIdentityProviderServiceProvider— required: oidc_redirect_uri, saml_entity_id, saml_acs_url, saml_metadata_urlThe values to enter at the identity provider, derived from the dashboard origin and the connection id.
IdentityProviderTest— required: at, ok, subject_presentThe outcome of the last test sign-in through the connection.
IdentityProviderWrite— required: kind, preset, name, proofInvitation— required: id, kind, delivery, status, project_id, created_at, expires_atInvitationAcceptRequest— required: token, modeNew credentials are required for create_login and reset. Link create_login requires the verified signup cookie and uses its display name and email.
Email create_login requires display_name. signed_in needs sign-in within ten minutes or an invitation_accept proof bound to the invitation id. buyer applies only to ad_buyer; new buyer projects start at zero credit.
InvitationAccepted— required: kind, project_id, nextInvitationAdvertising— required: screen_ids, slot_ids, policySeller-owned scope: at least one screen or slot. Screens only, slots only, or both are accepted.
InvitationBuyer— required: noneInvitationCreate— required: kindEmail delivery requires emails. Link delivery is project_member only and omits emails and advertising. ad_buyer requires advertising. At most 50 outstanding member and buyer invitations per organization; an outstanding email invitation of the same kind and address is reused.
InvitationCreated— required: invitationsInvitationCredential— required: noneInvitationEmailVerificationAccepted— required: status, expires_atInvitationEmailVerificationRequest— required: token, display_name, emailInvitationInspect— required: id, kind, delivery, project, expires_at, person, modes, methodsNon-consuming inspection. person is null for links. Never reports signup verification state, except that a sign-in reset, whose link went only to the address, says whether completing it replaces an unverified account (replaces_unverified_account). Email modes are create_login or signed_in; links offer both, and resets offer reset.
InvitationInspectRequest— required: tokenInvitationIssued— required: id, kind, delivery, status, project_id, created_at, expires_atInvitationKind— required: noneInvitationList— required: items, next_cursorInvitationPasskeyOptionsRequest— required: token, modeInvitationPasswordCredential— required: type, passwordInvitationSignup— required: email, display_name, verified, expires_atInvitationStatus— required: noneKVEntry— required: application_id, key, value_base64, content_type, bytes, sha256, revisionKVList— required: items, next_cursorKVSummary— required: application_id, key, content_type, bytes, sha256, revisionKVWebhook— required: id, application_id, key, description, url, created_atAn incoming key/value webhook. Its secret is returned only by create and rotate-secret.
KVWebhookCreate— required: keyKVWebhookDelivery— required: at, outcome, status, bytes, request_idKVWebhookDeliveryList— required: itemsKVWebhookList— required: itemsKVWebhookWithSecret— required: noneKVWebhookWriteResult— required: key, revision, bytes, sha256, content_type, updated_atKVWrite— required: value_base64LevelDate— required: dateOne specific civil date, the whole day or between start and end as a window's. It starts between from and until and is not an exception date.
LevelInteraction— required: noneThe level holds while someone is using the screen: from a touch, click or key until idle_s seconds pass with no input. Typical use: an interactive page above an attract loop. idle_s is stored and returned (60 when omitted).
LevelMonthly— required: noneA monthly occurrence in the screen timezone: day (1 to 31; a month without that day skips it), or nth (1 to 4, -1 the last) and weekday. start and end as a window's; the date the occurrence starts owns it.
LinearGradientBackground— required: type, stopsTop-to-bottom linear gradient. There is no angle field. stops has 2 through 8 entries, strictly increasing at in [0, 1], first at=0, last at=1.
LinearGradientStop— required: at, colorOne stop on a top-to-bottom linear canvas background. at=0 is the top edge, at=1 is the bottom edge.
LiveFrames— required: live_id, stateProgress of a live session's frame adapter. Open schema.
LiveSession— required: state, screen_id, viewer_urlA screen's live session. Open schema.
LiveStartRequest— required: noneLiveViewer— required: viewer_id, live_id, offer, ice_servers, expires_atA viewer's relay offer. Open schema.
LiveViewerAnswer— required: answerManifestActivatedContext— required: manifest_revisionManifestActivatedReport— required: severity, code, contextManifestUpgradeContext— required: manifest_revision, stateManifestUpgradePlaylist— required: id, name, revisionThe playlist identity behind an issued or queued desired revision: desired_playlist uses its exact desired grant or the read-only shared content goal for the current effective assignment before grant issuance. active_playlist always uses the exact acknowledged historical grant, never a newer assignment or content edit. id plus revision are the playlist version an operator reads as Target v42 / Playing v41; name is the playlist's display name, null when the playlist no longer exists or cannot be resolved.
A missing historical active grant is null, never a fabricated revision. A queued desired goal grants no content access.
ManifestUpgradeReport— required: severity, code, contextOne latest-wins manifest-upgrade lifecycle snapshot for the session screen.
The server stores one slot per screen and derives the project read model Screen.manifest_upgrade from it; a stale, superseded, or post-activation pre-activation report is accepted with no change (202), never refused as a conflict, and no report ever promotes a grant. severity is envelope-only: the derived project event severity comes from state.
McrString— required: noneExact integer millicredit amount as a canonical decimal string. 1 credit is 1000 mcr.
Media— required: id, filename, primitive, content_type, operation_id, sha256, bytes, revision, state, created_at, updated_atMediaAvailability— required: noneWhen dynamic selectors may pick the media, in each screen's time zone: inside [from, until) and on an occurrence of windows, monthly or dates (none of the three: every day) whose date is not in except. The schedule's level grammar without data and interaction.
MediaAvailabilityPatch— required: availabilityMediaCommit— required: content_type, bytes, sha256Declares only what the server can re-verify against the stored object byte for byte. Codec identity is not declared here: the server derives it from the container during asynchronous verification, so a client cannot assert a codec that drives player behaviour.
MediaFolder— required: id, name, parent_id, revision, created_at, updated_atOne media library folder. A playlist selector by:folder plays the ready files directly inside it, not those of its subfolders.
MediaFolderCreate— required: nameMediaFolderList— required: itemsMediaFolderPatch— required: noneMediaGenerationReference— required: noneExactly one of media_id or b64. HTTP URLs are rejected.
MediaGenerationRequest— required: promptRequests one still image. The prompt is customer bytes and is never returned, logged, or published on project SSE. aspect_ratio defaults to 16:9. quality is low, medium, or high and defaults to medium. tag is stored on the ready media object like an upload tag. Optional references are inline base64 or a project media_id; HTTP URLs are rejected.
The summed size of all references of one request, inline and media_id alike, must not exceed 25165824 bytes (24 MiB); a request over the bound is refused.
On Standard, remaining that cannot cover twice the vendor cost of the still, rounded up to whole credits, after the intro floor (negative 1,000,000 credits until 2027-01-01, then zero) returns payment_required, except development. Other plans are never refused for their balance.
MediaGenerationUsage— required: quality, credits, usdCustomer-facing debit for one stored still, the usage in a succeeded media.generate Operation's result. quality is the requested quality and changes the image, not a fixed price. credits are twice the vendor cost of that still, rounded up to whole ScreenRig credits. usd is the dollar equivalent of those credits.
Never a vendor name, vendor cost, or token count.
MediaList— required: itemsMediaMove— required: folder_idAt least one id across media_ids and folder_ids, at most 1000 in all.
MediaMoveResult— required: media, foldersMediaReplace— required: upload_idMediaRestore— required: noneMediaRestorePending— required: status, review, warningsMediaRestored— required: media, warningsMediaSelectorTagFilter— required: noneMediaSelectorWeights— required: noneShares of a weighted selector, keyed by media id or tag:NAME.
An item's weight is its media id key if present; otherwise the first tag:NAME key, in alphabetical order, among the tags it carries, with that weight split equally among the items of the selection that share the key, so a sponsor's share holds however many creatives it has; otherwise the item is its own group with weight 1.
Every full cycle plays each item exactly its share.
MediaSingleTagPatch— required: tagMediaSync— required: id, project_id, account_id, provider, name, folder_id, every_s, state, source, next_run_at, revision, created_at, updated_atOne source folder kept in step with one media folder.
MediaSyncAccount— required: id, project_id, kind, name, config, secret, state, revision, created_at, updated_atWhat syncs read through: the credentials of the project's own Microsoft Entra app (microsoft365), one person's connection to Canva (canva) or Dropbox (dropbox), or a Google service account that folders are shared with (google_drive). The client secret, the tokens and the key are sealed and never returned.
MediaSyncAccountConfig— required: noneMediaSyncAccountConnect— required: noneAn account answered with its one-time consent link: what creating a Canva or Dropbox account and media sync account connect answer. The person who owns the Canva or Dropbox account opens authorize_url, signed in to Canva or Dropbox, before authorize_expires_at. It is the link of one consent: send it to that person only. Reads of the account never carry it.
MediaSyncAccountList— required: items, next_cursorMediaSyncAccountPatch— required: noneMediaSyncAccountWrite— required: kind, nameMediaSyncCheck— required: name, ok, detailMediaSyncDesign— required: id, titleA Canva design the connected person can see.
MediaSyncDesignList— required: items, continuationMediaSyncList— required: items, next_cursorMediaSyncPatch— required: noneMediaSyncRun— required: at, status, added, updated, removed, pending_delete, skipped_totalThe summary of a sync's latest run.
MediaSyncRunError— required: code, detailMediaSyncSecret— required: set, hintHow an answer shows an account's client secret, a Canva or Dropbox account's tokens (set once connected, with no hint), or a Google Drive account's own service-account key (set when the account has one; hint is the last four characters of its private_key_id). The value is never returned.
MediaSyncSkipped— required: name, reasonMediaSyncSource— required: web_urlThe source a sync reads, resolved when the sync was created. A SharePoint folder has drive_id and item_id; a Dropbox folder item_id and path; a Google Drive folder item_id, drive_id when it is in a shared drive, and shared_by; a Canva design has design_id and the members after it.
MediaSyncTest— required: at, ok, checksMediaSyncWrite— required: provider, account_idMediaTagPatch— required: noneMediaTagsPatch— required: tagsMediaUploadDeclaration— required: filename, content_type, bytes, sha256Declares one upload. The bytes maximum is a plan-independent transport ceiling, not an achievable size. An image content type is bounded further at 20971520 bytes, 20 MiB, and a larger image declaration is rejected with invalid_request before any byte is transferred.
That image bound is a byte-only gate: declaration never decodes the image to enforce it, and it applies to animated GIF and animated WebP exactly as it applies to a still. The default plan has no product storage cap.
A custom storage ceiling, when present, is checked first and a declaration it cannot hold is rejected with quota_exceeded before any byte is transferred.
A Standard project with no credit left is refused declare and commit with payment_required when the bytes its payer stores, with this upload, pass 1000000000 bytes (1 GB, Standard's free storage); bandwidth is never charged.
An audio/mpeg (MP3) declaration is bounded at 178257920 bytes, 170 MiB, and a larger one is rejected the same way; read media_audio_bytes to preflight it.
A video/mp4 or video/webm declaration is bounded by the project's plan, 500000000 bytes, 500 MB, on Standard and 1000000000 bytes, 1 GB, on every other plan, and a larger one is rejected the same way, naming the limit that applies; read media_video_bytes from GET /api/project/capabilities to preflight it.
A plan change affects only later declarations: stored videos keep playing. Read media_image_bytes from the capabilities document to preflight the image bound, and project_content_bytes there, or content_limit_bytes on the project, to know any custom storage ceiling. An optional tag is stored on the ready media object and is not redeclared at commit.
An optional source_filename records the caller's original file name when the client transcoded the bytes before upload; the ready object's filename is then derived from it so distinct sources never collide (photo.png uploaded as photo.webp is stored as photo.png.webp, photo.jpg as photo.jpg.webp, and a source photo.webp stays photo.webp).
MediaUploadSession— required: id, operation, upload_url, method, headers, expires_atMediaUploadSource— required: filename, content_type, sha256, bytesVerified original upload identity when ingest normalized EXIF orientation. Read-only; use it to recognize retries. The ready media digest, size and type describe the normalized delivery bytes.
MediaUsage— required: playlists, screensMediaUsagePlaylist— required: id, name, uses, screen_countMediaUse— required: kind, whereOne way one playlist holds or reaches the media file; a playlist that does it on several pages is one use.
MediaVersion— required: number, current, filename, content_type, sha256, bytes, source, created_atMediaVersionActor— required: kindMediaVersionPage— required: items, next_cursorMediaWarning— required: code, detail, playlistsMediaWarningPlaylist— required: id, nameMemberActivity— required: id, project_id, user_id, display_name, at, kind, action, outcome, status, sourceMemberActivityClient— required: kindThe client as the edge request log names it, parsed from the User-Agent and Sec-CH-UA-Platform.
MemberActivityKind— required: nonesign_in and sign_in_failed carry auth_method; credential_changed is a passkey added, renamed or removed, a password set, or an email change requested or verified; reauthenticated is one checked proof (action reauthentication.<proof kind>); mutation is any other dashboard change.
MemberActivityList— required: items, next_cursor, recorded_sinceMemberActivityOutcome— required: noneok below status 400, refused for a 4xx answer, failed for a 5xx one.
MemberActivityResource— required: typeMemberApproverUpdate— required: can_approveMemberScreenScopeUpdate— required: screen_scopeMemberUploadScopeUpdate— required: upload_scopeNativeIdentityChallenge— required: nonce, expires_at, aud, op, request_hashCompare the returned op and request_hash with your locally computed values before signing. Each nonce expires in 120 seconds and can be consumed once.
NativeIdentityChallengeRequest— required: kid, op, request_hashSupply the operation and its request_hash for every challenge. An unknown key also requires public_key and op=pairing.start. Creating a challenge does not enroll the key.
NativeIdentityProof— required: kid, public_key, signatureCompact JWS protected header is exactly {"alg":"EdDSA"}. Claims are kid, nonce, aud=screenrig-runtime, op, integer iat/exp, nonempty jti, and request_hash.
Contextual proofs last at most 120 seconds and expire no later than their issued challenge. request_hash is lowercase SHA-256 hex of UTF-8 strings joined by one NUL, with no trailing NUL, starting with screenrig.native.identity.request.v1 and op.
Append Idempotency-Key for pairing.start and session.mint; append the exact srp_ pairing credential then completion_nonce for pairing.complete; append nothing for identity.reset. Every proof must include request_hash. A cached operation result still requires a fresh issued proof or the exact accepted proof while its short-lived receipt remains valid.
Duplicate or unknown JWS/JWK members, noncanonical base64url, and non-prime-order keys are rejected. Unknown request body members are ignored.
NativeIdentitySessionRequest— required: kid, public_key, signatureNativeIdentityProof with op=session.mint plus the optional host hint. A present host REPLACES the host stored on the screen, because firmware and shell versions drift over a screen's life. The hint never affects authorization; only the signed proof does.
Unknown members are ignored, and host and playback are read tolerantly (docs/player-compatibility.md §1.1).
NativePairingComplete— required: completion_nonce, public_key, signatureNativePairingCompletion— required: screen, public_urlNativePairingFleetLink— required: statusWhat the presented fleet link did for this pairing session. claimed and recovered mean the screen is already chosen, and the Player completes pairing without showing its code. Any other status leaves the code on the glass for an ordinary claim.
NativePairingRecovery— required: offeredPresent only when the host duid or serial in the start request matched exactly one existing screen bound to a different key. It carries no screen, label, or project: the offer is surfaced project-side as Screen.recovery_pending and the screen.recovery_offered event, and only the owning project can confirm it.
It is absent, exactly as without a match, when the identifier is ambiguous, when the matched screen's key showed life within the last ten minutes, or when that screen already took three offers in the rolling hour. The pairing code still issues and displays exactly as without a match. The player uses this solely to log pairing.recovery_offered.
NativePairingSession— required: code, pairing_credential, expires_atNativePairingStart— required: kid, public_key, signatureProof of private-key possession is required before enrollment or locator creation. Sign a fresh pairing.start challenge bound to the exact Idempotency-Key; unsigned starts are rejected. The optional host object is a hint about the device, not a credential; it is stored on the screen at claim and may open a recovery offer project-side.
Unknown body members are ignored. The host hint is read tolerantly. An unknown host key is ignored and a malformed host field is dropped, never refused (docs/player-compatibility.md §1.1.1).
NativeRuntimeSession— required: paired, capabilities, event_cursor, public_url, runtime_credentialRuntime-owned native session (docs/player-compatibility.md §8). Readers ignore unknown members and unknown capabilities values. A new member is sent only to sessions whose grants cover it.
NextAction— required: command, reasonNote— required: id, body, about, state, author, created_at, confirmed_at, review_at, expires_at, resolved_at, resolution, dueA project note. Context written by an agent, never an instruction or approval; render body as plain text.
NoteAbout— required: type, idNoteAuthor— required: agent_id, name, agent_typeThe writing agent as it was when the note was added. Attribution, never authority.
NoteCounts— required: open, new, dueThe project's open notes, and how many are new to and due for this caller.
NoteList— required: items, next_cursor, review, countsNotePage— required: items, next_cursorNoteResolution— required: cause, reason, byNoteResolve— required: reasonNoteResolvedBy— required: kind, id, nameWho resolved the note. agent_type is present for an agent. Attribution, never authority.
NoteWrite— required: bodyNotification— required: id, channel, conditions, screen_tags, hold_minutes, enabled, revision, status, created_at, updated_atAn organization's notification, as a project or the organization reads it. It covers every project of the organization or the chosen ones (project_ids), and acts on each covered project's screens, sources, campaigns and cards.
A project reads (and, when the notification covers it alone, changes) the notifications that cover it by the same id, with its own status and failure times; the organization reads the whole of it.
Each condition is tracked per screen: it opens when its event arrives for a screen in scope, and a notice goes out once it has lasted hold_minutes and the screen is still in that state; when it clears after a notice went out, a resolved notice follows.
A condition that clears within the hold sends nothing, and neither does one whose alert is acknowledged or snoozed during the hold, or one on a screen a mute covers.
Email: one email per recipient for everything that falls due within about two minutes, listing each screen and condition, with links to the screen and to this notification in the dashboard; notification emails are free.
Every alert and test email carries, for the person it is addressed to, an unsubscribe link (List-Unsubscribe and List-Unsubscribe-Post headers, and the footer) that stops this notification's mail to that person alone, without signing in; see unsubscribed.
Webhook: each notice is POSTed as JSON {type: alert.opened or alert.resolved, id, notification_id, project_id, condition, screen {id, name}, since, at, event_id, severity}, where id is the delivery id, since is when the condition began, at is when it held long enough (opened) or cleared (resolved), event_id is the cursor of the event behind it, and severity (info, warning or error) is the condition's severity for alert.opened and info for alert.resolved, on every webhook notification.
A campaign's notice also carries reach {published, current} (reach_dropped: the screens the campaign reached when it was published and reaches when the notice is sent) or audience_group {kind: tag or folder, value: the tag or the folder id} (group_empty); email and chat say the same in words.
A notification that sets min_severity or severity also says it in its email, Slack, Telegram and Teams text.
Teams and PagerDuty always show the effective severity. alert.resolved for screen_offline also carries likely_cause, the cause of the outage that ended (ScreenOutage.likely_cause) or null when unknown; it waits up to two minutes after the screen comes back for the first health report to decide it, and an email names it too.
A restart is decided at once; a network or platform cause needs a health report received after the screen is back, which a Player may send up to 15 minutes later, so a notice that went out before then carries null and the cause shows in ScreenOutage.likely_cause and screen.outage_classified.
Email and the chat channels say three or more screens that fall into one condition within five minutes and share a network, location, model, Player version or tag as one notice with one recovery (NotificationDeliveryGroup); the other screens' notices are skipped as grouped.
A webhook still sends one notice per screen. event_types are POSTed as the same JSON object GET /api/events returns.
Every POST carries ScreenRig-Webhook-Id (the notification id), ScreenRig-Event-Id (the delivery id for a notice, the event cursor for an event; stable across retries, deduplicate on it), User-Agent, and ScreenRig-Signature t=<unix seconds>,v1=<hex HMAC-SHA256 over "<t>.<raw body>" keyed with the secret string>.
A 2xx answer within 10 seconds succeeds; anything else is retried with exponential backoff (30 seconds doubling to one hour, jittered) for 24 hours and then failed; email failures retry the same way.
Screen notices and screen.* events are free; other events are metered like a listen-stream event. status is failing while nothing has succeeded since failing_since; after 72 hours of continuous failure the notification is disabled (disabled_reason delivery_failures) and notification.disabled is appended.
NotificationActiveHours— required: timezone, windowsWhen opened notices go out: the windows (screenrig.schedule/v1), read in timezone. A notice that falls due while they are closed waits for the next opening (outside hold, the default) and goes out then if its condition still holds, so a condition that clears first sends nothing, or is skipped (outside drop; last_error outside_hours).
A resolved notice for an opened notice that was sent always goes out, inside the hours or not, and a test ignores them. crashing is held or dropped like any other condition. The hours judge a notice when it first goes out; a failed one is retried on the usual schedule whatever the hours.
Adding, changing or removing the hours judges the notices held for the old ones again at once.
NotificationCondition— required: noneAn important screen state change. screen_offline: the Player lost its connection (screen.offline, written 60 seconds after its presence lapses); clears on screen.online. display_disconnected: the Player reports its display disconnected (screen.health_changed change display_disconnected); clears on display_reconnected. running_hot: the device reports a temperature at or above the hot threshold (temperature_high); clears on temperature_normal. on_battery: the device has a battery and its latest health report names a battery source of none, which is unplugged (screen.health_changed change on_battery); clears when a report names any other source (plugged_in).
The notice says the screen runs on its battery and names no cause. storage_full: the Player's storage cannot hold its content (screen.storage_shortfall); clears on screen.storage_shortfall_cleared. content_stuck: the screen's content update is retrying, failed, blocked or partial (screen.manifest_upgrade); clears when a later state is none of those. not_playing: the Player shows an error-level status instead of its content (screen.status_changed with level error, other than storage_full); clears when its status is no longer an error. loop_stalled: the Player reports nothing wrong and its content is current, yet no media start has reached the backend for longer than the screen's loop allows (screen.glass_changed with the playback verdict stalled; Screen.glass says when it judges and what it leaves alone); clears when a media start arrives or the screen stops being judged, for instance when it goes offline. glass_blank and glass_frozen: the backend read the screen's screenshots over a run that covers its loop and every one was a flat tone (glass_blank) or the same picture (glass_frozen) (screen.glass_changed with the picture verdict blank or frozen; Screen.glass says when it judges and what it leaves alone); clears when two screenshots in a row show a moving picture or the screen stops being judged.
A screen that stops being judged closes the alert, but email and chat notifications send no recovery notice for it, since nothing was seen to recover (the resolved row is skipped as no_longer_judged); a media start or two good screenshots do send one. crashing: the Player crashed or restarted its renderer repeatedly (screen.health_changed change crash_spike or renderer_restart_spike); sent at once, never held or resolved. source_failed and source_stale are about a data source, not a screen: the notice's screen member carries the source's id and name, the link is the source's page, and screen_tags never matches a source, so a notification scoped to tags does not watch them. source_failed: a refresh of the source failed (source.status_changed with state error); clears on the next refresh that works (state ok). source_stale: the source's last good refresh is older than its stale_after_seconds, the point its card data expires and pages that depend on it fall back (source.stale); clears on the next refresh that works.
Neither is a screen alert. reach_dropped and group_empty are about a campaign (a shared level of the schedule stack), not a screen: the notice's screen member carries the campaign's id and name, the link is the campaign's calendar in the dashboard, and a campaign is the project's, so screen_tags and audience never limit them. reach_dropped: a recount found the campaign reaching fewer screens than when it was last published, because screens left one of its tags or folders, were archived or deleted (campaign.reach_changed with current below published); reach_drop sets how large a drop is notified; clears when it reaches as many screens again.
A publish that sets a new baseline, or deleting the campaign, also clears it, which closes a pager incident and a webhook's alert but sends no recovery mail or chat notice (no_longer_judged). group_empty: a tag or folder of the campaign's audience matches no screen (campaign.group_changed with matched 0); one notice per tag or folder (audience_group); clears when it matches a screen again, or when a publish removes it from the audience (removed true), which closes a pager incident and a webhook's alert but sends no recovery mail or chat notice (no_longer_judged).
A screen's own schedule entries target playlists, not screens, so only campaigns have a reach.
Neither is a screen alert. card_data_stale is about a card, not a screen: the notice's screen member carries the card's id and its name (the card's type, as in "weather card"), the link is the dashboard's page editor on a page that places the card (the notification's own page when none does), and since is when its data expired.
It opens when a card's data document that an agent, a job or a person wrote with an expiry (ttl_seconds or expires_at, as in card data put --ttl) reaches that expiry without another write to renew it, while a screen shows the card (card.data_expired), so the card is blank on that screen; it clears when a write to the card's data lands (card.data_renewed).
A data source's documents are not announced: they expire by the source's stale period and source_stale covers them. A card no screen shows sends nothing, and neither does one that was renewed, deleted or taken off every screen before the notice is due.
Deleting a card's data ends the notice without a recovery notice, as a publish ends a drop (no_longer_judged). Size ttl_seconds to the writer's schedule, so a job that stopped is noticed. Like a data source, a card has no screen tags: a notification scoped to screen_tags does not watch cards, and it is not a screen alert.
Readers treat an unknown value as absent.
NotificationConditions— required: noneNotificationDelivery— required: id, notification_id, event_id, event_type, state, attempts, created_atNotificationDeliveryAttempt— required: attempt, at, status, error, duration_msNotificationDeliveryAttemptList— required: itemsNotificationDeliveryGroup— required: id, attribute, value, countA group of screens one notice stands for. When at least three screens of a project fall into the same condition within five minutes of one another and share a value, an email, Slack, Teams, PagerDuty or Telegram notification sends one notice for them instead of one per screen.
A screen that falls into the condition later with a value the open group is made of joins it without a new notice, and one notice says the group when its last screen recovers. A webhook notification still sends one notice per screen.
NotificationDeliveryList— required: items, next_cursorNotificationDestination— required: noneSlack, Teams, PagerDuty and Telegram only; required for them. The credential in the shape the service issues it: slack, the incoming webhook URL (hooks.slack.com); teams, the Workflows webhook URL (logic.azure.com or powerplatform.com; Office 365 connector URLs are retired); pagerduty, the 32-character Events API v2 routing key; telegram, the bot token.
Another shape is notification_destination_rejected. It is sealed, never returned, and appears on reads only as destination_hint.
NotificationEventTypes— required: noneWebhook only. Project events also POSTed as they happen, exactly as a webhook sends them: exact types such as screen.online, or prefixes ending in .* such as screen.*. notification.test is never fanned out.
NotificationList— required: itemsNotificationPatch— required: noneNotificationProjectIDs— required: noneLive projects of the organization, each named once. An empty list covers none.
NotificationReachDrop— required: noneThe smallest drop in a campaign's reach a reach_dropped notice is sent for: screens is the fewest screens lost (default 1) and percent, when set, the smallest share of the screens the campaign reached when it was published; a drop must meet both.
A smaller drop queues no notice, and a queued notice whose drop shrank below it by the time it is due is skipped (condition_cleared). Only with the reach_dropped condition; on a notification without it, invalid_request.
NotificationRecipients— required: noneEmail only. owner (whoever owns the project when a notice sends) or the user_id of a person who is a member of the project; a user_id that is not a member is invalid_request. Each is emailed at their verified address. One who has since left the project or has no verified address is skipped.
NotificationSeverity— required: noneThe severity of a notice, least severe first. An opened notice has its condition's severity: error for screen_offline and warning for every other condition, unless the notification's severity says otherwise. A resolved notice and a test are info.
NotificationSeverityOverrides— required: noneCondition to severity for this notification, such as {"not_playing": "error"}. It counts for min_severity and is the severity Teams and PagerDuty show for the condition. A condition not listed keeps its default. On a patch an empty object, like null, clears every override.
NotificationTelegramChat— required: noneTelegram only; required for it. The chat to post to: its numeric id (negative for a group or channel) or @channelname. Not a credential.
NotificationUnsubscription— required: user_id, atNotificationWrite— required: channelemail sends to recipients, webhook POSTs signed to url, and slack, teams, pagerduty and telegram post one message per outage and screen to a chat or pager with the write-only destination (telegram also needs telegram_chat_id), then a recovery after it; PagerDuty opens an incident and resolves it. They need at least one condition and take no event_types.
OAuthAuthorization— required: id, client_name, client_registered, redirect_host, access, capabilities, created_at, expires_atOAuthAuthorizationRedirect— required: redirect_uriOAuthError— required: errorOAuthTokenResponse— required: access_token, token_type, expires_inOnboardingSiteCTA— required: label, path, href, atThe sign-up or sign-in link the person clicked on the website.
OnboardingSiteVisit— required: path, atOnboardingStepName— required: noneA step of the entry flows, in display order. email_verified, email_gate_reached and the account milestones (agent_connected, screen_paired, playlist_with_content, activated) are written or joined by the server and are not names a client sends.
OnboardingStepRequest— required: journey_id, stepOnboardingValue— required: noneWhat the person entered at a step: the address typed (email_submitted, reset_requested), the name (signup_name_entered), yes or no (signup_marketing_choice), the options chosen or the follow-up text (question_answered, question_follow_up), an address or name entered on an invitation. Never a password, code or token. Empty is absent.
Operation— required: id, kind, state, created_at, updated_atOperationAccepted— required: id, release_id, operation_idOrganizationAdminInvitation— required: id, kind, delivery, status, recipient_email, organization_id, created_at, expires_atOrganizationAdminInvitationCreate— required: emailsOrganizationAdminInvitationCreated— required: invitationsOrganizationAdminInvitationList— required: items, next_cursorOrganizationAdmins— required: admins, candidates, revisionOrganizationAdminsUpdate— required: admin_user_idsOrganizationBillingUsage— required: org_id, period, pool, allowance, stored_bytes, screens, projects, next_cursorOrganizationDomainChallenge— required: organization_id, challenge_id, domain, name, value, expires_atOrganizationDomainInput— required: domainOrganizationDomainProof— required: challenge_idOrganizationDomainRemoved— required: statusOrganizationGuestPolicy— required: mode, domainsThe organization's guest policy in force. Guests are people whose home organization is another one, or who have none; a policy never removes anyone who is already a member.
OrganizationGuestPolicyDomain— required: noneA lowercase DNS name with at least two labels, as it appears after the @ of a verified email address (internationalized names in their xn-- form).
OrganizationGuestPolicyMode— required: noneallow admits every guest. domains admits a guest whose verified email address is at one of the listed domains (exact match). none admits no guest. Home members are never guests.
OrganizationGuestPolicyResult— required: mode, domains, outside_policyThe guest policy now in force, with how many people it leaves outside.
OrganizationGuestPolicyUpdate— required: modeOrganizationLibraryUpdate— required: project_idOrganizationList— required: organizations, new_organization_allowedOrganizationMemberInvitation— required: id, kind, delivery, status, recipient_email, organization_id, created_at, expires_atOrganizationMemberInvitationCreate— required: emailsOrganizationMemberInvitationCreated— required: invitationsOrganizationMemberInvitationList— required: items, next_cursorOrganizationNotificationList— required: itemsOrganizationNotificationPatch— required: noneA NotificationPatch and project_ids.
OrganizationNotificationWrite— required: channelA NotificationWrite and the projects the notification covers. Absent or null project_ids covers every project of the organization, including projects added later; a list covers exactly those projects, each a live project of the organization.
OrganizationPayments— required: organization_id, available, local_simulation, purchasesOrganizationPeople— required: projects, people, truncatedOrganizationPeopleMembership— required: project_id, role, can_approveOrganizationPeoplePerson— required: user_id, display_name, email, admin, member, managed, outside_guest_policy, projectsOrganizationPeopleProject— required: project_id, nameOrganizationPerson— required: user_id, display_name, emailOrganizationPersonal— required: noneThe organization is only its one project's wrapper: exactly one live project, no verified domain, no organization plan now or scheduled, and no admin other than that project's owner. The dashboard hides the organization level for it.
OrganizationProject— required: id, name, organization_id, status, screen_countOrganizationProjectAccess— required: project_id, joinedOrganizationProjectAccessChange— required: project_id, user_id, accessOrganizationProjectAccessRequest— required: changesOrganizationProjectAccessResult— required: changesOrganizationProjectOwner— required: project_id, owner_user_idOrganizationProjectOwnerUpdate— required: user_idOrganizationProjectPage— required: projects, next_cursorOrganizationRename— required: nameOrganizationSummary— required: id, name, personalOrganizationView— required: id, name, personal, revision, admin, home, project_count, plan_id, free, plan_required, education_eligible, education_eligibility_sourcePageDataCondition— required: keyA card-data predicate, evaluated before civil time. Missing or expired data never matches. The page also respects its ordinary time bounds. Requires host capability page.conditions; older hosts receive the page disabled.
PageFailure— required: page_id, code, atLatest accepted playback.page_failed report for this screen. Absent until a player reports one. Read-only project health metadata; it does not change screen revision or manifest authority.
PageVisibility— required: enabledOptional page visibility schedule. It is a sibling of advance and is never part of screenrig.canvas/v1; it describes playback orchestration, not geometry. The server validates and carries these rules as data. The player evaluates them against the screen timezone published at the manifest root.
A page is eligible when enabled is true, now is inside [from, until), and either windows is absent or at least one window matches. Every playlist must keep at least one page with no visibility field at all, so eligible content always exists. Conformance vectors for the evaluation rules are the screenrig.schedule/v1 family in packages/schedule-contract.
PageVisibilityWindow— required: daysOne recurring civil window. Omitting start and end selects the whole day. When end is less than or equal to start the window crosses midnight and the start day owns it, so fri 22:00 to 02:00 runs into Saturday morning.
PairScreen— required: codePairingClaim— required: screen, public_urlPairingClaimedEvent— required: type, completion_noncePairingComplete— required: completion_noncePairingCompletion— required: screen, public_urlPairingSession— required: code, expires_atPasskeyAdditionProof— required: authorization_ceremony_id, authorization_credentialPlaybackAggregate— required: screen_id, media_id, filename, day, play_count, last_page_id, last_manifest_revision, first_started_at, last_started_atPlaybackAggregateCsv— required: noneRFC 4180 CSV, CRLF records, UTF-8, with the header row screen_id,media_id,filename,primitive,day,play_count,last_page_id,last_manifest_revision,first_started_at,last_started_at. Times are RFC 3339 UTC. A cell beginning with =, +, -, @, tab, or carriage return is prefixed with a single quote so spreadsheets do not evaluate it.
Content-Disposition names playback-aggregates.csv. A stream that fails after the header is aborted at the connection.
PlaybackAggregateList— required: itemsPlaybackApplicationStartedContext— required: manifest_revision, page_id, primitive_id, release_idPlaybackApplicationStartedReport— required: severity, code, contextOne visible start of an application primitive on a page: the application became visible on glass, reported once per page appearance by the Player controller — the application content never reports its own visibility, and a package download is never a start.
The sending screen is authenticated; page, primitive, release and manifest identities are reported observations without content or grant membership checks. Stores one durable per-play record (GET /api/playback/plays). Project-owned release/application housekeeping timestamps follow through a recoverable batch projection.
Ordinary playback acknowledgments omit optional credit balance headers. There is no daily aggregate row for application starts; the play records are the evidence.
PlaybackCompleteness— required: day_from, day_to, measured_plays, missing_plays, completeness, days, gaps, truncatedHow many of the plays the screens made reached the backend, over the inclusive UTC days day_from to day_to. measured_plays and missing_plays are the sums of days, and completeness is measured_plays over their sum, null when both are 0. days lists, oldest first, only the days with a measured or a missing play. gaps lists the holes in the play numbering, newest first.
Plays without a number (older Players, starts stored before play numbers) are in none of these figures.
PlaybackCompletenessDay— required: day, measured_plays, missing_plays, completenessThe play numbering of one UTC receipt day.
PlaybackGap— required: screen_id, day, from, to, missing_playsOne run of play numbers a Player skipped, so that many plays were made and never arrived.
PlaybackGrant— required: noneThe playback handshake the backend granted: the intersection of the mint's PlaybackHandshake and what this backend supports, signed into the session. Omitted from the session when nothing intersects, so a session minted without a handshake is unchanged. A Player uses /screen/manifest only when manifest_versions contains 3.
PlaybackHandshake— required: noneOptional playback handshake a Player declares at mint. It is a hint that selects behavior, never authorization: unknown members and entries of the wrong type are ignored, and a value that is not an object negotiates nothing. The mint response echoes the supported intersection as PlaybackGrant. adslot-v1 is granted only to paired sessions.
PlaybackMediaStartedContext— required: media_id, manifest_revision, page_id, primitivePlaybackMediaStartedReport— required: severity, code, contextOne visible start of an image or video primitive on a page. Players send this once per page appearance of the media, for images exactly as for videos; loops inside one appearance are not restarts. The sending screen is authenticated; media, page, primitive and manifest identities are observations without content or grant membership checks.
Success acknowledges durable raw insertion; daily counts follow through a recoverable batched projection. Ordinary playback acknowledgments omit optional credit balance headers.
PlaybackPageFailedContext— required: page_id, codePlaybackPageFailedReport— required: severity, code, contextReports that one page could not become ready. On acceptance the server replaces Screen.last_page_failure and appends a durable project-only playback.page_failed event in the same transaction. It does not change screen revision or manifest authority.
PlaybackPlay— required: screen_id, page_id, media_id, primitive, received_atOne durable Player-reported start of an image, video, or application.
The session authenticates screen and project; item, page, manifest, primitive and release identities are reported observations and do not authorize content access. playlist_id and playlist_revision are optional metadata enriched asynchronously from a matching screen manifest; missing metadata stays absent.
Application starts carry release_id with empty media_id and primitive. primitive_id and started_at are present only when the Player reports them; started_at is clamped to [received_at - 24h, received_at + 5m].
A JSON page also names its screen, media, playlist and application as display text read when it is served; the CSV export and the xlsx and pdf reports omit those names.
PlaybackPlayCsv— required: noneRFC 4180 CSV, CRLF records, UTF-8, with the header row screen_id,playlist_id,page_id,primitive_id,media_id,primitive,started_at,received_at,manifest_revision,playlist_revision in that fixed order; an absent value is an empty cell. Times are RFC 3339 UTC. A cell beginning with =, +, -, @, tab, or carriage return is prefixed with a single quote.
Content-Disposition names playback-plays.csv. A complete export ends after its last CRLF record with no trailer; a stream that fails after the header is aborted at the connection (the client sees a transport error, never a clean end), so retry from the last received_at.
PlaybackPlayList— required: items, next_cursorPlaybackTotals— required: by, day_from, day_to, truncatedPlay totals over the inclusive UTC days day_from to day_to. Exactly one of days (by=day), screens (by=screen), media (by=media) or screen_days (by=screen_day, grouped by screen in rank order, days ascending) is present, the one the request named. truncated is true when a ranking may leave out keys that ranked below the best of a slice or a store.
PlaybackTotalsDay— required: day, play_count, first_started_at, last_started_atPlaybackTotalsMedia— required: media_id, play_count, first_started_at, last_started_atPlaybackTotalsScreen— required: screen_id, play_count, first_started_at, last_started_atPlaybackTotalsScreenDay— required: screen_id, day, play_countPlaybackVideoStartedContext— required: media_id, manifest_revision, page_idPlaybackVideoStartedReport— required: severity, code, contextReport one visible video start. This uses the same daily aggregation as playback.media_started with primitive video. Send one report per start; sending both event types counts the start twice. Use playback.media_started when reporting both images and videos.
PlayerBrowserIdentity— required: kind, version, pwaBrowser engine and installation mode for browser Players, including native shells. A malformed optional browser hint is dropped without rejecting the mint.
PlayerCardsIdentity— required: noneThe built-in cards library the Player carries (docs/player-compatibility.md §3.1): the library CalVer and each card's semver, whose major says which card placements the screen renders. Stored on the screen with the rest of player. Malformed entries are dropped one by one; it is never a 400.
PlayerIdentity— required: kindPlayer self-identification, sent only on the three mint routes (docs/player-compatibility.md §3.1). It is a hint and never authorization, and it is not covered by the native proof. It is read tolerantly: unknown members are ignored, an invalid optional member is dropped on its own, and a player that is not an object or has no valid kind declares nothing.
It is never a 400. A paired mint stores it on the screen for the operator fleet histogram.
PlayerPublicKey— required: kty, crv, xPlayerShellIdentity— required: kindThe shell a web Player runs under. Its compat tokens are recorded, never granted. An invalid kind drops the shell.
Playlist— required: id, name, revision, pagesPlaylistAdvance— required: nonePlaylistAdvanceWrite— required: nonePlaylistApplicationAdvance— required: mode, max_msPlaylistApplicationPrimitive— required: id, primitive, release_id, rect, layer, content_fitPlaylistApplicationPrimitiveWrite— required: id, primitive, release_id, layer, content_fitPlaylistApproval— required: state, review_idPresent while a review of this playlist is pending. The playlist itself is the live (approved) version.
PlaylistAudio— required: tracks, loop, volumeStored playlist soundtrack with defaults filled in.
PlaylistAudioCue— required: trackOptional soundtrack hint on an ordinary page. When the page becomes current, the Player switches to the named track from its start unless it is already playing; restart true restarts it even then. The sequence continues from that track.
Players may ignore a hint. track must name a track id in the playlist audio; a cue without a playlist audio is invalid_request. Not allowed on adslot pages. A page with advance mode audio_end ends when the cued track ends; it requires this cue.
PlaylistAudioEndAdvance— required: mode, max_msPlaylistAudioEndAdvanceWrite— required: modeThe page ends when the soundtrack track its audio_cue names ends. The page must carry an audio_cue and the playlist an audio soundtrack (docs/playlist-audio.md).
PlaylistAudioTrack— required: id, media_idPlaylistAudioWrite— required: tracksOptional playlist soundtrack: ordered ready audio (MP3) media that plays continuously while pages change (docs/playlist-audio.md). Page changes never stop or restart it. PUT replaces the whole playlist, so omitting audio removes the soundtrack.
PlaylistCanvas— required: width, height, backgroundPlaylistCardPrimitive— required: id, primitive, card, major, params, rect, layer, content_fit, controllerPlaylistCardPrimitiveWrite— required: id, primitive, card, layout, content_fitA built-in card the Player draws natively (clock, weather, menu board, ticker, KPI tiles and the rest of the vendored cards catalog, screenrig.cards.contract/v1). layout places it: full (the whole canvas), free (your own rect and layer), center (900 x 420), wide (the canvas inset 30 from the left and right and 80 from the top and bottom), header (the whole top edge, 80 high), footer (the whole bottom edge, 80 high), corner (270 square), chip (240 x 70) or strip (the whole edge, 120 high), in 1080p design units scaled by min(canvas width, canvas height) / 1080. corner and chip take an anchor corner (top-left, top-right, bottom-left, bottom-right) and strip an anchor edge (top, bottom, left, right); the other layouts take none.
Except for free, the server derives rect and layer from the canvas, or from the frame displays cover: omit them, or send exactly what it derives.
A page holds at most one full, center or wide card, one corner or chip card per corner, and one strip per edge (a card with presence may share an edge with a normal strip); a header takes the top edge's normal strip slot and a footer the bottom edge's, so a page holds at most one of each, no normal strip on that edge, and a card with presence may still share it.
A corner or chip on an edge that holds a strip moves in past it by 120, and past a header or footer by 80.
On a playlist with a format, displays is the frame the layout derives in (full is the frame, corner the frame's corner; free refuses displays), the card scale is the tile's min(width, height) / 1080 = 1 rather than derived from the frame, and two cards compete for a slot, or move in past each other's strips, only when their frames share a display (no displays is every display).
A card's tier decides its layouts (GET /api/card-types): core takes all nine, fullscreen takes full. params holds the placement's static properties (theme, location, titles, column definitions) and changes only with a republish.
A string in params may carry {screen.name}, {screen.timezone} and {screen.field.NAME}, and a location param may be the string {screen.location}; the backend expands them for each screen, so one playlist shows every screen its own values. data names a card data document (GET/PUT /api/card-data/{key}) that holds the card's dynamic properties (menu sections, ticker items, KPI tiles); agents change it without a republish. {screen.field.NAME} in a data key reads a per-screen document, for example menu-{screen.field.store_number}.
With data, params may not carry dynamic properties; without it, params carry the whole configuration. Every string in params that begins with media: is a media reference, media:<id>, naming a ready image in this project that the card draws (spotlight people[].photo, product-grid products[].photo); a card never takes an image URL.
Publish refuses an unknown card or major and invalid params (card_invalid), a layout, anchor, rect or layer the card may not carry (card_layout_invalid), cards that claim one place on the page (card_layout_conflict), a media reference that names no ready image of the project (card_media_invalid), and a bound key whose stored document the card rejects (card_data_invalid).
The screens showing the placement are granted its media, and a live write approves pending media it names, as with a by:id selector. A card with a third-party feed (weather) reads it from the key the server derives from its static params.
PlaylistCreateRequest— required: noneA playlist's pages, or a template to make it from, or the built-in starter.
PlaylistDisplays— required: nonePlaylists with a format only. The displays a primitive covers: ascending unique 1-based display numbers within 1..format.displays that fill a rectangle of the wall's tiles, such as [2], [1, 2] or, on a grid of 4 columns, [2, 3, 6, 7]. In a row or a column they are contiguous.
The server derives rect as the bounding box of those tiles; rect is omitted or exactly the derived value. Stored and echoed on read.
PlaylistDurationAdvance— required: mode, after_msPlaylistExpandRequest— required: nonePlaylistExpanded— required: pagesPlaylistFormat— required: noneWhat a playlist is authored for: a video wall (PlaylistWallFormat) or a region (PlaylistRegionFormat), never both. A playlist without a format is an ordinary playlist that binds to any screen as the page.
PlaylistFromTemplate— required: playlist_idCreates a playlist from a template: its current pages with the values filled in, validated by every playlist rule. A media value must be a ready media item of the right kind, a url value an http or https address.
PlaylistFromTemplateWrite— required: name, from_templateCreates a playlist from a template. Send only name and from_template.
PlaylistIframePrimitive— required: id, primitive, src, title, rect, layer, content_fit, controllerPlaylistIframePrimitiveWrite— required: id, primitive, src, title, layer, content_fitPlaylistImagePrimitive— required: id, primitive, selector, resolved_media, rect, layer, content_fit, controllerPlaylistImagePrimitiveWrite— required: id, primitive, selector, layer, content_fitPlaylistInstance— required: id, name, revision, template_revision, outdatedPlaylistInstanceList— required: itemsPlaylistIntrinsicSize— required: width, heightPlaylistList— required: itemsPlaylistMediaEndAdvance— required: mode, max_msPlaylistMediaEndAdvanceWrite— required: modePlaylistMediaSelector— required: nonePlaylistMediaSelectorByAll— required: byPlaylistMediaSelectorByFolder— required: by, folder_idThe ready files of the primitive directly inside one media folder (GET /api/media-folders), not those of its subfolders, ordered and paced like by:all. Like a tag, the folder is read again at every selector resolve, so moving files in or out changes what playing screens show from their next loop.
Writing a playlist requires the folder to exist in the project; it may be empty. A folder that holds no ready file of the primitive, or is deleted later, matches nothing. Players receive it as by:all; the server resolves the folder.
PlaylistMediaSelectorByID— required: by, media_idPlaylistMediaSelectorByIDs— required: by, media_idsPlaylistMediaSelectorByTag— required: byPlaylistPage— required: id, canvas, transition, advance, primitivesPlaylistPageAdd— required: pagePlaylistPageStructure— required: idOne page that stays, and the two page settings a person changes. advance replaces the page's advance when present and keeps it when omitted; an ad slot page has none. visibility replaces the page's visibility when an object, removes it when null and keeps it when omitted.
A visibility object that only sets enabled true is a no-op and is rejected: send null to remove the rules.
PlaylistPageV2— required: nonePlaylistPageWrite— required: id, canvas, transition, advance, primitivesPlaylistPageWriteV2— required: nonePlaylistPatch— required: noneOmitted fields are preserved. Supplied name, pages, audio and format replace those top-level values; audio or format null removes it. Supplied pages are checked against the stored format when format is omitted, and a supplied format against the stored pages when pages is omitted. Identity, revision, comments and timestamps cannot be set here.
Comments have their own endpoints.
PlaylistPrimitive— required: nonePlaylistPrimitiveLocked— required: noneA locked primitive is fixed brand content of a template (logo, background, layout, theme): only the project owner or a member who can approve adds, removes, moves or edits it, or clears its lock, and a playlist made from the template keeps it locked. Omitted means unlocked. A locked primitive cannot read a field.
A write by anyone else is primitive_locked (403), or a review under Propose.
PlaylistPrimitiveWrite— required: nonePlaylistRect— required: x, y, width, heightPlaylistRegionFormat— required: region, orientationA strip drawn over the page along one edge of a display: a header at the top, a footer at the bottom, a side at the right edge at full height. orientation is the orientation of the display it is made for, as the viewer sees it after rotation.
Every page has the same canvas (width and height); a header or footer is wider than tall and a side taller than wide, with the long side at least twice the short side, else 400 invalid_request at pages[i].canvas.
The playlist holds image and card primitives only (a video, stream, iframe, application or ad slot page is refused) and has no soundtrack (audio is refused); a primitive names no displays.
A region playlist is never a screen default, a folder default or a takeover: it plays as a schedule entry or a campaign with covers set to its region (covers main and the full page refuse it), and covers header, footer or side needs a region playlist of that region.
The entry or campaign is refused with 409 playlist_format_mismatch when the strip is laid out on none of the displays it targets: a strip is laid out on a display of its orientation, judged on the surface the Player reports after rotation (the screen's wall rotation when it has not reported one), and only while it would cover no more than half of the display (its height for a header or footer, its width for a side).
A campaign is refused only when it is laid out on none of the screens of its audience; on a screen of a mixed audience where it is not, the campaign leaves that screen's stack and its explanation says region_not_laid_out.
A format change that moves a playlist into or out of a region, or to another region, is refused with 409 resource_conflict while a screen schedule, takeover or default, a folder default or a campaign uses it.
PlaylistResolvedMedia— required: media_id, intrinsic_sizePlaylistRevision— required: revision, created_at, change, name, page_count, currentOne stored revision of a playlist. change says what made it: created (the first revision), edited (a replace, patch, structure change or applied review), promoted (a copy of an earlier revision, named by restored_from), or baseline (the revision the playlist held when its history began, before any write recorded one).
A value this reader does not know reads as edited.
PlaylistRevisionAuthor— required: type, nameWho made the revision, as the project's event log names them; absent when the write named no one (a baseline, or system work).
PlaylistRevisionDetail— required: revision, playlistPlaylistRevisionList— required: itemsPlaylistStarterWrite— required: starterAdds the built-in Urgent notice template (a headline and a message). Send only starter, and optionally name.
PlaylistStinger— required: noneA transition-only stinger card (major 1). All members are optional; defaults stay in the synthetic runtime placement. hang_ms defaults to 1000 and is at most 60% of the transition duration_ms (checked by the server). logo names a ready image in this project, using the card media grant path.
PlaylistStreamPrimitive— required: id, primitive, sources, fallback_media_id, rect, layer, content_fit, muted, controller, resolved_mediaPlaylistStreamPrimitiveWrite— required: id, primitive, sources, fallback_media_id, layer, content_fitPlaylistStructurePatch— required: noneA playlist's structure and nothing a page shows (patchPlaylistStructure). Omitted members are kept. A supplied name replaces the name. A supplied pages is the complete new order of the pages the playlist holds: a stored page it omits is removed.
Page content (canvas, transition, audio_cue, primitives, comments) and the soundtrack cannot be sent and are always the stored ones.
PlaylistTemplateField— required: name, label, typeOne value a template takes. Pages read it as {field.NAME}: in a card's params (text and url fields), as an image or video selector media_id (media field) or as the whole src of an iframe (url field).
PlaylistTemplateFields— required: noneDeclaring fields makes the playlist a template. Every token a page reads must name a declared field of the right type and every field must be read; a playlist without fields reads no tokens. A template is never shown on a screen (playlist_is_template). PUT without fields keeps them; null or an empty array removes them.
PlaylistTransition— required: type, duration_msPage transition. type and duration_ms are both required; OpenAPI does not default them. Swipe authoring examples use duration_ms 600; that value is not a schema default.
PlaylistV2— required: id, name, revision, pagesPlaylistV2List— required: itemsPlaylistValuesChange— required: valuesPlaylistVideoPrimitive— required: id, primitive, selector, resolved_media, muted, loop, rect, layer, content_fit, controllerPlaylistVideoPrimitiveWrite— required: id, primitive, selector, layer, content_fitPlaylistWallFormat— required: displays, arrangement, orientationThe video wall a playlist is authored for: 2 to 12 displays, tiles of 1920 x 1080 (landscape) or 1080 x 1920 (portrait), laid out in a row, a column or a grid. A row has the displays as columns and one row, a column one column and the displays as rows, a grid columns and displays / columns rows.
Display n sits at column (n - 1) mod columns and row floor((n - 1) / columns): left to right, then top to bottom.
The canvas of c columns and r rows of tiles of width w and height h is (c * w, r * h), for example 5760 x 1080 for 3 landscape displays in a row, 1080 x 3840 for 2 portrait displays in a column and 7680 x 3240 for 12 landscape displays in a grid of 4 columns.
Every ordinary page's canvas width and height must equal it exactly, else 400 invalid_request at pages[i].canvas. Card scale on such a playlist is the tile's min(width, height) / 1080 = 1.
A playlist with a format may be bound only to a screen whose wall has the same shape (displays = number of outputs, arrangement, columns, portrait if and only if rotation is 90 or 270), else 409 playlist_format_mismatch: assignment, fleet assignment, schedule entries and takeover. A playlist without a format binds to any screen.
PlaylistWarning— required: code, message, page_id, primitive_idSomething the author of a successful write should act on. The write stored what it carried; a warning never fails it. A playlist write answers with warnings on the playlist; a screen write (updateScreen, the dashboard's fields, location and playlist routes) on the screen; a fleet assign on the result page.
Two codes. iframe_framing_refused: a public https page named by an iframe primitive (its src or its fallback_src) sends X-Frame-Options (DENY, SAMEORIGIN or the obsolete ALLOW-FROM) or a Content-Security-Policy frame-ancestors list that does not admit the Player web app's origin.
The Player web app and the macOS, Samsung Tizen, LG webOS and BrightSign Players frame the page, so they cannot show it; Android, Linux and Windows Players open an iframe page as a whole page and are not affected.
The backend reads the page's response headers once the write commits, for at most 8 distinct pages per write; a page it cannot reach in time, or that answers an error, is not reported. Each distinct page appears once, naming the first primitive that uses it, and appends one playlist.iframe_framing_refused event (warning) to the project's event log.
A src with screen tokens is not one page and is not read. screen_fact_missing: a screen the write reaches lacks a value that an iframe's src or an application's params read through a screen token ({screen.field.NAME}, {screen.location.…}, {screen.timezone}), or the values it has fill into one the manifest refuses (an address over 2048 characters, a path segment of . or .., launch params over 4096 characters in the launch URL), so the primitive is left out of that screen's manifest (manifest diagnostic screen_fact_missing or screen_fact_invalid) until the screen has the value.
One warning per write names the first such primitive, the tokens no listed screen could fill (at most 64), the first 20 affected screens and how many there are.
A playlist write (a replace, patch, structure change, promote or applied review) reaches the screens that show the playlist; a screen write reaches that screen, for the playlist it shows and the one it was just assigned; a fleet assign reaches the screens of its page; approving a screen change reaches the screens it names.
PlaylistWrite— required: name, pagesPlaylistWriteV2— required: name, pagesPrimitiveEnter— required: typeOptional object enter. Playlist write and the runtime manifest use this object as-is; there is no snake_case rename inside it. Absent means paint at rest, full opacity. Optional integer stagger 0 through 8 delays start by stagger*120 ms after the shared 500 ms delay. Absent or 0 is today's start. Duration is a named constant in screenrig.canvas/v1.
PrimitiveMotion— required: noneOptional persistent object motion. Playlist write and the runtime manifest use this object as-is; there is no snake_case rename inside it. Absent means the primitive stays at rest after enter.
Copied through uninterpolated. spin and drift apply to image and video only; application and iframe with spin or drift are rejected by the server (motion_not_allowed_for_primitive). path applies to image, video, application, and iframe. This restriction is not encoded as OpenAPI if/then.
PrimitiveMotionDrift— required: type, zoom, direction, speedContinuous slow scale about the clipRect centre combined with a slow translate. Image and video only; the server rejects drift on application and iframe. Ping-pongs; never jumps.
PrimitiveMotionPath— required: type, points, rateTranslation along a polyline of canvas-unit waypoints after the authored rect. loop defaults to loop when omitted. Valid on image, video, application, and iframe.
PrimitiveMotionPoint— required: x, yPrimitiveMotionSpin— required: type, direction, speedContinuous rotation about the clipRect centre. Image and video only; the server rejects spin on application and iframe.
Problem— required: errors, instance, request_idThe problem document every route answers except the Player-facing runtime, content, and probe routes (RuntimeProblem). The error itself is errors[0]: its status, code, type, title, detail, and hint. Only document members stay at the top level. Branch on errors[0].code, never on detail or hint, which are prose.
A blamed request field adds errors[0].field; each further blamed field is its own entry with that field's detail.
ProblemError— required: status, code, type, title, detail, hintOne error of a problem document. Members beyond these describe this error further (for example dependencies on not_ready).
ProblemField— required: field, code, detailProject— required: id, revision, status, email, used_bytes, reserved_bytes, screen_count, content_limit_bytes, screen_limit, credit_remaining, created_at, updated_at, nameProjectBillingUsage— required: period, usage, stored_bytes, allowanceProjectCapabilities— required: project_id, plan_id, features, feature_revision, capabilities, media_video_bytes, plan_features, limitsProjectContext— required: project, organizationProjectContextList— required: projectsProjectCreate— required: nameProjectDeleteConfirmation— required: name, revisionProjectDeletionPreview— required: project, organization, devices, allowedProjectFeatures— required: advertiser, screensProjectMove— required: organization_idProjectMoveDestinations— required: organizationsProjectName— required: noneTrimmed printable project name. Rejects controls, URL-looking text, @, www., and ://.
ProjectOwnerTransfer— required: user_idProjectPatch— required: nameProjectPlanFeature— required: id, enabled, required_planProjectPlanFeatures— required: featuresets, featuresThe plan features the project has. The plan and the project's featuresets decide them; credit never unlocks one. A feature the project does not have is refused with feature_not_in_plan (402), whose required_plan names the lowest generally sold plan that includes it.
ProjectPlanLimits— required: screens, members, video_bytes, event_retention_daysThe project's limits as its plan and featuresets give them, the figures admission enforces. Zero is no plan cap.
ProjectUptime— required: itemsProvisionScreen— required: noneRTSPStreamSource— required: protocol, urlReadyResponse— required: status, degradedReleaseLaunch— required: launch_url, expires_atReport— required: kind, title, generated_at, columns, rows, truncatedOne report as a table: columns says what each row member is. Rows carry more members than the columns show (ids, parent_id and depth for a folder outline). Percent columns hold 0 to 100.
ReportCatalog— required: items, formatsReportColumn— required: key, label, typeReportKindInfo— required: kind, title, description, paramsReportParam— required: name, descriptionReportSchedule— required: cadenceReportSubscription— required: id, name, kind, filters, schedule, format, enabled, recipients, revision, created_at, updated_atReportSubscriptionList— required: itemsReportSubscriptionPatch— required: noneReportSubscriptionRecipient— required: user_idReportSubscriptionWrite— required: name, kind, schedule, format, recipientsReseller— required: id, name, spread_bps, spread_effective_at, customer_count, review_state, customer_limitResellerCustomer— required: organization_id, name, plan_id, delegated_at, project_count, screen_count, stored_bytes, annual_list_cents, annual_wholesale_centsResellerCustomerPage— required: items, next_cursorResellerCustomerProject— required: id, name, status, screen_count, memberResellerCustomerProjectPage— required: projects, next_cursorResellerEnrollment— required: organization_idResellerList— required: itemsResellerStatement— required: reseller_id, name, period, spread_bps, lines, usage_list_mcr, usage_wholesale_mcr, plan_annual_list_cents, plan_annual_wholesale_centsResellerStatementLine— required: organization_id, name, plan_id, delegated_from, project_count, screen_count, stored_bytes, api_events, usage_list_mcr, usage_wholesale_mcr, plan_annual_list_cents, plan_annual_wholesale_centsResellerSummary— required: id, nameReview— required: id, kind, state, subject, proposer, url, created_at, updated_at, impactReviewAdCreative— required: creative_id, creative_revision, content_pathReviewApproveRequest— required: noneReviewBlocker— required: id, state, subjectReviewChangesRequest— required: noteReviewDecider— required: noneReviewDecisionResult— required: reviewReviewDependentResult— required: id, stateReviewImpact— required: screensReviewList— required: items, next_cursor, pending_countReviewLocation— required: project_id, project_nameReviewMediaChange— required: media_id, filename, before, afterThe pending change of one media item a selector_refresh review asks about.
ReviewMediaFields— required: tags, folder_idReviewMediaFile— required: sha256, bytes, content_typeReviewMediaPlaylist— required: id, name, screens, media_added, media_removedReviewMediaReplace— required: media_id, filename, source, version, before, after, aspect_changed, playlistsThe replacement of one media item's file a media_replace review asks about. The media keeps its file until approval.
ReviewPageEdit— required: op, page_idReviewPending— required: status, reviewA review-class write under Propose access, or a write that reaches a protected screen at any access level. The change is not on glass until a person approves the review; a protected one is decided by a second approver (docs/screen-scope.md).
ReviewPerson— required: user_id, nameReviewPlaylistChange— required: operation, base_revision, proposed, diffReviewPlaylistDiff— required: name_changed, pages_added, pages_removed, pages_changed, soundtrack_changed, media_added, media_removedReviewPolicy— required: agent_changes_require_review, review_policy_revisionReviewPolicyUpdate— required: agent_changes_require_reviewReviewPreview— required: live, proposedLive and proposed playlists in the stored shape GET /api/playlists/{id} returns (pages with resolved_media).
ReviewProposer— required: kindReviewProtectedCampaign— required: noneA campaign_create, campaign_update or campaign_delete.
ReviewProtectedChange— required: subkindThe stored input of a protected_change review, applied through the owning service when a second approver approves. Exactly one of campaign, folder and move is present, as subkind names. Approval replays at base_revision; a campaign or folder that moved on makes the review stale (review_stale).
ReviewProtectedFolder— required: noneA folder_create, folder_update, folder_delete or folder_unprotect.
ReviewProtectedMove— required: folder_id, screen_ids, folder_idsA screens_move, the body of POST /api/screens/move.
ReviewRejectRequest— required: reasonReviewScreenChange— required: action, screens, request, playlist_idsReviewSubject— required: type, id, nameReviewSummary— required: id, kind, state, url, subject, impactRuntimeAdslotCandidate— required: page_id, occurrence_id, check_sequence, earliest_start_atRuntimeAdslotEvent— required: event_id, decision_id, occurrence_id, kind, client_sequenceRuntimeAdslotEventBatch— required: eventsRuntimeAdslotEventResponse— required: resultsRuntimeAdslotEventResult— required: event_id, stateRuntimeAdslotMedia— required: kind, duration_ms, content_type, bytes, sha256, content_path, fitRuntimeAdslotPage— required: id, type, adslot_idWhole-page advertising slot, sent only to a paired session granted adslot-v1. A Player that meets an unknown page type drops that page.
RuntimeAdslotResolveRequest— required: manifest_revision, candidatesRuntimeAdslotResolveResponse— required: server_time, resultsRuntimeAdslotResult— required: occurrence_id, check_sequence, stateRuntimeAdvance— required: modeRuntimeApplicationPackage— required: version, url, bytes, sha256, entrypoint, contentTypeImmutable normalized package built only from the independently validated application tree. Native players verify bytes and SHA-256 before atomically retaining this release for offline last-known-good restoration. The URL is an authorization-bound transport reference, not a public or durable object URL.
RuntimeApplicationPrimitive— required: id, primitive, applicationId, releaseId, launchUrl, origin, protocol, grantId, package, rect, layer, contentFitRuntimeAudio— required: loop, volume, tracksPlaylist soundtrack (docs/playlist-audio.md). Emitted only when the playlist has one and at least one track projected. The Player plays tracks in order, independently of the page sequencer; page changes, transitions and visibility never pause or restart it.
Track identity is id plus mediaId plus sha256, never src, so a manifest refresh that keeps the playing track does not restart it. Effective gain is volume times the Player volume. A defective track is dropped on its own (invalid_audio_track).
RuntimeAudioCue— required: track, restartSoundtrack hint emitted on a page that authored audio_cue. When the page becomes current: if a different track is playing, or none, switch to track from its start; if track is playing, restart it only when restart is true. The sequence continues from track. A hint may be ignored. A cue that names no manifest track is dropped (invalid_audio_cue).
RuntimeAudioTrack— required: id, mediaId, src, sha256, bytes, contentType, durationMs, grantIdRuntimeCanvas— required: width, height, viewportFit, backgroundRuntimeCardPrimitive— required: id, primitive, card, major, params, rect, layer, contentFitA built-in card. For a Player declaring CardML capabilities, the backend selects the newest compatible published revision within the pinned major, considering retained revisions and all required features; when none qualifies it omits the card with card_unavailable diagnostics and removes empty pages.
The Player merges the card's defaults, then params, then, when data is present, the dynamic properties of the document at GET /screen/card-data/{data} (only keys the card marks dynamic).
A card with data stays hidden until its first document arrives and then fades in; screen.card_data_changed for the key refetches it in place. feed, when present, is fetched from the same route and passed to the card as its feed. A Player that lacks the card or its major draws nothing for it.
A string in params or in the data document that begins with media: is a media reference, media:<id>, that the Player resolves to bytes it holds before the card draws it, and blanks when it cannot; it never passes a URL from params or data to a card. media lists the ready images params name, with the descriptors an image primitive's resolvedMedia carries, so the Player downloads and verifies them when the manifest arrives; a reference to media gone since publish is absent and the card draws its fallback.
Media a data document names come from /media/{media_id} when the document arrives (ETag is the sha256). Card media never hold a page. The backend has already expanded a placement's {screen.name}, {screen.timezone}, {screen.field.NAME} and {screen.location} tokens for this screen, so params and data carry its own values.
RuntimeChrome— required: schema_version, bannerRuntime-owned chrome (docs/player-compatibility.md §8). Readers read the members they know and ignore the rest; a banner shape a reader does not understand shows no banner.
RuntimeCodecs— required: noneServer-derived RFC 6381 codec list for this exact rendition, never client-declared. It is a sibling of contentType; a Player can form a capability query as contentType followed by codecs="VALUE". A video whose codec cannot be derived is rejected at upload. The property is optional. An absent value does not mean that the codec is unsupported.
A present value currently contains one video-track entry; the grammar permits comma-separated entries.
RuntimeCommandResultWrite— required: stateThe POST /screen/commands/{command_id}/result body. Unknown members are ignored (docs/player-compatibility.md §1.1).
RuntimeConditionReport— required: severity, code, contextRuntimeDisplay— required: noneThe screen's display control, evaluated on the device (docs/player-compatibility.md §6.5): the power schedule and override, the idle-dark setting and the brightness mode. Omitted when the screen has none; then the display stays on at the operator's brightness. Unknown members are ignored.
RuntimeHealthWrite— required: noneThe PUT /screen/health body. Every member is optional; send what the platform can measure. Unknown members are ignored and a value out of range is dropped (not refused). Integers are whole JSON numbers.
RuntimeHostHint— required: platformThe optional native host hint on pairing start, pairing completion, and identity session mint. It names a device and is never authorization.
It is read tolerantly (docs/player-compatibility.md §1.1.1): unknown members are ignored, a malformed field or capabilities entry is dropped, at most 32 capabilities are kept, and a value that is not an object or a platform outside the pattern drops the whole hint. None of these refuses the request.
A Player still sends only the registered platform values and the members below, because a pre-contract backend refuses anything else.
RuntimeHostHintDevice— required: noneOptional hardware identifiers in the native host hint. A malformed field is dropped.
RuntimeIframeCookie— required: name, value, domain, path, secure, httpOnly, sameSiteOne cookie of a shared browser tab, in the shape of the browser cookies API. The value is a secret.
RuntimeIframePrimitive— required: id, primitive, src, title, rect, layer, contentFitRuntimeImagePrimitive— required: id, primitive, selector, resolvedMedia, grantId, rect, layer, contentFitRuntimeImageResolvedMedia— required: mediaId, src, intrinsicSize, sha256, bytes, contentTypeRuntimeIntrinsicSize— required: width, heightRuntimeKVEntry— required: application_id, key, value_base64, content_type, bytes, sha256, revisionRuntimeKVList— required: itemsRuntimeKVSummary— required: application_id, key, content_type, bytes, sha256, revisionRuntimeKVWrite— required: value_base64RuntimeKiosk— required: lockThe kiosk lock (docs/player-compatibility.md §6.5). Emitted on every session while the screen's kiosk.lock is on and omitted otherwise. A Player that can pin itself to the screen does while lock is true and releases the pin when the member is absent or lock is false; one that cannot ignores it. Unknown members are ignored.
RuntimeLiveHeartbeat— required: connectionThe Player's own report. Open schema.
RuntimeLiveHeartbeatAnswer— required: lease_until, max_untilRuntimeLiveJoin— required: live_id, ice_servers, lease_until, max_until, heartbeat_ms, videoWhat a Player publishes to and within what limits. Open schema.
RuntimeLiveJoinRequest— required: noneEmpty. The body may be omitted.
RuntimeLiveOffer— required: sdpA session description without its type, as the Player's offer and the relay's answer.
RuntimeLiveStopped— required: reasonRuntimeManifest— required: schemaVersion, manifestRevision, grantId, contentGeneration, screenId, screenLabel, playlistRevision, generatedAt, pagesImmutable runtime manifest using the deterministic screenrig.canvas/v1 canvas, rectangle, fit, and source-order contract. Read-side additional properties are permitted so a player on this contract ignores unknown keys (compatibility rule 2). Write schemas stay closed.
Pages are ordinary canvas pages and, for a paired session granted adslot-v1, whole-page adslot pages; a manifest with no adslot page is house content.
RuntimeManifestDiagnostic— required: level, code, detailOne fail-safe runtime projection omission. Players and the CLI may show these; they are never authorization.
RuntimeMediaSelector— required: noneRuntimeMediaSelectorByAll— required: by, oneAtATimeRuntimeMediaSelectorByID— required: by, mediaId, oneAtATimeRuntimeMediaSelectorByIDs— required: by, mediaIds, oneAtATimeRuntimeMediaSelectorByTag— required: by, oneAtATimeRuntimeObservationSurfaceWrite— required: id, width, height, pixel_ratio, presentationRuntimeObservationWrite— required: observed_at, surfacesThe PUT /screen/observation body. Unknown members are ignored (docs/player-compatibility.md §1.1). The stored value is the project ScreenObservation.
RuntimeOrigins— required: contentThe delivered origin allowlist (docs/player-compatibility.md §2.3). Present on a mint response only for a Player granted origins-v1.
The Player unions content with its compiled content origins and ignores an invalid entry. release_suffix replaces the compiled release suffix only when it is a valid DNS suffix of at least two labels; it is absent when the runtime origin serves releases itself (local development).
The delivered set never changes the credential origin, and the backend never puts a URL on an origin this session's mint did not deliver. A Player stores the object it validated last-known-good under, so a later session without origins keeps that LKG valid.
RuntimePage— required: contractVersion, id, canvas, transition, advance, primitivesRuntimePairingScreen— required: id, public_id, label, state, revision, manifest_revision, content_access_generation, created_at, updated_atRuntime-owned screen summary in pairing and browser provisioning completions. Its member set is frozen: project Screen fields never appear here (docs/player-compatibility.md §8). state stays within the three values below.
RuntimePrimitive— required: noneRuntimeProblem— required: type, title, status, detail, instance, code, request_id, errorsThe problem document of the Player-facing screen (/screen/) and media (/media/) routes, the pairing streams and the probe routes (/.ready, /.health): every member flat at the top level. Store-released Players read its top-level code and action (docs/player-compatibility.md §5), so this shape stays as it is; every other route answers Problem.
RuntimeRect— required: x, y, width, heightRuntimeReport— required: noneReport bodies and contexts are read tolerantly (docs/player-compatibility.md §1.1). A code needs its own context members; other members are ignored. Players still send only the members below, because a pre-contract backend refuses any other.
RuntimeScreenLabel— required: screen_labelThe stored screen name, the value the manifest carries as screen_label.
RuntimeScreenLabelWrite— required: screen_labelThe PUT /screen/screen-label body. The server trims screen_label and stores 1 to 120 characters without control characters. Unknown members are ignored (docs/player-compatibility.md §1.1).
RuntimeScreenPlaylist— required: playlist_idThe screen's assigned playlist after the change.
RuntimeScreenPlaylistWrite— required: playlist_idThe PUT /screen/playlist body. Unknown members are ignored (docs/player-compatibility.md §1.1).
RuntimeScreenPlaylists— required: playlists, moreThe playlists a screen's on-device picker offers, alphabetical by name without regard to case, then by id.
RuntimeSelectorPlaylistSnapshot— required: selections, pageMaxMsOne stack playlist's selections, named by that playlist's own page ids.
RuntimeSelectorSnapshot— required: manifestRevision, snapshotRevision, selections, pageMaxMsRuntimeSession— required: paired, capabilities, event_cursor, public_urlRuntime-owned session (docs/player-compatibility.md §8). Readers ignore unknown members and unknown capabilities values. A new member is sent only to sessions whose grants cover it.
RuntimeSessionRequest— required: public_idAnonymous viewer mint. Unknown members are ignored (docs/player-compatibility.md §1.1).
RuntimeStack— required: levels, playlistsEvery level of the screen's stack, sent only to a Player that declared the schedule.stack host capability and only when the stack has more than one level (docs/player-compatibility.md §6.11). The manifest's root members are the bottom level's playlist; the Player resolves the levels itself with the schedule contract.
RuntimeStackPlaylist— required: playlistRevision, pagesRuntimeStatusWrite— required: observed_at, chipThe PUT /screen/status body. Unknown members are ignored (docs/player-compatibility.md §1.1). The stored value is the project ScreenStatus.
RuntimeStorageWrite— required: observed_at, volume, cache, durability, plan, transfer_24hThe PUT /screen/storage body. Unknown members are ignored (docs/player-compatibility.md §1.1). The stored value is the project ScreenStorage plus server received_at.
RuntimeStreamPrimitive— required: id, primitive, sources, muted, fallback, grantId, rect, layer, contentFitRuntimeSuspension— required: message, sinceEmitted while the screen is suspended (docs/player-compatibility.md §6.9): pages is [] with no diagnostics, and a Player shows message as a full-screen notice instead of the no-content surface. since is when the screen was first suspended. The manifest revision is the notice's own, never a playlist's, and the screen needs no assigned playlist.
RuntimeTelemetryBatch— required: telemetry_epoch, stream_id, sequence, created_at, reportsA durable FIFO Player queue sends at most one ordinary batch per minute with a randomized phase, or earlier when full. Total JSON body is at most 256 KiB; at most 64 reports, each at most 32 KiB. Every report retains its occurrence timestamp and stable play_id.
Keep one immutable in-flight batch on disk until 202, increment sequence only after acknowledgement, and retry the same bytes after lost replies or restarts. Sequence starts at 1 and has no gaps.
Dedupe retains at most eight live streams per screen, each watermark for at least the 24 hours in which its batch can still be replayed; keep the same stream through credential renewal. Records expire locally after 24 hours with explicit accounting. manifest.activated and manifest.upgrade stay individual and immediate.
Interactive writes, ad resolution, content fetches, screenshots, and SDK emitConfirmed() also stay immediate. Health sampling retains its fifteen-minute cadence; urgent display/health transitions are sent immediately. Observation and ordinary health snapshots are latest-wins with original occurrence times. Health requires health.write.
Existing individual reports remain accepted. Report item abuse limits apply per new item rather than per HTTP envelope; committed replay consumes only request admission.
RuntimeTransition— required: type, durationMsAuthored page transition. Plain moves retain type, duration and optional decorations. Stinger carries the normal card content shape above both layers. Missing code or an incompatible Player interpreter degrades only the transition to crossfade of the same duration, with transition_unavailable diagnostics.
RuntimeTransitionCard— required: primitive, card, major, params, version, src, sha256, model, requiresThe same projected card content as a canvas card, without canvas placement geometry. Stinger is pinned to major 1; params includes durationMs and hangMs.
RuntimeUpgradeNotice— required: state, player_kind, recommendedPresent on a mint response when the player's comparable version is below the recommended version for player_kind (docs/player-compatibility.md §6.1). The Player starts a platform update check and shows nothing on glass. A reader ignores the member for a state it does not know.
RuntimeVerbosity— required: noneThe lowest log level and toast level the Player generates and shows (docs/player-compatibility.md §6.12). Emitted on every session when the screen sets either; each member is omitted when unset. Errors are always generated and shown. A Player ignores an unknown member or value and keeps its default.
RuntimeVideoPrimitive— required: id, primitive, selector, resolvedMedia, muted, loop, grantId, rect, layer, contentFitRuntimeVideoResolvedMedia— required: mediaId, src, intrinsicSize, sha256, bytes, contentTypeRuntimeWall— required: outputs, arrangement, rotation, spanThe screen's video wall as its Player lays it out (docs/player-compatibility.md §6.8). Emitted only when the screen has a wall. Unknown members are ignored.
SCIMError— required: schemas, statusThe RFC 7644 section 3.12 error document.
SCIMEvent— required: at, action, statusSCIMEventList— required: eventsSCIMGroupList— required: groupsSCIMGroupProjectBinding— required: binding_id, project_id, project_name, roleSCIMGroupSummary— required: id, display_name, member_count, bindingsSCIMResource— required: noneA SCIM 2.0 document in the shape RFC 7643 and RFC 7644 give it at the route that carries it; each operation names which it is. It is a User or Group resource, a ListResponse, a PatchOp message, a ServiceProviderConfig, a ResourceType or a Schema. Members screenRIG does not use are accepted and ignored.
SCIMSettings— required: enabled, base_url, tokens, user_count, group_countSCIMSettingsWrite— required: enabled, proofSCIMToken— required: id, name, prefix, created_atSCIMTokenCreate— required: name, proofSCIMTokenCreated— required: id, name, prefix, created_at, tokenA SCIMToken with its secret, returned once.
SSODiscovery— required: sso, required, accountHow the address signs in or signs up. sso, required and provider_name are decided by the address's domain alone; account says whether a live person holds the address.
SSODiscoveryRequest— required: emailSSOErrorCode— required: noneThe stable code a single sign-in ends with when it does not complete: the value of #sso_error= in the address POST /api/sso/finish redirects to, and the code of a failed identity-provider test. no_email: the provider asserted no email address. email_unverified: it marked the address unverified. domain_not_allowed: the address is not at one of the organization's verified domains. account_exists: the address already belongs to an account with its own sign-in; the person signs in as before and links the identity from their account (intent link). managed_elsewhere: another organization manages the person. subject_mismatch: the account is linked to a different identity at the same provider. not_provisioned: nobody holds the address and the connection does not create people. sso_required: the organization requires single sign-on for the address. staff_account: a screenRIG staff account is never signed in, adopted or linked by a customer's identity provider. expired: the sign-in outlived its 10 minutes. bad_state: the sign-in's state or browser binding does not match, or it was used already. interaction_required: a silent renewal needs the person. provider_refused: the provider answered with an error. provider_unavailable: the provider could not be reached or read. verification_failed: the provider's token or assertion failed verification. deactivated: the person is deactivated.
SSOPolicy— required: required, break_glass_user_ids, passkey_admin_ids, policy_revisionSSOPolicyWrite— required: required, break_glass_user_ids, proofSSOStart— required: redirect_urlSSOStartRequest— required: emailSSOTestStart— required: redirect_urlScheduleCalendar— required: subject, from, to, screens, groupsScheduleCalendarConflict— required: kind, level_idsScheduleCalendarGroup— required: key, screens, screen_ids, displays, levels, spans, on_screen, conflictsScheduleCalendarOnScreen— required: display, from, toScheduleCalendarSpan— required: level_id, from, to, stateScheduleImportCommitted— required: preview, appliedScheduleImportLevel— required: key, action, home, content, screens, priority, rowsScheduleImportPreview— required: name, columns, valid, fingerprint, summary, rows, levels, removalsScheduleImportRemoval— required: homeScheduleImportRequest— required: rowsScheduleImportResolved— required: kind, id, nameScheduleImportRow— required: row, statusScheduleImportRowError— required: code, messageScheduleImportSummary— required: rows, rows_with_errors, levels, creates, updates, unchanged, removes, screens, playlistsSchedulePreview— required: gained, lost, calendarWhat a campaign or schedule write would change. gained are the screens that would carry a level of the write they do not carry now, lost those that carry one they would no longer; a level that stays on a stack changes neither and shows in calendar.
SchedulePreviewReach— required: count, screen_idsScreen— required: id, public_id, label, revision, manifest_revision, manifest_upgrade, content_access_generation, state, online, created_at, updated_atScreenAction— required: noneOne fleet action, discriminated by type. Each member carries only its own parameters; a member of another type is an unknown field and invalid_request.
ScreenActionAddTags— required: type, tagsAdds tags a screen does not already carry. A screen that would exceed 16 tags fails with invalid_request.
ScreenActionAssign— required: type, playlist_idScreenActionClearDisplaySchedule— required: typeRemoves each screen's display schedule; a screen without one is ok and unchanged.
ScreenActionClearPlaylist— required: typeClear each screen's own default playlist, retaining pairing and other content levels. With no effective content the Player shows its idle display. Review-class, like assign.
ScreenActionClearPlaylistSchedule— required: typeRemoves each screen's playlist schedule; a screen without one is ok and unchanged.
ScreenActionClearRebootSchedule— required: typeRemoves each screen's reboot schedule; a screen without one is ok and unchanged. Manage access only.
ScreenActionDisplay— required: type, powerSame rules as ScreenDisplayWrite. until is checked against the request clock when the Idempotency-Key has no fleet record, and again by each screen.
ScreenActionDisplayClear— required: typeEnds each screen's manual display override (DELETE /api/screens/{id}/display); a screen without one is ok and unchanged.
ScreenActionMaintenance— required: typePOST /api/screens/{id}/clear-cache, /restart-app or /update per screen; a screen whose host did not declare the capability fails alone with command_unsupported. An offline screen holds the command queued until it comes online (expires_in_s).
ScreenActionReboot— required: typePOST /api/screens/{id}/reboot per screen; a screen whose host did not declare reboot fails alone with reboot_unsupported. An offline screen holds the reboot queued until it comes online (expires_in_s).
ScreenActionReload— required: typePOST /api/screens/{id}/reload per screen. An offline screen holds the reload queued until it comes online (expires_in_s).
ScreenActionRemoveTags— required: type, tagsRemoves the listed tags; a tag the screen does not carry is ignored.
ScreenActionRequest— required: selector, actionScreenActionResult— required: action, matched, succeeded, failed, resultsScreenActionScreenResult— required: screen_id, statusOne screen's outcome. ok carries the result of the single-screen path: revision (and tags for tag actions) for assign, tag, takeover, takeover_clear, set_playlist_schedule, clear_playlist_schedule, set_reboot_schedule and clear_reboot_schedule actions, reload for reload, toast for toast. failed carries the problem that screen's own request would have answered.
ScreenActionSelector— required: noneScreenActionSelectorFolder— required: by, folder_idEvery active screen in the screen folder and every folder inside it, in creation order. There is no screen-count cap.
ScreenActionSelectorIds— required: by, screen_idsScreenActionSelectorTag— required: by, tagEvery active screen whose tags contain tag, in creation order. There is no screen-count cap.
ScreenActionSetDisplaySchedule— required: type, enabled, windowsReplaces each screen's display schedule (ScreenDisplayScheduleWrite), normalized once before fan-out. A screen without a timezone fails alone with invalid_request.
ScreenActionSetPlaylistSchedule— required: type, entriesReplaces each screen's playlist schedule with the same entries (ScreenPlaylistScheduleWrite). Entry ids are assigned once before fan-out. A screen without a timezone or without a default playlist fails alone with invalid_request.
ScreenActionSetRebootSchedule— required: type, enabled, windowsReplaces each screen's reboot schedule (ScreenRebootScheduleWrite), normalized once before fan-out. Manage access only. A screen without a timezone fails alone with invalid_request, and one whose Player did not declare reboot with reboot_unsupported.
ScreenActionSetTags— required: type, tagsReplaces each screen's tag set; an empty array clears it.
ScreenActionTakeover— required: type, playlist_idSame rules as ScreenTakeoverWrite. until is checked against the request clock before fan-out when the Idempotency-Key has no fleet record, and again by each screen.
ScreenActionTakeoverClear— required: typeEnds each screen's takeover; a screen without one is ok and unchanged.
ScreenActionToast— required: type, level, textSame level, text, and duration rules as ScreenToastWrite, validated once before fan-out.
ScreenCampaigns— required: screen_id, itemsScreenCommand— required: id, type, state, requested_at, expires_atOne reboot, reload, clear_cache, restart_app or update asked of a screen, in Screen.commands. A command sent to an online screen is stored sent.
One asked of an offline screen is stored queued with no runtime event; the backend appends the event, with this id and expires_at, when it applies the screen's next online transition before expires_at, and the command becomes sent.
Each command has exactly one runtime event, so a Player cannot see it twice. expired is derived on read for a queued command past expires_at. A newer command of the same type supersedes one still queued. A sent reboot becomes done when a health report with uptime_s puts the boot after sent_at; a reload never reaches done.
A clear_cache, restart_app or update becomes done or failed when its Player reports, with cause and detail.
ScreenCommandPage— required: items, next_cursorScreenCommandWrite— required: noneThe optional body of POST /api/screens/{id}/reboot, /reload, /clear-cache, /restart-app and /update. An empty body keeps the default.
ScreenDeviceSettingsPatch— required: noneA change of a screen's idle-dark and brightness settings and kiosk lock (patchScreenDeviceSettings). The body names at least one of the two members and no other.
ScreenDisplay— required: requested, sourceDisplay power state. requested is what the screen should show now: the active override, else the enabled display schedule evaluated in the screen timezone, else on (source override, schedule, or default); until is when that next changes, when known. reported is the Player's latest health display (power, connected, reported_at, stale after 35 minutes). idle and brightness are the screen's display.idle and display.brightness settings (set by ScreenPatch).
Absent when the screen has no schedule, no override, no idle or brightness setting, and no reported display.
ScreenDisplayBrightness— required: modedisplay.brightness: auto maps the device's ambient light to the window brightness; fixed (the default) keeps the operator's brightness. Applied by a Player that declares the host capability brightness.auto, which it declares only when the device has a light sensor.
ScreenDisplayIdle— required: after_s, wakedisplay.idle: the glass goes dark after after_s seconds without a touch, a key or, when wake lists presence, somebody in front of the screen (the device's camera, analysed on the device; no image leaves it). A wake event lights it again at once, and the touch that wakes a dark glass is consumed.
While the glass is dark the Player is in a display-off, as for a display schedule: it parks playback (no plays, no ads), reports display.power off in its health and resumes on wake. A display schedule or override that holds the panel off wins, and an application holding device.display wins over idle while its page is on glass.
Applied by a Player that declares the host capability display_power.idle.
ScreenDisplayOverride— required: override_id, power, until, ends_at, set_atScreenDisplayPatch— required: noneScreenDisplayReported— required: reported_at, staleThe Player's latest health display report. Observed power and connectivity, independent of requested power. stale is true after 35 minutes without a report; this is context, not proof that power caused a capture failure.
ScreenDisplaySchedule— required: enabled, windows, updated_atScreenDisplayScheduleView— required: display_scheduleScreenDisplayScheduleWrite— required: enabled, windowsScreenDisplayWrite— required: powerScreenEffectivePlaylist— required: id, sourceThe playlist the screen's runtime manifest is built from: the takeover, else the first campaign that applies (entry_id is the campaign id), else the first matching playlist schedule entry (entry_id), else the assigned default, else the default playlist of the nearest folder holding the screen (source folder, entry_id the folder id). until is the next instant this is known to change (takeover end or the next schedule boundary within eight days), absent when nothing time-based changes it.
The boundary worker applies a change at most 60 seconds late. A takeover or entry whose playlist cannot be shown is skipped. Absent when the screen shows no playlist.
ScreenExplainAt— required: instant, effective_playlist, verdicts, not_judgedWhat the screen shows at instant and why, from the resolver the boundary sweep runs. The screen's takeover, playlist schedule and the project's campaigns are read as they are now, not as they were then. A campaign whose playlist is missing or unapproved is skipped, as the sweep skips it.
On a screen whose Player resolves its own stack (schedule.stack) the verdicts read that stack, and not_judged says so (player_stack).
ScreenFieldsPatch— required: fieldsA change of a screen's fields (patchScreenFields). The body has no other member.
ScreenFolder— required: id, name, parent_id, revision, created_at, updated_atOne screen folder. A screen sits in at most one folder (Screen.folder_id); a folder targets its screens and those of every folder inside it.
ScreenFolderCreate— required: nameScreenFolderDeleteResult— required: levelsScreenFolderList— required: itemsScreenFolderPatch— required: noneScreenGlass— required: noneWhat the backend concluded about the screen's picture from the reports it received, never from what the Player says about itself. A check that has nothing to say leaves its member absent. Read-only on the project API. Never authorization or admission. ScreenPatch cannot write it.
ScreenGlassChangedDetails— required: check, verdict, previous, since, evidencedetails object on a durable project-only screen.glass_changed event, appended when a glass check changes its verdict for a screen. It is not a runtime audience command and is not delivered on GET /screen/events.
The loop_stalled condition opens on a playback verdict stalled and ends on any other; glass_blank and glass_frozen open on a picture verdict blank or frozen and end on any other.
ScreenHealth— required: reported_at, staleThe latest device health report (PUT /screen/health), sanitized, with server reported_at. A member the Player did not send is absent. stale is true when reported_at is more than 35 minutes old; stale health is still shown, never deleted; archive and screen recovery clear it.
Transitions append the project-only screen.health_changed with details.changes, a list of {change, ...}: display_disconnected, display_reconnected, display_power (from, to), temperature_high (at or above 80 °C), temperature_normal (below 75 °C again), on_battery (battery.source became none; level_percent when reported) and plugged_in (any other source again; source), crash_spike and renderer_restart_spike (the 24-hour count rose by 3 or more since the last spike event; crashes_24h or renderer_restarts_24h and previous). battery is present only when the device reports one; the backend reads nothing from it but battery.source for the on_battery condition and infers no cause.
Read-only; never authorization.
ScreenHealthHistory— required: noneA page of reports (resolution raw) or hourly summaries (resolution hour).
ScreenHealthHour— required: hour, reports, temperature_max_c, temperature_avg_c, cpu_load_max, memory_used_ratio_max, wifi_rssi_min_dbm, display_disconnected, crashes_24h_max, renderer_restarts_24h_max, last_reported_atOne screen's reports in one UTC hour. A member is null when no report of the hour carried it.
ScreenHealthHours— required: resolution, hours, next_cursorScreenHealthSample— required: reported_atOne stored device health report: Screen.health without stale, and without cpu.utilization_percent and cpu.app_percent, which only the latest report (Screen.health) carries. A member the Player did not send is absent.
ScreenHealthSamples— required: resolution, samples, next_cursorScreenIdentifyAccepted— required: identify_id, duration_ms, expires_atScreenIdentifyDetails— required: identify_id, label, screen_id, duration_ms, at, expires_atdetails object on a durable player.identify event.
ScreenIdentifyWrite— required: noneScreenKiosk— required: lockkiosk.lock: the Player pins itself to the screen (Android screen pinning with startLockTask; the person at the screen confirms the first time, and status bar, home and recents are blocked while pinned; the managed flavour as device owner keeps its lock task). Present only while the lock is on.
The operator settings panel offers Leave kiosk lock, which unpins until the next manifest or restart applies it again. Applied by a Player that declares the host capability kiosk_lock.
ScreenKioskPatch— required: lockScreenLevelChange— required: source, id, playlist_id, screensScreenLevelChanges— required: gained, lostWhat a write does to the schedules of the screens it changes: the campaigns whose audience they enter (gained) or leave (lost), and the folder default playlists that start or stop being their default. screens counts the affected screens per level.
On a tag write a campaign entry reports audience membership: every live campaign the screens enter or leave, whether or not it plays there, which depends on the screen's default playlist. A move or folder change lists the campaigns whose folder audience moves on screens that play them.
ScreenLink— required: hint, created_atA Player URL that opens one screen in a browser that keeps no storage, such as a vMix or OBS browser source.
ScreenLinkSessionRequest— required: screen_linkThe screen link secret plus the optional host, playback and player hints. Unknown members are ignored, and host, playback and player are read tolerantly (docs/player-compatibility.md §1.1). The secret is a credential: it is never logged or echoed.
ScreenLinkSummary— required: hint, created_atThe screen's link, without its url. Present only while the screen has a link. Read-only; POST and DELETE /api/screens/{id}/link change it.
ScreenList— required: itemsScreenLocation— required: latitude, longitudeWhere a screen is, in decimal degrees, with an optional short place name and a street address. A card placement reads it through the {screen.location} token, and the screen list filters by distance from a point (near).
ScreenLocationPatch— required: locationA change of where a screen is (patchScreenLocation). The body has no other member.
ScreenMaintenanceAccepted— required: command_id, command, expires_at, stateScreenManifestUpgrade— required: desired_revision, active_revision, desired_playlist, active_playlist, state, code, attempt, retry_at, missing_page_count, state_since, reported_atAlways-present computed manifest-upgrade state for this screen, derived at read time from its issued desired revision or queued shared-playlist content goal, its last server-acknowledged active manifest revision (manifest.activated), and the latest accepted manifest.upgrade report slot.
Read-only project state: no body can write it, and no report ever promotes a grant. active_revision is the last acknowledged activation, not proof that every simultaneous session already shows it.
Being offline never produces failure, and an overdue retry_at on a retrying state is client presentation, never a server judgment. desired_revision null is none. state pending names a desired revision with no matching report; activating names a target already on glass whose acknowledgement is still pending; a report slot whose revision stopped matching desired_revision is ignored until a new report matches, so a new desired manifest derives pending without clearing anything.
Before the next manifest contact issues a shared edit's desired grant, the desired revision and playlist can expose that queued goal as read-only intention; it grants no access. Active identity always comes from the exact acknowledged historical grant. An older activation cannot announce the newer queued goal as current.
ScreenMove— required: folder_idAt least one id across screen_ids and folder_ids, at most 1000 in all.
ScreenMoveResult— required: screens, folders, levelsScreenNamePatch— required: nameA screen's new name (patchScreenName). The body has no other member.
ScreenNotJudged— required: code, detailSomething the explanation does not judge.
ScreenObservation— required: observed_at, surfacesPlayer-reported playback surface. Absent on Screen until the first accepted PUT /screen/observation. Read-only on the project API; ScreenPatch, PairScreen, pairing bodies, session bodies, canvas, runtime manifest, and POST /screen/reports cannot write it.
ScreenObservationSurface— required: id, width, height, pixel_ratio, presentationScreenOutage— required: offline_sinceThe screen's latest presence outage and, once the first health report after the screen is back decides it, why the screen was gone. A new outage replaces it; a screen that has never been offline since outages were recorded has none.
The cause comes from the Player's own uptime counters in that report, read against the outage and never against the device clock: device_restart (the device started after the outage began), app_crash (the Player started and its 24-hour crash count rose), app_restart (the Player started and the device did not), network (neither started, and nothing points at screenRIG) and platform (neither started, and a player.stream_down report reached screenRIG during the outage, or the presence observer was down or recovering when the screen fell silent). likely_cause is absent when unknown, and it stays unknown rather than guess: a report without uptimes, one that is too close to the start of the outage, a restart that came after the screen was back, and a Player report within a minute of the reconnect decide nothing.
A report decides only within 35 minutes of the screen coming back. device_restart cannot tell power loss from a reboot, and network cannot tell the screen's own network from its upstream.
Deciding it appends the project-only event screen.outage_classified once, with details {offline_since, back_at, likely_cause, evidence, online_event_id}; the resolved notification of an offline screen names the cause when it is decided by the time the notice goes out. Readers treat a likely_cause they do not know as unknown. Read-only; never authorization.
ScreenPatch— required: noneScreenPlayPageAccepted— required: screen_id, playlist_id, page_id, manifest_revision, expires_atScreenPlayPageDetails— required: page_id, playlist_id, manifest_revision, expires_atdetails object on a durable player.play_page event.
ScreenPlayPageWrite— required: page_idScreenPlayerIdentity— required: legacy, seen_atAsserted identity from the latest paired runtime mint. Read-only display metadata, never authorization. Internal grants and mint fields are withheld.
ScreenPlaylistPatch— required: playlist_idAn assignment of a screen's playlist (patchScreenPlaylist). The body has no other member.
ScreenPlaylistSchedule— required: entries, updated_atServer-evaluated playlist schedule. It never reaches the runtime manifest; the manifest is built from the effective playlist it selects. Write it with PUT /api/screens/{id}/playlist-schedule.
ScreenPlaylistScheduleView— required: entriesScreenPlaylistScheduleWrite— required: entriesScreenPresenceHistory— required: items, next_cursorScreenPresenceTransition— required: at, stateScreenProvisioning— required: screen, public_url, provisioning_url, expires_atScreenRebootAccepted— required: reboot_id, expires_at, stateScreenRebootSchedule— required: enabled, windows, updated_atThe windows in which the backend reboots the screen. Present while one is set.
ScreenRebootScheduleView— required: reboot_schedule, next_reboot_atScreenRebootScheduleWrite— required: enabled, windowsScreenRecoveryHost— required: noneWhat the device asking to recover this screen says it is, copied from the pairing start's host object. Descriptive fields only; it never carries duid, serial, or mac. Absent fields were not reported.
ScreenRecoveryPending— required: expires_atPresent while a native pairing session that presented this screen's hardware identity is waiting for the owning project to confirm with POST /api/screens/{id}/recovery/confirm. Carries the deadline, which is the pairing session's own expiry, and the offering device's descriptive host fields (never duid, serial, or mac).
A DUID is readable by any application on the panel, so an offer is a phishing surface: compare host.model and host.firmware with the display in front of you before confirming.
The server refuses an offer while the screen's bound key has shown life within the last ten minutes (session mint, runtime stream, manifest fetch, or presence heartbeat), and accepts at most three offers per screen per rolling hour; a new offer replaces the pending one. Absent once confirmed, lapsed, or claimed as a new screen. Read-only.
ScreenReloadAccepted— required: reload_id, expires_at, stateScreenReplaceWrite— required: withScreenScheduleEntry— required: id, playlist_idScreenScheduleEntryWrite— required: playlist_idOne playlist schedule entry, a level of the screen's own stack (packages/schedule-contract SEMANTICS.md, Levels and the stack). id is optional (entry_N is assigned in order when omitted) and unique in the schedule.
It holds while all its rule members hold: from (inclusive) and until (exclusive), civil minute-precision bounds with no offset; an occurrence of windows, monthly or dates (none of the three recurs every day) whose date is not in except; data, a card-data condition; interaction, while someone is using the screen.
An entry sets at least one rule member. data, interaction, covers and a displays lock that leaves outputs out are honored by a Player that resolves its own stack; the server's resolution for other Players skips such an entry (stack_only).
ScreenSchedulePreviewRequest— required: entriesScreenScheduleRevision— required: revision, created_at, change, current, entriesOne stored revision of a screen's playlist schedule: its whole entries list after a write (no entries: the screen had no schedule). revision is the screen's revision at the write. change is edited, restored (a copy of an earlier revision, named by restored_from) or baseline (the schedule the screen held when its history began).
ScreenScheduleRevisionList— required: itemsScreenScheduleWindow— required: daysOne screenrig.schedule/v1 window in the screen timezone. days are stored mon-to-sun. Omit start and end for the whole day. end <= start crosses midnight and the start day owns it (fri 22:00-02:00 runs into Saturday morning); equal edges run a full 24 hours.
ScreenScope— required: folder_ids, screen_idsWhat a scoped member or agent may act on (docs/screen-scope.md): the screens in the listed screen folders and in every folder inside them, and the listed screens. At least one id in all. An id that names no live folder or screen of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing.
The scope is read on each request, so a change applies to the next one. Tags are never scope.
ScreenScreenshotAccepted— required: capture_id, expires_atScreenScreenshotFailedDetails— required: capture_id, reasondetails object on a durable screen.screenshot_failed event.
ScreenScreenshotReadyDetails— required: capture_id, bytes, width, height, sha256details object on a durable screen.screenshot_ready event. Pixels and object keys are not present.
ScreenScreenshotRequestedDetails— required: capture_id, expires_at, max_width_divisor, max_height_divisor, format, quality, max_bytesdetails object on a durable screen.screenshot_requested event. Image bytes and object keys are not present.
ScreenScreenshotStatus— required: stateScreenStackStatus— required: manifest_revision, levels, playing, reported_atWhat the screen's Player last reported of its stack (docs/player-compatibility.md §6.11): each level's readiness and the level each display shows in each region (the page and any strip). Present only for a Player that resolves its own stack, while that stack is current.
ScreenStatus— required: chip, observed_at, received_atThe status chip the screen's Player last reported with PUT /screen/status (docs/player-compatibility.md §9.5): the screenrig.overlay/v1 condition that wins its rank, or null when the Player shows no chip. Absent until the first accepted report. Cleared on identity reset, unpair, and archive.
It is the Player's last report, so consumers treat it as stale when the screen is not online (Screen.online), like every Player report. level and icon come from the backend's catalogue for chip, never from the Player, and are absent when chip is null. Read-only on the project API. Never authorization or admission. ScreenPatch cannot write it.
ScreenStatusChangedDetails— required: chipdetails object on a durable project-only screen.status_changed event, appended when PUT /screen/status changes the stored chip, level or icon. level and icon are absent when chip is null. This type is not a runtime audience command and is not delivered on GET /screen/events.
ScreenStorage— required: observed_at, received_at, volume, cache, durability, plan, transfer_24hSanitized player storage report. Absent until the first accepted PUT /screen/storage. Cleared on identity reset, unpair, and archive. Consumers treat received_at older than 24 hours as stale. Read-only on the project API. Never authorization or admission. ScreenPatch cannot write it.
ScreenStorageCache— required: house_used_bytes, capacity_bytes, reserve_bytes, protected_bytes, fallback_bytes, warm_bytes, ad_headroom_bytesScreenStorageExcludedPage— required: page_id, reasonScreenStorageForecast— required: manifest_revision, fit, excluded_page_count, basis, received_atApproximate steady-state target selection (plan A.3 only, no transition) from the last reported capacity and the screen's desired manifest. Absent without a storage report, which consumers read as unknown. basis is reported_capacity. Recomputed when a storage report arrives and when the screen's manifest revision changes. Never authorization or admission.
ScreenStorageForecastDryRun— required: fit, excluded_page_count, required_bytes, capacity_bytes, basis, received_atPre-assignment fit dry run for one playlist. fit, excluded_page_count, and required_bytes come from plan A.3 target selection against the screen's last reported capacity (basis reported_capacity); there is no transition prediction and nothing is treated as local. required_bytes is the retained cost of every page of the playlist. fit unknown means the screen has no storage report, or the playlist's content references are not ready; required_bytes, capacity_bytes, and received_at are null then. received_at is the storage report's receipt time, and a report older than 24 hours is stale but still forecast from, exactly like Screen.storage_forecast.
It is read-only metadata and never authorization or admission.
ScreenStorageForecastRequest— required: playlist_idBody of the pre-assignment fit dry run. playlist_id must be a playlist of the authenticated project; another project's or an unknown id is not_found.
ScreenStoragePlan— required: manifest_revision, fit, transition, required_bytes, target_bytes, excluded_pagesScreenStorageShortfall— required: at, fit, required_bytes, capacity_bytes, excluded_page_countPresent while the player's reported fit is partial, transition_blocked, or none_fit, except a transition_blocked plan whose only blocking reason is anchor_unavailable: that is a failed download on a device with room, not a shortfall. at is when the condition began.
It is set once when the condition begins and removed when it ends, each transition appending one project-only event, screen.storage_shortfall or screen.storage_shortfall_cleared. Those events are not delivered on GET /screen/events. Cleared with Screen.storage on identity reset, unpair, and archive.
Read-only project health metadata; it does not change screen revision or manifest authority and is never authorization.
ScreenStorageTransfer— required: new, refetch, repair, failedScreenStorageVolume— required: total_bytes, available_bytesScreenSurfaceChangedDetails— required: observed_at, surfacesdetails object on a durable screen.surface_changed project event. Pixels are not present. This type is not a runtime audience command and is not delivered on GET /screen/events.
ScreenSuspendWrite— required: messageScreenSuspension— required: message, sincePresent while the screen is suspended (docs/player-compatibility.md §6.9). The runtime manifest carries the same object as its suspension member.
ScreenTags— required: noneFleet selector tags, 0 to 16 unique exact tags, each matching the media tag grammar. On ScreenPatch the array replaces the whole set and an empty array clears it; a duplicate or malformed tag is invalid_request. Changing tags bumps the screen revision (If-Match guards it) and appends screen.updated with details.tags; it never changes the manifest revision.
Tags are never on the runtime manifest and never authorization.
ScreenTagsPatch— required: tagsA screen's complete new tag set (patchScreenTags). The array replaces every tag the screen carries and an empty array clears them. The body has no other member.
ScreenTakeover— required: playlist_id, until, set_atThe playlist that wins over campaigns, the schedule and the assignment until until, or until cleared when until is null.
ScreenTakeoverWrite— required: playlist_idScreenTimelineSegment— required: from, to, sourceScreenToastAccepted— required: expires_atScreenToastDetails— required: level, text, duration_ms, expires_atdetails object on a durable screen.toast event. Colours are not present.
ScreenToastWrite— required: level, textScreenUptime— required: itemsScreenVerbosityPatch— required: noneA change of a screen's log level and toast level (patchScreenVerbosity). The body names at least one of the two members and no other.
ScreenVerdict— required: kind, playlist_id, verdictOne candidate at the explained instant. won: the resolver picks it (on a screen whose Player resolves its own stack: it fills its region on a display it targets). outranked: it holds, and by is the candidate that won. not_started: before its from. ended: at or after its until (a takeover: its end). outside_hours: inside from and until, outside its windows. conditional: on a screen whose Player resolves its own stack, it holds by time and plays while what only the Player observes holds too (reason interaction or data). off: it does not apply, and reason says why.
ScreenWall— required: outputs, arrangementThe video wall a screen's Player lays out: two to twelve outputs that show one canvas. Setting it needs a Player that declared the display.wall host capability (docs/player-compatibility.md §6.5, §6.8). A playlist with a format spans it.
ScreenshotCaptureID— required: noneOptional stage_, qa_, or development_ prefix, then shot_ and a random suffix. New suffixes use 16 alphanumeric characters excluding oOlL; retained legacy suffixes are accepted.
SelectorPreview— required: primitive, selector, matched_count, candidatesSelectorPreviewInput— required: primitive, selectorSelectorPreviewMedia— required: media_id, filename, tagsServiceClient— required: id, project_id, name, capabilities, access, state, created_by, keys, secrets, created_at, updated_at, authenticated_requests, tokens_issuedServiceClientChange— required: noneServiceClientKey— required: kid, alg, thumbprint, jwk, added_atServiceClientList— required: itemsServiceClientResult— required: clientServiceClientSecret— required: id, hint, added_atSessionDescription— required: type, sdpSessionSSOStartRequest— required: intentSignInPolicy— required: password, passkey, passkey_enrollment_required, policy_revision, federatedVerified person sign-in policy. Passive defaults preserve local sign-in. An enforced organization policy directs sign-in through its identity provider.
SignInResetAccepted— required: statusIdentical for known and unknown addresses; no address, credential, project, or delivery status is returned.
SignInResetRequest— required: emailSignupCreated— required: user_id, project_id, organization_idSignupHeardDetail— required: noneThe follow-up to heard. kind names a choice from a short list, and text is free text.
SignupPasskeyOptionsRequest— required: email, display_nameSignupProfile— required: noneThe answers a person gives after signing up. Every member is optional and may be null, meaning unanswered or skipped.
SignupRequest— required: email, display_name, marketing_email, credentialThe email address and display name are those the ceremony was started with.
Source— required: id, name, kind, target, enabled, config, preset, settings, secrets, refresh_seconds, stale_after_seconds, revision, status, created_at, updated_atSourceArea— required: key, name, rooms, status, screensSourceCreate— required: name, targetJSON, CSV and Sheets config may set key_template, a card data key with {screen.field.NAME} tokens. Its mapping then produces {documents:[{fields:{NAME:VALUE},document:{...}}]} instead of one document. Each output is validated against the target card.
Keys are unique and outside card-instance and feed namespaces; source test previews the envelope and validates every output. Each key uses the source stale period. Missing keys are retained with their existing expiry; storage failures can leave partial writes and the next run retries.
SourceError— required: code, message, atSourceKind— required: noneWhere the data comes from. ics reads iCalendar and webcal:// addresses (the other kinds are described in /docs/sources). A kind that is not available answers invalid_request at kind.
SourceList— required: items, next_cursorSourcePreset— required: name, title, category, description, kind, cards, fields, secrets, refresh_seconds, min_refresh_seconds, stale_after_seconds, attribution, docsA ready-made, tested configuration of the json data source.
SourcePresetAttribution— required: textSourcePresetCard— required: type, defaultSourcePresetField— required: name, label, type, required, helpOne thing a person tells a preset. A text is checked against pattern and max_length, a choice is one of options, and a list holds min_items to max_items objects filled in with fields.
SourcePresetList— required: itemsSourcePresetOption— required: value, labelSourcePresetSecret— required: name, label, help, how_to_get, requiredA credential a preset needs, entered under name in secrets.
SourceRoom— required: key, status, screensSourceRoomRef— required: id, nameSourceRoomScreen— required: id, name, kind, field, value, statusSourceRooms— required: source_id, room_field, room_key_prefix, area_field, area_key_prefix, rooms_as_of, summary, rooms, areas, screens, warningsThe room mapping of a data source that writes a document for each room or area: the rooms the source offered at its last good refresh against the screens that read them.
A screen belongs to the report for a field when it has a value for it, or when its own playlist places a card whose data key is the key prefix followed by {screen.field.NAME}; a playlist it reaches through a folder, campaign or schedule level is not followed.
SourceRoomsSummary— required: rooms, unmapped_rooms, areas, unmapped_areas, screens, missing_screens, unknown_screensSourceSecretView— required: set, hint, updated_atSourceStaleAfter— required: noneHow long the card keeps the source's last good data when it cannot refresh, in seconds.
The source writes its documents to expire that long after the last good refresh, so when it keeps failing they blank on every screen (offline ones included) and pages whose visibility depends on the data fall back to the pages after them; a source that is not failing renews the deadline as it refreshes.
At least twice refresh_seconds. null keeps the last good data until the source refreshes again (the default for every kind but momentus, whose default is 1800 when the member is omitted on create). A failing source that passes the period is stale (status.stale) and appends source.stale.
SourceStatus— required: state, last_attempt_at, last_success_at, last_change_at, next_run_at, failures, stale, data_expires_at, error, warnings, statsWhat the last runs did. state is pending (not run yet), ok (the last fetch succeeded and the card accepted the document), error (the last run failed and the card kept its last good document) or disabled. ok does not judge whether the upstream data is right or current, nor whether any screen shows the card.
SourceTarget— required: card_idSourceTestRequest— required: noneSourceTestResult— required: document, valid, warnings, stats, fetched_atSourceUpdate— required: noneStackLevel— required: id, source, playlist_idOne level of a screen's stack (packages/schedule-contract SEMANTICS.md, Levels and the stack). id is takeover, campaign:ID, schedule:ENTRY_ID, default, or folder:FOLDER_ID; record_id names the campaign, entry or folder, name the campaign's or folder's name. rule absent always holds.
StackLevelRule— required: noneStreamSource— required: noneStreamSourceWrite— required: noneA stream source. A source may give only its url and let the scheme name the protocol: https is hls, rtsp and rtsps are rtsp.
SupportConversation— required: id, subject, created_at, updated_at, last_sequence, human_requested, unread_staff, closedSupportConversationList— required: items, next, unread_staff_totalSupportHours— required: timezone, start, end, weekdays, excludes_bc_holidaysSupportMessage— required: id, conversation_id, sequence, role, author, body, created_atSupportMessageList— required: itemsSupportRead— required: sequenceSupportStatus— required: chat_allowed, available, staffed, human_available, hours, human_wait_seconds, docs_urlSupportSubmission— required: conversation, messageSupportWrite— required: bodyTrigger— required: id, kind, label, target, default_for_seconds, url, username, revision, created_at, updated_atA CAP alert trigger or an overlay trigger. Its password is returned only by create and rotate-secret.
TriggerAlert— required: id, revision, state, status, msg_type, sender, identifier, until, screens, started_at, updated_atThe trigger's latest alert, from the CAP message fields.
TriggerAlertScreens— required: targeted, taken_over, cleared, skipped, failed, pendingWhat the latest takeover change did. skipped counts screens without a default playlist, archived or gone, and, when clearing, screens another takeover has replaced since.
A screen in a protected screen folder is left as it is and counted in protected, not skipped; ending an alert (cancel, all clear, an operator clear) still clears what the trigger set on such a screen.
TriggerClear— required: noneTriggerDelivery— required: at, outcome, status, bytes, request_idTriggerDeliveryList— required: itemsTriggerList— required: itemsTriggerOverlay— required: show, style, enabled, signing, alerts, screens, pendingAn overlay trigger's configuration and state.
TriggerOverlayAlert— required: id, until, started_at, updated_at, revisionTriggerOverlayReceipt— required: outcome, trigger_id, until, activeTriggerOverlayRequest— required: noneOne post to an overlay trigger, as read after the trigger's mapping (if any). A clear ignores every member but alert_id, clear and state.
TriggerOverlayScreens— required: targeted, holding, taken_over, removed, edge_taken, skipped, failedWhat the latest fan-out did to the target screens. A screen in a protected screen folder is left as it is and counted in protected, not skipped; ending the alert still clears what the trigger set on it.
TriggerOverlayStyle— required: noneThe text card's static params an overlay is drawn with. The trigger chooses the layout.
TriggerOverlayWrite— required: showTriggerPatch— required: noneTriggerReceipt— required: outcome, trigger_idTriggerSecretRotate— required: noneTriggerTarget— required: noneExactly one of tag and screen_ids.
TriggerWithPassword— required: noneTriggerWithSecrets— required: noneTriggerWrite— required: label, targetUDPStreamSource— required: protocol, group, portURLStreamSource— required: urlUploadScope— required: playlist_ids, folder_idsWhat an uploader may use (docs/uploaders.md): the playlists it changes pages of and the media folders it uploads into. Folders are exactly the listed ones, never their subfolders. An id that names no live object of the project when written is invalid_request at its position; a later deletion leaves a dead id, which grants nothing.
The scope is read on each request, so a change applies to the next one.
UptimeDay— required: day, online_s, offline_s, availability, coverageOne UTC day. availability is online_s over online_s plus offline_s, null when nothing was recorded. coverage is the share of the day (of every reporting screen's day in a project row) with a recorded state, 0 to 1: a gap lowers coverage and is never offline time. intervals (screen rows) is how many recorded intervals touched the day; screens (project rows) how many screens had a recorded state; live marks a screen day that includes the open interval up to now.
UsageFrame— required: page_id, primitive_id, rectOne image or video primitive that names the media file, with its frame and how the file fills it.
UsageScreen— required: id, label, screen, playlist_ids, howOne active screen that uses at least one of the listed playlists.
UsageScreenHow— required: playlist_id, usesUsageTagFilter— required: all, any, noneA tag selector's filter. A file matches when it has every all tag, at least one any tag when any are named, and no none tag; the shorthand tag selector is all with one tag.
UsageTrack— required: index, idUserEmailChangeAccepted— required: noneUserEmailChangeRequest— required: email, proofRequests a verified-email change using fresh proof. The current email stays effective while verification is pending. The old address receives a warning only, with no undo link.
VerifiedOrganizationDomain— required: domain, organization_id, state, verified_atVersionResponse— required: version, commit, api_version, protocol_versionWebhook— required: id, url, event_types, enabled, revision, status, created_at, updated_atA project webhook. Player log records (runtime.reported) are not delivered.
Each matching project event is POSTed to url as the same JSON object GET /api/events returns, with headers ScreenRig-Webhook-Id, ScreenRig-Event-Id (the event cursor, stable across retries), User-Agent, and ScreenRig-Signature t=<unix seconds>,v1=<hex HMAC-SHA256 over "<t>.<raw body>" keyed with the secret string>.
A 2xx answer within 10 seconds succeeds; any other status, a redirect (never followed), a timeout, or a connection or TLS failure is retried with exponential backoff (30 seconds doubling to one hour, jittered) for 24 hours and then failed. After three consecutive timeouts the attempt timeout drops to 5 seconds until a response arrives.
A webhook has at most one attempt in flight and a project at most two, across all servers. Delivery is at least once and unordered; deduplicate on ScreenRig-Event-Id.
Each attempt is metered like a listen-stream event (1 credit after the monthly 10,000 API/SSE allowance), and the same events that are free on the listen stream are free here: screen.*, runtime.*, alert_mute.*, application.event, playback.page_failed, and agent.command.
An attempt refused before anything is sent (url_rejected, dns_failed) is not billed. payment_required and secret_unavailable back off but never count toward failing or auto-disable. status is failing while no delivery has succeeded since failing_since; after 72 hours of continuous failure the webhook is disabled (disabled_reason delivery_failures) and notification.disabled is appended.
WebhookDelivery— required: id, webhook_id, event_id, event_type, state, attempts, created_atWebhookDeliveryAttempt— required: attempt, at, status, error, duration_msWebhookDeliveryAttemptList— required: itemsWebhookDeliveryList— required: items, next_cursorWebhookEventTypes— required: noneExact event types such as screen.online, or prefixes ending in .* such as screen.* (every type starting with screen.). notification.test is never fanned out by a subscription; it is sent only by POST /api/webhooks/{id}/test.
WebhookHandshake— required: noneA vendor handshake the URL answers, for a vendor with no signature scheme here. microsoft_graph answers a POST that carries a validationToken query parameter by echoing the decoded token as text/plain. monday answers a JSON POST of {"challenge": "..."} with the same JSON. trello answers its HEAD check with 200. azure_event_grid answers a SubscriptionValidationEvent (aeg-event-type: SubscriptionValidation) with {"validationResponse": "<validationCode>"}. verify.vendor slack answers Slack's signed url_verification challenge, and verify.vendor asana answers Asana's X-Hook-Secret handshake and keeps that secret to verify its events.
A handshake is answered only for a webhook set up for that vendor, writes nothing, is never billed and spends no rate budget, and is recorded in the delivery log with outcome handshake. A disabled webhook answers none.
WebhookList— required: itemsWebhookPatch— required: noneWebhookVerify— required: vendor, secretThe vendor verification of a webhook or trigger, as a read shows it. The secret is only ever a hint.
WebhookVerifyVendor— required: noneWebhookVerifyWrite— required: vendorA vendor whose own signature authenticates a post in place of the secret, because these vendors cannot send ours.
The header and scheme of each are the vendor's own and fixed: github sends X-Hub-Signature-256, sha256= and the hex HMAC-SHA256 of the body; stripe sends Stripe-Signature, t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>"> (several v1 while a secret is rolled, t within five minutes); shopify sends X-Shopify-Hmac-Sha256, the base64 HMAC-SHA256 of the body; slack sends X-Slack-Signature, v0= and the hex HMAC-SHA256 of "v0:<X-Slack-Request-Timestamp>:<body>" (within five minutes); linear sends Linear-Signature, the hex HMAC-SHA256 of the body, and the webhookTimestamp member of the body (Unix milliseconds) must be within a minute; vercel sends x-vercel-signature, the hex HMAC-SHA1 of the body; asana sends X-Hook-Signature, the hex HMAC-SHA256 of the body keyed by the X-Hook-Secret of Asana's handshake; generic_hmac is any sender that signs with HMAC-SHA256 in the header you name.
A post that carries a valid signature is accepted without the secret, and the secret keeps working beside it. verify cannot be combined with signing. A post is judged on the body as it arrived, before it takes any rate budget. Slack and Asana also answer their handshakes (see WebhookHandshake).
WebhookWithSecret— required: id, url, event_types, enabled, revision, status, created_at, updated_at, secretA Webhook plus its signing secret. Returned only by create and rotate-secret.
WebhookWrite— required: url, event_typesWebsiteSetupCode— required: code, expires_at