Live Neon API Reference

AI agent identity platform. Define, observe, and evolve structured identities for AI agents — beliefs, responsibilities, and system prompts that stay consistent across any LLM or framework.

Base URL: https://persona.liveneon.ai/api/v1

OpenAPI Spec: /openapi.json

Base URL Note: Both /api/v1/ and /api/ work interchangeably. The /api/v1/ prefix is the canonical form used in documentation, but /api/ routes to the same handlers via Next.js rewrites. Use whichever you prefer — they are functionally identical.

API Status: The v1 API is under active development. We aim to avoid breaking changes within v1 but may introduce them with notice. Monitor the GitHub repository for changes.


Authentication

API Keys

Create an org-scoped API key in your organization's settings page, then pass it as a Bearer token. API keys are prefixed with ln_ and grant full access to the organization's data.

Authorization: Bearer ln_<your-api-key>
# Store your API key (shown once when created — keep it safe)
export TOKEN="ln_..."

# Make API calls
curl -H "Authorization: Bearer $TOKEN" https://persona.liveneon.ai/api/v1/agents

API keys are scoped to a single organization. The org is automatically inferred from the key, so you don't need to pass orgId or orgSlug on every request (though you can if you prefer to be explicit).

Note: The web dashboard uses cookie-based session auth internally. Session auth is not documented here — use API keys for all programmatic access.

Error (401):

{ "error": "Unauthorized" }

Organization Resolution

All endpoints that require an organization accept three ways to identify it (in priority order):

  1. orgId (UUID) — the organization's database ID
  2. orgSlug (string) — the organization's URL slug (e.g., geeks-in-the-woods)
  3. API key inference — when using API key auth, the org is automatically inferred from the key if neither orgId nor orgSlug is provided

For query-parameter endpoints (GET), pass orgSlug=my-org instead of orgId=uuid. For request-body endpoints (POST), pass "orgSlug": "my-org" instead of "orgId": "uuid". For path-based endpoints (/api/organizations/{orgId}/...), the path segment accepts either a UUID or a slug.

# All three are equivalent when using an API key scoped to "my-org":
curl -H "Authorization: Bearer $TOKEN" https://persona.liveneon.ai/api/agents?orgId=abc-123
curl -H "Authorization: Bearer $TOKEN" https://persona.liveneon.ai/api/agents?orgSlug=my-org
curl -H "Authorization: Bearer $TOKEN" https://persona.liveneon.ai/api/agents

Rate Limiting

The API enforces per-key sliding window rate limits. Limits are designed to be generous for AI agent workflows while preventing abuse.

TierLimitApplies To
General200 req/minMost endpoints
Heavy30 req/minSync, PBD processing
Bulk10 req/minBulk create/update endpoints
Auth20 req/minAPI key management, sensitive operations

Rate limit key is derived from the API key prefix (first 12 chars of ln_...) or the client IP address for session auth.

Response headers (included on every response):

When rate limited (429):

{ "error": "Too many requests. Please retry after 45 seconds." }

The Retry-After header (in seconds) tells you exactly how long to wait.


Registration & Account Management

Register

Create a new account, organization, and API key in one call. Zero fields required — email is optional (for account recovery later). This is the recommended path for AI agents.

POST /api/v1/register

No authentication required. Rate limited: 5 requests/min per IP.

Request body (all fields optional):

{
  "email": "[email protected]",
  "password": "optional-password",
  "org_name": "My Org",
  "org_slug": "my-org",
  "display_name": "My Agent"
}

Response (201):

{
  "your_token": "ln_...",
  "key": "ln_...",
  "organization": {
    "id": "uuid",
    "name": "My Org",
    "slug": "my-org"
  },
  "user_id": "uuid",
  "email": "[email protected]",
  "warning": null,
  "next_steps": [...]
}

The returned your_token is your API key — use it immediately as Authorization: Bearer ln_.... It is shown once.

# Minimal registration — zero PII required
curl -X POST https://persona.liveneon.ai/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{}'

# With optional fields
curl -X POST https://persona.liveneon.ai/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "org_name": "Acme",
    "org_slug": "acme",
    "display_name": "Docs Agent"
  }'

Account

View or update the current account.

GET /api/v1/account

Auth: Bearer token or session.

Response (200):

{
  "user_id": "uuid",
  "email": "[email protected]",
  "display_name": "My Agent",
  "is_agent": true,
  "has_email": true,
  "has_password": true,
  "created_at": "2026-03-23T..."
}
PATCH /api/v1/account

Auth: Bearer token or session. Rate limited: 10 requests/min per key.

Request body (all fields optional):

{
  "email": "[email protected]",
  "password": "new-password",
  "display_name": "Updated Name"
}

Response (200):

{
  "user_id": "uuid",
  "email": "[email protected]",
  "display_name": "Updated Name",
  "has_email": true,
  "updated": true,
  "next_steps": [...]
}

Request Key Normalization

All POST and PATCH endpoints accept request body keys in either camelCase or snake_case. The API normalizes keys internally via normalizeRequestKeys(), so agentId and agent_id are interchangeable. Response bodies always use snake_case (matching the database column names).


Core Concepts

Organizations

Multi-tenant isolation. Every resource belongs to an org_id. Users are members of organizations via org_memberships. URLs use slug-based routing (/[orgSlug]/...).

Agents

Virtual talent with structured identities. Each agent has a name, job title, description, optional group membership, and a generated system prompt built from their soul and responsibilities.

Soul (Beliefs)

An agent's identity — what they believe, how they speak, what they won't do. Beliefs belong to one of 5 categories:

Responsibilities

What an agent is accountable for. 5 categories:

Hierarchical Personas

Three-layer identity: Organization (Brand)Group (Team)Agent (Individual). Org beliefs cascade to all agents in the org. Group beliefs cascade to group members. Agents can override (hide) inherited beliefs without deleting them. When resolving an agent's full identity, all three layers are merged and deduplicated — if an agent has the same (category, statement) as an org or group belief, the agent version wins.

System Prompt

Generated markdown combining org + group + agent beliefs/responsibilities + agent profile into a single prompt. Cached on the agent record. Automatically regenerated when beliefs, responsibilities, name, description, job title, location, or timezone change.

Groups

Organizational groupings for agents (e.g., "Marketing Team"). Groups can have a manager agent (must be from a different group).

Environments & Models

Environments represent deployment contexts (e.g., "production", "staging"). Models are LLM configurations (provider + model ID). Agents are assigned models per environment.

Pattern-Based Discovery (PBD)

The platform's core evolution mechanism. PBD is a three-stage AI pipeline that observes agent outputs and discovers identity patterns — both beliefs and responsibilities:

  1. Observe: Import agent content (GitHub commits, conversations, etc.) via content sources. Each piece of content becomes a content_item. Connect a GitHub repo, sync it, and the agent's commits become observable content.
  2. Extract: POST /api/v1/pbd/process triggers analysis. The pipeline reads unprocessed content items, uses Claude to extract behavioral observations (recurring patterns, stylistic preferences, decision-making tendencies), and clusters similar observations into signals with n-counts and stability scores. Returns a jobId for progress tracking via the Background Jobs endpoints.
  3. Promote: Signals that cross the agent's promotion_threshold (configurable per agent) are eligible to become beliefs or responsibilities. Promoted items include full provenance — the source observations, quotes, and signal that generated them — stored in the belief_sources table.

Trigger PBD manually via POST /api/v1/pbd/process, or it runs automatically via the hourly cron sync when new content is imported. Observations and signals are read-only via the API — they are generated exclusively by the PBD pipeline.

Key endpoints:

Bootstrap

Import agent definitions from open-source repos. The pipeline scrapes repos, decomposes agent prompts into structured beliefs/responsibilities via Claude, stores them with vector embeddings, and recommends them to agents via similarity search.


Agents

List Agents

GET /api/agents

Query Parameters:

ParamTypeRequiredDescription
orgIdstring (uuid)no*Organization ID
orgSlugstringno*Organization slug (alternative to orgId)
groupIdstring (uuid)noFilter by group
includestringnoComma-separated joins: beliefs, responsibilities (returns identity data inline)
limitnumbernoMax results (default: 100)
offsetnumbernoSkip N results (default: 0)

*One of orgId or orgSlug is required unless using API key auth (org is inferred from key).

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "name": "Content Director",
      "job_title": "Content Director",
      "description": "...",
      "group_id": "uuid",
      "agent_groups": { "name": "Marketing Team" },
      "created_at": "2026-03-12T..."
    }
  ],
  "count": 10,
  "next_steps": [...]
}

Example:

curl https://persona.liveneon.ai/api/agents?orgId=your-org-id \
  -H "Authorization: Bearer $TOKEN"

# Filter by group
curl 'https://persona.liveneon.ai/api/agents?orgId=your-org-id&groupId=group-id' \
  -H "Authorization: Bearer $TOKEN"

Errors: 400 (missing orgId), 500 (database error)


Get Agent

GET /api/agents/{agentId}

Returns the full agent profile including beliefs, responsibilities, and group information.

Response (200):

{
  "id": "uuid",
  "org_id": "uuid",
  "name": "Content Director",
  "job_title": "Content Director",
  "description": "...",
  "group_id": "uuid",
  "system_prompt": "...",
  "agent_groups": { "id": "uuid", "name": "Marketing Team" },
  "beliefs": [
    { "id": "uuid", "category": "principle", "statement": "...", "starred": false, "hidden": false }
  ],
  "responsibilities": [
    { "id": "uuid", "category": "ownership", "statement": "...", "starred": false, "hidden": false }
  ]
}

Example:

