Data API
Search Fields
Discover filterable fields and enum values with POST /data/fields/search.
POST https://api.parcelengineering.com/api/v1/data/fields/searchThe fields dataset describes the filter grammar itself: one row per filterable field per dataset, with kind, description, enum values and their meanings, and relational sub-fields. Agents and API clients call it to discover valid fields and values before composing a search. It costs 0 credits and is never throttled.
Requires the Pro plan, like every Data API read. A request from a Free or Starter workspace receives 403 forbidden.
Query parameters
| Parameter | Description |
|---|---|
workspace_id | Attribution. Required when authenticating with a user JWT (e.g. via the MCP server); optional for API keys. Omitting it on a user-token request returns 400 bad_request. |
Request body
All filters are optional; an empty body returns every descriptor (the full set is bounded registry metadata).
| Field | Type | Description |
|---|---|---|
dataset | string | One of accounts, projects, contacts, signals, workspace_accounts, workspace_projects, workspace_contacts. |
kind | string | One of enum, text, number, date, relation. Ignored when keys is present. |
keys | string[] | Exact field keys to fetch (this is how the get_fields MCP tool calls the route). Without dataset, keys match across all seven datasets. When keys is present, metadata.not_found lists the requested keys that matched nothing. |
query | string | Case-insensitive substring match over key and description. Ignored when keys is present: exact-key lookup and fuzzy discovery are separate modes. |
Example
curl -X POST https://api.parcelengineering.com/api/v1/data/fields/search \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "dataset": "projects", "keys": ["stage"] }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/data/fields/search', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ dataset: 'projects', keys: ['stage'], }), });
const { data, metadata } = await response.json();Response
{ "data": [ { "dataset": "projects", "key": "stage", "kind": "enum", "description": "Canonical development stage of the project.", "values": [ { "value": "filed", "label": "Filed", "description": "A formal land-use application has been submitted." }, { "value": "approved", "label": "Approved", "description": "The discretionary land-use approval has been granted." } ] } ], "metadata": { "total": 1, "credits": 0, "not_found": [] }}The values array is trimmed to two entries above; the live stage descriptor returns all seven stage values (pre_filing through completed). Omit keys to fetch every descriptor for a dataset, or send an entirely empty body to fetch every descriptor across all seven datasets.
Metadata fields
| Field | Description |
|---|---|
total | Count of descriptors returned by this request. |
credits | Always 0. Search Fields never consumes credits. |
not_found | Present only when keys was sent: the requested keys that matched nothing ([] when every key matched). |
Relation rows (has_signal, without_project, …) carry sub_fields: the related dataset’s filterable fields, one level deep, each with its own enum values where applicable.