Create Saved Event Filter (View)
create_viewCreates a View: a saved predicate over events that records a Match each time an event enters it and sends alerts by email or to an attached alert destination, with an optional Digest. A new View starts sending alerts at once unless enabled is false. Use it when the person asks to be alerted about every event that meets market conditions. Test the predicate with screen_events first; to change a View that exists use update_view; for alerts on one event use watch_event.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"name": "<name>",
"channels": "<channels>"
},
"name": "create_view"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The View's name, up to 120 characters. | |
| tags | No | 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. | |
| color | No | Accent color the app shows on this View's pill/dot. One of: red, orange, amber, green, teal, blue, violet, pink. Cosmetic only. | |
| digest | No | The View's Digest: each Issue is the View's events with Movers, New and Gone against the previous Issue. Only the fields you pass change. Turning it on past the plan's Digest count is refused with the plan that carries more. | |
| enabled | No | Default true: the View starts recording Matches and sending alerts at once. false saves it paused. | |
| parking | No | Parking events: a parking pass that is its own event (sub_category_id 75 or 'Parking' in the name). `hide` (the default) leaves them out; `only` returns parking alone; `include` returns parking beside every other event. Set `only` when the user asks for parking, `include` when they want both; leave it at `hide` otherwise. The View keeps this choice. | hide |
| channels | 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: add one in the Ticker web app under Settings, then pass its id as delivery_target_id here, or attach it later with update_view's attach_destination. A View may carry both. | |
| predicate | No | {} = always-true, combined with max_firings_per_eval to get a "top-N of the universe" alert. 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 listings-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. Views and alerts do not gate freshness: when a View is about movement, add a relative-date leg on ea.last_price_snapshot_date (days_ago 1). 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. Views and alerts do not gate freshness: a View about movement adds the leg {"col":"ea.last_price_snapshot_date","op":">=","val":{"days_ago":1}}, which 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. A weekend is {"col":"e.local_date","op":"between","val":{"weekend":"this","tz":"America/New_York"}} ("next" for the one after): the coming Friday to Sunday in tz, from today on a Friday to Sunday, resolved on every run. e.event_id, e.venue_id and e.performer_id take the ids rows carry (ev..., vn..., pf...). Authoritative validation runs in SQL; an invalid predicate returns a clear error. | |
| listing_trigger | No | Alert as soon as we see a listing in one section of one event with at least min_quantity tickets at or under max_price on the required price_basis (including or excluding fees). Missing prices never match. Send it without predicate. One alert per listing, and again only at a lower price. Max and Ultra. | |
| notification_mode | No | Per-alert EMAIL delivery mode: 'instant' (email each match), 'batched' (the Matches wait and go out together in one email, at the pace notification_cadence sets: `hourly` or `daily`; with `off` it is saved as 'muted'), or 'muted' (Matches recorded, never emailed). 'instant' and 'muted' override notification_cadence. 'digest' is the old name of 'batched', accepted until 2026-11-30 and stored as 'batched'. Alert destinations are unaffected: they always deliver instantly. | |
| delivery_target_id | No | Optionally attach an alert destination (an id from list_destinations; new destinations are added in the Ticker web app under Settings) so this alert's Matches are delivered there the instant they fire, in addition to email. Omit for email-only. | |
| apply_economic_floor | No | Whether the rule's predicate is AND-ed with the economic floor (listings_current >= 25 AND median_price_current >= 40). Default true. | |
| max_firings_per_eval | No | 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. | |
| notification_cadence | No | How often this alert's Matches may leave as EMAIL: `immediate` (each match), `hourly`, `daily` (batched), or `off` (Matches recorded but never emailed; a 'batched' alert with `off` is saved as 'muted'). Only paces EMAIL; an attached alert destination always delivers the instant a Match fires, regardless of this. |