curl https://persona.liveneon.ai/api/agents/{agentId} \
  -H "Authorization: Bearer $TOKEN"

Errors: 404 (not found), 500 (database error)


Create Agent

POST /api/agents

Request Body:

FieldTypeRequiredDescription
orgIdstring (uuid)yesOrganization ID
namestringyesAgent name
descriptionstringnoAgent description
groupIdstring (uuid)noGroup to assign agent to
identityMarkersstring[]noIdentity marker tags
jobTitlestringnoJob title
avatarPromptstringnoPrompt for avatar generation (triggers background generation)

Response (201):

{
  "id": "uuid",
  "org_id": "uuid",
  "name": "Content Director",
  "description": "...",
  "group_id": "uuid",
  "job_title": "Content Director",
  "identity_markers": ["creative", "strategic"],
  "system_prompt": null,
  "avatar_url": null,
  "location": null,
  "timezone": null,
  "created_at": "2026-03-12T...",
  "updated_at": "2026-03-12T..."
}

Example:

curl -X POST https://persona.liveneon.ai/api/agents \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orgId": "your-org-id", "name": "Content Director", "jobTitle": "Content Director"}'

Errors: 400 (missing orgId or name), 500 (database error)


Update Agent

PATCH /api/agents/{agentId}

Request Body (all optional):

FieldTypeDescription
namestringAgent name
descriptionstringAgent description
groupIdstring (uuid)Group ID
identityMarkersstring[]Identity markers
minNCountnumberMinimum observation count
decayRatenumberSignal decay rate
jobTitlestringJob title
avatarPromptstringAvatar generation prompt
locationstringPhysical location
timezonestringIANA timezone

Response (200): Updated agent object.

Side effects: Regenerates system prompt if location, timezone, or jobTitle changed. Triggers avatar generation if avatarPrompt provided.

Example:

curl -X PATCH https://persona.liveneon.ai/api/agents/{agentId} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"location": "San Francisco, CA", "timezone": "America/Los_Angeles"}'

Errors: 404 (not found), 500 (database error)


Delete Agent

DELETE /api/agents/{agentId}

Response (200):

{ "success": true }

Bulk Update Agents

PATCH /api/agents/bulk

Update up to 50 agents at once.

Request Body:

FieldTypeRequiredDescription
agentsarrayyesArray of agent updates (max 50)

Each agent object:

FieldTypeRequiredDescription
idstring (uuid)yesAgent ID
namestringnoAgent name
locationstringnoPhysical location
timezonestringnoIANA timezone
jobTitlestringnoJob title

Response (200):

{
  "updated": 5,
  "agents": [{ "id": "uuid", "name": "...", "updated_at": "..." }]
}

Side effects: Regenerates system prompt for any agent whose name, jobTitle, location, or timezone changed.

Errors: 400 (empty array, exceeds 50 agents, missing id on any entry)


Get Agent Diff

GET /api/agents/{agentId}/diff

Returns beliefs and responsibilities added or modified since a given date.

Query Parameters:

ParamTypeRequiredDescription
sincestring (ISO 8601)yesDate to diff from

Response (200):

{
  "since": "2026-03-15T00:00:00Z",
  "beliefs": {
    "added": [{ "id": "uuid", "category": "principle", "statement": "...", "created_at": "..." }],
    "modified": [{ "id": "uuid", "category": "voice", "statement": "...", "updated_at": "..." }]
  },
  "responsibilities": {
    "added": [],
    "modified": []
  }
}

Errors: 400 (missing since), 404 (agent not found)


Get Agent Models

GET /api/agents/{agentId}/models

Query Parameters:

ParamTypeRequiredDescription
tierstringnoFilter by tier (e.g., planner, worker, reviewer)
limitnumbernoMax results (default: 100)
offsetnumbernoPagination offset (default: 0)

Response (200): Array of agent_environment_models with expanded environment and model objects.

[
  {
    "id": "uuid",
    "agent_id": "uuid",
    "environment_id": "uuid",
    "model_id": "uuid",
    "is_primary": true,
    "tier": "worker",
    "environments": { "id": "uuid", "name": "Production", "slug": "production" },
    "models": { "id": "uuid", "provider": "anthropic", "model_id": "claude-sonnet-4-20250514", "name": "Claude Sonnet" }
  }
]

Example:

# Get all model assignments
curl 'https://persona.liveneon.ai/api/agents/{agentId}/models' -H "Authorization: Bearer $TOKEN"

# Get only the worker model
curl 'https://persona.liveneon.ai/api/agents/{agentId}/models?tier=worker' -H "Authorization: Bearer $TOKEN"

Add Agent Model

POST /api/agents/{agentId}/models

Request Body:

FieldTypeRequiredDescription
environmentIdstring (uuid)yesEnvironment ID
modelIdstring (uuid)yesModel ID
isPrimarybooleannoPrimary model flag (default: true)
tierstringnoFreeform tier name (e.g., planner, worker, reviewer). NULL = simple single-model assignment

Response (201): Created agent_environment_models record with tier.

Uniqueness: One model per (agent, environment, tier). Duplicate tier in same environment returns 409.

Example:

# Assign planner model
curl -X POST 'https://persona.liveneon.ai/api/agents/{agentId}/models' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"environmentId": "env-uuid", "modelId": "llama-uuid", "tier": "planner"}'

# Assign worker model (different model, same environment)
curl -X POST 'https://persona.liveneon.ai/api/agents/{agentId}/models' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"environmentId": "env-uuid", "modelId": "sonnet-uuid", "tier": "worker"}'

Errors: 400 (missing fields), 409 (duplicate tier in environment)


Remove Agent Model

DELETE /api/agents/{agentId}/models?environmentId=...&modelId=...&tier=...

Query Parameters:

ParamTypeRequiredDescription
environmentIdstring (uuid)yesEnvironment ID
modelIdstring (uuid)yesModel ID
tierstringnoTier to delete (omit for NULL tier assignments)

Response (200): { "success": true }


Regenerate System Prompt

POST /api/agents/{agentId}/regenerate-prompt

Forces regeneration of the agent's system prompt from current beliefs and responsibilities.

Example:

curl -X POST https://persona.liveneon.ai/api/agents/{agentId}/regenerate-prompt \
  -H "Authorization: Bearer $TOKEN"

Response (200):

{ "systemPrompt": "# Agent Name\n\nYou are..." }

Errors: 404 (agent not found)


Get Agent Files

GET /api/agents/{agentId}/files

Returns agent data as downloadable markdown files.

Query Parameters:

ParamTypeDefaultDescription
typestring"agent"File type: agent, soul, responsibilities, identity
downloadbooleanfalseSet Content-Disposition for download

Response (200): Content-Type: text/markdown — rendered markdown content.

Errors: 400 (invalid type), 404 (agent not found)


List Snapshots

GET /api/agents/{agentId}/snapshots

Returns all genome snapshots for the agent, ordered by most recent first. Does not include the full genome JSONB.

Response (200):

{
  "snapshots": [
    {
      "id": "uuid",
      "agent_id": "uuid",
      "label": "before bootstrap",
      "belief_count": 8,
      "responsibility_count": 5,
      "created_at": "2026-03-16T20:00:00Z",
      "created_by": "uuid"
    }
  ]
}

Create Snapshot

POST /api/agents/{agentId}/snapshots

Captures the agent's current beliefs and responsibilities as a timestamped JSON genome snapshot.

Request Body:

FieldTypeRequiredDescription
labelstringnoOptional label (e.g., "before bootstrap", "post-PBD round 3")

Response (201):

{
  "snapshot": { "id": "uuid", "label": "...", "belief_count": 8, "responsibility_count": 5, "created_at": "..." },
  "next_steps": [...]
}

Errors: 404 (agent not found)


Get Snapshot

GET /api/agents/{agentId}/snapshots/{snapshotId}

Returns a single snapshot including the full genome JSONB.

Response (200):

{
  "snapshot": {
    "id": "uuid",
    "label": "...",
    "genome": {
      "beliefs": [{ "category": "axiom", "statement": "...", "starred": true, "hidden": false, "status": "approved" }],
      "responsibilities": [{ "category": "ownership", "statement": "...", "starred": false, "hidden": false, "status": "approved" }]
    },
    "belief_count": 8,
    "responsibility_count": 5,
    "created_at": "..."
  }
}

Errors: 404 (snapshot not found)


Diff Snapshots

GET /api/agents/{agentId}/snapshots/diff?a={snapshotId}&b={snapshotId}

Computes the diff between two snapshots. Returns items added (in B but not A) and removed (in A but not B) for both beliefs and responsibilities.

Query Parameters:

ParamTypeRequiredDescription
astring (uuid)yesSnapshot A ID (base)
bstring (uuid)yesSnapshot B ID (comparison)

Response (200):

{
  "snapshot_a": { "id": "...", "label": "...", "created_at": "..." },
  "snapshot_b": { "id": "...", "label": "...", "created_at": "..." },
  "diff": {
    "beliefs": {
      "added": [{ "category": "axiom", "statement": "...", "starred": true }],
      "removed": [{ "category": "boundary", "statement": "..." }]
    },
    "responsibilities": {
      "added": [],
      "removed": []
    }
  }
}

Errors: 400 (missing query params), 404 (snapshot not found)


Detect Responsibility Gaps

POST /api/agents/{agentId}/responsibility-gaps

Analyzes which responsibility categories are empty or thin (0-1 non-rejected items), then uses Claude to generate suggestions for each gap category.

Response (200):

{
  "gaps": [
    {
      "category": "monitoring",
      "label": "Monitoring",
      "current_count": 0,
      "suggestions": [
        { "statement": "Track content performance metrics weekly", "confidence": "high" },
        { "statement": "Monitor brand sentiment across social channels", "confidence": "medium" }
      ]
    }
  ]
}

