Workspace API
Discover Skills
Search or browse skills as content-free summaries with POST /workspace/skills/find and /workspace/skills/list.
Both routes below return content-free summaries — never instructions or file contents — and never expose another member’s drafts. Each costs 0 credits, requires skills:read, and is exempt from the Pro-plan gate for a verified Spec run token. Pass workspace_id as a query parameter when authenticating with a user JWT; API keys infer it.
The Workspace API requires the Pro plan. A request from a non-Pro workspace or a non-member receives 403 forbidden. A verified Spec run token on either read below is exempt from the plan gate.
find is the ranked search surface (Spec’s L1 discovery). list is the browse surface: it returns an overridden skill alongside the override that replaces it, and the caller controls order with sort.
Find Skills
POST https://api.parcelengineering.com/api/v1/workspace/skills/findSearches skills the caller may discover and returns content-free summaries. This is the Spec L1 surface: it applies override precedence and stays published-only.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Free-text query matched against names and descriptions. Max 500 characters. |
limit | integer | No | Maximum summaries to return. Route accepts any integer ≥ 1. The service default is the workspace L1 results limit (8 unless configured). A value above 20 is 422 limit_exceeded. |
scope | active | explore | No | active (default) searches custom and installed skills. explore searches the official catalog only, including skills already installed, and reports installed on each; it never returns custom skills. |
query is a ranking input. Results are ordered by rank.
Example
curl -X POST https://api.parcelengineering.com/api/v1/workspace/skills/find \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "query": "pdf", "limit": 8, "scope": "active" }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/skills/find', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ query: 'pdf', limit: 8, scope: 'active' }), });
const { data, metadata } = await response.json();state is ready, not_populated, unavailable, or disabled. Production Explore currently returns { "state": "not_populated", "items": [] } because the official catalog is not populated. When skill loading is off, active returns { "state": "disabled", "items": [] }.
List Skills
POST https://api.parcelengineering.com/api/v1/workspace/skills/listLists skills visible to the caller as content-free summaries. Unlike find, list returns an overridden skill alongside the override that replaces it, and the caller controls order with sort.
Request body
The facet vocabulary is the skill entity in the filter registry. Discover it with GET /filters?entity=skill. Facets accept one value or an array. An omitted facet means all values; an omitted lifecycle therefore means every lifecycle the caller may read (object auth still hides other members’ drafts).
| Field | Type | Required | Description |
|---|---|---|---|
q | string | No | Match predicate over name, slug, and description. 1-500 characters. A row is kept when its find-rank is below 5. q does not change order. |
source | custom | official | array | No | Authored here, or installed from the catalog. |
lifecycle | draft | published | unpublished | array | No | deleted is never offered. |
audience | personal | workspace | array | No | Who the skill is for. |
installed | installed | not_installed | No | Official installations versus custom skills. |
updated_after | YYYY-MM-DD | No | Last changed on or after this UTC calendar day (inclusive). |
updated_before | YYYY-MM-DD | No | Last changed before this UTC calendar day (exclusive). |
sort | name | updated_at | No | Defaults to updated_at. slug ascending is the unconditional tiebreak. |
direction | asc | desc | No | Defaults to desc for both sort keys. |
limit | integer 1..200 | No | Defaults to 50. There is no cursor. |
Example
curl -X POST https://api.parcelengineering.com/api/v1/workspace/skills/list \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "lifecycle": "published", "audience": "workspace", "sort": "updated_at", "direction": "desc", "limit": 50 }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/skills/list', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ lifecycle: 'published', audience: 'workspace', sort: 'updated_at', direction: 'desc', limit: 50, }), });
const { data, metadata } = await response.json();When skill loading is off, the response is { "state": "disabled", "items": [] }.
Response shape
Both routes return the same summary shape:
{ "data": { "state": "ready", "items": [ { "locator": { "source": "custom", "skillId": "8a1f3c2e-0001-4b7d-9e5a-000000000010" }, "name": "PDF helper", "description": "Fills and stamps PDF forms.", "audience": "workspace", "source": "custom", "lifecycle": "published", "slug": "pdf-helper", "createdBy": "8a1f3c2e-0001-4b7d-9e5a-000000000013", "maintainerId": "8a1f3c2e-0001-4b7d-9e5a-000000000013", "installed": false, "revision": "1", "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "updatedAt": "2026-09-01T12:00:00.000Z", "hasUnpublishedChanges": false } ] }, "metadata": { "credits": 0 }}See Workspace Skills for override and runtime-block behavior.