Skip to content
threada Docs

Public API

Threada’s public developer surface is the public BFF. The plain REST API is for tenant-scoped WorkItem intake and follow-up. First-party shells use typed gRPC-web, and agent clients use the MCP or A2A JSON-RPC facades.

The OpenAPI contract for the API-key REST surface is generated at /openapi/public-api.openapi.yaml. The committed source copy lives next to this guide as public-api.openapi.yaml and is regenerated with task quality:openapi:generate; task quality:docs:validate fails when it drifts from the BFF router or public API wire structs.

What is live

  • Create, list, read, and update WorkItems through /api/v1/public/work-items.
  • Append and list WorkItem messages through /api/v1/public/work-items/{work_item_id}/messages.
  • Create governed action proposals through /api/v1/public/work-items/{work_item_id}/action-proposals.
  • Execute or dry-run existing governed action proposals through /api/v1/public/action-proposals/{proposal_id}/execute.
  • List and read action records through /api/v1/public/actions.
  • Call data projection boundary routes under /api/v1/public/data/.... These validate requests, enforce tenant API-key scopes, and return structured data snapshots when a backing source is configured. Otherwise they return projection_unavailable.
  • Discover the agent surface at /.well-known/agent-card.json.
  • Use MCP at /api/v1/mcp or A2A at /api/v1/a2a for governed proposals and agent task orchestration.

Direct action execution through POST /api/v1/public/work-items/{work_item_id}/actions is intentionally disabled. It returns 403 with authorization_error; use governed proposals via the public action-proposals routes, MCP/A2A, or first-party Work/Studio flows for side-effecting actions.

Authentication

Send a tenant API key in the X-Api-Key header:

curl https://api.threada.ai/api/v1/public/work-items \
  -H "X-Api-Key: $THREADA_API_KEY"

API keys are tenant-scoped and validated by identity-tenancy. The tenant is resolved from the credential, never from request headers or body fields.

Create, list, and revoke tenant API keys from the Admin governance API-key surface. The underlying authenticated BFF methods are ListIdentityApiKeys, CreateIdentityApiKey, and RevokeIdentityApiKey; the secret is returned once at creation time.

Scopes

Use the smallest scope set needed for the integration.

ScopeAllows
work.readList/get WorkItems and messages.
work.writeCreate/update WorkItems and append messages.
work.actionsCreate and execute governed action proposals, list/get action records, and authenticate agent-equivalent action flows.
catalog.readRead bounded data projection views and entities.
source.readRead bounded source health/freshness projection routes.

Agent endpoints also accept scoped delegation bearer tokens. Existing API keys with work.read, work.write, or work.actions are mapped to the agent scope taxonomy where the key has equivalent authority.

Keys minted before the scope vocabulary was unified may carry the legacy runtime_automation.read/.write/.actions names. These are accepted at creation and normalized to their work.* equivalents (and canonicalized again on validation), so existing integrations keep working without re-issuing keys.

Data Projection Boundary

The bounded data projection routes are API-key authenticated and tenant-scoped in the same way as WorkItem routes. Requests are validated, authorized, and rate-limited by API key. When a backing source is configured, the routes return the latest structured snapshot; when the source is missing or unavailable, they return 503 with error code projection_unavailable.

curl "https://api.threada.ai/api/v1/public/data/views/racing_catalog?limit=20" \
  -H "X-Api-Key: $THREADA_API_KEY"

Supported boundary routes:

RouteScopeRequired query
GET /api/v1/public/data/views/{view_key}catalog.readnone
GET /api/v1/public/data/entities/{entity_type}/{entity_id}catalog.readview_key
GET /api/v1/public/data/sources/{source_key}/healthsource.readview_key

data/views/{view_key} returns a cursor-paged view summary: up to limit complete groups with their events, evidence, aggregate health, per-source health, schema metadata, typed extension attributes, and advertised capabilities. Pass the returned opaque next_cursor unchanged to request the next page. Full entity payloads can be loaded through data/entities/{entity_type}/{entity_id}?view_key=... using generic entity types such as group, event, or participant; both legacy source IDs and canonical IDs resolve during migration. Event rows include start_label as a provider display label, UTC starts_at, source timezone, and localized_starts_at converted to the validated request timezone. Group attributes may include domain-neutral display metadata such as location, country, and meeting_class; integrations should use location/state values for timezone and filter logic, and keep class/type labels as display metadata. filter_key supports all, today, upcoming, live, and finished; date applies an exact local date. Both use the requested IANA timezone (UTC by default). Source health requests require the stable registered {source_key} and return 404 when the optional date is outside that source revision’s coverage. Clients should feature-detect optional data such as form, scratchings, calendar, and results through capabilities rather than assuming every provider supplies it.