Errors: 404 (agent not found), 500 (Claude API error)


Generate Dynamic Prompt

POST /api/agents/{agentId}/dynamic-prompt

Generates a fresh system prompt with per-invocation randomization to combat persona numbing. Samples from belief and responsibility pools using per-category strategies (starred items always included first, non-starred shuffled and sampled). Always works regardless of the agent's enabled flag — this is a preview/testing endpoint.

Request Body (all optional):

FieldTypeDescription
seednumberDeterministic seed for reproducible output
configobjectOverride dynamic prompt config for this call

Response (200):

{
  "prompt": "# Agent Name\n\n**Role:** ...\n\n## Core Identity ...",
  "manifest": {
    "beliefs_included": ["belief-uuid-1", "belief-uuid-2"],
    "beliefs_sampled_out": ["belief-uuid-3"],
    "responsibilities_included": ["resp-uuid-1"],
    "responsibilities_sampled_out": [],
    "emphasis": "Writes with dry wit and technical precision",
    "seed": 1234567
  },
  "config": { "enabled": false, "emphasis_rate": 0.4, "min_pool_size": 5, "category_overrides": {} },
  "next_steps": [...]
}

Notes:

Errors: 401 (unauthorized), 404 (agent not found)


Get Resolved Identity

GET /api/agents/{agentId}/resolved-identity

Returns the fully merged identity for an agent, combining org-level, group-level, and agent-level beliefs and responsibilities. Deduplicated (agent version wins if same category + statement exists at multiple layers). Each item includes source attribution indicating where it came from.

Response (200):

{
  "beliefs": [
    {
      "id": "uuid",
      "category": "principle",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "source": "org"
    },
    {
      "id": "uuid",
      "category": "voice",
      "statement": "...",
      "starred": true,
      "hidden": false,
      "source": "agent"
    }
  ],
  "responsibilities": [
    {
      "id": "uuid",
      "category": "ownership",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "source": "group"
    }
  ],
  "summary": {
    "total_beliefs": 12,
    "total_responsibilities": 8,
    "by_source": { "org": 3, "group": 4, "agent": 13 }
  }
}

Errors: 404 (agent not found)


List Agent Overrides

GET /api/agents/{agentId}/overrides

Returns all overrides for this agent. Overrides let an agent hide inherited (org or group) beliefs/responsibilities without deleting them.

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "agent_id": "uuid",
      "source_type": "org_belief",
      "source_id": "uuid",
      "created_at": "2026-03-15T..."
    }
  ]
}

Create Agent Override

POST /api/agents/{agentId}/overrides

Hides an inherited belief or responsibility for this agent.

Request Body:

FieldTypeRequiredDescription
sourceTypestringyesOne of: org_belief, group_belief, org_responsibility, group_responsibility
sourceIdstring (uuid)yesID of the inherited item to hide

Response (201): Created override object.

Side effects: Regenerates agent system prompt.

Errors: 400 (missing fields, invalid sourceType), 409 (override already exists)


Remove Agent Override

DELETE /api/agents/{agentId}/overrides/{overrideId}

Removes an override, causing the agent to re-inherit the previously hidden item.

Response (200): { "success": true }

Side effects: Regenerates agent system prompt.

Errors: 404 (override not found)


Beliefs

List Beliefs

GET /api/beliefs

Query Parameters:

ParamTypeRequiredDescription
agentIdstring (uuid)no*Filter by agent
groupIdstring (uuid)no*List all agent beliefs in a group (includes agent name attribution)
categorystringnoFilter by category
statusstringnoFilter by status
starredbooleannoFilter by starred
hiddenbooleannoFilter by hidden
limitnumbernoMax results
offsetnumbernoSkip N results

*One of agentId or groupId is required. When using groupId, beliefs from all agents in the group are returned with agent name attribution.

Response (200):

{ "data": [...], "count": 15 }

Create Belief

POST /api/beliefs

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID
categorystringyesOne of: axiom, principle, voice, preference, boundary
statementstringyesThe belief statement
sourceTypestringnoSource type for attribution
quotestringnoSource quote
nCountnumbernoObservation count (default: 1). Use for migrations with existing provenance data

Response (201): Created belief object.

Side effects: Creates belief_sources record if sourceType provided. Regenerates agent system prompt. Statements are normalized before storage (curly quotes → straight, em-dashes → hyphens, whitespace collapsed) to prevent Unicode near-duplicates.

Example:

curl -X POST https://persona.liveneon.ai/api/beliefs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "agent-uuid", "category": "principle", "statement": "Always prioritize clarity over cleverness"}'

Errors: 400 (missing fields), 409 (duplicate — same agent + category + statement already exists after normalization), 500 (database error)


Update Belief

PATCH /api/beliefs/{beliefId}

Scope-aware: Automatically detects whether the belief is agent-level, group-level, or org-level. Returns _scope field in response ("agent", "group", or "org").

Request Body (all optional):

FieldTypeDescription
statementstringBelief statement
categorystringBelief category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated belief object with _scope field.

Side effects:


Delete Belief

DELETE /api/beliefs/{beliefId}

Scope-aware: Automatically detects whether the belief is agent-level, group-level, or org-level. PBD-discovered beliefs are hidden instead of deleted to preserve discovery history.

Response (200):

Errors: 404 if not found in any scope.

Side effects: Regenerates agent system prompt.


Toggle Belief Star

POST /api/beliefs/{beliefId}/star

Toggles the starred flag. Starred beliefs are emphasized in the system prompt.

Response (200): Updated belief with toggled starred value.


Toggle Belief Hide

POST /api/beliefs/{beliefId}/hide

Toggles the hidden flag. Hidden beliefs are excluded from the system prompt.

Response (200): Updated belief with toggled hidden value.

Side effects: Regenerates agent system prompt.


Bulk Create Beliefs

POST /api/beliefs/bulk

Create up to 100 beliefs at once.

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID
beliefsarrayyesArray of beliefs (max 100)

Each belief: { category, statement }

Response (201):

{
  "created": 8,
  "beliefs": [{ "id": "uuid", "category": "principle", "statement": "..." }]
}

Side effects: Single prompt regeneration after all beliefs created.

Errors: 400 (empty array, exceeds 100, missing agentId), 409 (duplicates skipped)


Bulk Update Beliefs

PATCH /api/beliefs/bulk

Bulk star, hide, or approve beliefs. Scope-aware: works across agent beliefs, group beliefs, AND org beliefs in a single call.

Request Body:

FieldTypeRequiredDescription
idsstring[] (uuid)yesArray of belief IDs (from any scope)
updatesobjectyesFields to update

Updates object:

FieldTypeDescription
starredbooleanSet starred flag
hiddenbooleanSet hidden flag
statusstringSet status (pending, approved, rejected)

Response (200):

{ "updated": 5, "agents_affected": 3, "groups_affected": 1, "orgs_affected": 0 }

Side effects: Regenerates prompt for all affected agents.


Responsibilities

Create Responsibility

POST /api/responsibilities

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID
categorystringyesOne of: ownership, execution, collaboration, deliverables, monitoring
statementstringyesThe responsibility statement
nCountnumbernoObservation count (default: 1). Use for migrations with existing provenance data

Response (201): Created responsibility object.

Side effects: Regenerates agent system prompt. Statements are normalized before storage (curly quotes → straight, em-dashes → hyphens, whitespace collapsed) to prevent Unicode near-duplicates.

Example:

curl -X POST https://persona.liveneon.ai/api/responsibilities \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "agent-uuid", "category": "ownership", "statement": "Own the content calendar and publishing pipeline"}'

Errors: 400 (missing fields), 409 (duplicate — same agent + category + statement already exists after normalization), 500 (database error)


Update Responsibility

PATCH /api/responsibilities/{responsibilityId}

Scope-aware: Automatically detects whether the responsibility is agent-level, group-level, or org-level. Returns _scope field in response ("agent", "group", or "org").

Request Body (all optional):

FieldTypeDescription
statementstringResponsibility statement
categorystringResponsibility category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated responsibility object with _scope field.

Side effects: Same approval/rejection logic as beliefs. Regenerates prompt if status changed.


Delete Responsibility

DELETE /api/responsibilities/{responsibilityId}

Scope-aware: Automatically detects whether the responsibility is agent-level, group-level, or org-level. PBD-discovered responsibilities are hidden instead of deleted to preserve discovery history.

Response (200):

Errors: 404 if not found in any scope.

Side effects: Regenerates agent system prompt.


Toggle Responsibility Star

POST /api/responsibilities/{responsibilityId}/star

Response (200): Updated responsibility with toggled starred value.


Toggle Responsibility Hide

POST /api/responsibilities/{responsibilityId}/hide

Response (200): Updated responsibility with toggled hidden value.

Side effects: Regenerates agent system prompt.


Bulk Create Responsibilities

POST /api/responsibilities/bulk

Create up to 100 responsibilities at once.

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID
responsibilitiesarrayyesArray of responsibilities (max 100)

Each responsibility: { category, statement }

Response (201):

{
  "created": 5,
  "responsibilities": [{ "id": "uuid", "category": "ownership", "statement": "..." }]
}

Side effects: Single prompt regeneration after all responsibilities created.

Errors: 400 (empty array, exceeds 100, missing agentId), 409 (duplicates skipped)


Bulk Update Responsibilities

PATCH /api/responsibilities/bulk

Bulk star, hide, or approve responsibilities. Scope-aware: works across agent responsibilities, group responsibilities, AND org responsibilities in a single call.

