Workspaces API — Manage Multi-Tenant Agency Client Portfolios
REST API for agencies to create, list, audit, and run bulk operations across many client workspaces. Includes per-workspace usage, integration health checks, and bulk-action endpoints for cross-portfolio automation.
The workspace API is how agencies manage their portfolio: create new client workspaces, list them, audit health, and run any tool across many at once. For the conceptual model, see Workspaces.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/workspaces | List workspaces the key can access |
| GET | /v1/workspaces/{id} | Get one workspace |
| POST | /v1/workspaces | Create a workspace |
| PATCH | /v1/workspaces/{id} | Update settings, brand profile, integrations |
| DELETE | /v1/workspaces/{id} | Archive (soft delete) |
| POST | /v1/workspaces/bulk-action | Run any tool across many workspaces |
| GET | /v1/workspaces/{id}/usage | Credits used + quotas |
| GET | /v1/workspaces/{id}/health | Integration health |
GET /v1/workspaces
curl -G "https://api.citationbench.com/v1/workspaces" \
-H "Authorization: Bearer sk_live_agency_***" \
--data-urlencode "include=usage,health,lastActiveAt" \
--data-urlencode "limit=50"| Param | Notes |
|---|---|
include | csv: usage, health, lastActiveAt, brand, integrations |
tag | Filter by workspace tag (e.g., industry:fintech) |
active | true / false |
limit, cursor | Pagination |
Response
{
"data": [
{
"id": "ws_acme",
"name": "Acme PM",
"primaryDomain": "acme.com",
"tags": ["industry:saas", "tier:premium"],
"active": true,
"createdAt": "2026-02-01T...",
"lastActiveAt": "2026-05-24T08:14:32Z",
"usage": {
"creditsUsedThisCycle": 1247,
"creditsRemaining": 1753,
"cycleEndsAt": "2026-06-01T00:00:00Z"
},
"health": {
"ahrefs": "healthy",
"dataforseo": "healthy",
"gsc": "warning",
"apollo": "not_connected"
}
},
{ "id": "ws_beta", "...": "..." }
],
"nextCursor": null,
"total": 38
}GET /v1/workspaces/{id}
curl https://api.citationbench.com/v1/workspaces/ws_acme \
-H "Authorization: Bearer sk_live_agency_***"Returns full workspace data including settings, brand profile, integration list, recent activity summary. See the Workspaces concept for the full shape.
POST /v1/workspaces
curl -X POST https://api.citationbench.com/v1/workspaces \
-H "Authorization: Bearer sk_live_agency_***" \
-d '{
"name": "Beta Project Management",
"primaryDomain": "betapm.com",
"tags": ["industry:saas", "tier:standard"],
"settings": {
"primaryLocation": "uk",
"primaryLanguage": "en",
"defaultModel": "claude-sonnet-4-6",
"approvalPolicy": {
"publish": "require_approval",
"outreach_send": "require_approval"
},
"creditPool": "agency"
},
"brand": {
"shortDescription": "Project management for design and creative teams",
"primaryKeywords": ["project management for design teams"]
}
}'Response: 201 Created with the new workspace's full record.
PATCH /v1/workspaces/{id}
curl -X PATCH https://api.citationbench.com/v1/workspaces/ws_beta \
-d '{
"settings": {
"approvalPolicy": { "publish": "auto" }
}
}'Partial updates. Any subset of name, tags, active, settings, brand accepted.
DELETE /v1/workspaces/{id}
Soft delete — sets active: false. Data retained for 90 days, then purged.
curl -X DELETE https://api.citationbench.com/v1/workspaces/ws_betaReturns 204 No Content.
POST /v1/workspaces/bulk-action
The agency-defining endpoint. Run any tool across many workspaces with one call.
curl -X POST https://api.citationbench.com/v1/workspaces/bulk-action \
-H "Authorization: Bearer sk_live_agency_***" \
-d '{
"action": "agent.invoke",
"workspaces": "all",
"config": {
"skill": "rank_monitor",
"input": { "alertOn": { "drop": 5 } }
}
}'Parameters
| Field | Type | Required | Notes |
|---|---|---|---|
action | string | yes | Any tool name (agent.invoke, research.keyword.research, indexing.gsc.submit, link_building.serp_outreach.create, etc.) |
workspaces | string[] | "all" | yes | Explicit IDs or "all" |
filter | object | no | When workspaces: "all", narrow by tag / active / etc. |
config | object | yes | Base config passed to the action; per-workspace variables ({{ workspace.X }}) resolve at runtime |
concurrency | number | no | Max parallel children (default: 10) |
schedule | string | no | Cron-style — recurring instead of one-shot ("weekly:mon:09:00", "daily:22:00", etc.) |
tags | string[] | no | Tags on the parent invocation for later filtering |
Response
HTTP/1.1 202 Accepted
{
"parentInvocationId": "inv_01HVZ...",
"agentId": "agt_01HVZ...",
"status": "RUNNING",
"workspacesFanout": 30,
"estimatedCost": { "credits": 450, "durationSeconds": 180 },
"children": [
{ "workspaceId": "ws_acme", "invocationId": "inv_***A", "status": "PENDING" },
{ "workspaceId": "ws_beta", "invocationId": "inv_***B", "status": "PENDING" },
{ "workspaceId": "ws_gamma", "invocationId": "inv_***C", "status": "PENDING" }
// 27 more
]
}When complete, the parent invocation's result is an aggregate summary with per-workspace results plus a list of failures.
Workspace-variable substitution
Inside config, use {{ workspace.* }} placeholders that resolve per-child:
{
"action": "link_building.serp_outreach.create",
"workspaces": "all",
"config": {
"seed": { "keyword": "{{ workspace.brand.primaryKeywords[0] }}" },
"outreach": { "ourContentUrl": "{{ workspace.brand.featuredContentUrl }}" }
}
}GET /v1/workspaces/{id}/usage
curl https://api.citationbench.com/v1/workspaces/ws_acme/usage{
"workspaceId": "ws_acme",
"billingCycle": {
"startDate": "2026-05-01T00:00:00Z",
"endDate": "2026-06-01T00:00:00Z",
"durationDays": 31
},
"credits": {
"allocated": 3000,
"used": 1247,
"remaining": 1753,
"rolloverEligible": 506
},
"breakdown": {
"research": 412,
"produce": 587,
"indexing": 82,
"link_building": 166
},
"quotas": {
"gscIndexingDaily": { "limit": 10, "usedToday": 7 }
}
}GET /v1/workspaces/{id}/health
curl https://api.citationbench.com/v1/workspaces/ws_acme/health{
"status": "warning",
"integrations": [
{ "name": "ahrefs", "status": "healthy", "lastCheckedAt": "..." },
{ "name": "dataforseo", "status": "healthy", "lastCheckedAt": "..." },
{
"name": "gsc",
"status": "warning",
"message": "Cookie expires in 3 days",
"lastCheckedAt": "..."
},
{ "name": "apollo", "status": "not_connected" },
{ "name": "instantly", "status": "healthy", "lastCheckedAt": "..." }
]
}MCP
> List all my workspaces.Claude calls workspaces.list.
> Run a rank check across every workspace, alert on drops of 5+ positions.Claude calls workspaces.bulk_action.
> Create a new workspace for newclient.com.Claude calls workspaces.create.
Errors
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | Missing required fields |
| 403 | agency_key_required | Endpoint requires an agency master key |
| 403 | workspace_forbidden | Key can't act on this workspace |
| 404 | workspace_not_found | — |
| 409 | workspace_exists | Same primaryDomain already has a workspace under this agency |
| 422 | no_matching_workspaces | filter matched zero workspaces |
Cost
| Action | Credits |
|---|---|
| All workspace CRUD | free |
bulk-action | sum of per-workspace costs |
Use cases (string things together)
A. Monday-morning portfolio report
curl -X POST .../v1/workspaces/bulk-action -d '{
"action": "distribute.track_rank",
"workspaces": "all",
"schedule": "weekly:mon:09:00",
"config": { "alertOn": { "drop": 5 } }
}'One scheduled job; one alert webhook; one client report.
B. Onboard 5 clients at once
for DOMAIN in client1.com client2.com client3.com client4.com client5.com; do
WS=$(curl -sf -X POST .../v1/workspaces \
-d "{\"name\":\"$DOMAIN\",\"primaryDomain\":\"$DOMAIN\"}" | jq -r '.id')
curl -X POST .../v1/agent/invoke \
-H "X-Workspace-Id: $WS" \
-d "{\"skill\":\"bootstrap_brand\",\"input\":{\"domain\":\"$DOMAIN\"}}"
doneC. Audit which workspaces have unhealthy GSC
curl -G .../v1/workspaces --data-urlencode "include=health" \
| jq '.data[] | select(.health.gsc != "healthy") | .id'D. Cross-portfolio content gap
curl -X POST .../v1/workspaces/bulk-action -d '{
"action": "research.content_gap.find",
"workspaces": "all",
"config": { "competitors": "{{ workspace.competitors.top_3 }}" }
}'Surfaces gaps across every client; useful for finding agency-wide content themes.
Related
- Concept: Workspaces
- Concept: Agent
- API: Inventory
- Get started: Authentication
- Get started: Workspaces & multi-brand
CRM
CRUD endpoints for the CRM beneath every link-building activity. Shared accounts (partner domains), contacts, per-workspace relationships, and an event log — all queryable and writable via REST and MCP.
Catalog
Authoritative inventory of CitationBench MCP tools, namespaced by pillar — research, produce, distribute, memory, agents, jobs, workspaces. Every REST endpoint is also exposed as an MCP tool.