---
**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.
---
**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.
Operations
GET /bases
List Grid bases
A base is a Grid workspace (Airtable-style) that owns a set of tables; it sits at the top of the bases → tables → fields/records/views hierarchy, so start here when you don't yet have a baseId. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List Grid bases",
"description": "A base is a Grid workspace (Airtable-style) that owns a set of tables; it sits at the top of the bases → tables → fields/records/views hierarchy, so start here when you don't yet have a baseId. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Base"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /bases
Create a Grid base
Creates an empty base (workspace) that tables can then be added to. Only `name` is meaningful on input — id, tableCount and timestamps are server-assigned.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a Grid base",
"description": "Creates an empty base (workspace) that tables can then be added to. Only `name` is meaningful on input — id, tableCount and timestamps are server-assigned.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Base"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Base"
}
}
}
}
}
}
GET /bases/{id}
Get a Grid base by id
Returns one base's metadata (name, tableCount, creator). Use when you already have a base id; use the list operation to find one by name.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get a Grid base by id",
"description": "Returns one base's metadata (name, tableCount, creator). Use when you already have a base id; use the list operation to find one by name.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Base"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /bases/{id}
Rename or edit a Grid base
Partial update of base-level metadata such as `name`. Does not change the tables or records inside the base.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Rename or edit a Grid base",
"description": "Partial update of base-level metadata such as `name`. Does not change the tables or records inside the base.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Base"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Base"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /bases/{id}
Delete a Grid base
DESTRUCTIVE and irreversible: removes the base record. Its tables/records are not cleaned up, so they are orphaned rather than restored. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a Grid base",
"description": "DESTRUCTIVE and irreversible: removes the base record. Its tables/records are not cleaned up, so they are orphaned rather than restored. Confirm with the user before calling.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}
GET /tables
List Grid tables
A table is a spreadsheet-like grid of records inside one base, and owns its own fields (columns) and views. Pass `baseId` to scope the list to a single base — that is the normal second step after listing bases. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List Grid tables",
"description": "A table is a spreadsheet-like grid of records inside one base, and owns its own fields (columns) and views. Pass `baseId` to scope the list to a single base — that is the normal second step after listing bases. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
},
{
"in": "query",
"name": "baseId",
"schema": {
"type": "string"
},
"description": "Filter tables by parent base ID"
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Table"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /tables
Create a Grid table
Adds a table to an existing base. Requires `name` and the parent `baseId`; fieldCount/recordCount are maintained by the server. Create the table before creating its fields or records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a Grid table",
"description": "Adds a table to an existing base. Requires `name` and the parent `baseId`; fieldCount/recordCount are maintained by the server. Create the table before creating its fields or records.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Table"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Table"
}
}
}
}
}
}
GET /tables/{id}
Get a Grid table by id
Returns one table's metadata (name, parent baseId, field/record counts). Does NOT return the table's rows — use the records list filtered by `tableId` for data.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get a Grid table by id",
"description": "Returns one table's metadata (name, parent baseId, field/record counts). Does NOT return the table's rows — use the records list filtered by `tableId` for data.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Table"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /tables/{id}
Rename or edit a Grid table
Partial update of table metadata such as `name` or the parent `baseId`. Does not alter rows or columns.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Rename or edit a Grid table",
"description": "Partial update of table metadata such as `name` or the parent `baseId`. Does not alter rows or columns.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Table"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Table"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /tables/{id}
Delete a Grid table
DESTRUCTIVE and irreversible: removes the table definition. Its fields, records and views are left orphaned, not restored. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a Grid table",
"description": "DESTRUCTIVE and irreversible: removes the table definition. Its fields, records and views are left orphaned, not restored. Confirm with the user before calling.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}
GET /fields
List Grid field (column) definitions
A field is a column definition on a table — its name, its type (singleSelect, date, formula, rollup, …) and type-specific `options` such as select choices. Fields describe the SHAPE of a table; the cell values live on records. Pass `tableId` to get one table's columns, e.g. before writing a record. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List Grid field (column) definitions",
"description": "A field is a column definition on a table — its name, its type (singleSelect, date, formula, rollup, …) and type-specific `options` such as select choices. Fields describe the SHAPE of a table; the cell values live on records. Pass `tableId` to get one table's columns, e.g. before writing a record. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
},
{
"in": "query",
"name": "tableId",
"schema": {
"type": "string"
},
"description": "Filter fields by parent table ID"
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Field"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /fields
Add a column to a Grid table
Defines a new field (column) on the table given by `tableId`: `name`, `type`, and for choice/lookup types an `options` object. Adding a field does not backfill values on existing records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Add a column to a Grid table",
"description": "Defines a new field (column) on the table given by `tableId`: `name`, `type`, and for choice/lookup types an `options` object. Adding a field does not backfill values on existing records.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Field"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Field"
}
}
}
}
}
}
GET /fields/{id}
Get a Grid field definition by id
Returns one column definition (name, type, options). Use it to learn the legal values of a singleSelect before setting that cell on a record.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get a Grid field definition by id",
"description": "Returns one column definition (name, type, options). Use it to learn the legal values of a singleSelect before setting that cell on a record.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Field"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /fields/{id}
Edit a Grid field definition
Partial update of a column's name, type or `options` (e.g. adding a select choice). Changing `type` does not convert values already stored on records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Edit a Grid field definition",
"description": "Partial update of a column's name, type or `options` (e.g. adding a select choice). Changing `type` does not convert values already stored on records.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Field"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Field"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /fields/{id}
Delete a Grid field definition
DESTRUCTIVE and irreversible: removes the column definition. Values previously written under that field name stay on the records but become undescribed. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a Grid field definition",
"description": "DESTRUCTIVE and irreversible: removes the column definition. Values previously written under that field name stay on the records but become undescribed. Confirm with the user before calling.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}
GET /records
List Grid records (table rows)
A record is one row of user data in a Grid table: the built-in name/status/assignee columns plus a free-form `fields` map of cell values keyed by field name. This is the actual content of the Grid app — use fields for column definitions and views for saved layouts. Pass `tableId` to scope to one table. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List Grid records (table rows)",
"description": "A record is one row of user data in a Grid table: the built-in name/status/assignee columns plus a free-form `fields` map of cell values keyed by field name. This is the actual content of the Grid app — use fields for column definitions and views for saved layouts. Pass `tableId` to scope to one table. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
},
{
"in": "query",
"name": "tableId",
"schema": {
"type": "string"
},
"description": "Filter records by parent table ID"
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Record"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /records
Create a Grid record (row)
Inserts a row into the table given by `tableId`. Set the built-in name/status/assignee columns directly and any custom columns inside `fields`, keyed by the field name from the fields collection.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a Grid record (row)",
"description": "Inserts a row into the table given by `tableId`. Set the built-in name/status/assignee columns directly and any custom columns inside `fields`, keyed by the field name from the fields collection.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Record"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Record"
}
}
}
}
}
}
GET /records/{id}
Get a Grid record (row) by id
Returns one row with all of its cell values. Use the records list with `tableId` when you don't have the row id.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get a Grid record (row) by id",
"description": "Returns one row with all of its cell values. Use the records list with `tableId` when you don't have the row id.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Record"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /records/{id}
Edit cell values on a Grid record
Partial update of one row — the normal way to change a cell (e.g. move a row's `status`, reassign it, or set a custom column inside `fields`). Only the keys you send are changed.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Edit cell values on a Grid record",
"description": "Partial update of one row — the normal way to change a cell (e.g. move a row's `status`, reassign it, or set a custom column inside `fields`). Only the keys you send are changed.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Record"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Record"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /records/{id}
Delete a Grid record (row)
DESTRUCTIVE and irreversible: permanently removes the row and its cell values. There is no trash or undo. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a Grid record (row)",
"description": "DESTRUCTIVE and irreversible: permanently removes the row and its cell values. There is no trash or undo. Confirm with the user before calling.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}
GET /views
List Grid saved views
A view is a saved presentation of one table — its layout (grid, kanban, calendar, gantt, gallery, form), its `filterByFormula` and its sort. Views hold no data of their own; read records for rows. Pass `tableId` to list one table's views. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List Grid saved views",
"description": "A view is a saved presentation of one table — its layout (grid, kanban, calendar, gantt, gallery, form), its `filterByFormula` and its sort. Views hold no data of their own; read records for rows. Pass `tableId` to list one table's views. Server-side query: $filter/$select/$orderby/$search/$top/$skip/$count (MCP clients pass these as _filter, _top, … because $ is not a legal tool-input key).",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
},
{
"in": "query",
"name": "tableId",
"schema": {
"type": "string"
},
"description": "Filter views by parent table ID"
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/View"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /views
Create a Grid saved view
Saves a new layout over the table given by `tableId`: a `type` (grid/kanban/calendar/…), an optional `filterByFormula` and a sort field/direction. Creating a view never changes the underlying rows.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a Grid saved view",
"description": "Saves a new layout over the table given by `tableId`: a `type` (grid/kanban/calendar/…), an optional `filterByFormula` and a sort field/direction. Creating a view never changes the underlying rows.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/View"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/View"
}
}
}
}
}
}
GET /views/{id}
Get a Grid saved view by id
Returns one view's configuration (type, filter formula, sort). It does not evaluate the view or return matching rows.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get a Grid saved view by id",
"description": "Returns one view's configuration (type, filter formula, sort). It does not evaluate the view or return matching rows.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/View"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /views/{id}
Edit a Grid saved view
Partial update of a view's name, layout type, `filterByFormula` or sort. Affects presentation only, never record data.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Edit a Grid saved view",
"description": "Partial update of a view's name, layout type, `filterByFormula` or sort. Affects presentation only, never record data.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/View"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/View"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /views/{id}
Delete a Grid saved view
DESTRUCTIVE and irreversible: removes the saved layout. The table's records are unaffected. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a Grid saved view",
"description": "DESTRUCTIVE and irreversible: removes the saved layout. The table's records are unaffected. Confirm with the user before calling.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}
GET /automations
List automations
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List automations",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Automation"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
}
}
POST /automations
Create automation
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create automation",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Automation"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Automation"
}
}
}
}
}
}
GET /automations/{id}
Get automation
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get automation",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Automation"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
PATCH /automations/{id}
Update automation
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Update automation",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Automation"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Automation"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /automations/{id}
Delete automation
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete automation",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
}
}