Workspace API
Projects
Save and annotate the global projects you care about. Search returns only the ones you have saved to your workspace.
You save global Parcel projects to your workspace and annotate them with your own data. Workspace project search returns only the projects you have saved, not the full global dataset. To start tracking a new project, find it via the Data API and then upsert it by id.
The Workspace API requires the Pro plan. Non-Pro requests receive a 403 forbidden response.
Fields you set
These fields are written by your team via the upsert endpoint.
| Field | Type | Notes |
|---|---|---|
workspace_status | string | null | Your workflow state for this project (for example, "watching", "bid_submitted"). Any string value or null. |
notes | string | null | Free-text notes for your team. Explicit null clears the value. |
tags | string[] | Arbitrary labels. Full-replace on every write (sending ["boston"] replaces the entire tag set). |
Fields Parcel computes (read-only)
These fields are computed by Parcel from permit records and market signals. You cannot set them directly.
| Field | Type | Notes |
|---|---|---|
stage | string | null | Current lifecycle stage of the project (for example, "filed", "permitted", "under_construction", "completed"). |
last_signal_at | string | null | ISO 8601 timestamp of the most recent signal on this project. |
primary_use | string | null | Primary building use type (for example, "residential", "commercial", "mixed_use"). |
residential_units | integer | null | Number of residential units, when applicable. |
gross_floor_area_sf | number | null | Total gross floor area in square feet. |
cost_of_construction_usd | number | null | Estimated construction cost in US dollars. |
Filters
Default sort: updated_at desc
| Filter key | Type | Notes |
|---|---|---|
workspace_status | field | Your workflow status string. Exact match. |
tags | field | Tag value. Array value = OR. |
name | field | Project name. Supports fuzzy match. |
stage | field | Project stage value. Array value = OR. |
primary_use | field | Building use type. Array value = OR. |
city | field | City name. exact_match: false (default) = fuzzy. |
state | field | State code, for example "MA". |
Sort keys: updated_at, last_signal_at, name, created_at
Range keys: residential_units, gross_floor_area_sf, cost_of_construction_usd, last_signal_at
stage and primary_use are enum-backed: a value outside the allowed set returns 400 (not zero rows), and they do not accept exact_match since a closed vocabulary is always exact. See Errors.
Relational filters
Use relational filters to match projects based on properties of their related records.
| Key | Inner filter fields |
|---|---|
has_account | role, name, account_type |
without_account | role, name, account_type |
has_signal | signal_type, filed_at (range) |
without_signal | signal_type, filed_at (range) |
has_contact | role, title |
without_contact | role, title |
Each without_* key takes the same inner fields as its has_* twin, but matches your saved projects with NO related record satisfying the inner criteria (NOT EXISTS) instead of at least one. See Filtering for the semantics.
Includes
Valid include keys for GET /workspace/projects/{id}: accounts, contacts, signals.
Example: GET /workspace/projects/<id>?include=accounts&include=signals
Search example
The query below finds your saved projects currently under construction in Massachusetts, sorted by most recent signal.
curl -X POST https://api.parcelengineering.com/api/v1/workspace/projects/search \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "filters": { "stage": { "value": "under_construction" }, "state": { "value": "MA" } }, "sort": { "field": "last_signal_at", "order": "desc" }, "limit": 25 }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/projects/search', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ filters: { stage: { value: 'under_construction' }, state: { value: 'MA' }, }, sort: { field: 'last_signal_at', order: 'desc' }, limit: 25, }), });
const { data, metadata } = await response.json();{ "data": [ { "id": "7b2e4d1f-0001-4c8e-af6b-000000000001", "name": "100 Seaport Boulevard", "city": "Boston", "state": "MA", "stage": "under_construction", "primary_use": "mixed_use", "residential_units": 120, "gross_floor_area_sf": 180000, "cost_of_construction_usd": 42000000, "last_signal_at": "2026-06-01T00:00:00Z", "workspace_status": "watching", "notes": "GC bid due Q3", "tags": ["seaport", "mixed-use"] } ], "metadata": { "total": 11, "total_is_capped": false, "credits": 11 }}The data array is trimmed to one record above. A page returning 11 projects costs 11 credits (1 per row returned).