Download all docs
foundation

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.

Ci
type

Circle

Unified identity container for users, organizations, and teams

foundationatomdefinition

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.
visibilitystring
entrypointsobject
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.
billingobject
membershipobject
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:00Z

members

Response

members: []
    count: 0

children

Response

children: []
    total: 0

transfer

Response

transferred: true
previous_owner: ''
new_owner: ''
transferred_at: 2026-01-01T00:00:00Z

Errors / 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

PropertyValue
Typecircle
Categoryfoundation
Formcompound
Iconlight_mode / #8B5CF6

Circle Types

TypeDescriptionAuthSub-circles
personalHuman identity (EUDI-verified)yesno
organizationalLegal entity or teamnoyes

Identity Tiers

TierRankDescription
unknown0Unverified (password-only)
wallet1Wallet-authenticated
silicon2Registered AI agent
carbon3Verified human (passport/ID/PID)
eu_inc4EU-incorporated legal entity

Properties

PropertyTypeDefaultDescription
circle_typeenumorganizationalpersonal or organizational
visibilityenumprivateprivate, internal, or public
identityobjectLegal identity (required in production)
bound_byuuidnullParent circle (null = sovereign)
billingobjectPlan: free, starter, professional, enterprise
limitsobjectmax_apps, max_members, max_storage_gb
messagingobjectAI 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

CodeClassRetryableDescription
CIRCLE_QUOTA_EXCEEDEDlimitnoResource quota exceeded
CIRCLE_MEMBER_LIMITlimitnoMax member limit reached
CIRCLE_VERIFICATION_REQUIREDauthyesIdentity 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

OperationMethodPathAuthDescription
verifyPOSTverifyownerStart EUDI identity verification
membersGETmembersreadList circle members
invitePOSTmembersadminInvite another circle as member
transferPOSTtransferownerTransfer ownership (irreversible)
delegatePOSTdelegatewriteDelegate task to element (async)
reconcilePOSTreconcilewriteSync elements from git to DB

Wallet Operations

All accessed via /ops/wallet/...:

OperationPathDescription
wallet_balancewallet/balanceAU balance (1 AU = €1)
wallet_secretswallet/secretsList secret names (no values)
wallet_secret_setwallet/secrets/{name}Store encrypted secret
wallet_topupwallet/topupAdd credits

Version Sets (MVCC)

OperationPathDescription
version_sets_createversion-sets/createSnapshot all HEAD refs
version_sets_mergeversion-sets/{id}/mergeApply overrides to main
version_sets_abandonversion-sets/{id}/abandonDiscard 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-circleGET /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

PropertyTypeDefaultDescription
circle_typestring"personal"Circle type:
- personal: Human identity (verified via password / passkey / EUDI wallet)
- organizational: (coming soon) Legal entity or subdivision
bound_bystringParent circle reference. Determines sovereignty:
- null: Sovereign circle (owns itself)
- : Sub-circle owned by parent
iconstringIcon identifier for display
intentionstringShort 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.
visibilitystring"private"
entrypointsobjectPublic-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.
identityobjectLegal identity - verified directly or inherited
billingobject
limitsobject
settingsobject
membershipobjectMembership policy — how external users join this circle
publish_defaultsobjectDefault visibility applied to new elements that don’t declare their own
visibility_labelsobjectPer-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_labelsobjectPer-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).
messagingobjectCircle-level messaging configuration
subconsciousobjectOptional expression personality layer — enable to show mood indicators for this circle’s user
preferencesobject
federationobjectA2A 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.
mirrorobjectExternal 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 total field 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_status to 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 status is refused the response carries the controller’s stated reason; when it’s escalated the 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_at timestamp 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. When status is partially_fulfilled the response carries a per-category breakdown of what was and wasn’t deleted; when escalated it 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_id to bond a known circle directly (existing collaborator path), or pass email to invite by email — the auth layer looks up the email across the platform and either bonds the existing personal circle or silently mints one (with no surfaced UI). Two relationship kinds: ‘collaborator’ (default) for circles that participate in editing this circle’s library — roles viewer / member / admin (ownership transfers use the transfer operation). ‘audience’ is for end-users of this circle’s published product — roles audience / audience-admin / audience-billing — and triggers the audience-portal render mode on the host subdomain. Email-based invitations issue a magic-link token via email; the recipient clicks through to /api/auth/accept-invite to claim membership. Invitation expires after 7 days. See .triform/design/audience-members-and-publish.md for 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 (with revoked_at populated) for audit, but dsar_dispatch and erasure_request reject any mandate_id whose revoked_at is 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_dispatch and erasure_request calls 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.frontend on 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’s entrypoints.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 view main.py for 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.frontend from 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, and intention are all independently optional. spec MUST 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 from update) 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 by update_element_meta storage 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

CodeClassRetryableDescription
CIRCLE_QUOTA_EXCEEDEDlimitnoCircle resource quota exceeded (max depth or max sub-circles reached)
CIRCLE_MEMBER_LIMITlimitnoMaximum member limit reached
CIRCLE_VERIFICATION_REQUIREDauthyesIdentity verification required
SUBCIRCLE_CYCLE_DETECTEDvalidationnoCannot create sub-circle: the target is already an ancestor of this circle (would form a cycle)
SUBCIRCLE_NOT_ALLOWEDvalidationnoThis circle type does not support sub-circles (only organizational circles can have sub-circles)
CIRCLE_DEPTH_EXCEEDEDlimitnoCannot nest sub-circle: the resulting hierarchy depth would exceed the parent’s limits.max_nesting_depth
CIRCLE_SUBCIRCLE_LIMITlimitnoCannot add sub-circle: the parent circle has reached its limits.max_subcircles cap
MIRROR_AUTH_CONNECTOR_INVALIDvalidationnoconnector_ref does not point at a valid github element in this circle’s library
MIRROR_CONNECTOR_TOKEN_MISSINGauthnogithub connector is missing GITHUB_PERSONAL_ACCESS_TOKEN — set env_refs on the connector before mirroring
MIRROR_REPO_NAME_TAKENvalidationnoA repo with this name already exists in the target account — pick a different repo_name or disconnect the existing one
MIRROR_PUSH_FAILEDinternalyesPush to GitHub failed — see last_error on the mirror property block for the underlying cause
MIRROR_NOT_CONNECTEDvalidationnoCannot 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