racing_catalog is the canonical reusable racing projection. The older customer_feed key remains a compatibility alias and should not be used by new integrations. A view key identifies a stable projection contract, not a screen in a consuming application.

WorkItem Flow

Create a WorkItem with a subject, source channel, and initial message:

curl https://api.threada.ai/api/v1/public/work-items \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $THREADA_API_KEY" \
  -d '{
    "subject": "Password reset",
    "priority": "high",
    "channel": "email",
    "channel_id": "support-inbox",
    "tags": ["billing", "vip"],
    "contact": {
      "name": "Sam Customer",
      "email": "sam@example.com",
      "locale": "en-US"
    },
    "initial_message": {
      "role": "user",
      "content": "I need help resetting my password.",
      "external_message_id": "msg-123"
    }
  }'

initial_message.role may be omitted or set to user. When initial_message.channel is supplied, it must match the top-level channel. initial_message.external_message_id is forwarded to runtime automation as the WorkItem source reference.

List WorkItems with cursor pagination:

curl "https://api.threada.ai/api/v1/public/work-items?status=new&limit=50" \
  -H "X-Api-Key: $THREADA_API_KEY"

Use the returned next_page_token as page_token on the next request. Empty cursor responses are returned as null.

Append a follow-up message:

curl https://api.threada.ai/api/v1/public/work-items/work-item-123/messages \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $THREADA_API_KEY" \
  -d '{
    "message": {
      "role": "user",
      "content": "Adding the latest invoice number: INV-1001."
    }
  }'

Follow-up messages support content plus optional role: "user". Per-message agent identity, channel, and external message IDs are not accepted on append because the runtime comment contract does not persist those fields.

Accepted channel and status labels are defined by the generated OpenAPI WorkItemChannel and WorkItemStatus schemas. Use those schemas as the source of truth for live enum values; documented status aliases are on the list WorkItems query parameter in the same contract.

Actions And Proposals

Create governed action proposals through REST when an external system needs to ask Threada to perform a side effect under runtime policy:

curl https://api.threada.ai/api/v1/public/work-items/work-item-123/action-proposals \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $THREADA_API_KEY" \
  -d '{
    "integration_id": "integration-1",
    "approval_reason": "Customer requested a refund",
    "resource_type": "order",
    "action_operation": "shopify_refund",
    "payload": {
      "type": "shopify_refund",
      "order_id": "gid://shopify/Order/1001",
      "amount": "42.00",
      "currency": "USD",
      "reason": "requested_by_customer"
    }
  }'

The response includes the persisted proposal_id, runtime status, proposed action type, and the API-key identity stamped as proposed_by.

Execute an approved or auto-approved proposal by id:

curl https://api.threada.ai/api/v1/public/action-proposals/proposal-123/execute \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $THREADA_API_KEY" \
  -d '{
    "dry_run": false
  }'

The response includes the updated proposal and the persisted action record. Set "dry_run": true to have runtime validate the proposal and policy without performing side effects, creating an action record, or changing proposal status.

List action records after governed execution:

curl "https://api.threada.ai/api/v1/public/actions?work_item_id=work-item-123" \
  -H "X-Api-Key: $THREADA_API_KEY"

For agent-native flows, use one of the governed paths:

  • MCP tools/list discovers the exact tool catalog and JSON Schemas.
  • MCP tools/call supports create_work_item, get_work_item, propose_action, approve_action, execute_action, get_evidence, and export_packet.
  • A2A maps message/send, tasks/get, tasks/cancel, message/stream, and tasks/resubscribe onto WorkItem task lifecycle operations.

Execution is policy-gated. Approval requirements, dry-run support, and risk annotations are advertised by the agent card and MCP tool catalog. Real execution re-checks policy at execution time and uses idempotency keys inside the governed action path so retries do not double-execute.

Errors

REST errors use this envelope:

{
  "error": {
    "type": "validation_error",
    "message": "Validation error: subject is required",
    "code": "validation_error",
    "correlation_id": "",
    "retryable": false
  }
}

Common statuses:

StatusMeaning
400Invalid query, path, or JSON body. Unknown request fields are rejected.
401Missing or invalid API key.
403API key is valid but lacks the required scope, or direct action execution is disabled.
404WorkItem or action was not found.
429Public or agent rate limit exceeded.
503Required downstream service is unavailable.