Workspace API
Upsert Subscription
Subscribe to a signal type or update an existing subscription with POST /workspace/signal-subscriptions/upsert.
POST https://api.parcelengineering.com/api/v1/workspace/signal-subscriptions/upsertCreates a signal subscription or updates an existing one. The operation is keyed by signal_type: if the workspace already has a subscription for the given type, the provided fields are merged in; fields not present in the body are left unchanged. The queue watermark is never reset on an update.
Creating a subscription starts queueing signals discovered from now on, with a short lookback so the queue is not empty on arrival.
Upsert costs 0 credits.
The Workspace API requires the Pro plan. A request from a non-Pro workspace receives 403 forbidden.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
signal_type | string | Yes | The signal type to subscribe to. See Valid signal types below. |
priority | string | No | Queue priority: high, medium, or low. Defaults to medium on create. |
channels | string[] | No | Delivery channels: any combination of email, teams, slack. Email is the only channel that delivers today; Teams and Slack are coming soon. Defaults to [] on create. |
notify_all_members | boolean | No | When true, email alerts go to every workspace member (including future members) and recipient_user_ids is ignored. Defaults to false on create. |
recipient_user_ids | string[] (UUIDs) | No | Specific workspace members to notify. All IDs must be current workspace members. Maximum 100. Defaults to [] on create. |
cadence | string | No | Delivery cadence: immediate (send as signals arrive) or daily_digest (batched once per day). Defaults to immediate on create. |
Valid signal types
The signal_type field accepts any value from the subscribable signal taxonomy:
project_announcement, pre_application, site_acquisition, demolition_permit, site_prep, entitlement_application, environmental_review, public_review, entitlement_approval, entitlement_denial, withdrawal, expiration, permit_filed, permit_issued, construction_loan, under_construction, team_update, completion
Note: rfp is a reserved type and is not subscribable.
Partial update semantics
- Omitted fields are left unchanged on an existing subscription. You can update
prioritywithout touchingchannelsor recipients. channels,notify_all_members, andrecipient_user_idsare always validated as a unit against the merged post-update state. If the result would haveemailselected with no recipients (andnotify_all_membersisfalse), the request returns400 email_requires_recipients.
Recipient rules
- Every UUID in
recipient_user_idsmust be a current workspace member. Non-members cause a400 invalid_recipientserror. - Setting
notify_all_members: trueemails all current and future workspace members and takes precedence over any explicitrecipient_user_ids.
Example
curl -X POST https://api.parcelengineering.com/api/v1/workspace/signal-subscriptions/upsert \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "signal_type": "permit_issued", "priority": "high", "channels": ["email"], "notify_all_members": false, "recipient_user_ids": ["d1e2f3a4-0001-4b7c-9e5d-000000000001"], "cadence": "immediate" }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/signal-subscriptions/upsert', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ signal_type: 'permit_issued', priority: 'high', channels: ['email'], notify_all_members: false, recipient_user_ids: ['d1e2f3a4-0001-4b7c-9e5d-000000000001'], cadence: 'immediate', }), });
const { data, metadata } = await response.json();Response
The response is the full list of the workspace’s subscriptions after the upsert, in the same shape as List Subscriptions.
{ "data": { "subscriptions": [ { "id": "7c3f1a2b-0001-4e8d-bf6a-000000000001", "signal_type": "permit_issued", "priority": "high", "channels": ["email"], "notify_all_members": false, "recipient_user_ids": ["d1e2f3a4-0001-4b7c-9e5d-000000000001"], "cadence": "immediate", "new_count": 14, "created_at": "2025-06-01T12:00:00.000Z", "updated_at": "2025-06-15T09:30:00.000Z" } ], "filed_floor_days": 90 }, "metadata": { "credits": 0 }}Upsert costs 0 credits.