Update a view
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "PATCH");curl_easy_setopt(hnd, CURLOPT_URL, "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f");
struct curl_slist *headers = NULL;headers = curl_slist_append(headers, "Idempotency-Key: looker-sync-2027-03-04-0001");headers = curl_slist_append(headers, "Authorization: Bearer <token>");headers = curl_slist_append(headers, "Content-Type: application/json");curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }");
CURLcode ret = curl_easy_perform(hnd);using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Patch, RequestUri = new Uri("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f"), Headers = { { "Idempotency-Key", "looker-sync-2027-03-04-0001" }, { "Authorization", "Bearer <token>" }, }, Content = new StringContent("{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f"
payload := strings.NewReader("{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Idempotency-Key", "looker-sync-2027-03-04-0001") req.Header.Add("Authorization", "Bearer <token>") req.Header.Add("Content-Type", "application/json")
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/a8d4d188-91cb-429f-9fa9-b6b20b43600f")) .header("Idempotency-Key", "looker-sync-2027-03-04-0001") .header("Authorization", "Bearer <token>") .header("Content-Type", "application/json") .method("PATCH", HttpRequest.BodyPublishers.ofString("{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }")) .build();HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }");Request request = new Request.Builder() .url("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f") .patch(body) .addHeader("Idempotency-Key", "looker-sync-2027-03-04-0001") .addHeader("Authorization", "Bearer <token>") .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();import axios from 'axios';
const options = { method: 'PATCH', url: 'https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f', headers: { 'Idempotency-Key': 'looker-sync-2027-03-04-0001', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, data: { name: 'Launch calendar', type: 'grid', filters: [ { column_id: 'c6873aab-85de-4200-9afc-43470b21cf4c', operator: 'equals', value: 'Meta' } ], visible_column_ids: [ '3298a55a-c00c-4438-9a92-661ae534c8a3', 'c6873aab-85de-4200-9afc-43470b21cf4c', '993784a7-2dfc-4c80-b52b-0601abf0b58f', '3b3fe259-8156-4c9d-967d-614c0eb60daa' ], position: 1 }};
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/a8d4d188-91cb-429f-9fa9-b6b20b43600f';const options = { method: 'PATCH', headers: { 'Idempotency-Key': 'looker-sync-2027-03-04-0001', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"name":"Launch calendar","type":"grid","filters":[{"column_id":"c6873aab-85de-4200-9afc-43470b21cf4c","operator":"equals","value":"Meta"}],"visible_column_ids":["3298a55a-c00c-4438-9a92-661ae534c8a3","c6873aab-85de-4200-9afc-43470b21cf4c","993784a7-2dfc-4c80-b52b-0601abf0b58f","3b3fe259-8156-4c9d-967d-614c0eb60daa"],"position":1}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}val client = OkHttpClient()
val mediaType = MediaType.parse("application/json")val body = RequestBody.create(mediaType, "{ \"name\": \"Launch calendar\", \"type\": \"grid\", \"filters\": [ { \"column_id\": \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"operator\": \"equals\", \"value\": \"Meta\" } ], \"visible_column_ids\": [ \"3298a55a-c00c-4438-9a92-661ae534c8a3\", \"c6873aab-85de-4200-9afc-43470b21cf4c\", \"993784a7-2dfc-4c80-b52b-0601abf0b58f\", \"3b3fe259-8156-4c9d-967d-614c0eb60daa\" ], \"position\": 1 }")val request = Request.Builder() .url("https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f") .patch(body) .addHeader("Idempotency-Key", "looker-sync-2027-03-04-0001") .addHeader("Authorization", "Bearer <token>") .addHeader("Content-Type", "application/json") .build()
val response = client.newCall(request).execute()use std::str::FromStr;use serde_json::json;use reqwest;
#[tokio::main]pub async fn main() { let url = "https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f";
let payload = json!({ "name": "Launch calendar", "type": "grid", "filters": ( json!({ "column_id": "c6873aab-85de-4200-9afc-43470b21cf4c", "operator": "equals", "value": "Meta" }) ), "visible_column_ids": ("3298a55a-c00c-4438-9a92-661ae534c8a3", "c6873aab-85de-4200-9afc-43470b21cf4c", "993784a7-2dfc-4c80-b52b-0601abf0b58f", "3b3fe259-8156-4c9d-967d-614c0eb60daa"), "position": 1 });
let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Idempotency-Key", "looker-sync-2027-03-04-0001".parse().unwrap()); headers.insert("Authorization", "Bearer <token>".parse().unwrap()); headers.insert("Content-Type", "application/json".parse().unwrap());
let client = reqwest::Client::new(); let response = client.request(reqwest::Method::from_str("PATCH").unwrap(), url) .headers(headers) .json(&payload) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request PATCH \ --url https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600f \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: looker-sync-2027-03-04-0001' \ --data '{ "name": "Launch calendar", "type": "grid", "filters": [ { "column_id": "c6873aab-85de-4200-9afc-43470b21cf4c", "operator": "equals", "value": "Meta" } ], "visible_column_ids": [ "3298a55a-c00c-4438-9a92-661ae534c8a3", "c6873aab-85de-4200-9afc-43470b21cf4c", "993784a7-2dfc-4c80-b52b-0601abf0b58f", "3b3fe259-8156-4c9d-967d-614c0eb60daa" ], "position": 1 }'wget --quiet \ --method PATCH \ --header 'Idempotency-Key: looker-sync-2027-03-04-0001' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --body-data '{ "name": "Launch calendar", "type": "grid", "filters": [ { "column_id": "c6873aab-85de-4200-9afc-43470b21cf4c", "operator": "equals", "value": "Meta" } ], "visible_column_ids": [ "3298a55a-c00c-4438-9a92-661ae534c8a3", "c6873aab-85de-4200-9afc-43470b21cf4c", "993784a7-2dfc-4c80-b52b-0601abf0b58f", "3b3fe259-8156-4c9d-967d-614c0eb60daa" ], "position": 1 }' \ --output-document \ - https://api.focal.inc/v1/boards/24ce3d43-516c-4375-9d51-e9a2c8333683/views/a8d4d188-91cb-429f-9fa9-b6b20b43600fEdit a board view. Only the properties you send change; omitting filters leaves the view’s saved conditions untouched, and omitting visible_column_ids leaves its column set alone.
Read filters_complete before sending back a filters array you read from the view: when it is false the view holds conditions the public vocabulary can’t express, and writing the approximation back would change which rows the view shows.
Creating and changing a board’s views needs the same edit:board permission the app’s view editor does, checked against the connected user; an organization-scoped API token isn’t restricted this way.
Required scope: boards:write. Rate-limit cost: 10. Honors an optional Idempotency-Key.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Example
24ce3d43-516c-4375-9d51-e9a2c8333683Example
a8d4d188-91cb-429f-9fa9-b6b20b43600fHeader Parameters
Section titled “Header Parameters”Example
looker-sync-2027-03-04-0001Client-generated key (≤255 chars) for safe retries. A retry with the same key replays the original response instead of re-applying the write; reusing a key with a different body → 400, an in-flight key → 409. Required on POST; optional on PATCH/DELETE. A UUID v4 is recommended — generate one per logical write and reuse it only for retries of that write.
Request Bodyrequired
Section titled “Request Bodyrequired”object
The view’s label on the board’s view bar. 1–255 characters. Unlike column names, view names need not be unique — the app allows duplicates.
Example
Launch calendarHow the view presents the board’s rows.
Conditions a row must satisfy to appear in the view, in the same vocabulary as POST /v1/boards/{id}/rows/search — so a search you just ran can be saved as a view verbatim. Every entry must hold; a value array on an equals entry is any-of. [] clears the view’s filters. Two limits differ from search: is_empty / is_not_empty can’t be stored on a view (400), and not_equals takes a single value, because “none of these” has no stored form.
object
The column to filter on — a Column.id from GET /v1/boards/{id}/columns.
Example
c6873aab-85de-4200-9afc-43470b21cf4cHow 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).
Column IDs the view shows, in display order — Column.id values from GET /v1/boards/{id}/columns. [] means the view declares no columns of its own and falls back to showing every column on the board, which is what a freshly created view does. A column the board doesn’t have is a 400, and so is naming the same column twice. Don’t send back a list you read from a view whose visible_columns_pinned is false: that set was synthesized from the board, and writing it pins it.
Example
[ "3298a55a-c00c-4438-9a92-661ae534c8a3", "c6873aab-85de-4200-9afc-43470b21cf4c", "993784a7-2dfc-4c80-b52b-0601abf0b58f", "3b3fe259-8156-4c9d-967d-614c0eb60daa"]Sort position among the board’s views; lower renders first. Defaults to the end of the board’s views on create.
Example
1Responses
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.
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" }}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" }}The request conflicts with the current state. Most commonly a concurrent request carrying the same Idempotency-Key is still in flight. error.code is conflict.
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": "conflict", "message": "A request with this Idempotency-Key is already being processed" }}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.