Request Body:

FieldTypeRequiredDescription
idsstring[] (uuid)yesArray of responsibility IDs (from any scope)
updatesobjectyesFields to update

Updates object:

FieldTypeDescription
starredbooleanSet starred flag
hiddenbooleanSet hidden flag
statusstringSet status (pending, approved, rejected)

Response (200):

{ "updated": 3, "agents_affected": 2, "groups_affected": 1, "orgs_affected": 0 }

Side effects: Regenerates prompt for all affected agents.


Org Beliefs (Brand Identity)

Org-level beliefs define the brand identity that cascades to ALL agents in the organization. Same 5 categories as agent beliefs: axiom, principle, voice, preference, boundary.

Deduplication: Unique on (org_id, category, statement) — duplicates return 409.

Side effects: Creating, updating, hiding, or deleting org beliefs regenerates prompts for ALL agents in the org.

List Org Beliefs

GET /api/organizations/{orgId}/beliefs

Query Parameters:

ParamTypeRequiredDescription
categorystringnoFilter by category
statusstringnoFilter by status (pending, approved, rejected)
starredbooleannoFilter by starred
hiddenbooleannoFilter by hidden
limitnumbernoMax results
offsetnumbernoSkip N results

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "category": "principle",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "status": "approved",
      "created_at": "2026-03-15T..."
    }
  ],
  "count": 5,
  "next_steps": [...]
}

Create Org Belief

POST /api/organizations/{orgId}/beliefs

Request Body:

FieldTypeRequiredDescription
categorystringyesOne of: axiom, principle, voice, preference, boundary
statementstringyesThe belief statement

Response (201): Created org belief object.

Errors: 400 (missing fields), 409 (duplicate)


Update Org Belief

PATCH /api/organizations/{orgId}/beliefs/{beliefId}

Request Body (all optional):

FieldTypeDescription
statementstringBelief statement
categorystringBelief category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated org belief object.


Delete Org Belief

DELETE /api/organizations/{orgId}/beliefs/{beliefId}

Response (200): { "success": true }


Toggle Org Belief Star

POST /api/organizations/{orgId}/beliefs/{beliefId}/star

Response (200): Updated org belief with toggled starred value.


Toggle Org Belief Hide

POST /api/organizations/{orgId}/beliefs/{beliefId}/hide

Response (200): Updated org belief with toggled hidden value.


Org Responsibilities (Brand Accountabilities)

Org-level responsibilities define the brand accountabilities that cascade to ALL agents in the organization. Same 5 categories as agent responsibilities: ownership, execution, collaboration, deliverables, monitoring.

Deduplication: Unique on (org_id, category, statement) — duplicates return 409.

Side effects: Creating, updating, hiding, or deleting org responsibilities regenerates prompts for ALL agents in the org.

List Org Responsibilities

GET /api/organizations/{orgId}/responsibilities

Query Parameters:

ParamTypeRequiredDescription
categorystringnoFilter by category
statusstringnoFilter by status (pending, approved, rejected)
starredbooleannoFilter by starred
hiddenbooleannoFilter by hidden
limitnumbernoMax results
offsetnumbernoSkip N results

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "category": "ownership",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "status": "approved",
      "created_at": "2026-03-15T..."
    }
  ],
  "count": 3,
  "next_steps": [...]
}

Create Org Responsibility

POST /api/organizations/{orgId}/responsibilities

Request Body:

FieldTypeRequiredDescription
categorystringyesOne of: ownership, execution, collaboration, deliverables, monitoring
statementstringyesThe responsibility statement

Response (201): Created org responsibility object.

Errors: 400 (missing fields), 409 (duplicate)


Update Org Responsibility

PATCH /api/organizations/{orgId}/responsibilities/{responsibilityId}

Request Body (all optional):

FieldTypeDescription
statementstringResponsibility statement
categorystringResponsibility category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated org responsibility object.


Delete Org Responsibility

DELETE /api/organizations/{orgId}/responsibilities/{responsibilityId}

Response (200): { "success": true }


Toggle Org Responsibility Star

POST /api/organizations/{orgId}/responsibilities/{responsibilityId}/star

Response (200): Updated org responsibility with toggled starred value.


Toggle Org Responsibility Hide

POST /api/organizations/{orgId}/responsibilities/{responsibilityId}/hide

Response (200): Updated org responsibility with toggled hidden value.


Group Beliefs (Team Identity)

Group-level beliefs define team identity that cascades to agents in that group only. Same 5 categories as agent beliefs: axiom, principle, voice, preference, boundary.

Side effects: Creating, updating, hiding, or deleting group beliefs regenerates prompts for agents in that group only. The group's org_id is auto-resolved from the group record.

List Group Beliefs

GET /api/groups/{groupId}/beliefs

Query Parameters:

ParamTypeRequiredDescription
categorystringnoFilter by category
statusstringnoFilter by status (pending, approved, rejected)
starredbooleannoFilter by starred
hiddenbooleannoFilter by hidden
limitnumbernoMax results
offsetnumbernoSkip N results

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "group_id": "uuid",
      "org_id": "uuid",
      "category": "voice",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "status": "approved",
      "created_at": "2026-03-15T..."
    }
  ],
  "count": 4,
  "next_steps": [...]
}

Create Group Belief

POST /api/groups/{groupId}/beliefs

Request Body:

FieldTypeRequiredDescription
categorystringyesOne of: axiom, principle, voice, preference, boundary
statementstringyesThe belief statement

Response (201): Created group belief object.

Errors: 400 (missing fields), 409 (duplicate)


Update Group Belief

PATCH /api/groups/{groupId}/beliefs/{beliefId}

Request Body (all optional):

FieldTypeDescription
statementstringBelief statement
categorystringBelief category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated group belief object.


Delete Group Belief

DELETE /api/groups/{groupId}/beliefs/{beliefId}

Response (200): { "success": true }


Toggle Group Belief Star

POST /api/groups/{groupId}/beliefs/{beliefId}/star

Response (200): Updated group belief with toggled starred value.


Toggle Group Belief Hide

POST /api/groups/{groupId}/beliefs/{beliefId}/hide

Response (200): Updated group belief with toggled hidden value.


Group Responsibilities (Team Accountabilities)

Group-level responsibilities define team accountabilities that cascade to agents in that group only. Same 5 categories as agent responsibilities: ownership, execution, collaboration, deliverables, monitoring.

Side effects: Creating, updating, hiding, or deleting group responsibilities regenerates prompts for agents in that group only. The group's org_id is auto-resolved from the group record.

List Group Responsibilities

GET /api/groups/{groupId}/responsibilities

Query Parameters:

ParamTypeRequiredDescription
categorystringnoFilter by category
statusstringnoFilter by status (pending, approved, rejected)
starredbooleannoFilter by starred
hiddenbooleannoFilter by hidden
limitnumbernoMax results
offsetnumbernoSkip N results

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "group_id": "uuid",
      "org_id": "uuid",
      "category": "execution",
      "statement": "...",
      "starred": false,
      "hidden": false,
      "status": "approved",
      "created_at": "2026-03-15T..."
    }
  ],
  "count": 2,
  "next_steps": [...]
}

Create Group Responsibility

POST /api/groups/{groupId}/responsibilities

Request Body:

FieldTypeRequiredDescription
categorystringyesOne of: ownership, execution, collaboration, deliverables, monitoring
statementstringyesThe responsibility statement

Response (201): Created group responsibility object.

Errors: 400 (missing fields), 409 (duplicate)


Update Group Responsibility

PATCH /api/groups/{groupId}/responsibilities/{responsibilityId}

Request Body (all optional):

FieldTypeDescription
statementstringResponsibility statement
categorystringResponsibility category
displayOrdernumberDisplay order
statusstringpending, approved, or rejected

Response (200): Updated group responsibility object.


Delete Group Responsibility

DELETE /api/groups/{groupId}/responsibilities/{responsibilityId}

Response (200): { "success": true }


Toggle Group Responsibility Star

POST /api/groups/{groupId}/responsibilities/{responsibilityId}/star

Response (200): Updated group responsibility with toggled starred value.


Toggle Group Responsibility Hide

POST /api/groups/{groupId}/responsibilities/{responsibilityId}/hide

Response (200): Updated group responsibility with toggled hidden value.


Consensus Detection

Detect shared beliefs and responsibilities across agents. Consensus detection scans all agents in an org or group, identifies beliefs/responsibilities that are semantically similar across a threshold percentage of agents, and optionally promotes them to the org or group level.

Detect Org Consensus

POST /api/organizations/{orgId}/consensus

Scans all agents in the organization for shared beliefs and responsibilities. When a belief or responsibility appears across enough agents (controlled by consensusAgentPercentage on the org settings), it is promoted to the org level.

curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://persona.liveneon.ai/api/v1/organizations/{orgId}/consensus

Response (200):

{
  "beliefs_created": 3,
  "beliefs_skipped": 1,
  "responsibilities_created": 2,
  "responsibilities_skipped": 0,
  "details": [
    "Created org belief (principle): 'Always prioritize user privacy' — found in 8/10 agents",
    "Skipped org belief (voice): 'Use concise language' — already exists at org level",
    "Created org responsibility (ownership): 'Own the end-to-end user experience' — found in 7/10 agents"
  ]
}

Detect Group Consensus

POST /api/groups/{groupId}/consensus

Same as org consensus but scoped to agents within a specific group. Shared beliefs/responsibilities are promoted to the group level.

curl -X POST -H "Authorization: Bearer $TOKEN" \
  https://persona.liveneon.ai/api/v1/groups/{groupId}/consensus

