Skip to content

ask_onfire

ask_onfire is the one query surface over OnFire’s data lake. You author a structured QueryIR object — entity, select, filters, insight filters, joins, aggregations — and the server compiles it to Snowflake SQL. There is no raw-SQL tool: everything filterable goes through this grammar.

It replaces the old per-shape search tools (search_people, search_companies, query_employee_footprint, get_events, get_personas, get_industries).

  • Any deterministic filter over people or companies — title, seniority, persona, technology, country, size band, keywords in a profile.
  • Signal entities: job postings, event attendance, GitHub stars and forks, past employment, monthly headcount, and the evidence trail behind any insight.
  • Distributions and trends — aggregate mode with group_by + buckets.

Don’t use it for AI-ranked “who should I approach at this account” (that’s ai_prospecting) or for identity resolution from a partial name / email (match_person / match_company).

resolve_insights is optional. Call it when you want the exact persona or technology value before writing an insight_filter; otherwise ask_onfire resolves the values you pass when the query is submitted, and returns suggestions for any it can’t match.

It matters because OnFire curates roughly 1,800 insights (personas plus the technology space), and an agent that hasn’t looked one up tends to guess a job_title string instead — which searches a different, usually smaller population. Measured at one company: a devops title guess matched 25 people; the devops persona matched 159.

ParameterTypeRequiredDescription
queryQueryIRYesThe structured query object — see the grammar below.
FieldTypeDescription
entitystringThe entity the question is about — contact for people, company for organisations, plus the signal entities in the catalog.
selectstring[]Attributes / dimensions / measures to return. Must be empty in aggregate mode.
filtersDimensionFilter[]Dimension filters — dimension, op, value.
insight_filtersInsightFilter[]Curated persona / technology filters — kind, value (a list means OR).
joinsJoinSpec[]Constrain the base entity by a related one. Filter-only — you cannot select joined columns. Always a list.
distinct_bystringCollapse to one row per distinct value of this field (e.g. one contact per company).
group_bystring[]Aggregate mode. Dimension names or buckets names.
bucketsBucket[]Date-truncation keys — name, field, part (month / quarter / year).
aggregationsAggregation[]Metrics per group — count, count_distinct, sum, avg, min, max.
havingHavingFilter[]Post-aggregation filters referencing an aggregation name.
order_byOrderBy[]field + direction (asc / desc).
limitintegerExact rows wanted. Unset returns the match count only (free). Hard ceiling 1,000.
confirmedbooleanRequired true for pulls above the confirmation threshold (50 rows).
allow_extended_poolbooleanOpt in to naming a gated extended-pool entity (people_pool, experiences_pool) directly. Rarely needed.

Unknown keys are rejected (extra="forbid") — a SQL-ism or a guessed field name comes back as a validation error with the valid names and a did-you-mean list, having never executed and never billed.

Filter operators: eq, in, gte, lte, contains. The SQL symbols =, >=, <= are accepted as aliases. contains takes a list to match a whole keyword family in one filter.

Filtering from a dataset: instead of re-typing hundreds of URLs, a dimension filter can read its values from a previous result — from_dataset (a ds_... id) plus column.

This is the one rule that changes what you get back:

  • insight_filters search OnFire’s curated signal layer — deduped, active-employment-aware, high precision. This is the default for any role, department, function or seniority the persona catalog covers, including when the query names a single company.
  • A job_title contains [...] filter searches the full population as free text. Correct in three cases only: the user named an exact title, the user asked to search titles, or the concept has no curated persona.

Each insight carries a coverage band (common / moderate / thin / none) telling you what to expect before you bet a query on it.

JSON with:

  • dataset — handle to the full result (CSV in S3, 7-day TTL).
  • preview_rows — a small inline sample.
  • total_count — the number of matching rows.
  • coverage — present when the query ran against a different population than the entity you named, saying which and why.

When limit is unset the response is a count only — no rows pulled, nothing billed — so the volume can be settled with the user first.

Security leadership at one company, by title (the user named exact titles):

{
"entity": "contact",
"select": ["full_name", "job_title", "linkedin_url"],
"filters": [
{ "dimension": "current_company_url", "op": "eq", "value": "linkedin.com/company/acme" },
{ "dimension": "job_title", "op": "contains", "value": ["security", "cyber", "infosec", "ciso"] }
],
"limit": 25
}

Five AppSec people at five different large North American companies:

{
"entity": "contact",
"select": ["full_name", "job_title", "current_company_name"],
"insight_filters": [{ "kind": "persona", "value": "appsec" }],
"joins": [
{
"entity": "company",
"filters": [
{ "dimension": "size_band", "op": "eq", "value": "10001+" },
{ "dimension": "location_country", "op": "in", "value": ["united states", "canada"] }
]
}
],
"distinct_by": "current_company_url",
"limit": 5
}

Engineering hires per month at one company (aggregate mode):

{
"entity": "people_experiences",
"filters": [
{ "dimension": "company_url", "op": "eq", "value": "linkedin.com/company/acme" },
{ "dimension": "title_name", "op": "contains", "value": "engineer" }
],
"buckets": [{ "name": "hire_month", "field": "start_date", "part": "month" }],
"group_by": ["hire_month", "title_role"],
"aggregations": [{ "name": "hires", "fn": "count_distinct", "field": "person_url" }],
"order_by": [{ "field": "hire_month", "direction": "desc" }],
"limit": 200
}

Date windows are applied after the query on the experience entities — start_date / end_date are text attributes there, not filterable dimensions. Slice the window with query_datasets.

  • Confirmation threshold: 50 rows. Above it the call returns the match count and asks for confirmed=true.
  • Hard ceiling: 1,000 rows per call, enforced even with confirmed. Narrow the query (e.g. distinct_by) or split it.
  • Rows returned consume tenant credits; counts are free.
  • Field names are per entity — call describe_onfire_schema with the entity ids you plan to query rather than guessing.
  • Filter only on dimensions flagged filterable, or via insight_filters. Attributes (e.g. full_name) are display-only.
  • A 0-row result is an answer about OnFire’s coverage, not a reason to fall back to another source.
  • Full playbook — entity choice, aggregate mode, provenance and hiring recipes, dataset chaining: load_onfire_guide("ask_onfire").