create_view
Create Saved View
Changes something you own.
Use this to save a new View that alerts when events match a screen. Use update_view instead to change a View that exists, and screen_events to try a predicate without saving it. Create a saved View (a saved Screen that fires Alerts when events match). The predicate is validated by the SQL compiler; an invalid one returns a clear validation error. Predicate DSL (recursive JSON): a leaf is {col, op, val}; composites are {all:[...]} (AND) or {any:[...]} (OR). Columns are prefixed: "ea." = event_analytics, "e." = events, "pa." = performer_analytics, "p." = performers (p.name, the act the row is about), "v." = venues (v.venue_name, the venue's name; v.city, v.state, v.country_code, v.capacity). Ops: =, !=, >, >=, <, <=, between (val=[lo,hi]), in (val=[...]), contains (case-insensitive substring on a TEXT column; val is a plain string), not_contains (removes the rows whose TEXT column holds val as a case-insensitive substring; a row with no value stays; % and _ in val match themselves, while contains reads them as wildcards). Example: {"all":[{"col":"ea.price_self_z_7d_xs_z_subcat_tte","op":">=","val":2},{"col":"ea.days_to_event","op":"between","val":[7,60]}]}. NAMED ACTS / TEAMS / PERFORMERS, use p.name. When the user names the act the events are BY (a musician like 'Taylor Swift', a team like 'Lakers', a comedian, a touring show), filter the performer column: {col:'p.name', op:'contains', val:'Taylor Swift'}. p.name is the act the row is KEYED on. e.event_name is the TITLE, which reads 'A at B' for a sports event and so also returns the opponent's home games, and for a concert also returns parking and support billings. A performer or team ask NEVER goes to e.event_name. Keep writing 'contains' for an act name. When what was typed IS a whole act name in the catalog, the server rewrites that leaf to op '=' and says so; anything else stays a substring. REMOVING ROWS BY A WORD. 'NOT Little', 'not the Lakers', 'without Taylor', 'excluding Hamilton' remove every row whose title contains the word: {col:'e.event_name', op:'not_contains', val:'Little'}. Never write '!=' for a word: '!=' compares the WHOLE value, so e.event_name != 'Manilow' removes nothing. A MISSPELLED superlative is still a superlative. 'htotest', 'hotest', 'bigest movers', 'chepest', 'lowset price' read as the word they intend and take the ordering that word takes. A ranking word is NEVER a name: never put it in a 'contains' leaf on e.event_name or p.name, and never answer with sort null because the spelling was odd. ECONOMIC FLOOR. The server already ANDs ea.listings_current >= 25 AND ea.median_price_current >= 40 into every screen. Do NOT add a book-depth floor of your own. A column whose name ends in d1 or 1d is a ONE-DAY CHANGE, not yesterday's value. 'Inventory dropping' or 'listings falling' is a SEVEN-DAY change or an absorption column, never a one-day ratio. A stale row, or an event that is not active, is NEVER a move: its numbers describe the last day it was priced. Gate a movement ask on ea.last_price_snapshot_date with a relative date. The economic floor CAN be turned off at the call site (apply_floor=false, or apply_economic_floor=false on a rule), only when you explicitly want the raw universe. A freshness gate is NOT applied by default, so supply it yourself: {"col":"ea.last_price_snapshot_date","op":">=","val":{"days_ago":1}} means "priced within the last day" and compiles to CURRENT_DATE - 1, re-evaluated every tick, where a literal date string silently rots. The {"days_ago": N} value form is valid on any date/timestamp column. Authoritative validation runs in SQL; an invalid predicate returns a clear error.
Parameters
| Name | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | yes | Up to 120 characters. | |
predicate | object | {} | Predicate DSL object (see tool description). {} = always-true, combined with max_firings_per_eval to get a "top-N of the universe" alert. | |
enabled | boolean | true | ||
channels | string[] | yes | Delivery channels, at least one. "email" mails the Matches to the account's address. The other value routes them to the destinations attached to this View instead, which is how a Slack or Discord channel receives them: create one with create_destination, then attach it with set_view_target. A View may carry both. One of email, webhook. | |
max_firings_per_eval | integer | Cap on events this rule can match per evaluation tick (top-N by event_id (deterministic cap, not magnitude-prioritized)). Bounds alert volume since percentile predicates can't self-limit. Default 25. From 1 to 500. | ||
apply_economic_floor | boolean | Whether the rule's predicate is AND-ed with the economic floor (listings_current >= 25 AND median_price_current >= 40). Default true. | ||
delivery_target_id | string or null | Optionally attach an alert destination (an id from list_destinations or create_destination) so this alert's Matches are delivered there the instant they fire, in addition to email. Omit for email-only. | ||
notification_cadence | string or null | How often this alert's Matches may leave as EMAIL: 'immediate' (each match), 'hourly', 'daily' (batched), or 'off' (Matches recorded but never emailed). Only paces EMAIL; an attached alert destination always delivers the instant a Match fires, regardless of this. One of immediate, hourly, daily, off. | ||
notification_mode | string or null | Per-alert EMAIL delivery mode: 'instant' (email each match), 'digest' (the Matches wait and go out together in an hourly email or a daily email, at the pace notification_cadence sets), or 'muted' (Matches recorded, never emailed). 'instant' and 'muted' override notification_cadence. Alert destinations are unaffected: they always deliver instantly. One of instant, digest, muted. | ||
tags | string[] | Free-text organization labels for this View (max 5, deduplicated). Purely for grouping/filtering in the app; tags never change what the View matches or where alerts deliver. Up to 5 items. | ||
color | string or null | Accent color the app shows on this View's pill/dot. One of: red, orange, amber, green, teal, blue, violet, pink. Cosmetic only. One of red, orange, amber, green, teal, blue, violet, pink. |
Example
{
"name": "Sharp 7d movers, 1–2 months out",
"predicate": {
"all": [
{ "col": "ea.price_self_z_7d_xs_z_subcat_tte", "op": ">=", "val": 2.5 },
{ "col": "ea.days_to_event", "op": "between", "val": [30, 60] },
{ "col": "ea.last_price_snapshot_date", "op": ">=", "val": { "days_ago": 1 } }
]
},
"channels": ["email"],
"max_firings_per_eval": 25
}