List views
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");curl_easy_setopt(hnd, CURLOPT_URL, "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views");
struct curl_slist *headers = NULL;headers = curl_slist_append(headers, "Authorization: Bearer <token>");curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Get, RequestUri = new Uri("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views"), Headers = { { "Authorization", "Bearer <token>" }, },};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "net/http" "io")
func main() {
url := "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views")) .header("Authorization", "Bearer <token>") .method("GET", HttpRequest.BodyPublishers.noBody()) .build();HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .url("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views") .get() .addHeader("Authorization", "Bearer <token>") .build();
Response response = client.newCall(request).execute();import axios from 'axios';
const options = { method: 'GET', url: 'https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views', headers: {Authorization: 'Bearer <token>'}};
try { const { data } = await axios.request(options); console.log(data);} catch (error) { console.error(error);}const url = 'https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}val client = OkHttpClient()
val request = Request.Builder() .url("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views") .get() .addHeader("Authorization", "Bearer <token>") .build()
val response = client.newCall(request).execute()use reqwest;
#[tokio::main]pub async fn main() { let url = "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views";
let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Authorization", "Bearer <token>".parse().unwrap());
let client = reqwest::Client::new(); let response = client.get(url) .headers(headers) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request GET \ --url https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views \ --header 'Authorization: Bearer <token>'wget --quiet \ --method GET \ --header 'Authorization: Bearer <token>' \ --output-document \ - https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/viewsList the views defined on a board — saved lenses that filter, order, and project a subset of the board’s columns. Ordered by creation time descending; archived views are excluded.
Each view carries visible_column_ids (the columns it shows) and a type describing how it’s meant to render. To read a view’s rows with its filters and column projection applied, call GET /v1/boards/{id}/views/{viewId}/rows.
Returns 404 when the board doesn’t exist, belongs to another organization, or sits outside the token’s space allow-list.
Pass fields to trim the response to the properties you actually need — e.g. ?fields=name,html_url returns just those (plus id, always). Unknown names are rejected rather than ignored.
Required scope: boards:read. Rate-limit cost: 5.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Example
24ce3d43-516c-4375-9d51-e9a2c8333683Query Parameters
Section titled “Query Parameters”Max views per page (1–100). Default 25.
Pagination cursor returned as next_cursor from the previous page.
Return only these properties of each view, instead of the full object. id is always included. Omit for the complete object. Selectable: id, board_id, name, type, position, filters, filters_complete, visible_column_ids, visible_columns_pinned, organization_id, space_id, created_at, updated_at, html_url.
Responses
Section titled “Responses”OK
object
object
ID of the board this view belongs to.
How the view presents the board’s rows (grid, board, timeline, cards). The rows feed applies the same filters and column projection regardless of type.
The view’s sort position among its board’s views (lower renders first). null when unset.
The view’s saved conditions, in the same vocabulary as POST /v1/boards/{id}/rows/search. A row must satisfy every entry; within an entry a value array is any-of. Pass this straight to the rows search to reproduce the view’s slice, or to POST/PATCH a view to save it. Empty when the view filters nothing. Check filters_complete before writing it back.
object
The column to filter on — a Column.id from GET /v1/boards/{id}/columns.
How the cell is compared to value:
equals/not_equals— the cell holds (or doesn’t hold) the value. A multi-value cell matches on any of its values, sonot_equalsreads as “has none of these”. Strings are case-insensitive;datecompares by calendar day.contains— case-insensitive substring of the cell’s text. Not on id-backed cells (tags,user,media,relation) — useequals(it accepts option names) orquery.gt/gte/lt/lte— against a single number or ISO date.is_empty/is_not_empty— whether the cell holds anything. Takes novalue, and the only operators available on structured cells (checklist,insights,metricsdata,externaldata,mediaattribute).
false when the view’s stored conditions can’t be expressed faithfully in the public filter vocabulary — a condition on a column that no longer exists, or two conditions on one column that the public AND semantics can’t reproduce. filters then describes the view only approximately, so PATCHing it back would change which rows the view shows. Re-state the filters you want instead of round-tripping them.
Column IDs this view shows, in display order — match against Column.id from GET /v1/boards/{id}/columns. This is the set GET /v1/boards/{id}/views/{viewId}/rows projects onto each row’s fields. Falls back to every column on the board when the view declares none — see visible_columns_pinned, and don’t write such a list back: it pins today’s board columns onto the view, so a column added later never appears in it. [] restores “follow the board”.
Whether visible_column_ids is the view’s own column set (true) or was synthesized from the board because the view declares none (false). Writing a synthesized list back pins it — the view stops following the board’s columns — so PATCH visible_column_ids only when you mean to choose the set, and send [] to go back to following the board.
Browser URL that opens this view on its board in the Focal web app. Hand this to users as a clickable link.
Pass back as cursor to fetch the next page. null on the last page.
true when another page is available. Equivalent to next_cursor !== null.
Example
{ "data": [ { "id": "a8d4d188-91cb-429f-9fa9-b6b20b43600f", "board_id": "24ce3d43-516c-4375-9d51-e9a2c8333683", "name": "Launch calendar", "type": "grid", "position": 0, "filters": [ { "column_id": "c6873aab-85de-4200-9afc-43470b21cf4c", "operator": "equals", "value": "Meta" } ], "filters_complete": true, "visible_column_ids": [ "3298a55a-c00c-4438-9a92-661ae534c8a3", "c6873aab-85de-4200-9afc-43470b21cf4c", "993784a7-2dfc-4c80-b52b-0601abf0b58f", "3b3fe259-8156-4c9d-967d-614c0eb60daa" ], "visible_columns_pinned": true, "organization_id": "b46d9033-24b2-4441-87ad-2151088fde01", "space_id": "6916bc3d-e1ab-425b-ac25-eefada01a9a2", "created_at": "2027-03-04T10:15:00Z", "updated_at": "2027-03-06T14:42:00Z", "html_url": "https://app.focal.inc/spaces/6916bc3d-e1ab-425b-ac25-eefada01a9a2/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/a8d4d188-91cb-429f-9fa9-b6b20b43600f" } ], "next_cursor": "eyJ0IjoiMjAyNy0wMy0wNFQxMDoxNTowMC4wMDBaIiwiaSI6Ijc4NmJlZWRhLTU1YWMtNDAyNS1iZjZlLTQzYjI5MThlODRhOSJ9", "has_more": true}The request was malformed (invalid cursor, unknown type, validation failure, etc.). error.code is bad_request.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "bad_request", "message": "Invalid request body", "details": [ { "field": "space_id", "issue": "Invalid UUID" } ] }}Missing, invalid, or disabled token. error.code is unauthenticated.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "unauthenticated", "message": "Bearer token is missing or invalid" }}The token doesn’t have the scope this endpoint requires. error.code is forbidden.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "forbidden", "message": "Token doesn't have the required scope" }}Resource doesn’t exist or is outside the token’s space allow-list. The two cases share a response shape so a restricted token can’t probe for the existence of out-of-scope spaces. error.code is not_found.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "not_found", "message": "Not found" }}Rate limit exceeded. error.code is rate_limited. Wait the number of seconds in the Retry-After header before retrying. Quotas are per-token; see the “Rate limiting” section at the top of this document.
Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.
object
object
object
Example
{ "error": { "code": "rate_limited", "message": "Rate limit of 1000 units per 60s exceeded (this request cost 5)" }}Headers
Section titled “Headers”Example
12Seconds to wait before retrying.