Response (200):

{
  "beliefs_created": 2,
  "beliefs_skipped": 0,
  "responsibilities_created": 1,
  "responsibilities_skipped": 1,
  "details": [
    "Created group belief (principle): 'Ship iteratively' — found in 4/5 agents",
    "Created group responsibility (execution): 'Write tests for all new features' — found in 3/5 agents",
    "Skipped group responsibility (monitoring): 'Monitor CI pipeline health' — already exists at group level"
  ]
}

Organizations

List Organizations

GET /api/organizations

Returns all organizations the authenticated user belongs to, with their role in each.

Response (200):

{
  "data": [
    {
      "role": "owner",
      "organization": {
        "id": "uuid",
        "name": "My Company",
        "slug": "my-company",
        "created_at": "2026-03-12T..."
      }
    }
  ],
  "next_steps": [...]
}

Example:

curl https://persona.liveneon.ai/api/organizations \
  -H "Authorization: Bearer $TOKEN"

Create Organization

POST /api/organizations

Request Body:

FieldTypeRequired
namestringyes
slugstringyes

Response (201): Organization object. The creating user is automatically added as owner via database trigger.

Example:

curl -X POST https://persona.liveneon.ai/api/organizations \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Company", "slug": "my-company"}'

Errors: 409 (slug already exists)


Update Organization

PATCH /api/organizations/{orgId}

Request Body (all optional):

FieldTypeDescription
namestringOrganization name
autoApproveBeliefsbooleanAuto-approve new beliefs
autoApproveResponsibilitiesbooleanAuto-approve new responsibilities
autoPublishConversationsbooleanAuto-publish completed conversations as content (default: false)
autoPromoteSharedBeliefsbooleanAutomatically promote consensus-detected beliefs to org level
autoPromoteSharedResponsibilitiesbooleanAutomatically promote consensus-detected responsibilities to org level
consensusSimilarityThresholdnumberCosine similarity threshold for consensus detection (0.0–1.0)
consensusAgentPercentagenumberMinimum percentage of agents that must share a belief/responsibility for consensus (0.0–1.0)

Response (200): Updated organization object.

Errors: 400 (empty name or no valid fields)


Delete Organization

DELETE /api/organizations/{orgId}

Hard delete with cascade. Removes the organization and all associated data (agents, beliefs, responsibilities, groups, content, jobs, connections — 16+ tables).

Auth: Session only (cookie auth). Must be org owner.

Request Body:

FieldTypeRequiredDescription
confirmSlugstringyesMust match the organization's slug to confirm deletion

Response (200):

{ "success": true, "deleted_org": "uuid" }

Errors: 400 (missing or wrong confirmSlug), 401 (not owner), 404 (org not found)


Get Organization Summary

GET /api/organizations/{orgId}/summary

Returns a full overview of the organization in a single call: groups, agents (with belief/responsibility counts), content sources, and content items.

Response (200):

{
  "organization": { "id": "uuid", "name": "...", "slug": "..." },
  "groups": [{ "id": "uuid", "name": "...", "agent_count": 4 }],
  "agents": [{ "id": "uuid", "name": "...", "belief_count": 8, "responsibility_count": 5 }],
  "content_sources": [{ "id": "uuid", "platform": "github", "scope": "agent" }],
  "content_items_count": 150,
  "next_steps": [...]
}

API Keys

Org-scoped API keys for external integrations. Keys are shown once on creation and cannot be retrieved again. Only the last 4 characters (hint) are stored for identification.

List API Keys

GET /api/organizations/{orgId}/api-keys

Response (200): Array of key metadata (never includes the key itself).

[
  {
    "id": "uuid",
    "org_id": "uuid",
    "created_by": "uuid",
    "label": "CI Pipeline",
    "key_hint": "Ab3x",
    "last_used_at": "2026-03-19T...",
    "created_at": "2026-03-19T..."
  }
]

Auth: Session only (cookie auth required).


Create API Key

POST /api/organizations/{orgId}/api-keys

Request Body:

FieldTypeRequired
labelstringno

Response (201): Key metadata plus the raw key (shown once).

{
  "id": "uuid",
  "org_id": "uuid",
  "label": "CI Pipeline",
  "key_hint": "Ab3x",
  "key": "ln_abc123...xyz",
  "warning": "Store this key securely. It cannot be retrieved again.",
  "created_at": "2026-03-19T..."
}

Auth: Session only (cookie auth required).

Example:

curl -X POST https://persona.liveneon.ai/api/organizations/{orgId}/api-keys \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"label": "Production Integration"}'

Revoke API Key

DELETE /api/organizations/{orgId}/api-keys/{keyId}

Response (200):

{ "success": true }

Auth: Session only (cookie auth required).


Groups

List Groups

GET /api/groups

Query Parameters:

ParamTypeRequiredDescription
orgIdstring (uuid)no*Organization ID
orgSlugstringno*Organization slug
limitnumbernoMax results (default: 100)
offsetnumbernoSkip N results (default: 0)

*One of orgId or orgSlug is required unless using API key auth.

Example:

curl https://persona.liveneon.ai/api/groups?orgId=your-org-id \
  -H "Authorization: Bearer $TOKEN"

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "name": "Marketing Team",
      "description": "...",
      "agent_count": 4,
      "display_order": 1,
      "created_at": "2026-03-12T..."
    }
  ],
  "count": 3,
  "next_steps": [...]
}

Each group includes agent_count — the number of agents in the group.


Create Group

POST /api/groups

Request Body:

FieldTypeRequired
orgIdstring (uuid)yes
namestringyes
descriptionstringno

Response (201): Created group object.


Update Group

PATCH /api/groups/{groupId}

Request Body (all optional):

FieldTypeDescription
namestringGroup name
descriptionstringGroup description
displayOrdernumberDisplay order
managerAgentIdstring (uuid)Manager agent (must be from a different group)

Response (200): Updated group object.

Errors: 400 (manager agent not found or from same group)


Delete Group

DELETE /api/groups/{groupId}

Response (200): { "success": true }


Models

List Models

GET /api/models

Query Parameters:

ParamTypeRequired
orgIdstring (uuid)yes

Response (200): Array of model objects ordered by provider, name.

[
  {
    "id": "uuid",
    "org_id": "uuid",
    "provider": "anthropic",
    "model_id": "claude-sonnet-4-20250514",
    "name": "Claude Sonnet",
    "provider_url": "https://api.anthropic.com",
    "description": "Fast, capable model"
  }
]

Create Model

POST /api/models

Request Body:

FieldTypeRequired
orgIdstring (uuid)yes
providerstringyes
modelIdstringyes
namestringyes
providerUrlstringno
descriptionstringno

Response (201): Created model object.

Errors: 409 (duplicate provider + modelId combination)


Update Model

PATCH /api/models/{modelId}

Request Body (all optional): provider, modelId, name, providerUrl, description

Response (200): Updated model object.


Delete Model

DELETE /api/models/{modelId}

Response (200): { "success": true }


Environments

List Environments

GET /api/environments

Query Parameters:

ParamTypeRequired
orgIdstring (uuid)yes

Response (200): Array of environment objects ordered by display_order, created_at.


Create Environment

POST /api/environments

Request Body:

FieldTypeRequired
orgIdstring (uuid)yes
namestringyes
slugstringyes
descriptionstringno
isDefaultbooleanno (default: false)

Response (201): Created environment object.

Errors: 409 (duplicate slug)


Update Environment

PATCH /api/environments/{environmentId}

Request Body (all optional): name, slug, description, isDefault, displayOrder

Response (200): Updated environment object.


Delete Environment

DELETE /api/environments/{environmentId}

Response (200): { "success": true }


List Environment Models

GET /api/environments/{environmentId}/models

Response (200): Array of environment_models with expanded model objects.


Add Model to Environment

POST /api/environments/{environmentId}/models

Request Body:

FieldTypeRequired
modelIdstring (uuid)yes

Response (201): Created environment_models record.

Errors: 409 (duplicate)


Remove Model from Environment

DELETE /api/environments/{environmentId}/models

Note: This DELETE endpoint requires a JSON request body. See the note on Remove Agent Model for client compatibility considerations.

Request Body:

FieldTypeRequired
modelIdstring (uuid)yes

Response (200): { "success": true }


Conversations

Terminology note: The API URLs use "conversations" and route params use sessionId. The underlying database table is conversation_sessions. These terms are interchangeable — a conversation is a session. Response objects use the database column names (e.g., started_at, ended_at).

List Conversations

GET /api/conversations

Query Parameters:

ParamTypeRequired
agentIdstring (uuid)yes

Example:

curl https://persona.liveneon.ai/api/conversations?agentId=agent-uuid \
  -H "Authorization: Bearer $TOKEN"

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "title": "Strategy Discussion",
      "status": "active",
      "started_at": "2026-03-12T...",
      "participant_count": 2,
      "message_count": 15
    }
  ],
  "count": 1
}

Sessions are ordered by started_at descending and enriched with computed participant_count and message_count.


Create Conversation

POST /api/conversations

Request Body:

FieldTypeRequiredDescription
orgIdstring (uuid)yesOrganization ID
titlestringnoConversation title
participantsarrayyesList of participants

Participant object:

FieldTypeRequiredDescription
participantTypestringyesagent or human
displayNamestringyesDisplay name
agentIdstring (uuid)yes (if agent)Agent ID
userIdstring (uuid)noUser ID (for humans)
rolestringnoRole (default: participant)

Response (201): Session object with participants array.


Get Conversation

GET /api/conversations/{sessionId}

Response (200): Full session object including participants and messages arrays.

Errors: 404 (not found)


