Workspace API
View Records
Add, remove, and search the records a View exposes with POST/DELETE /workspace/views/{id}/records.
The Workspace API requires the Pro plan. A request from a non-Pro workspace or a non-member receives 403 forbidden.
Add View Records
POST https://api.parcelengineering.com/api/v1/workspace/views/{id}/records/addPOST https://api.parcelengineering.com/api/v1/workspace/views/{id}/records/add/batchAdds one or more record IDs to a Static View. Dynamic Views are rejected with 400 view_type_mismatch. Adds are idempotent: an ID already in the View reports ok.
Add costs 0 credits. Requires workspace:write.
Path parameters
| Parameter | Description |
|---|---|
id | UUID of the Static View. |
Request body
Single:
{ "record_id": "8a1f3c2e-0001-4b7d-9e5a-000000000001" }Batch:
{ "inputs": [ { "record_id": "8a1f3c2e-0001-4b7d-9e5a-000000000001" }, { "record_id": "9b2e4d3f-0002-4b7d-9e5a-000000000002" } ]}1–100 inputs. Each record must exist in the View’s dataset and be visible to the workspace under the same pre-write rules as the web table. A candidate membership cannot authorize itself.
Example
curl -X POST \ https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/add \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "record_id": "8a1f3c2e-0001-4b7d-9e5a-000000000001" }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/add', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ record_id: '8a1f3c2e-0001-4b7d-9e5a-000000000001', }), });
const { data, metadata } = await response.json();Response
{ "data": { "view_id": "a1b2c3d4-0001-4e8d-bf6a-000000000001", "results": [ { "index": 0, "record_id": "8a1f3c2e-0001-4b7d-9e5a-000000000001", "status": "ok" } ] }, "metadata": { "credits": 0, "succeeded": 1, "failed": 0 }}Errors
| Code | Status | When |
|---|---|---|
view_not_found | 404 | Unknown View. |
view_type_mismatch | 400 | Parent is Dynamic. |
record_not_found | (per item) | ID missing from the dataset. |
record_not_visible | (per item) | Record exists but is not visible to the workspace. |
Remove View Records
DELETE https://api.parcelengineering.com/api/v1/workspace/views/{id}/records/{recordId}POST https://api.parcelengineering.com/api/v1/workspace/views/{id}/records/delete/batchRemoves one or more record IDs from a Static View. Dynamic Views are rejected with 400 view_type_mismatch. Removes are idempotent: an ID not in the View still reports success.
Remove costs 0 credits. Requires workspace:write.
Removing a Contact from its last Static View ends that visibility source only when no book overlay or project link remains. It does not delete an existing workspace overlay. See Workspace Views.
Path parameters (single)
| Parameter | Description |
|---|---|
id | UUID of the Static View. |
recordId | UUID of the record to remove. |
Batch body
{ "ids": ["8a1f3c2e-0001-4b7d-9e5a-000000000001", "9b2e4d3f-0002-4b7d-9e5a-000000000002"] }1–100 UUIDs. Per-item results in input order.
Example
curl -X DELETE \ https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/8a1f3c2e-0001-4b7d-9e5a-000000000001 \ -H "Authorization: Bearer pcl_your_api_key"const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/8a1f3c2e-0001-4b7d-9e5a-000000000001', { method: 'DELETE', headers: { 'Authorization': 'Bearer pcl_your_api_key' }, });
const { data, metadata } = await response.json();Response
{ "data": { "view_id": "a1b2c3d4-0001-4e8d-bf6a-000000000001", "results": [ { "index": 0, "record_id": "8a1f3c2e-0001-4b7d-9e5a-000000000001", "status": "ok" } ] }, "metadata": { "credits": 0, "succeeded": 1, "failed": 0 }}Search View Records
POST https://api.parcelengineering.com/api/v1/workspace/views/{id}/records/searchRuns a shared View and returns hydrated records for its dataset. This is the broader View executor — not the book-only entity searches.
- Dynamic: compiles the stored web-filter definition against the workspace-visible browse base for that dataset.
- Static: membership is the base; request filters only narrow the current result.
Request filters and relations use the existing Workspace entity SearchRequest descriptors for that dataset and are ephemeral: they are ANDed onto the View for this call only. They never rewrite the stored Dynamic definition, mark the View dirty, or change Static membership.
The stored Dynamic definition is a different grammar (web FilterState). Presentation sorting is ignored; use an explicit request sort or the dataset default.
Search costs 1 credit per returned row. Requires the same Pro / workspace gates as other Workspace searches.
Ordinary POST /workspace/accounts/search, contacts, and projects remain “your book” and do not accept view_id. Use this endpoint (or MCP workspace_search_* with view_id) for View-scoped execution.
Path parameters
| Parameter | Description |
|---|---|
id | UUID of the View. Unknown or out-of-workspace → 404. |
Request body
Same shape as the corresponding Workspace entity search: optional query, filters, sort, limit, offset, search_after. Filter and relational keys match the book endpoints for that dataset.
Sort keys (View-record only)
| Dataset | Valid sort fields | Default |
|---|---|---|
| Account | name, created_at, projects_count | name asc |
| Contact | name, created_at, projects_count | name asc |
| Project | last_signal_at, name, created_at, signals_count | last_signal_at desc |
Overlay-only updated_at and atlas_score sorts are book-only. View execution LEFT JOINs overlays, so those values are nullable; keyset pagination cannot advance through a null tail. Book entity searches keep their existing sort enums unchanged.
Example
curl -X POST \ https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/search \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "filters": { "city": { "value": "Boston" } }, "sort": { "field": "name", "order": "asc" }, "limit": 25 }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/views/a1b2c3d4-0001-4e8d-bf6a-000000000001/records/search', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ filters: { city: { value: 'Boston' } }, sort: { field: 'name', order: 'asc' }, limit: 25, }), });
const { data, metadata } = await response.json();Response
{ "data": { "dataset": "account", "records": [ { "id": "8a1f3c2e-0001-4b7d-9e5a-000000000001", "name": "Acme Development Group", "account_types": ["developer"], "icp_tier": "tier_1", "atlas_score": 87, "workspace_status": "active", "tags": ["boston"], "updated_at": "2026-07-01T12:00:00.000Z" } ] }, "metadata": { "total": 4, "total_is_capped": false, "limit": 25, "offset": 0, "search_after": null, "credits": 4 }}Workspace overlay fields may be null when the record is View-visible but not in the book. Page size is 1–100. Totals use the same cap as other searches.
See Workspace Views for definition vs request-filter grammar.