Saved Searches API
A saved search is a named report query owned by your organization. In the app it drives the Reports page’s condition builder and the channel wizard’s Pre-Filter step; over the API it is a resource you create, list, rename, and delete, and a query a channel can reference as its pre-filter. A channel that references a search reads its query live, so editing the search changes the routing of every channel that uses it. This page documents the saved search resource and the query it stores. Everything here is also in the Platform API reference.
Authentication and base URL
Section titled “Authentication and base URL”Requests are JSON:API over HTTPS against https://api.disclosebot.io/v1 with a service account
bearer token, as described in the API quickstart.
curl https://api.disclosebot.io/v1/saved_searches \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx"The saved search object
Section titled “The saved search object”| Attribute | Type | Meaning |
|---|---|---|
name | string | Unique within the organization, up to 80 characters. |
query | object | The query in two parts (below). |
conditions | array | The query as conditions, one per chip the app shows (below). |
created_by_id | uuid, nullable | The user who saved it from the app; null when created over the API. |
created_at, updated_at | date-time | Timestamps. |
Relationships: organization and channels, the channels whose pre-filter references the search.
The query
Section titled “The query”A query has two parts, both optional but at least one condition between them:
{ "q": { "title_cont": "sql", "submitted_through_id_in": ["9b1e…"], "created_at_gteq": "2026-09-01" }, "results": { "severity": { "eq": "critical" }, "cvss": { "gteq": "9" } }}q: the report’s own fields
Section titled “q: the report’s own fields”Ransack predicates, keyed <attribute>_<predicate>:
| Attribute | Predicates | Example |
|---|---|---|
title_or_description, title, description | cont, not_cont, eq, not_eq | { "title_cont": "sql" } |
submitted_through_id (the brief, by id) | in, not_in (an array of brief ids) | { "submitted_through_id_in": ["9b1e…"] } |
created_at (received at) | gteq, lteq | { "created_at_gteq": "2026-09-01" } |
encrypted | eq | { "encrypted_eq": "false" } |
results: triage and integration results
Section titled “results: triage and integration results”A map of result key to predicate to value. A result is a value written against a report by a triage step, an integration, or the API; each report holds at most one value per key.
| Key | Values | Written by |
|---|---|---|
severity | critical, high, medium, low | The severity triage step. |
validity | valid, invalid | The validity triage step. |
scope | in_scope, out_of_scope | The scope triage step. |
duplicate | true, false | The duplicate triage step. |
summary | text | The summary triage step. |
| any other key | text | An integration, for example a local app posting cvss or jira_status. |
Predicates are eq, not_eq, cont, not_cont, gteq, and lteq; values are compared as
text. Any key that exists in your organization’s results is accepted; the app’s condition builder
lists the triage keys and every key your results hold. not_eq and not_cont match reports that
have no result with that value, including reports without the key.
Conditions across both parts are combined with AND. Unknown q keys, unknown predicates, and
malformed result keys are dropped; a query with nothing left is refused with 422.
Conditions
Section titled “Conditions”conditions is the same query described one condition at a time, in the order the app shows
them:
{ "part": "q", "attribute": "submitted_through_id", "label": "brief", "predicate": "in", "predicate_label": "is", "value": ["9b1e…"], "value_label": "Mobile app" }value_label is the value as the app shows it: the brief names for a brief condition, the
value itself otherwise.
Every condition applies when the query filters the Reports page or GET /v1/reports. A channel’s
pre-filter runs at dispatch, before any triage step, so it ignores results conditions there
(the app shows them struck through in the channel wizard). A brief condition decides which
briefs’ reports the channel takes.
Filtering reports with a query
Section titled “Filtering reports with a query”GET /v1/reports accepts the same two parts as request parameters: q[...] for the report’s
fields and results[<key>][<predicate>] for results.
curl "https://api.disclosebot.io/v1/reports?q[title_cont]=sql&q[submitted_through_id_in][]=9b1e…&results[severity][eq]=critical" \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx"Endpoints
Section titled “Endpoints”List saved searches
Section titled “List saved searches”GET /v1/saved_searches
Ordered by name. Filters: q[name_cont], q[created_at_gteq], and q[s] to sort (for example
created_at desc). Pagination: page[number] and page[size] (at most 30).
curl "https://api.disclosebot.io/v1/saved_searches?q[name_cont]=critical&page[size]=20" \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx"{ "data": [ { "id": "7f2c…", "type": "saved_search", "attributes": { "name": "Critical SQL", "query": { "q": { "title_cont": "sql" }, "results": { "severity": { "eq": "critical" } } }, "conditions": [ { "part": "q", "attribute": "title", "label": "title", "predicate": "cont", "predicate_label": "contains", "value": "sql", "value_label": "sql" }, { "part": "results", "attribute": "severity", "label": "severity", "predicate": "eq", "predicate_label": "is", "value": "critical", "value_label": "critical" } ], "created_by_id": null, "created_at": "2026-09-09T20:08:19.000Z", "updated_at": "2026-09-09T20:08:19.000Z" }, "relationships": { "organization": { "data": { "id": "6d4d…", "type": "organization" } } } } ], "links": { "self": "…", "first": "…", "last": "…" }}Create a saved search
Section titled “Create a saved search”POST /v1/saved_searches
curl -X POST https://api.disclosebot.io/v1/saved_searches \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx" \ -H "Content-Type: application/vnd.api+json" \ -d '{ "data": { "type": "saved_search", "attributes": { "name": "Critical SQL", "query": { "q": { "title_cont": "sql" }, "results": { "severity": { "eq": "critical" } } } } } }'201 with the saved search. A blank or taken name, or a query with no valid condition, answers
422:
{ "errors": [ { "status": "422", "title": "Unprocessable Entity", "detail": "Name is already used by another saved search", "source": { "pointer": "/data/attributes/name" } }, { "status": "422", "title": "Unprocessable Entity", "detail": "Query needs at least one condition", "source": { "pointer": "/data/attributes/query" } } ]}Show a saved search
Section titled “Show a saved search”GET /v1/saved_searches/{id}
200 with the saved search; 404 for an unknown id or another organization’s search.
Update a saved search
Section titled “Update a saved search”PATCH /v1/saved_searches/{id}
Send name, query, or both. Every channel that references the search reads the new query from
the next report on.
curl -X PATCH https://api.disclosebot.io/v1/saved_searches/7f2c… \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx" \ -H "Content-Type: application/vnd.api+json" \ -d '{ "data": { "type": "saved_search", "attributes": { "name": "Critical SQL injections" } } }'Delete a saved search
Section titled “Delete a saved search”DELETE /v1/saved_searches/{id}
204. While a channel references the search the request answers 422 with a detail naming
the channels, for example Used by channels: Critical SQL, Paging; a channel draft that
references it loses the reference.
Using a saved search on a channel
Section titled “Using a saved search on a channel”A channel references a search through prefilter_saved_search_id and reads its query live; it
stores no copy. Create the channel with the id and no prefilter_query, as documented on the
Channels API page:
curl -X POST https://api.disclosebot.io/v1/channels \ -H "Authorization: Bearer disclosebot_svc_xxxxxxxx" \ -H "Content-Type: application/vnd.api+json" \ -d '{ "data": { "type": "channel", "attributes": { "name": "Critical SQL", "prefilter_saved_search_id": "7f2c…", "definition": { "steps": { "severity": { "kind": "triage", "task": "severity", "engine": "ai", "scale": "standard" } } } } } }'The channel evaluates the search’s q conditions at dispatch and ignores its severity result
condition there. Change the search and the channel follows. Pass include=prefilter_saved_search
on channel requests to embed the search.
Errors
Section titled “Errors”Errors are a JSON:API errors array of objects with status, title, detail, and
source.pointer.
| Status | When |
|---|---|
401 | Missing or invalid bearer token. |
404 | Unknown id, or a saved search of another organization. |
422 | A blank or taken name, a query with no valid condition, or deleting a search a channel references. |
Pagination and filtering
Section titled “Pagination and filtering”page[number]andpage[size](default and maximum 30); responses carrymeta.pagination(current,records= the total, andfirst,prev,next,lastwhen they exist) andlinksfor the same pages. See Pagination.q[...]filters use ransack predicates onnameandcreated_at;q[s]sorts.