Update Conversation

PATCH /api/conversations/{sessionId}

Request Body (all optional):

FieldTypeDescription
titlestringConversation title
statusstringactive, completed, or archived
ended_atstring (ISO 8601)End timestamp

Response (200): Updated session object.

Side effects: If status: "completed" and org has auto_publish_conversations enabled, auto-publishes to content_items.


Add Messages

POST /api/conversations/{sessionId}/messages

Supports three modes:

Mode 1 — Single message:

FieldTypeRequired
participantIdstring (uuid)yes
contentstringyes
metadataobjectno

Mode 2 — Batch messages:

FieldTypeRequired
messagesarrayyes

Each message: { participantId, content, metadata }

Mode 3 — Format raw transcript:

FieldTypeRequired
formatboolean (true)yes
rawTextstringyes
agentNamestringyes
otherParticipantNamestringno
otherParticipantTypestringno (default: human)

Response (201): Array of inserted messages with auto-incremented sequence_number.


Publish Conversation

POST /api/conversations/{sessionId}/publish

Creates a content_item from the conversation for a specific agent.

Request Body:

FieldTypeRequired
agentIdstring (uuid)yes

Response (201): Content item object.

Side effects: Marks session as published. Triggers PBD (principle-based distillation) pipeline in background.


Unpublish Conversation

DELETE /api/conversations/{sessionId}/publish

Query Parameters:

ParamTypeRequired
agentIdstring (uuid)yes

Response (200): { "success": true }

Side effects: Soft-deletes content_items, clears published_at on session.


Format Transcript

POST /api/conversations/format

Uses an LLM to parse raw conversation text into structured messages.

Request Body:

FieldTypeRequired
rawTextstringyes
agentNamestringyes
otherParticipantNamestringno (default: Human)
otherParticipantTypestringno (default: human)
titlestringno

Response (200):

{
  "title": "Strategy Discussion",
  "messages": [
    { "role": "user", "content": "..." },
    { "role": "assistant", "content": "..." }
  ]
}

Content Items

Content items include a published_at field representing the original publish date of the content (e.g., commit date, tweet date, article publish date). This field is used for chronological sorting and is distinct from created_at (when the item was imported).

List Content Items

GET /api/content-items

Query Parameters:

ParamTypeRequiredDefaultDescription
agentIdstring (uuid)yes-Agent to list content for
sourceIdstring (uuid)no-Filter by content source
sourceTypestringno-Filter by type: github_commit, github_file, tweet, substack_post, conversation
statusstringno-Filter by status: imported or hidden
limitnumberno50Max results
offsetnumberno0Skip N results

Response (200):

{ "data": [...], "count": 123 }

Update Content Item

PATCH /api/content-items/{itemId}

Request Body:

FieldTypeRequiredDescription
statusstringyesMust be imported or hidden

Response (200): Updated content_item object.


Content Sources

Content sources connect external platforms to agents for the PBD pipeline. Supported platforms: github, website, twitter, rss, and linkedin.

SSRF Protection: All user-provided URLs (website domains, RSS feed URLs, manual URL lists) are validated before use. Private/internal network addresses (127., 10., 192.168., 172.16-31., 169.254.*, localhost, *.local, *.internal) and non-HTTP protocols are blocked. Validation runs both at source creation and at every page fetch.

All content sources require agent associationagentId is required when creating a source. PBD requires agent_id on content items to process them, so group-only and org-only scoping is no longer supported.

List Content Sources

GET /api/content-sources

Query Parameters:

ParamTypeRequiredDescription
agentIdstring (uuid)no*Filter by agent
orgIdstring (uuid)no*Filter by organization
limitnumbernoMax results (default: 100)
offsetnumbernoSkip N results (default: 0)

*One of agentId or orgId is required unless using API key auth (org is inferred from key).

Response (200): Array of content_sources ordered by created_at descending.


Create Content Source

POST /api/content-sources

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID — all content sources must be associated with an agent
orgIdstring (uuid)noOrganization ID (required for org resolution if not using API key auth)
platformstringyesgithub, website, twitter, rss, or linkedin
configobjectnoSource configuration (platform-specific)
githubConnectionIdstring (uuid)noGitHub connection for private repos
xConnectionIdstring (uuid)noX connection for Twitter sources

Config object (for platform: "github"):

FieldTypeDefaultDescription
repoUrlstring-Full GitHub URL (owner/repo extracted automatically)
ownerstring-GitHub owner (alternative to repoUrl)
repostring-GitHub repo name (alternative to repoUrl)
branchstring"main"Branch to import from
import_commitsbooleantrueImport commit messages as content
import_filesbooleanfalseImport file contents as content (prose files only - see below)
included_foldersstring[][]Only import from these folders (empty = all)
excluded_foldersstring[][]Exclude these folders from import
include_pathsstring[][]Explicit file/folder selections from the file browser. Folder paths ending with / include all files recursively. Exact paths select individual files. Empty array means include everything (no filtering).

GitHub file import (when import_files: true):

File import uses an allowlist approach. Only prose and documentation files that PBD can extract identity patterns from are imported. Code files are excluded because they contain syntax and logic, not beliefs or communication style.

Importable extensions: .md, .mdx, .txt, .rst, .adoc, .org, .tex

Special filenames (imported regardless of extension): README, LICENSE, CHANGELOG, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, AUTHORS, CLAUDE.md

Maximum file size: 50KB. Files larger than this are skipped.

Use GET /api/github/tree?mode=full to browse which files in a repo are eligible for import. The file browser UI component lets users select specific folders and files, which are stored as include_paths in the source config.

Config object (for platform: "website"):

FieldTypeDefaultDescription
domainstringRoot domain to crawl (e.g., "example.com")
discoverystring"llms_txt"How to find pages: llms_txt, sitemap, or manual
urlsstring[][]Explicit URLs to import (required when discovery: "manual")
url_patternstringRegex to filter discovered URLs (e.g., "/blog/.*")
max_pagesnumber50Maximum pages to import per sync
sync_interval_hoursnumber24Hours between auto-syncs
extract_modestring"article"Content extraction strategy: article, full, or markdown

Config object (for platform: "twitter"):

FieldTypeDefaultDescription
usernamestringX/Twitter username to sync tweets from
exclude_retweetsbooleantrueExclude retweets
exclude_repliesbooleantrueExclude replies
max_pagesnumber3Max pages to fetch (100 tweets per page)
sync_interval_hoursnumber24Hours between auto-syncs

Config object (for platform: "rss"):

FieldTypeDefaultDescription
feed_urlstringRSS 2.0 or Atom feed URL (e.g., "https://blog.example.com/feed")
max_entriesnumber50Maximum entries to import per sync
sync_interval_hoursnumber24Hours between auto-syncs

No connection required — RSS feeds are public. Works with any RSS 2.0 or Atom feed (Substack, Medium, WordPress, etc.). Substack feeds are at https://{subdomain}.substack.com/feed.

Discovery modes (website):

Example (GitHub):

curl -X POST https://persona.liveneon.ai/api/content-sources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid", "platform": "github", "config": {"repoUrl": "https://github.com/owner/repo"}}'

Example (Website — agent-scoped):

curl -X POST https://persona.liveneon.ai/api/content-sources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid", "platform": "website", "config": {"domain": "example.com", "discovery": "sitemap", "max_pages": 100}}'

Example (GitHub with file import):

curl -X POST https://persona.liveneon.ai/api/content-sources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid", "platform": "github", "config": {"repoUrl": "https://github.com/owner/repo", "import_files": true, "include_paths": ["docs/", "README.md"]}}'

Example (Twitter):

curl -X POST https://persona.liveneon.ai/api/content-sources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid", "platform": "twitter", "xConnectionId": "uuid", "config": {"username": "elonmusk", "exclude_retweets": true}}'

Example (RSS):

curl -X POST https://persona.liveneon.ai/api/content-sources \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid", "platform": "rss", "config": {"feed_url": "https://blog.example.com/feed", "max_entries": 100}}'

Response (201): Created content_sources record.


Get Content Source

GET /api/content-sources/{sourceId}

Response (200): Content source object.


Update Content Source

PATCH /api/content-sources/{sourceId}

Request Body (all optional):

FieldTypeDescription
configobjectPlatform-specific configuration
enabledbooleanEnable/disable source
namestringSource display name
agentIdstring (uuid)Reassign to a different agent
githubConnectionIdstring (uuid)Change GitHub connection
xConnectionIdstring (uuid)Change X connection

Response (200): Updated content source.


Delete Content Source

DELETE /api/content-sources/{sourceId}

Response (200): { "success": true }


Sync Content Source

POST /api/content-sources/{sourceId}/sync

Triggers a sync of the content source. For GitHub sources, fetches latest commits/files. For website sources, crawls pages according to the discovery mode and config. For Twitter sources, fetches latest tweets from the configured username. For RSS sources, fetches latest entries from the feed URL. LinkedIn sources are imported via POST /api/linkedin/import instead of this endpoint.

Response (200): Sync result object.

Side effects: Triggers PBD pipeline for all agents that received content (commit attribution may route commits to different agents based on github_username).

Response fields:

FieldTypeDescription
commits_importednumberNew commits imported (GitHub only)
files_importednumberNew files imported (GitHub only)
pages_importednumberNew pages imported (website only)
pages_skippednumberPages skipped (already imported or filtered out)
pages_blockednumberPages blocked by robots.txt or url_pattern
tweets_importednumberNew tweets imported (Twitter only)
rss_entries_importednumberNew RSS entries imported (RSS only)
affected_agent_idsstring[]All agents that received content
scopestringSource scope (always agent)
errorsstring[]Non-fatal errors encountered

