Circle
The root of everything in Triform. A circle is simultaneously an identity, a tenancy boundary, an isolated database schema, and a git repository — every other element you build lives inside exactly one.
Working with it
Opening a Circle drills into its contents on the canvas rather than opening a dedicated workbench.
How it appears
The same element type rendered as a definition, a circle instance, and a live workspace card.
When to use / not
When to use
- Onboarding a new user, team, or organization — each gets a sovereign namespace.
- Isolating a project, client, or environment so its data can never leak into another.
- Anywhere you need a dedicated database schema + git repo + wallet as one unit.
When not to use
- Grouping a few related elements for convenience — just create them in your existing circle; elements sit flat, no wrapper needed.
- A short-lived scratch experiment — circles are durable tenancy boundaries, not throwaway folders.
- Modelling a workflow or pipeline — that is what apps and automations are for; a circle is the tenant they run inside.
Topology
A structural container: it holds and organises other elements inside it on the canvas.
Properties
circle_typestring- Circle type: - personal: Human identity (verified via password / passkey / EUDI wallet) - organizational: (coming soon) Legal entity or subdivision
iconstring- Icon identifier for display
intentionstring- Short first-person intention — "what this circle is up to". Rendered inline in the floating circle header next to the circle name and avatar, and surfaces in circle-pickers that need a one-line summary. Edited from the circle props panel.
visibilitystringentrypointsobject- Public-facing entrypoints owned by the circle. A custom domain may resolve to the circle and then use `entrypoints.frontend` to select the app or frontend that represents the circle externally.
billingobjectmembershipobject- Membership policy — how external users join this circle
publish_defaultsobject- Default visibility applied to new elements that don't declare their own
visibility_labelsobject- Per-circle UI labels for visibility tokens. Backend tokens stay element-agnostic ('everyone', 'audience', 'collaborator'); UI labels are a per-circle render concern. The Share popover and toolbar visibility-as-chrome sentence consume these. Tokens not present in the map fall back to platform defaults. Disposition: §12.1 + §12.4.
relationship_labelsobject- Per-circle UI labels for access-relationship tokens — the SUBJECT axis of the capability graph (`.triform/design/access-capability-graph.md`), distinct from `visibility_labels` (the publishing/`visible_to` axis). Backend tokens stay element-agnostic ('collaborator', 'audience', 'agent', 'service', 'peer'); UI labels are a per-circle render concern. The Access surface consumes these to label grant rows and facet tabs. Tokens not present fall back to platform defaults. Adding a token here never requires a schema change (free-form, additionalProperties).
messagingobject- Circle-level messaging configuration
subconsciousobject- Optional expression personality layer — enable to show mood indicators for this circle's user
federationobject- A2A federation policy — which inbound peer scopes auto-accept, which queue for owner review, which require explicit consent. Owns org-level trust governance for the circle's agents.
Capabilities
Defined for this element
- Storage
Operations
- activityGET
- add_subcirclePOST
- agent_healthGET
- agent_session_deleteDELETE
- agent_session_getGET
- agent_sessions_listGET
- agent_signalsGET
- agent_ui_contextPOST
- apply_template_updatePOST
- approve_peer_trustPOST
- attachmentsGET
- batch_statsGET
- childrenGET
- circle-screencast-streamGET
- composePOST
- contextGET
- conversations_add_memberPOST
- conversations_approve_capabilitiesPOST
- conversations_archivePOST
- conversations_createPOST
- conversations_deletePOST
- conversations_delete_messagePOST
- conversations_distribute_group_keysPOST
- conversations_edit_messagePOST
- conversations_exchange_keysPOST
- conversations_getGET
- conversations_listGET
- conversations_list_messagesGET
- conversations_message_feedbackPOST
- conversations_post_messagePOST
- conversations_remove_memberDELETE
- conversations_renamePOST
- conversations_restorePOST
- createPOST
- delegatePOST
- delegate_cancelPOST
- delegation_getPOST
- deleteDELETE
- dev_events_emitPOST
- dev_events_listGET
- dev_presenceGET
- dev_watchers_listGET
- dev_watchers_startPOST
- dev_watchers_stopDELETE
- disablePOST
- dsar_dispatchPOST
- dsar_statusGET
- enablePOST
- erasure_requestPOST
- erasure_statusGET
- export_bundleGET
- export_databasePOST
- export_manifestGET
- friction_patternsGET
- getGET
- git_commitPOST
- git_create_branchPOST
- git_diffGET
- git_list_branchesGET
- git_logGET
- git_read_fileGET
- git_revertPOST
- import_bundlePOST
- import_circle_bundlePOST
- import_databasePOST
- import_manifestPOST
- import_templatePOST
- intentionGET
- invitePOST
- list_peer_trustsGET
- mandate_listGET
- mandate_revokePOST
- mandate_signPOST
- member_removeDELETE
- member_rolePATCH
- membersGET
- metric_statsGET
- mirror_auditPOST
- mirror_connectPOST
- mirror_disconnectPOST
- mirror_sync_nowPOST
- promotePOST
- publishPOST
- readmeGET
- readme_updatePOST
- reconcilePOST
- remove-modifierPOST
- reparentPOST
- request_peer_trustPOST
- restorePOST
- revoke_peer_trustPOST
- schemaGET
- sessions_agent_conversationGET
- sessions_createPOST
- sessions_listGET
- set_frontendPOST
- sourceGET
- source_branchesGET
- source_promotePOST
- source_repairPOST
- source_statusGET
- source_validatePOST
- statsGET
- template_lockPOST
- template_updatesGET
- transferPOST
- treeGET
- unset_frontendPOST
- updatePATCH
- update_metaPATCH
- usageGET
- verifyPOST
- versionGET
- version_sets_abandonPOST
- version_sets_createPOST
- version_sets_diffGET
- version_sets_getGET
- version_sets_listGET
- version_sets_mergePOST
- wallet_balanceGET
- wallet_eudiGET
- wallet_eudi_pidGET
- wallet_eudi_refreshPOST
- wallet_keysGET
- wallet_secret_deleteDELETE
- wallet_secret_getGET
- wallet_secret_setPUT
- wallet_secretsGET
- wallet_topupPOST
- wallet_transactionsGET
- ws-thumbnailsGET
Composition
Operation examples
verify
Response
session_id: 00000000-0000-0000-0000-000000000000
authorization_request: openid4vp://example
expires_at: 2026-01-01T00:00:00Zmembers
Response
members: []
count: 0children
Response
children: []
total: 0transfer
Response
transferred: true
previous_owner: ''
new_owner: ''
transferred_at: 2026-01-01T00:00:00ZErrors / when it fails
- Personal circles require identity verification
- This is a sovereign circle (no parent) - ensure this is intentional
- Inherited identity requires identity.inherited_from to be set
Validation rules
- Public circle without verified members - consider enabling for trust
- Audit logging is disabled - no compliance trail available
- Free plan with high storage limit - ensure this is within plan allowance
Circle (circle)
Category: foundation | Form: | Symbol: Ci
Unified identity container for users, organizations, and teams
The root identity container — represents a user, team, or organization. Each circle gets its own isolated database and git repository, so data never leaks between circles. Circles are created through authentication (POST /api/auth/dev in dev mode), not via element creation. Names must be 3-32 characters, lowercase letters/numbers/hyphens, starting with a letter. A circle automatically includes a wallet (for credentials and secrets) and a git repository. It can contain sub-circles and every top-level element type directly — elements sit flat under the circle, no intermediate library wrapper. Use “personal” type for individual users and “organizational” for teams or companies. Note: GET /api/{name} returns the circle itself, while GET /api/{name}/ (with trailing slash) lists its children.
Guide
Unified identity container for users, organizations, and teams
What It Does
A Circle is the root identity and isolation boundary in Triform. Every element lives inside a circle. Circles represent identity: a person, organization, or team. Each circle gets a dedicated PostgreSQL schema (circle_{uuid}) providing complete data isolation. When created, circles auto-scaffold one repository per element type and provision intrinsic wallet and git repository.
Element Definition
| Property | Value |
|---|---|
| Type | circle |
| Category | foundation |
| Form | compound |
| Icon | light_mode / #8B5CF6 |
Circle Types
| Type | Description | Auth | Sub-circles |
|---|---|---|---|
| personal | Human identity (EUDI-verified) | yes | no |
| organizational | Legal entity or team | no | yes |
Identity Tiers
| Tier | Rank | Description |
|---|---|---|
| unknown | 0 | Unverified (password-only) |
| wallet | 1 | Wallet-authenticated |
| silicon | 2 | Registered AI agent |
| carbon | 3 | Verified human (passport/ID/PID) |
| eu_inc | 4 | EU-incorporated legal entity |
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| circle_type | enum | organizational | personal or organizational |
| visibility | enum | private | private, internal, or public |
| identity | object | — | Legal identity (required in production) |
| bound_by | uuid | null | Parent circle (null = sovereign) |
| billing | object | — | Plan: free, starter, professional, enterprise |
| limits | object | — | max_apps, max_members, max_storage_gb |
| messaging | object | — | AI mode, routing, moderation settings |
Topology
- Contains: circle, repository
- Accepts modifiers: api-token, auth-policy, rate-limit
- Intrinsics: wallet (credentials, secrets, billing), git repository
Error Codes
| Code | Class | Retryable | Description |
|---|---|---|---|
| CIRCLE_QUOTA_EXCEEDED | limit | no | Resource quota exceeded |
| CIRCLE_MEMBER_LIMIT | limit | no | Max member limit reached |
| CIRCLE_VERIFICATION_REQUIRED | auth | yes | Identity verification required |
Quick Start
Creating a Circle (Dev Mode)
Circles are created via auth endpoints, not standard element creation:
curl -s -X POST http://localhost:3000/api/auth/dev \
-H "Content-Type: application/json" \
-d '{"circle_name": "my-circle"}' \
-c /tmp/cookies.txt
Getting the Circle
# Get circle itself
curl -s -b /tmp/cookies.txt http://localhost:3000/api/my-circle
# List children (different response shape!)
curl -s -b /tmp/cookies.txt http://localhost:3000/api/my-circle/
Circle Name Rules
- 3-32 characters, lowercase
- Allowed:
a-z,0-9,-(hyphens only, no underscores) - Must start with a lowercase letter
- No consecutive
--, no leading/trailing- - Silicon format:
silicon-{alphanumeric}(special case)
Structure
When a circle is created, the platform auto-scaffolds repositories:
my-circle/
├── actions/python/ (repository)
├── agents/triformer/ (repository)
├── data/sql/ (repository)
├── frontend/spa/ (repository)
├── io/http/ (repository)
├── modifiers/rate-limit/ (repository)
├── my-app (app — references library elements)
└── sub-team (sub-circle)
Operations
Key Circle-Specific Operations
| Operation | Method | Path | Auth | Description |
|---|---|---|---|---|
| verify | POST | verify | owner | Start EUDI identity verification |
| members | GET | members | read | List circle members |
| invite | POST | members | admin | Invite another circle as member |
| transfer | POST | transfer | owner | Transfer ownership (irreversible) |
| delegate | POST | delegate | write | Delegate task to element (async) |
| reconcile | POST | reconcile | write | Sync elements from git to DB |
Wallet Operations
All accessed via /ops/wallet/...:
| Operation | Path | Description |
|---|---|---|
| wallet_balance | wallet/balance | AU balance (1 AU = €1) |
| wallet_secrets | wallet/secrets | List secret names (no values) |
| wallet_secret_set | wallet/secrets/{name} | Store encrypted secret |
| wallet_topup | wallet/topup | Add credits |
Version Sets (MVCC)
| Operation | Path | Description |
|---|---|---|
| version_sets_create | version-sets/create | Snapshot all HEAD refs |
| version_sets_merge | version-sets/{id}/merge | Apply overrides to main |
| version_sets_abandon | version-sets/{id}/abandon | Discard version set |
Common Patterns
Modifiers
Circle-level modifiers (api-token, auth-policy, rate-limit) cascade to all children:
# Create and attach rate-limit modifier
curl -s -b /tmp/cookies.txt -X POST http://localhost:3000/api/my-circle/ \
-H "Content-Type: application/json" \
-d '{"element_type": "rate-limit", "slug": "api-limit", "name": "API Limit", "spec": {"requests_per_minute": 100}}'
curl -s -b /tmp/cookies.txt -X POST \
http://localhost:3000/api/my-circle/api-limit/ops/attach \
-d '{"target_id": "<circle-uuid>"}'
Common Mistakes
- Underscores in names: Use hyphens (
my-circle), not underscores (my_circle) - Creating via POST /api/: Circles are created via auth endpoints, not element creation
- Trailing slash confusion:
GET /api/my-circle≠GET /api/my-circle/(different response shapes) - Missing identity in production: Dev auth creates circles without identity; some ops may fail in production
Relationships
- Contains: circle, app, python, javascript, ruby, rust-fn, go-fn, csharp, hitl, triformer, claude-code, codex, open-code, external-agent, automation, lab, diagram, sql, document, vector, graph, timeseries, contacts, organizations, entity, schema, files, cookie-jar, view, spa, ssr, three-d, slack, teams, discord, email, matrix, mattermost, rocketchat, http, queue, websocket, schedule, platform-trigger, phone-number, linkedin, twitter, facebook, instagram, github, fortnox, platform, chromeless, user-browser, recorder, board, planning-board, sales-board, recruitment-board
- Attaches to: api-token, auth-policy, oauth, oauth-client, rate-limit, brand
Capabilities
- root-namespace: Root-level namespace with dedicated database schema
- containment: Can contain child elements (compound form)
- activity-scope-all: Activity queries scope to all children
- identity: Verified human identity via EUDI
- namespace: Universal namespace for resources
- hierarchy: Nested circles for organization
- wallet: Intrinsic wallet for credentials/billing
- repository: Git repository for version control
- membership: Role-based access control
Properties
| Property | Type | Default | Description |
|---|---|---|---|
circle_type | string | "personal" | Circle type: - personal: Human identity (verified via password / passkey / EUDI wallet) - organizational: (coming soon) Legal entity or subdivision |
bound_by | string | — | Parent circle reference. Determines sovereignty: - null: Sovereign circle (owns itself) - |
icon | string | — | Icon identifier for display |
intention | string | — | Short first-person intention — “what this circle is up to”. Rendered inline in the floating circle header next to the circle name and avatar, and surfaces in circle-pickers that need a one-line summary. Edited from the circle props panel. |
visibility | string | "private" | |
entrypoints | object | — | Public-facing entrypoints owned by the circle. A custom domain may resolve to the circle and then use entrypoints.frontend to select the app or frontend that represents the circle externally. |
identity | object | — | Legal identity - verified directly or inherited |
billing | object | — | |
limits | object | — | |
settings | object | — | |
membership | object | — | Membership policy — how external users join this circle |
publish_defaults | object | — | Default visibility applied to new elements that don’t declare their own |
visibility_labels | object | — | Per-circle UI labels for visibility tokens. Backend tokens stay element-agnostic (‘everyone’, ‘audience’, ‘collaborator’); UI labels are a per-circle render concern. The Share popover and toolbar visibility-as-chrome sentence consume these. Tokens not present in the map fall back to platform defaults. Disposition: §12.1 + §12.4. |
relationship_labels | object | — | Per-circle UI labels for access-relationship tokens — the SUBJECT axis of the capability graph (.triform/design/access-capability-graph.md), distinct from visibility_labels (the publishing/visible_to axis). Backend tokens stay element-agnostic (‘collaborator’, ‘audience’, ‘agent’, ‘service’, ‘peer’); UI labels are a per-circle render concern. The Access surface consumes these to label grant rows and facet tabs. Tokens not present fall back to platform defaults. Adding a token here never requires a schema change (free-form, additionalProperties). |
messaging | object | — | Circle-level messaging configuration |
subconscious | object | — | Optional expression personality layer — enable to show mood indicators for this circle’s user |
preferences | object | — | |
federation | object | — | A2A federation policy — which inbound peer scopes auto-accept, which queue for owner review, which require explicit consent. Owns org-level trust governance for the circle’s agents. |
mirror | object | — | External git mirror of the circle’s element tree. Internal CAS stays canonical; mirror is a clone-able reflection. |
Operations
activity
Get /ops/activity | Auth: Read
Get activity events for this element
Scope depends on element capabilities: individual elements query by element_id, project-form elements with activity-scope-members include member activities, circle-level elements with activity-scope-all query the entire circle. Gracefully returns empty list if activities table is missing (old circles).
add_subcircle
Post /ops/add-subcircle | Auth: Admin
Create a sub-circle nested inside this circle
Creates a new organizational sub-circle nested under this circle. Requires the parent circle to be of type ‘organizational’ (personal circles cannot have sub-circles). The child gets its own isolated Postgres schema, git repository, and encryption key. A reference-tile element (element_type=circle-ref) is written into the parent’s elements table so the child appears on the parent’s canvas. The caller is made owner of the new child circle via a circle_memberships row. Encryption mode: ‘standard’ wraps the child key under the parent’s CDEK (parent can access child data); ‘independent’ creates a sovereign child key (fully isolated). Returns CIRCLE_QUOTA_EXCEEDED if depth/width limits are reached. Cycle detection is enforced — attempting to make an ancestor a child is rejected with SUBCIRCLE_CYCLE_DETECTED.
agent_health
Get /ops/agent/health | Auth: Read
Agent health summary with success rates and friction patterns
agent_session_delete
Delete /ops/agent/sessions/{session_id} | Auth: Write
Delete an agent session
agent_session_get
Get /ops/agent/sessions/{session_id} | Auth: Read
Get session messages
agent_sessions_list
Get /ops/agent/sessions | Auth: Read
List agent sessions
agent_signals
Get /ops/agent/signals | Auth: Read
List agent tool-level signals extracted from executions
agent_ui_context
Post /ops/agent/ui-context | Auth: Write
Request current UI context from the user’s browser
Requests a screenshot or DOM snapshot from the user’s currently connected browser session. Used by agents to understand what the user is seeing. Returns the UI context asynchronously via WebSocket. Only works when a user has an active browser session connected to this circle.
apply_template_update
Post /ops/apply-template-update | Auth: Write
Apply (or dry-run preview) the latest template version, preserving local edits
Merge-preserve apply: per element matched by (slug, element_type), template keys win, local-only keys survive, display_name/intention are preserved, and elements present only locally are never deleted. dry_run=true (query param) returns the per-element diff with no writes. to_version defaults to the latest published version. On success, stamps circles.template_version = to_version. Returns 409 if the circle is pinned (unlock via template-lock first), 404 if the circle has no template_slug or to_version doesn’t exist.
approve_peer_trust
Post /ops/federation/peer_trusts/approve | Auth: Owner
Approve an inbound peer trust (mirrors request side)
Creates the inbound counterpart of a peer’s outbound request: “their agents may publish to me.” May narrow scopes (intersection of requested and approved is what ends up granted). target_agent_slug optionally narrows the inbound surface to one agent in this circle. A working A→B channel requires BOTH this and the peer’s outbound request_peer_trust on their side. Only the circle owner can approve.
attachments
Get /ops/attachments | Auth: Read
List all modifiers and resources attached to this element
Returns both modifiers (policy enforcement) and resources (data injection) with is_modifier flag to distinguish. Items in the generated MODIFIER_TYPES list are modifiers; everything else is a resource. Includes cascade_policy and version pin info.
batch_stats
Get /ops/batch_stats | Auth: Read
Get per-element statistics for all children of this element
Returns per-child stats plus an aggregate. Most meaningful on compound or manifest form elements (repositories, circles, projects); atoms have no children so the result is an empty children array with a zeroed aggregate. Uses efficient GROUP BY SQL. Weighted averages for eval scores.
children
Get /ops/children | Auth: Read
List compound children (elements) in this circle
Returns a paginated list of all elements contained in this circle. Filter by element type with ?type= (e.g. ?type=python lists only Python functions). Use ?type=circle-ref to list only sub-circle reference tiles. The
totalfield reflects the total matching elements, not just the page size.
circle-screencast-stream
Get /ops/circle-screencast-stream | Auth: Read
Multi-element circle JPEG SSE. Fans frames from N browser elements (chosen via ?elements=uuid1,uuid2,…) into one SSE stream so the multi-browser overview paints every visible tile from a single connection.
compose
Post /ops/compose | Auth: Execute
Batch add and remove modifiers on this element in a single call
Declarative composition: add modifiers by ref path (slug or path@version) and remove by attachment ID, all in one atomic call on the target element. Each ‘add’ entry resolves the source element, validates topology, attaches with optional priority and cascade policy. Each ‘remove’ entry deletes the attachment row. Returns a summary of what was added and removed. Example: compose({ add: [{ref: “my-prompt”}, {ref: “rate-limit/api@v2”, priority: 50}], remove: [{attachment_id: “uuid”}] })
context
Get /ops/context | Auth: Read
Get connected elements (graph traversal)
Graph traversal showing all connected elements with their relationship type (contains, contained_by, references, referenced_by, attaches, etc.). Use ?depth=N to control traversal depth (default 1) and ?types=actor,data to filter by element types.
conversations_add_member
Post /ops/conversations/{conversation_id}/members | Auth: Write
Add a member to a conversation
conversations_approve_capabilities
Post /ops/conversations/{conversation_id}/approve-capabilities | Auth: Write
Approve elevated capabilities (shell, delegation) for a conversation
conversations_archive
Post /ops/conversations/{conversation_id}/archive | Auth: Write
Archive a conversation
conversations_create
Post /ops/conversations | Auth: Write
Create a new conversation
conversations_delete
Post /ops/conversations/{conversation_id}/delete | Auth: Write
Soft-delete a conversation (24h grace window before hard-delete)
conversations_delete_message
Post /ops/conversations/{conversation_id}/messages/{message_id}/delete | Auth: Write
Soft-delete a message in a conversation
conversations_distribute_group_keys
Post /ops/conversations/{conversation_id}/group-keys | Auth: Write
Distribute group encryption keys
conversations_edit_message
Post /ops/conversations/{conversation_id}/messages/{message_id}/edit | Auth: Write
Edit a message in a conversation
conversations_exchange_keys
Post /ops/conversations/{conversation_id}/keys | Auth: Write
Exchange encryption keys for E2EE
conversations_get
Get /ops/conversations/{conversation_id} | Auth: Read
Get a specific conversation
conversations_list
Get /ops/conversations | Auth: Read
List conversations in this circle
conversations_list_messages
Get /ops/conversations/{conversation_id}/messages | Auth: Read
List messages in a conversation
conversations_message_feedback
Post /ops/conversations/{conversation_id}/messages/{message_id}/feedback | Auth: Write
Submit feedback (helpful/not helpful) on a message
conversations_post_message
Post /ops/conversations/{conversation_id}/messages | Auth: Write
Post a message to a conversation
conversations_remove_member
Delete /ops/conversations/{conversation_id}/members/{member_type}/{member_id} | Auth: Write
Remove a member from a conversation
conversations_rename
Post /ops/conversations/{conversation_id}/rename | Auth: Write
Rename a conversation
conversations_restore
Post /ops/conversations/{conversation_id}/restore | Auth: Write
Restore a soft-deleted conversation if within the 24h grace window
create
Post /ops/create | Auth: Write
Create child element
POST to the parent path — element_type goes in the request body, NOT the URL. Both element_type and slug are required and must be non-empty. Name is derived from slug if omitted. Writes to both Git and PostgreSQL. All elements are stored flat under the circle — no intermediate library wrapper rows.
delegate
Post /ops/delegate | Auth: Write
Delegate a task to another element (agent, function). Returns immediately with a delegation_id.
Circle-scoped task delegation — not tied to a specific agent. Returns immediately with delegation_id for async polling via delegation_get. Target element is identified by target_slug (must exist in this circle). Use parent_session_id and parent_element_slug to chain delegations for multi-agent coordination. A2A-aligned: once a delegation reaches a terminal state (completed, failed, cancelled) it is immutable.
delegate_cancel
Post /ops/delegation/cancel | Auth: Write
Cancel a running delegation (A2A-aligned: terminal states immutable)
Cancels a running delegation. Idempotent — cancelling an already-cancelled delegation returns success. Cannot cancel completed or failed delegations (terminal states are immutable per A2A protocol). Provide optional reason for audit trail.
delegation_get
Post /ops/delegation/get | Auth: Read
Check the status of a delegated task
delete
Delete /ops/delete | Auth: Admin
Delete element (soft delete)
Soft delete — sets state to ‘deleted’ but retains the record. Cannot delete elements that have children (has_no_bond precondition) or active runs. Requires admin auth and confirmation.
dev_events_emit
Post /ops/dev/events | Auth: Write
Emit a dev event
dev_events_list
Get /ops/dev/events | Auth: Read
List dev events for this circle
dev_presence
Get /ops/dev/presence | Auth: Read
Get developer presence information
dev_watchers_list
Get /ops/dev/watchers | Auth: Read
List active file watchers
dev_watchers_start
Post /ops/dev/watchers | Auth: Write
Start a new file watcher
dev_watchers_stop
Delete /ops/dev/watchers/{watcher_id} | Auth: Write
Stop a file watcher
disable
Post /ops/disable | Auth: Admin
Disable element (hides and prevents use)
Idempotent — safe to call on already-disabled elements. Optionally pass a reason string. Disabled elements cannot be invoked or executed. Inverse of enable.
dsar_dispatch
Post /ops/dsar/dispatch | Auth: Write
Dispatch a GDPR Art. 15 access request to one controller under a signed mandate
Sends the access request to the controller’s registered contact channel (DPO email / reception-desk address from the entity’s child rows) carrying the mandate_id and the wallet signature as proof. Tracks a 30-day deadline (Art. 12(3)) and stores the resulting dispatch_id for status polling. Rejects with 400 if the mandate_id is missing, expired, or revoked. A single controller_id per dispatch — loop the call for a multi-controller campaign and use
dsar_statusto track each one.
dsar_status
Get /ops/dsar/status | Auth: Read
Check the status of a previously-dispatched DSAR
Returns the current state of a single dispatch plus any response payload the controller has supplied. When
statusisrefusedthe response carries the controller’s stated reason; when it’sescalatedthe response carries the supervisory-authority reference (DPA complaint id) the system has opened. The 30-day Art. 12(3) deadline is always present so callers can decide whether to escalate without waiting on a webhook.
enable
Post /ops/enable | Auth: Admin
Enable element (makes usable and visible)
Idempotent — safe to call on already-enabled elements. Transitions element to ready/enabled state. Cannot enable deleted elements. Inverse of disable.
erasure_request
Post /ops/erasure/request | Auth: Write
Submit a GDPR Art. 17 erasure request to one controller under a signed mandate
Sends the right-to-erasure request to the controller carrying the mandate_id and signature as proof. Tracks a 30-day Art. 12(3) deadline plus a 90-day verification window (re-DSAR after 90 days to confirm the controller actually deleted the data). Stores a request_id for status polling. Rejects with 400 if the mandate_id is missing, expired, revoked, or was issued without the Art. 17 scope.
erasure_status
Get /ops/erasure/status | Auth: Read
Check the status of a previously-submitted erasure request
Returns the current state of a single erasure request. The
verification_attimestamp is always present and is the moment the system will automatically issue a follow-up Art. 15 DSAR to confirm the controller deleted the data. Whenstatusispartially_fulfilledthe response carries a per-category breakdown of what was and wasn’t deleted; whenescalatedit carries the DPA complaint reference.
export_bundle
Get /ops/export/bundle | Auth: Read
Download circle as git export JSON with embedded per-element bundles
export_database
Post /ops/export/database | Auth: Admin
Export the full circle database payload, including platform rows and the per-circle schema
Full database export is not a setup clone. It includes runtime data from the circle schema plus related platform rows, and is intended for backup, migration, and forensic handoff workflows. Use recursive Source clone for setup-only cloning of all element definitions.
export_manifest
Get /ops/export | Auth: Read
Get circle export manifest with element inventory
friction_patterns
Get /ops/agent/friction | Auth: Read
Recurring tool failure patterns (3+ occurrences)
get
Get /ops/get | Auth: Read
Get element details
Element is already resolved by the routing layer — this returns the cached element, not a fresh DB query. Use the path /api/{circle}/{slug} to address elements.
git_commit
Post /ops/git/commit | Auth: Write
Create a git commit
git_create_branch
Post /ops/git/branches | Auth: Write
Create a new git branch
git_diff
Get /ops/git/diff | Auth: Read
Get git diff
git_list_branches
Get /ops/git/branches | Auth: Read
List git branches
git_log
Get /ops/git/log | Auth: Read
Get git log history
git_read_file
Get /ops/git/file | Auth: Read
Read a file from the git repository
git_revert
Post /ops/git/revert | Auth: Write
Revert a git commit
import_bundle
Post /ops/import/bundle | Auth: Write
Import circle from uploaded git bundle
import_circle_bundle
Post /ops/import/circle | Auth: Write
Import circle export JSON (flat model — elements import directly under circle)
import_database
Post /ops/import/database | Auth: Admin
Restore a full circle database from an export payload (write-mode supported when both dry_run=false and confirm_write_mode=true)
Full database import supports two modes. Dry-run (default) validates the payload shape and reports schema compatibility without writing anything. Write-mode performs per-table UPSERT restores with source UUIDs preserved; it requires both dry_run=false AND confirm_write_mode=true to prevent accidental data overwrites. Each table is imported in its own transaction; one table failing does not abort others.
import_manifest
Post /ops/import | Auth: Write
Import/reconcile circle from git state
import_template
Post /ops/import/template | Auth: Write
Import a named element template into this circle
Imports a pre-built element template (e.g., agent-team) into this circle. Creates all template elements that don’t already exist, skips existing ones. Default template is “agent-team”. Idempotent — re-running skips already-created elements.
intention
Get /ops/intention | Auth: Read
Get element intention with full inheritance chain
Returns three levels: direct (this element’s intention), inherited (from category and root), and resolved (final merged intention). Useful for understanding an element’s purpose in context of its hierarchy.
invite
Post /ops/invite | Auth: Admin
Invite a circle (or email) as member
Two addressing modes: pass
circle_idto bond a known circle directly (existing collaborator path), or pass/api/auth/accept-inviteto claim membership. Invitation expires after 7 days. See.triform/design/audience-members-and-publish.mdfor the full design.
list_peer_trusts
Get /ops/federation/peer_trusts | Auth: Read
List active peer trusts for this circle
Returns active (un-revoked, un-expired) trust grants with this circle as
home_circle_id. Filter by direction with ?direction=inbound|outbound. Used by the consent-flow UI to render “trusts I have granted/accepted.”
mandate_list
Get /ops/mandate/list | Auth: Read
List wallet-held data-subject-rights mandates
Returns all mandates issued by this circle’s wallet, newest-first. Each entry carries the mandate_id, scope list, target controller count, issuance timestamp, expiry, and revocation status. Use this to audit outstanding DSAR/erasure rights before signing a new one and to confirm a revocation has propagated.
mandate_revoke
Post /ops/mandate/revoke | Auth: Write
Revoke a previously-signed mandate — the wallet marks the mandate_id inactive
Records a revocation timestamp on the named mandate. Revoked mandates remain in
mandate_list(withrevoked_atpopulated) for audit, butdsar_dispatchanderasure_requestreject any mandate_id whoserevoked_atis set. Idempotent — revoking an already-revoked mandate preserves the original revocation time and returns the existing record.
mandate_sign
Post /ops/mandate/sign | Auth: Write
Sign a wallet-held data-subject-rights mandate over a controller inventory (eIDAS 2.0 Art. 25)
Records a wallet-signed mandate — the qualified electronic signature over the canonical JSON form of (subject_did, scopes, controller_ids, issued_at, expires_at). For v1 the signature is produced by the circle’s wallet via the standard Ed25519 signing primitive; integrating with a live EUDI PID issuer is a follow-up that gates real signing behind a future admin op. The mandate_id returned is the on-disk reference; downstream
dsar_dispatchanderasure_requestcalls accept it as proof of consent.
member_remove
Delete /ops/members/{member_id} | Auth: Admin
Remove a member from the circle
member_role
Patch /ops/members/{member_id}/role | Auth: Admin
Update member role
members
Get /ops/members | Auth: Read
List circle members
Returns paginated list of circle members with roles (viewer, member, admin, owner). Filter by role with ?role=admin. Members are other circles invited into this circle — not individual users. Each member has a verified flag indicating EUDI identity status.
metric_stats
Get /ops/metric_stats | Auth: Read
Get timeseries metric data for the circle
mirror_audit
Post /ops/mirror/audit | Auth: Owner
Dry-run preview of what would be pushed to the mirror
Walks the CAS as if syncing but does not actually push. Returns counts and a sample of excluded items so the connect-wizard’s Step 5 can show users what won’t sync (sensitive elements + redacted fields). Safe to run without an active mirror — uses the supplied connector_ref to determine visibility (and therefore which exclusion rules apply).
mirror_connect
Post /ops/mirror/connect | Auth: Owner
Connect this circle to an external git mirror
Provisions a remote repo using the connector_ref’s auth and writes the resulting state to the mirror property block. Auto-creates the repo (no bring-your-own-repo in v1). For v1, public-only — the tier gate forcing freemium → public is enforced runtime-side. Idempotent: re-running with a matching repo_name is a no-op; with a different repo_name fails with MIRROR_REPO_NAME_TAKEN. Returns the populated mirror block on success.
mirror_disconnect
Post /ops/mirror/disconnect | Auth: Owner
Disconnect this circle from its external git mirror
Clears the mirror property block on the circle. The remote repo itself is NOT deleted — user data stays where they put it. Webhook registration on the remote is removed (in v2). Sync stops immediately. Idempotent: a no-op when mirror.enabled is already false.
mirror_sync_now
Post /ops/mirror/sync-now | Auth: Owner
Push current circle state to the external mirror
Walks the CAS commit range (mirror.last_pushed_sha → HEAD), filters via is_excluded_for_public_sync + excluded_fields_for, translates to GitHub Git Data API calls (blobs → trees → commits → ref update), and updates mirror.last_pushed_sha + mirror.last_synced_at on success. v1 is manual via this op; v2 wires it into the git.commit.created event for auto-push. Returns the new state of the mirror block.
promote
Post /ops/promote | Auth: Admin
Promote element configuration to a target environment
Only for manifest-form elements (projects). Environments advance: dev → demo → live. dev→demo requires member+ role, demo→live requires admin. Freezes member versions at promotion time (creates snapshot). Persists environment config to spec.environments.
publish
Post /ops/publish | Auth: Admin
Publish the circle’s current state as a new versioned template
Snapshots the circle’s elements into a manifest and inserts a published_circles catalog row. template_slug defaults to the circle’s name; version defaults to an auto-incremented minor of the latest published version (first publish is 1.0.0). Best-effort git-tags the captured state (circles/{slug}/v{n}). Stamps the publishing circle’s own template_slug/template_version so the canonical source never self-nags. Returns 409 INVALID_INPUT if (template_slug, version) already exists.
readme
Get /ops/readme | Auth: Read
Get element README.md content
Reads README.md from the element’s git repository. Returns empty content (not an error) if no README exists. Always returns markdown format.
readme_update
Post /ops/readme_update | Auth: Write
Update element README.md content
Creates or overwrites README.md in the element’s git repo. Commits to the draft branch. Content must be provided as a markdown string.
reconcile
Post /ops/reconcile | Auth: Write
Reconcile circle elements from git, surfacing validation warnings
Reads element.yaml files from the circle’s git repository and syncs them to the database. Creates missing elements, updates existing ones. Returns imported/skipped counts and validation warnings. Use after manual git changes or to recover from DB/git drift. Idempotent — safe to run multiple times.
remove-modifier
Post /ops/remove-modifier | Auth: Execute
Remove an attached modifier from this element by attachment ID
Removes a modifier/resource attachment by its row ID. The ID comes from the attachments or context API. This is the reverse of attach — called on the target element, not the source.
reparent
Post /ops/reparent | Auth: Owner
Move this circle under a different parent (or to root)
Changes the circle’s parent in the hierarchy. Pass new_parent_id to move under a different circle, or pass null to detach to root (make this a sovereign circle). Enforcement rules: - Cycle detection: the new_parent_id must not be this circle or any of its descendants - Depth check: the resulting depth must not exceed limits.max_nesting_depth on the new parent - Type check: personal circles cannot be reparented under other circles (only organizational circles may be nested)
- Orphan path: null new_parent_id sets bound_by=null and makes this circle sovereign. Useful for recovering orphaned sub-circles after parent deletion. Requires confirm:true to prevent accidental reparenting. Only the circle owner may call this. Returns SUBCIRCLE_CYCLE_DETECTED if the move would form a cycle, CIRCLE_DEPTH_EXCEEDED if it would exceed the depth limit, and SUBCIRCLE_NOT_ALLOWED if the circle type is personal.
request_peer_trust
Post /ops/federation/peer_trusts/request | Auth: Owner
Request a peer-circle trust grant (outbound)
Creates an outbound trust grant: “my agents may publish to peer.” Identifies the peer by slug (the circle’s
name). Scopes come from the v0 vocabulary — publish:direct-message, publish:broadcast, subscribe:inbox, read:profile, request:reply, dlq:read. One outbound row per scope. expires_in_days defaults to 90. target_agent_slug optionally narrows the grant to one agent in this circle; omit for “any agent.” A working channel additionally requires the peer’s owner to call approve_peer_trust on their side. Only the circle owner can request.
restore
Post /ops/restore | Auth: Admin
Restore element to a specific version
Automatically snapshots the current state before restoring (creates a ‘Before restore to vN’ version entry). Writes restored spec to git as .triform/spec.yaml. Git failures warn but don’t fail the operation — DB state is authoritative. Cannot restore deleted elements.
revoke_peer_trust
Post /ops/federation/peer_trusts/revoke | Auth: Owner
Revoke a peer trust (either direction)
Sets revoked_at on a single trust row. Either side can revoke unilaterally. Idempotent — repeated calls return the same revoked row. Once revoked, the row is no longer matched by the active-trust lookups during cross-circle send/receive, so traffic stops at the next message (modulo JWKS cache TTL on the issuer side). Only the circle owner can revoke.
schema
Get /ops/schema | Auth: Read
Get element input/output schema (MCP tools/list compatible)
Returns type-level port schemas from the TypeRegistry — not instance-specific overrides. Includes direction (input/output), required flag, and JSON schema per port. Useful for understanding what data an element accepts and produces.
sessions_agent_conversation
Get /ops/sessions/{session_id}/agents/{agent_id}/conversation | Auth: Read
Get the agent’s conversation within a session
sessions_create
Post /ops/sessions | Auth: Write
Create a new session
sessions_list
Get /ops/sessions | Auth: Read
List sessions for the authenticated user
set_frontend
Post /ops/set-frontend | Auth: Admin
Set the circle’s product-facing app or frontend
Configures
spec.entrypoints.frontendon the circle. Custom domains that target the circle resolve through this entrypoint. The target may be an app or a concrete frontend element; apps continue to choose their own SPA through the app’sentrypoints.frontend.element.
source
Get /ops/source | Auth: Read
Get any file’s content from the element’s git repository
Reads an arbitrary file from the element’s CAS-backed git tree by its relative path. Same store as
readme, just generalized. Path safety: rejects..traversal, leading/, and null bytes. Use this to viewmain.pyfor action elements, asset files for SPAs, etc. Returns empty content (not an error) if the file doesn’t exist.
source_branches
Get /ops/source/branches | Auth: Read
List Source branches for this element
Returns the standard draft/demo/live Source branches, their current commits, and promotion relationships. Use GET /api/{element_path}/ops/source/branches.
source_promote
Post /ops/source/promote | Auth: Write
Promote Source branch forward
Promotes draft to demo or demo to live through the generated element op path. Direct Git pushes to demo/live are blocked by Source policy.
source_repair
Post /ops/source/repair | Auth: Write
Inspect or repair the element Source index
Runs Source repair through the element operation path. Defaults to dry_run=true; set dry_run=false only after reviewing a dry-run report.
source_status
Get /ops/source/status | Auth: Read
Get Source control status for this element
Returns the branch-aware clone URL, checkout commands, current draft commit, child source-link count, portable export summary, Source health, warnings, and auth hints for the addressed element. Use the element-first path: GET /api/{element_path}/ops/source/status.
source_validate
Post /ops/source/validate | Auth: Read
Validate Source branch contents
Validates a Source branch before accepting local Git workflow changes or promotion. Defaults to branch=draft and rejects runtime data, generated output, secret material, and unreadable CAS refs.
stats
Get /ops/stats | Auth: Read
Get aggregate statistics for this element
Health status is computed: error if errors_per_day > 5 or success_rate < 0.8, warning if errors_per_day > 0 or success_rate < 0.95. Firing alerts escalate health to error/warning. Default period is ‘day’. Returns runs_per_day, success_rate, avg_duration_ms, and more.
template_lock
Post /ops/template-lock | Auth: Write
Pin or unpin the circle at its current template version
Sets circles.locked_to_template_version. When locked, the circle is excluded from the update nag (template-updates returns update_available:false, locked:true) and apply refuses with 409. The circle-grain analog of the agent drawer’s per-element ‘Lock at current version’ toggle.
template_updates
Get /ops/template-updates | Auth: Read
Detect whether a newer template version is available for this circle
Pure read — never mutates, claims, or auto-applies. Reads the circle’s template lineage (template_slug/template_version) and compares against the latest published_circles row for that slug. Returns update_available plus a cheap element_summary {added, changed, unchanged, local_only}. A standalone circle (no template_slug) returns update_available:false. A pinned circle returns update_available:false but locked:true so the drawer shows the pin state. Idempotent and safe to poll (the portal badge calls it freely).
transfer
Post /ops/transfer | Auth: Owner
Transfer circle ownership
Irreversible ownership transfer. Requires confirm:true to prevent accidental transfers. The new_owner_id must be a circle that is already a member. After transfer, your role downgrades from owner to admin. Only the current owner can initiate.
tree
Get /ops/tree | Auth: Read
Get the element’s position in the graph — ancestors, children, references, and subtree statistics
Uses per-circle ElementGraph cache for O(1) lookups. Returns ancestors (containment chain), children (direct), members (references), referenced_by (reverse refs), attachments, and subtree stats. Default depth is 3, max is 10. Pass ?include_metadata=true for name/state on each node.
unset_frontend
Post /ops/unset-frontend | Auth: Admin
Clear the circle’s product-facing app/frontend entrypoint
Removes
spec.entrypoints.frontendfrom the circle. Custom domains that target the circle will stop resolving to a face app until a new frontend is configured.
update
Patch /ops/update | Auth: Write
Update element
Partial update — send only the fields you want to change.
spec,name, andintentionare all independently optional.specMUST be a JSON object when present; deep-merged into the existing spec by default. Empty{"spec":{}}preserves existing spec content but still records a new version (no-op for content, not for version state). To clear/replace the entire spec wholesale send{"spec":{...},"deep":false}. List-typed spec fields use replace semantics (the patch list replaces the existing list, no array merging). Coordinates Git + DB writes. Slug cannot be changed after creation.
update_meta
Patch /ops/update_meta | Auth: Write
Update element metadata (lightweight merge — does NOT bump version or snapshot spec)
Shallow JSONB merge into element.meta. Top-level keys in the provided value replace existing meta values; other keys are preserved. Used for UI metadata like canvas positions, panel state, viewer preferences. Wire-shape op_name is
update_meta(distinct fromupdate) so SSE subscribers + the cache auto-invalidator can distinguish lightweight metadata changes from spec edits without inspecting the payload. The MutatingElementStore wrapper stamps this op_name on the lifecycle event emitted byupdate_element_metastorage calls.
usage
Get /ops/usage | Auth: Admin
Get platform usage events for billing and capacity
verify
Post /ops/verify | Auth: Owner
Initiate EUDI identity verification
Starts an EUDI wallet verification flow. Returns a session_id and authorization_request URI that the user’s wallet app must scan. Session expires after the returned expires_at. Only the circle owner can initiate verification. Successful verification upgrades identity_level to carbon (human) or eu_inc (legal entity).
version
Get /ops/version | Auth: Read
Get current version or full history
Returns current version by default. Pass ?history=true for full version history (up to ?limit=N, default 50). Versions are backed by the element_versions table. Every spec update creates a new version entry.
version_sets_abandon
Post /ops/version-sets/{id}/abandon | Auth: Write
Abandon a version set
version_sets_create
Post /ops/version-sets/create | Auth: Write
Create a version set — snapshots all current HEAD refs for isolation
Creates an MVCC snapshot of all element HEAD refs in this circle. Changes within the version set are isolated from main until merged. Use for safe experimentation — like a git branch but for element state. Requires a unique name. Merge or abandon when done.
version_sets_diff
Get /ops/version-sets/{id}/diff | Auth: Read
Show changes in version set vs main
version_sets_get
Get /ops/version-sets/{id} | Auth: Read
Get a version set by ID
version_sets_list
Get /ops/version-sets | Auth: Read
List version sets for this circle
version_sets_merge
Post /ops/version-sets/{id}/merge | Auth: Write
Merge version set overrides to main refs
Applies all version set overrides to main element refs. Use force:true to overwrite conflicting changes made to main since the snapshot. Without force, conflicts cause merge failure. After merge the version set is consumed and cannot be reused.
wallet_balance
Get /ops/wallet/balance | Auth: Read
Get current balance
Returns the current wallet balance as a euro amount (and internal accounting credits). All operations draw from this balance — check it before expensive operations. It is debited automatically on element invocations and tool usage.
wallet_eudi
Get /ops/wallet/eudi | Auth: Read
Get EUDI credentials summary
wallet_eudi_pid
Get /ops/wallet/eudi/pid | Auth: Read
Get Person Identification Data
wallet_eudi_refresh
Post /ops/wallet/eudi/refresh | Auth: Admin
Request credential refresh from EUDI wallet
wallet_keys
Get /ops/wallet/keys | Auth: Read
List cryptographic keys (public parts only)
wallet_secret_delete
Delete /ops/wallet/secrets/{name} | Auth: Admin
Delete a secret
wallet_secret_get
Get /ops/wallet/secrets/{name} | Auth: Admin
Get secret value
wallet_secret_set
Put /ops/wallet/secrets/{name} | Auth: Admin
Set or update a secret
wallet_secrets
Get /ops/wallet/secrets | Auth: Read
List secrets (names only, no values)
Lists secret names and timestamps only — never returns values. To read a secret value, use wallet_secret_get (requires admin auth). Secrets are encrypted at rest with the circle’s CDEK (Circle Data Encryption Key). Use secrets for API keys, connection strings, and other sensitive configuration that elements need at runtime.
wallet_topup
Post /ops/wallet/topup | Auth: Admin
Add credits to wallet
wallet_transactions
Get /ops/wallet/transactions | Auth: Read
List billing transactions
ws-thumbnails
Get /ops/ws/thumbnails | Auth: Read
Circle-scoped thumbnail WS — per-tab thumbnails for the multi-browser overview grid.
Error Codes
| Code | Class | Retryable | Description |
|---|---|---|---|
CIRCLE_QUOTA_EXCEEDED | limit | no | Circle resource quota exceeded (max depth or max sub-circles reached) |
CIRCLE_MEMBER_LIMIT | limit | no | Maximum member limit reached |
CIRCLE_VERIFICATION_REQUIRED | auth | yes | Identity verification required |
SUBCIRCLE_CYCLE_DETECTED | validation | no | Cannot create sub-circle: the target is already an ancestor of this circle (would form a cycle) |
SUBCIRCLE_NOT_ALLOWED | validation | no | This circle type does not support sub-circles (only organizational circles can have sub-circles) |
CIRCLE_DEPTH_EXCEEDED | limit | no | Cannot nest sub-circle: the resulting hierarchy depth would exceed the parent’s limits.max_nesting_depth |
CIRCLE_SUBCIRCLE_LIMIT | limit | no | Cannot add sub-circle: the parent circle has reached its limits.max_subcircles cap |
MIRROR_AUTH_CONNECTOR_INVALID | validation | no | connector_ref does not point at a valid github element in this circle’s library |
MIRROR_CONNECTOR_TOKEN_MISSING | auth | no | github connector is missing GITHUB_PERSONAL_ACCESS_TOKEN — set env_refs on the connector before mirroring |
MIRROR_REPO_NAME_TAKEN | validation | no | A repo with this name already exists in the target account — pick a different repo_name or disconnect the existing one |
MIRROR_PUSH_FAILED | internal | yes | Push to GitHub failed — see last_error on the mirror property block for the underlying cause |
MIRROR_NOT_CONNECTED | validation | no | Cannot sync — circle is not connected to an external mirror. Run mirror_connect first. |
Lifecycle / runtime
Defined for this element
On member add
- validate_permissions
- update_quotas
On member remove
- cleanup_resources
Execution model: async
Observability
Defined for this element
Metrics
- member_count
- app_count
- element_count
- storage_bytes
- active_sessions
- commit_count
Events
- circle.created
- circle.updated
- circle.deleted