Workspace API
Upsert Account
Add or update a workspace account with POST /workspace/accounts/upsert.
POST https://api.parcelengineering.com/api/v1/workspace/accounts/upsertAdds an account to your workspace or updates its custom fields if it is already saved. Accepts either an explicit account UUID or a domain-based match to identify the target account.
Upsert costs 0 credits.
The Workspace API requires the Pro plan. A request from a non-Pro workspace or a non-member receives 403 forbidden.
Request body
Provide exactly one of account_id or match to identify the account. All annotation fields are optional.
| Field | Type | Required | Description |
|---|---|---|---|
account_id | string (UUID) | One of account_id or match | The UUID from a Data API account record. |
match | object | One of account_id or match | Domain-based lookup. Provide { "domain": "example.com" }. |
icp_tier | string | null | No | Your ICP tier: tier_1, tier_2, tier_3, disqualified, or null to clear. Setting this schedules an Atlas recompute. |
workspace_status | string | null | No | Your workflow state (any string). null clears the value. |
notes | string | null | No | Free-text notes. null clears the value. |
tags | string[] | No | Full-replace tag set. Sending [] clears all tags. |
Match resolution rules
When using match: { domain }, Parcel looks up the domain against the canonical account registry:
| Domain matches | Result |
|---|---|
| Exactly 1 account | Upsert proceeds |
| 0 accounts | 404 not_found |
| More than 1 account | 409 ambiguous_match |
A 409 ambiguous_match means the domain is associated with more than one canonical account. Resolve the ambiguity by fetching the candidate accounts from the Data API and providing an explicit account_id instead.
Partial update semantics
- Omitted fields are left unchanged. You can update
icp_tierwithout touchingnotesortags. - Explicit
nullclears a field. Sending"notes": nullremoves the existing note. tagsis full-replace. Sending"tags": ["boston"]replaces the entire tag set, not merges it with the existing one.
Example
curl -X POST https://api.parcelengineering.com/api/v1/workspace/accounts/upsert \ -H "Authorization: Bearer pcl_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "match": { "domain": "acmedev.com" }, "icp_tier": "tier_1", "workspace_status": "active", "notes": "Met at BuildBoston 2025", "tags": ["boston", "developer"] }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/workspace/accounts/upsert', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ match: { domain: 'acmedev.com' }, icp_tier: 'tier_1', workspace_status: 'active', notes: 'Met at BuildBoston 2025', tags: ['boston', 'developer'], }), });
const { data, metadata } = await response.json();Response
{ "data": { "id": "8a1f3c2e-0001-4b7d-9e5a-000000000001", "name": "Acme Development Group", "account_types": ["developer"], "domains": ["acmedev.com"], "website": "https://acmedev.com", "icp_tier": "tier_1", "workspace_status": "active", "notes": "Met at BuildBoston 2025", "tags": ["boston", "developer"], "atlas_score": 87, "atlas_trend": "up" }, "metadata": { "credits": 0 }}Upsert costs 0 credits. The atlas_score and atlas_trend in the response reflect the values at the time of the call. If you set icp_tier, a recompute is scheduled and the score updates asynchronously.