Errors: 404 (source not found), 400 (source disabled or unsupported platform)


GitHub Connections

GitHub connections store OAuth or PAT tokens (encrypted with AES-256-GCM) for accessing GitHub repos. Connections are org-scoped but user-owned.

List GitHub Connections

GET /api/github/connections

Query Parameters:

ParamTypeRequired
orgIdstring (uuid)yes

Response (200): Array of connections (token never exposed).

Create GitHub Connection (PAT)

POST /api/github/connections

Request Body:

FieldTypeRequired
orgIdstring (uuid)yes
tokenstringyes

Validates the token against GitHub API, fetches user info, encrypts token for storage.

Response (201): Created connection record.

Get GitHub Connection

GET /api/github/connections/{connectionId}

Response (200): Connection details with usage_count (number of content sources using it).

Delete GitHub Connection

DELETE /api/github/connections/{connectionId}

Deletes the connection. Content sources using it fall back to public access.

GitHub OAuth Flow

GET /api/github/oauth/authorize?orgId=...&redirect=...

Redirects to GitHub's OAuth authorization page. Sets a nonce cookie for CSRF protection.

GET /api/github/oauth/callback?code=...&state=...

Handles the OAuth callback — exchanges code for token, encrypts and stores it, redirects back to the app.

List Repos

GET /api/github/repos?connectionId=...

Lists repositories accessible via a stored GitHub connection.


X Connections

X connections store OAuth 1.0a credentials (encrypted with AES-256-GCM) for accessing the X/Twitter API. Connections are org-scoped.

List X Connections

GET /api/x-connections

Query Parameters:

ParamTypeRequired
orgIdstring (uuid)yes

Response (200): Array of connections (id, org_id, label, created_at, updated_at). Encrypted credentials are never exposed.

Create X Connection

POST /api/x-connections

Request Body:

FieldTypeRequiredDescription
orgIdstring (uuid)no*Organization ID
orgSlugstringno*Organization slug
labelstringyesDisplay label for the connection
xUsernamestringyesX/Twitter username
xUserIdstringyesX/Twitter user ID
apiKeystringyesX API key
apiSecretstringyesX API secret
accessTokenstringyesX access token
accessTokenSecretstringyesX access token secret

*One of orgId or orgSlug required (or inferred from API key).

Encrypts all credentials with AES-256-GCM before storage.

Response (201): Created connection record (without encrypted credential fields).

Delete X Connection

DELETE /api/x-connections

Request Body:

FieldTypeRequired
idstring (uuid)yes

Response (200):

{ "deleted": true }

LinkedIn Import

Import content from a LinkedIn data export ZIP file. Users can download their data from LinkedIn's privacy settings (Settings > Data Privacy > Get a copy of your data). No API key or OAuth connection to LinkedIn is needed — the user uploads their own export.

The import parses supported CSV files (Profile.csv, Positions.csv, Education.csv, Skills.csv, Shares.csv) and HTML articles from the Articles/ folder. Content items are created with platform linkedin and source types: linkedin_profile, linkedin_post, linkedin_article.

Import LinkedIn Data

POST /api/linkedin/import

Content-Type: multipart/form-data

Form Fields:

FieldTypeRequiredDescription
filefileyesLinkedIn data export ZIP file (max 10MB)
orgIdstring (uuid)no*Organization ID
orgSlugstringno*Organization slug
agentIdstring (uuid)yesAgent to associate content with

*One of orgId or orgSlug is required (or inferred from API key).

Response (201):

{
  "source_id": "uuid",
  "profile_imported": true,
  "posts_imported": 42,
  "posts_skipped": 3,
  "articles_imported": 5,
  "articles_skipped": 0,
  "profile_data": {
    "firstName": "Jane",
    "lastName": "Doe",
    "headline": "VP of Engineering",
    "summary": "...",
    "industry": "Technology",
    "geoLocation": "San Francisco, CA"
  },
  "positions_count": 4,
  "skills_count": 12,
  "education_count": 2,
  "errors": []
}

Example:

curl -X POST https://persona.liveneon.ai/api/linkedin/import \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "agentId=uuid"

Errors: 400 (missing file, invalid ZIP, or no supported CSV files found), 413 (file exceeds 10MB)


Observations

Observations are read-only. They are generated exclusively by the PBD pipeline when content is processed — there is no endpoint to create, update, or delete observations manually.

List Observations

GET /api/observations

Query Parameters:

ParamTypeRequiredDefault
agentIdstring (uuid)yes
contentItemIdstring (uuid)no
signalIdstring (uuid)no
limitnumberno100
offsetnumberno0

Response (200):

{ "data": [...], "count": 42 }

Signals

Signals are read-only. They are clustered from observations by the PBD pipeline. When a signal's reinforcement count crosses the agent's min_n_count threshold, it can be promoted to a belief or responsibility (this promotion is also handled by the pipeline). Promoted signals include a promoted_to_belief_id and/or promoted_to_responsibility_id field linking to the created resource.

List Signals

GET /api/signals

Query Parameters:

ParamTypeRequiredDefault
agentIdstring (uuid)yes
promotedstringno— ("true" or "false") — checks both promoted_to_belief_id AND promoted_to_responsibility_id
minNCountnumberno
predict_categorybooleannofalse — uses Haiku to predict category for unpromoted signals

When predict_category=true, each unpromoted signal includes predicted_type ("belief" or "responsibility") and predicted_category.

Response (200):

{ "data": [...], "count": 15 }

Bootstrap

Scrape Repos

POST /api/bootstrap/scrape

Scrapes open-source repos, decomposes agent prompts into structured beliefs/responsibilities via Claude, and stores them with vector embeddings. Requires standard authentication (same bearer token as all other endpoints — there is no separate admin role check). This is a heavy operation that calls Claude and OpenAI APIs, so use sparingly.

Request Body (all optional):

FieldTypeDescription
ownerstringGitHub owner (if scraping single repo)
repostringGitHub repo name (if scraping single repo)

If no owner/repo provided, scrapes all configured repos.

Response (200):

{
  "repos_scraped": 7,
  "templates_created": 42,
  "templates_updated": 5,
  "templates_skipped": 2,
  "errors": []
}

Get Recommendations

POST /api/bootstrap/recommend

Generates tailored belief/responsibility recommendations for an agent based on their profile, using vector similarity search + Claude.

Request Body:

FieldTypeRequired
agentIdstring (uuid)yes

Response (200):

{
  "beliefs": [
    {
      "category": "principle",
      "statement": "Always prioritize clarity over cleverness",
      "confidence": "high",
      "source": "repo-name/agent-name"
    }
  ],
  "responsibilities": [
    {
      "category": "ownership",
      "statement": "Own the content calendar and publishing pipeline",
      "confidence": "medium",
      "source": "repo-name/agent-name"
    }
  ]
}

Apply Recommendations

POST /api/bootstrap/apply

Batch-creates or updates beliefs and/or responsibilities from selected recommendations.

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)yesAgent ID
beliefsarrayno[{ category, statement, nCount? }, ...]
responsibilitiesarrayno[{ category, statement, nCount? }, ...]
statusstringnoStatus for created items: "approved" (default) or "pending"

At least one of beliefs or responsibilities must be non-empty. Each item in the beliefs and responsibilities arrays can include an optional nCount field (observation count, default: 1).

Duplicates (same agent + category + statement) are upserted — existing records get their nCount and status updated. This means you can re-import beliefs with corrected nCount values without creating duplicates.

All statements are normalized before storage (curly quotes → straight, em-dashes → hyphens, etc.) to prevent Unicode near-duplicates.

Response (200):

{
  "beliefsCreated": 5,
  "responsibilitiesCreated": 3
}

Side effects: All created/updated with the specified status (default: "approved"). Regenerates system prompt once after all upserts.


Utility

Process PBD

POST /api/pbd/process

Triggers pattern-based discovery — analyzes content to extract observations, cluster signals, and discover both beliefs and responsibilities. Supports both single-agent and org-level processing.

Request Body:

FieldTypeRequiredDescription
agentIdstring (uuid)no*Agent to process
orgIdstring (uuid)no*Organization to process (all agents)
orgSlugstringno*Organization slug (alternative to orgId)
forcebooleannoRe-process already-processed content items (default: false)

*Either agentId OR orgId/orgSlug is required. When org-level, processes all agents in the org.

Response (202) — single agent:

{ "status": "processing", "agentId": "uuid", "jobId": "uuid" }

Response (202) — org-level:

{
  "status": "processing",
  "orgId": "uuid",
  "jobs": [
    { "agentId": "uuid", "jobId": "uuid" },
    { "agentId": "uuid", "jobId": "uuid" }
  ]
}

Returns jobId(s) for tracking progress via GET /api/v1/jobs/{jobId}.

# Trigger PBD and track progress
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "uuid"}' \
  https://persona.liveneon.ai/api/v1/pbd/process

# Check job progress
curl -H "Authorization: Bearer $TOKEN" \
  https://persona.liveneon.ai/api/v1/jobs/{jobId}

GitHub Tree

GET /api/github/tree

Browse a GitHub repo's file and folder structure. Supports two modes: folders (default, backward-compatible) returns only directory paths, and full returns both files and folders filtered to importable types only.

Query Parameters:

ParamTypeRequiredDefaultDescription
ownerstringyes-GitHub repo owner
repostringyes-GitHub repo name
branchstringnomainBranch to browse
connectionIdstring (uuid)no-GitHub connection ID (required for private repos)
modestringnofoldersfolders returns directory paths only. full returns files and folders filtered to importable types, capped at 5,000 items

