baserow — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited baserow (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Access the Baserow API with managed API key authentication. Manage database rows with full CRUD operations, filtering, sorting, and batch operations.
# List rows from a table
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/baserow/api/database/rows/table/{table_id}/?user_field_names=true')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFhttps://gateway.maton.ai/baserow/{native-api-path}Replace {native-api-path} with the actual Baserow API endpoint path. The gateway proxies requests to api.baserow.io and automatically injects your API token.
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEYEnvironment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"Manage your Baserow API key connections at https://ctrl.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=baserow&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFpython <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'baserow'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFpython <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"connection": {
"connection_id": "90a5d047-b856-4577-ac05-faccaabf8989",
"status": "ACTIVE",
"creation_time": "2026-03-02T12:01:29.812801Z",
"last_updated_time": "2026-03-02T12:02:17.932675Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "baserow",
"metadata": {},
"method": "API_KEY"
}
}Open the returned url in a browser to enter your Baserow database token.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFIf you have multiple Baserow connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/baserow/api/database/rows/table/123/')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '90a5d047-b856-4577-ac05-faccaabf8989')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFIf omitted, the gateway uses the default (oldest) active connection.
#### List Rows
GET /baserow/api/database/rows/table/{table_id}/Query parameters:
user_field_names=true - Use human-readable field names instead of field_123 IDssize - Number of rows per page (default: 100)page - Page number (1-indexed)order_by - Field name to sort by (prefix with - for descending)filter__{field}__{operator} - Filter rows (see Filtering section)search - Search query across all fieldsinclude - Comma-separated field names to includeexclude - Comma-separated field names to excludeResponse:
{
"count": 5,
"next": "http://api.baserow.io/api/database/rows/table/123/?page=2&size=2",
"previous": null,
"results": [
{
"id": 1,
"order": "1.00000000000000000000",
"Assignee Name": "Alice Johnson",
"Email": "[email protected]",
"Tasks": []
}
]
}#### Get Row
GET /baserow/api/database/rows/table/{table_id}/{row_id}/Response:
{
"id": 1,
"order": "1.00000000000000000000",
"field_7456198": "Alice Johnson",
"field_7456201": "[email protected]",
"field_7456215": []
}#### Create Row
POST /baserow/api/database/rows/table/{table_id}/
Content-Type: application/json
{
"field_7456198": "New User",
"field_7456201": "[email protected]"
}Or with user field names:
POST /baserow/api/database/rows/table/{table_id}/?user_field_names=true
Content-Type: application/json
{
"Assignee Name": "New User",
"Email": "[email protected]"
}Response:
{
"id": 6,
"order": "6.00000000000000000000",
"field_7456198": "New User",
"field_7456201": "[email protected]",
"field_7456215": []
}#### Update Row
PATCH /baserow/api/database/rows/table/{table_id}/{row_id}/
Content-Type: application/json
{
"field_7456198": "Updated Name"
}Response:
{
"id": 1,
"order": "1.00000000000000000000",
"field_7456198": "Updated Name",
"field_7456201": "[email protected]",
"field_7456215": []
}#### Delete Row
DELETE /baserow/api/database/rows/table/{table_id}/{row_id}/Returns HTTP 204 No Content on success.
#### Batch Create Rows
POST /baserow/api/database/rows/table/{table_id}/batch/
Content-Type: application/json
{
"items": [
{"field_7456198": "User 1", "field_7456201": "[email protected]"},
{"field_7456198": "User 2", "field_7456201": "[email protected]"}
]
}Response:
{
"items": [
{"id": 7, "order": "7.00000000000000000000", "field_7456198": "User 1", ...},
{"id": 8, "order": "8.00000000000000000000", "field_7456198": "User 2", ...}
]
}#### Batch Update Rows
PATCH /baserow/api/database/rows/table/{table_id}/batch/
Content-Type: application/json
{
"items": [
{"id": 7, "field_7456198": "Updated User 1"},
{"id": 8, "field_7456198": "Updated User 2"}
]
}Response:
{
"items": [
{"id": 7, "order": "7.00000000000000000000", "field_7456198": "Updated User 1", ...},
{"id": 8, "order": "8.00000000000000000000", "field_7456198": "Updated User 2", ...}
]
}#### Batch Delete Rows
POST /baserow/api/database/rows/table/{table_id}/batch-delete/
Content-Type: application/json
{
"items": [7, 8]
}Returns HTTP 204 No Content on success.
#### List Fields
GET /baserow/api/database/fields/table/{table_id}/Response:
[
{
"id": 7456198,
"table_id": 863922,
"name": "Assignee Name",
"order": 0,
"type": "text",
"primary": true,
"read_only": false,
"description": null
},
{
"id": 7456201,
"table_id": 863922,
"name": "Email",
"order": 1,
"type": "text",
"primary": false
}
]#### List All Tables
Get all tables across all databases accessible by your token.
GET /baserow/api/database/tables/all-tables/Response:
[
{
"id": 863922,
"name": "Assignees",
"order": 0,
"database_id": 419329
},
{
"id": 863923,
"name": "Tasks",
"order": 1,
"database_id": 419329
}
]Reposition a row within a table.
PATCH /baserow/api/database/rows/table/{table_id}/{row_id}/move/Query parameters:
before_id - Row ID to move before (if omitted, moves to end)Example - Move row to before row 3:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request(
'https://gateway.maton.ai/baserow/api/database/rows/table/863922/5/move/?before_id=3',
method='PATCH'
)
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"id": 5,
"order": "2.50000000000000000000",
"field_7456198": "Moved User",
"field_7456201": "[email protected]"
}#### Upload File via URL
Upload a file from a publicly accessible URL.
POST /baserow/api/user-files/upload-via-url/
Content-Type: application/json
{
"url": "https://example.com/image.png"
}Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'url': 'https://httpbin.org/image/png'}).encode()
req = urllib.request.Request(
'https://gateway.maton.ai/baserow/api/user-files/upload-via-url/',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFResponse:
{
"url": "https://files.baserow.io/user_files/...",
"thumbnails": {
"tiny": {"url": "...", "width": 21, "height": 21},
"small": {"url": "...", "width": 48, "height": 48},
"card_cover": {"url": "...", "width": 300, "height": 160}
},
"visible_name": "image.png",
"name": "abc123_image.png",
"size": 8090,
"mime_type": "image/png",
"is_image": true,
"image_width": 100,
"image_height": 100,
"uploaded_at": "2026-03-02T12:00:00Z"
}#### Upload File (Multipart)
Upload a file directly using multipart form data.
POST /baserow/api/user-files/upload-file/
Content-Type: multipart/form-dataExample:
curl -X POST "https://gateway.maton.ai/baserow/api/user-files/upload-file/" \
-H "Authorization: Bearer $MATON_API_KEY" \
-F "file=@/path/to/file.pdf"Response: Same format as upload-via-url.
#### Using Uploaded Files in Rows
After uploading, use the file object in a file field:
POST /baserow/api/database/rows/table/{table_id}/?user_field_names=true
Content-Type: application/json
{
"Attachment": [{"name": "abc123_image.png"}]
}Use filter parameters to query rows:
filter__{field}__{operator}={value}With user_field_names=true:
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&filter__Assignee+Name__contains=AliceMultiple filters use AND logic by default. Use filter_type=OR to change to OR logic.
#### Text Filters
| Operator | Description |
|---|---|
equal | Exact match |
not_equal | Not equal |
contains | Contains substring |
contains_not | Does not contain substring |
contains_word | Contains whole word |
doesnt_contain_word | Does not contain whole word |
length_is_lower_than | Text length is less than value |
#### Numeric Filters
| Operator | Description |
|---|---|
higher_than | Greater than |
higher_than_or_equal | Greater than or equal |
lower_than | Less than |
lower_than_or_equal | Less than or equal |
is_even_and_whole | Value is even and whole number |
#### Date Filters
| Operator | Description |
|---|---|
date_is | Date equals (use with timezone) |
date_is_not | Date does not equal |
date_is_before | Date is before |
date_is_on_or_before | Date is on or before |
date_is_after | Date is after |
date_is_on_or_after | Date is on or after |
date_is_within | Date is within period |
date_equal | Date equals (legacy) |
date_not_equal | Date does not equal (legacy) |
date_equals_today | Date is today |
date_before_today | Date is before today |
date_after_today | Date is after today |
date_within_days | Date within X days |
date_within_weeks | Date within X weeks |
date_within_months | Date within X months |
date_equals_days_ago | Date equals X days ago |
date_equals_weeks_ago | Date equals X weeks ago |
date_equals_months_ago | Date equals X months ago |
date_equals_years_ago | Date equals X years ago |
date_equals_day_of_month | Date equals specific day of month |
date_before_or_equal | Date is before or equal (legacy) |
date_after_or_equal | Date is after or equal (legacy) |
#### Boolean Filters
| Operator | Description |
|---|---|
boolean | Boolean equals (true/false) |
#### Link Row Filters
| Operator | Description |
|---|---|
link_row_has | Has linked row with ID |
link_row_has_not | Does not have linked row with ID |
link_row_contains | Linked row contains text |
link_row_not_contains | Linked row does not contain text |
#### Single Select Filters
| Operator | Description |
|---|---|
single_select_equal | Single select equals option ID |
single_select_not_equal | Single select does not equal option ID |
single_select_is_any_of | Single select is any of option IDs |
single_select_is_none_of | Single select is none of option IDs |
#### Multiple Select Filters
| Operator | Description |
|---|---|
multiple_select_has | Has option selected |
multiple_select_has_not | Does not have option selected |
multiple_select_is_exactly | Exactly these options selected |
#### Collaborator Filters
| Operator | Description |
|---|---|
multiple_collaborators_has | Has collaborator |
multiple_collaborators_has_not | Does not have collaborator |
#### File Filters
| Operator | Description |
|---|---|
filename_contains | File name contains |
has_file_type | Has file of type (image, document) |
files_lower_than | Number of files less than |
#### Empty/Not Empty Filters
| Operator | Description |
|---|---|
empty | Field is empty (value: true) |
not_empty | Field is not empty (value: true) |
#### User Filters
| Operator | Description |
|---|---|
user_is | User field equals user ID |
user_is_not | User field does not equal user ID |
Text contains:
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&filter__Name__contains=JohnDate within last 7 days:
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&filter__Created__date_within_days=7Multiple filters (AND):
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&filter__Status__single_select_equal=1&filter__Priority__higher_than=3Multiple filters (OR):
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&filter_type=OR&filter__Status__equal=Active&filter__Status__equal=PendingUse order_by parameter:
# Sort ascending by field name
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&order_by=Assignee+Name
# Sort descending (prefix with -)
GET /baserow/api/database/rows/table/{table_id}/?user_field_names=true&order_by=-Assignee+NameUse size and page parameters:
GET /baserow/api/database/rows/table/{table_id}/?size=25&page=2Response includes next and previous URLs:
{
"count": 100,
"next": "http://api.baserow.io/api/database/rows/table/123/?page=3&size=25",
"previous": "http://api.baserow.io/api/database/rows/table/123/?page=1&size=25",
"results": [...]
}// List rows with user field names
const response = await fetch(
'https://gateway.maton.ai/baserow/api/database/rows/table/863922/?user_field_names=true',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.results);import os
import requests
# Create a row
response = requests.post(
'https://gateway.maton.ai/baserow/api/database/rows/table/863922/?user_field_names=true',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'Assignee Name': 'New User',
'Email': '[email protected]'
}
)
row = response.json()
print(f"Created row ID: {row['id']}")field_{id} format; use user_field_names=true for human-readable namesrecXXX format)| Status | Name | Description |
|---|---|---|
| 200 | Ok | Request completed successfully |
| 204 | No Content | Success (for DELETE operations) |
| 400 | Bad Request | The request contains invalid values or the JSON could not be parsed |
| 401 | Unauthorized | Invalid or missing database token |
| 404 | Not Found | Row or table not found |
| 413 | Request Entity Too Large | The request exceeded the maximum allowed payload size |
| 429 | Too Many Requests | Rate limited (10 concurrent requests on cloud) |
| 500 | Internal Server Error | The server encountered an unexpected condition |
| 502 | Bad Gateway | Baserow is restarting or an unexpected outage is in progress |
| 503 | Service Unavailable | The server could not process your request in time |
MATON_API_KEY environment variable is set:echo $MATON_API_KEYpython <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFEnsure your URL path starts with baserow. For example:
https://gateway.maton.ai/baserow/api/database/rows/table/{table_id}/https://gateway.maton.ai/api/database/rows/table/{table_id}/~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.