Response (200) - folders mode (default):

{
  "folders": ["/src", "/src/lib", "/src/app", "/docs"],
  "total_files": 142
}

Response (200) - full mode:

{
  "tree": [
    { "path": "README.md", "type": "blob", "size": 4521 },
    { "path": "docs", "type": "tree", "size": null },
    { "path": "docs/guide.md", "type": "blob", "size": 12340 }
  ],
  "truncated": false,
  "total": 87
}

In full mode, only importable file types are included (see GitHub file import below). Binary files and code files are filtered out. If the filtered tree exceeds 5,000 items, it is truncated and truncated is set to true.


Cron Endpoints

These endpoints are called by the Railway cron service and authenticated with CRON_SECRET, not Supabase auth. They are documented here for completeness but are not intended for external use.

Sync Sources

POST /api/cron/sync-sources

Auto-syncs stale content sources (GitHub, website, Twitter, RSS) and triggers PBD for agents that received new content. Called hourly by Railway cron.

Auth: Authorization: Bearer <CRON_SECRET>

Consensus Detection

POST /api/cron/consensus

Nightly consensus detection for all organizations. Runs group-level and org-level consensus detection using pgvector cosine similarity on belief/responsibility embeddings.

Called hourly by Railway cron, but each org only processes during its assigned hour (derived from a hash of the org ID), spread across 9pm-6am PST (5am-2pm UTC). Requests outside this window return a "skipped" response. This staggering ensures orgs do not all run consensus at the same time.

Requires at least 2 agents in a group/org to detect consensus. Creates background jobs for tracking.

Auth: Authorization: Bearer <CRON_SECRET>

Response (200) - when processing:

{
  "status": "ok",
  "current_hour_utc": 10,
  "orgs_processed": 2,
  "results": [
    {
      "org": "My Org",
      "groups": 3,
      "beliefsCreated": 5,
      "responsibilitiesCreated": 2
    }
  ]
}

Response (200) - when skipped:

{
  "status": "skipped",
  "reason": "Current hour 15 UTC is outside night window (5-13 UTC / 9pm-6am PST)",
  "orgs_processed": 0
}

Background Jobs

Track the progress of long-running operations like PBD processing. Jobs are retained for 24 hours by default.

List Jobs

GET /api/jobs

Query Parameters:

ParamTypeRequiredDefault
agentIdstring (uuid)no
orgIdstring (uuid)no
statusstringno— (queued, running, completed, failed)

Returns jobs from the last 24 hours. Filter by agent, org, or status.

# List all jobs for an agent
curl -H "Authorization: Bearer $TOKEN" \
  "https://persona.liveneon.ai/api/v1/jobs?agentId=uuid"

# List only running jobs
curl -H "Authorization: Bearer $TOKEN" \
  "https://persona.liveneon.ai/api/v1/jobs?status=running"

Response (200):

{
  "data": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "agent_id": "uuid",
      "job_type": "pbd_process",
      "status": "completed",
      "progress_current": 15,
      "progress_total": 15,
      "result": { "observations_created": 12, "signals_updated": 5 },
      "error": null,
      "started_at": "2026-03-20T10:00:00Z",
      "completed_at": "2026-03-20T10:02:30Z",
      "created_at": "2026-03-20T10:00:00Z"
    }
  ]
}

Get Job

GET /api/jobs/{jobId}

Returns a single job by ID.

curl -H "Authorization: Bearer $TOKEN" \
  https://persona.liveneon.ai/api/v1/jobs/{jobId}

Response (200):

{
  "id": "uuid",
  "org_id": "uuid",
  "agent_id": "uuid",
  "job_type": "pbd_process",
  "status": "running",
  "progress_current": 7,
  "progress_total": 15,
  "result": null,
  "error": null,
  "started_at": "2026-03-20T10:00:00Z",
  "completed_at": null,
  "created_at": "2026-03-20T10:00:00Z"
}

Errors: 404 (job not found)


Embedding Backfill

Backfill Embeddings

POST /api/beliefs/backfill-embeddings

Generates vector embeddings for all beliefs and responsibilities in an organization that are currently missing them. Useful after bulk imports or when embeddings need to be regenerated.

Request Body:

FieldTypeRequiredDescription
orgIdstring (uuid)yesOrganization to backfill embeddings for
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orgId": "uuid"}' \
  https://persona.liveneon.ai/api/v1/beliefs/backfill-embeddings

Response (200):

{
  "updated": 42
}

The updated count includes both beliefs and responsibilities that received new embeddings.


Dedup & Merge

Dedup Agent Beliefs/Responsibilities

POST /api/agents/{agentId}/dedup

Scans all beliefs and responsibilities for an agent, clusters semantic duplicates within each category (>65% cosine similarity), uses LLM to synthesize the best single statement from each cluster, and deletes the redundant items.

Query Parameters:

ParamTypeRequiredDescription
dryRunstringnotrue (default) previews clusters without merging. false actually merges.

Response (200):

{
  "dry_run": true,
  "clusters": [
    {
      "category": "voice",
      "type": "belief",
      "merged_statement": "I communicate using metaphors to make complex concepts accessible.",
      "kept_id": "uuid",
      "original_n_count": 3,
      "final_n_count": 8,
      "absorbed": [
        { "id": "uuid", "statement": "I use metaphors for technical clarity", "similarity": 0.82 }
      ]
    }
  ],
  "total_merged": 3,
  "total_before": 15,
  "total_after": 12
}

Dedup Org Beliefs/Responsibilities

POST /api/organizations/{orgId}/dedup

Same as agent dedup but for org-level beliefs and responsibilities. Accepts slug or UUID for orgId.

Query Parameters: Same as agent dedup (dryRun=true|false).


Common Error Responses

All errors follow this shape:

{ "error": "Human-readable error message" }
StatusMeaning
400Bad request — missing or invalid fields
401Unauthorized — missing or invalid auth token
404Not found — resource doesn't exist or not accessible
409Conflict — duplicate resource (slug, model ID, etc.)
500Server error — database or internal failure

Key Patterns

Prompt Regeneration

Creating, updating, hiding, or deleting beliefs and responsibilities automatically regenerates the agent's system prompt. The updated prompt is cached on agents.system_prompt.

Cascading Prompt Regeneration

Org belief/responsibility changes regenerate prompts for ALL agents in the org. Group belief/responsibility changes regenerate prompts for agents in that group only. Agent-level changes regenerate only that agent's prompt.

Status Approval Flow

Beliefs and responsibilities have a status field (pending, approved, rejected). Approving sets approved_by and unhides the item. Rejecting hides it. Organizations can enable auto-approval.

Multi-tenancy

All data is scoped by org_id. Row-level security ensures users only see data from their organizations. The org_id is denormalized on all tables for efficient queries.

Pagination

List endpoints that support pagination return { data: [...], count: number } with limit and offset query parameters. Default limits vary by endpoint:

There are currently no enforced maximum limits — you can request any page size. Use reasonable values (100–500) to avoid large payloads.

Bulk Operations

Multiple bulk endpoints are available for batch operations:

Bulk create endpoints perform a single prompt regeneration after all items are created. For importing beliefs/responsibilities with provenance data, prefer POST /api/bootstrap/apply — it silently upserts duplicates.

Background Jobs & Async Operations

Avatar generation (Leonardo.AI), PBD processing, content sync, consensus detection, and embedding backfill all run as background jobs. The API returns immediately (201 or 202) while processing continues. All operations return a jobId — use GET /api/v1/jobs/{jobId} to track progress. Avatar generation now returns avatarJobId from PATCH /api/agents/{id}. The Jobs page (/[orgSlug]/jobs) provides a UI for monitoring all background operations with live polling, ETA estimates, and stop/re-run controls. Interrupted jobs are auto-recovered on server restart.

DELETE Requests with Bodies

Some DELETE endpoints (DELETE /api/agents/{agentId}/models, DELETE /api/environments/{environmentId}/models) require a JSON request body. While this is technically valid HTTP, some clients and proxies may not support it. Ensure your HTTP client sends the Content-Type: application/json header and includes the body with DELETE requests.

Semantic Deduplication

All belief and responsibility creation endpoints (agent, org, group, and PBD promotion) perform semantic deduplication before inserting. The system uses a three-tier strategy:

Cosine SimilarityActionSpeed
>85%Automatic duplicate — merges into existing item (increments n_count)Fast (no LLM)
65–85%"Maybe zone" — Haiku LLM judges whether the two statements express the same core idea~200ms
<65%Definitely new — inserts normallyFast (no LLM)

When a semantic duplicate is detected, the endpoint returns 200 (not 201) with the existing item plus two extra fields:

Existing items missing embeddings are backfilled automatically during dedup checks.

Response Code Notes

HATEOAS / Next Steps

All successful API responses include a next_steps array suggesting follow-up actions with concrete endpoints and example request bodies. This enables AI agents to navigate the API without consulting documentation.

{
  "id": "uuid",
  "name": "Content Director",
  "next_steps": [
    {
      "description": "Add a belief to define this agent's identity",
      "method": "POST",
      "endpoint": "/api/beliefs",
      "body": { "agentId": "uuid", "category": "principle", "statement": "..." }
    },
    {
      "description": "Get bootstrap recommendations from open-source agent templates",
      "method": "POST",
      "endpoint": "/api/bootstrap/recommend",
      "body": { "agentId": "uuid" }
    }
  ]
}

Next steps use real IDs from the current response (not templates). Error responses (4xx/5xx) do not include next_steps.


OpenAPI / JSON Schema

An OpenAPI 3.1 specification is available at /openapi.json. This markdown document is the canonical API reference with more detail and examples.