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):
orgId(UUID) — the organization's database IDorgSlug(string) — the organization's URL slug (e.g.,geeks-in-the-woods)- API key inference — when using API key auth, the org is automatically inferred from the key if neither
orgIdnororgSlugis 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.
| Tier | Limit | Applies To |
|---|---|---|
| General | 200 req/min | Most endpoints |
| Heavy | 30 req/min | Sync, PBD processing |
| Bulk | 10 req/min | Bulk create/update endpoints |
| Auth | 20 req/min | API 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):
X-RateLimit-Limit— requests allowed per windowX-RateLimit-Remaining— requests remaining in current windowX-RateLimit-Reset— Unix timestamp when the window resets
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:
- axiom — foundational truths
- principle — guiding rules
- voice — communication style
- preference — tendencies and defaults
- boundary — lines they won't cross
Responsibilities
What an agent is accountable for. 5 categories:
- ownership — what they own
- execution — how they get things done
- collaboration — how they work with others
- deliverables — what they produce
- monitoring — what they watch
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:
- 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. - Extract:
POST /api/v1/pbd/processtriggers 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 ajobIdfor progress tracking via the Background Jobs endpoints. - 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 thebelief_sourcestable.
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:
GET /api/v1/observations?agentId=...— view extracted observations (supportscontentItemId,signalIdfilters,limit/offset)GET /api/v1/signals?agentId=...— view clustered signals (supportspromoted,minNCountfilters)POST /api/v1/pbd/process— trigger pipeline for an agent (returnsjobId)GET /api/v1/jobs/{jobId}— track pipeline progress
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:
| Param | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | no* | Organization ID |
| orgSlug | string | no* | Organization slug (alternative to orgId) |
| groupId | string (uuid) | no | Filter by group |
| include | string | no | Comma-separated joins: beliefs, responsibilities (returns identity data inline) |
| limit | number | no | Max results (default: 100) |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | yes | Organization ID |
| name | string | yes | Agent name |
| description | string | no | Agent description |
| groupId | string (uuid) | no | Group to assign agent to |
| identityMarkers | string[] | no | Identity marker tags |
| jobTitle | string | no | Job title |
| avatarPrompt | string | no | Prompt 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):
| Field | Type | Description |
|---|---|---|
| name | string | Agent name |
| description | string | Agent description |
| groupId | string (uuid) | Group ID |
| identityMarkers | string[] | Identity markers |
| minNCount | number | Minimum observation count |
| decayRate | number | Signal decay rate |
| jobTitle | string | Job title |
| avatarPrompt | string | Avatar generation prompt |
| location | string | Physical location |
| timezone | string | IANA 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:
| Field | Type | Required | Description |
|---|---|---|---|
| agents | array | yes | Array of agent updates (max 50) |
Each agent object:
| Field | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | yes | Agent ID |
| name | string | no | Agent name |
| location | string | no | Physical location |
| timezone | string | no | IANA timezone |
| jobTitle | string | no | Job 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:
| Param | Type | Required | Description |
|---|---|---|---|
| since | string (ISO 8601) | yes | Date 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:
| Param | Type | Required | Description |
|---|---|---|---|
| tier | string | no | Filter by tier (e.g., planner, worker, reviewer) |
| limit | number | no | Max results (default: 100) |
| offset | number | no | Pagination 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:
| Field | Type | Required | Description |
|---|---|---|---|
| environmentId | string (uuid) | yes | Environment ID |
| modelId | string (uuid) | yes | Model ID |
| isPrimary | boolean | no | Primary model flag (default: true) |
| tier | string | no | Freeform 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:
| Param | Type | Required | Description |
|---|---|---|---|
| environmentId | string (uuid) | yes | Environment ID |
| modelId | string (uuid) | yes | Model ID |
| tier | string | no | Tier 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:
| Param | Type | Default | Description |
|---|---|---|---|
| type | string | "agent" | File type: agent, soul, responsibilities, identity |
| download | boolean | false | Set 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:
| Field | Type | Required | Description |
|---|---|---|---|
| label | string | no | Optional 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:
| Param | Type | Required | Description |
|---|---|---|---|
| a | string (uuid) | yes | Snapshot A ID (base) |
| b | string (uuid) | yes | Snapshot 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):
| Field | Type | Description |
|---|---|---|
| seed | number | Deterministic seed for reproducible output |
| config | object | Override 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:
- Call twice without a seed to get different prompts (different ordering, different belief subsets)
- Call twice with the same seed to get identical prompts
- Starred beliefs/responsibilities are always included and rendered first in stable order
- Axioms and boundaries are never sampled out (core identity and safety constraints)
emphasisis a voice belief promoted as dominant tone (~40% of calls)
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:
| Field | Type | Required | Description |
|---|---|---|---|
| sourceType | string | yes | One of: org_belief, group_belief, org_responsibility, group_responsibility |
| sourceId | string (uuid) | yes | ID 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:
| Param | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | no* | Filter by agent |
| groupId | string (uuid) | no* | List all agent beliefs in a group (includes agent name attribution) |
| category | string | no | Filter by category |
| status | string | no | Filter by status |
| starred | boolean | no | Filter by starred |
| hidden | boolean | no | Filter by hidden |
| limit | number | no | Max results |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID |
| category | string | yes | One of: axiom, principle, voice, preference, boundary |
| statement | string | yes | The belief statement |
| sourceType | string | no | Source type for attribution |
| quote | string | no | Source quote |
| nCount | number | no | Observation 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Belief statement |
| category | string | Belief category |
| displayOrder | number | Display order |
| status | string | pending, approved, or rejected |
Response (200): Updated belief object with _scope field.
Side effects:
status: "approved"→ setsapproved_byto current user,hiddento falsestatus: "rejected"→ setshiddento true- Regenerates prompt if status changed
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):
- Manual items:
{ "action": "deleted", "scope": "agent"|"group"|"org" } - PBD-discovered items:
{ "action": "hidden", "reason": "..." }
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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID |
| beliefs | array | yes | Array 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:
| Field | Type | Required | Description |
|---|---|---|---|
| ids | string[] (uuid) | yes | Array of belief IDs (from any scope) |
| updates | object | yes | Fields to update |
Updates object:
| Field | Type | Description |
|---|---|---|
| starred | boolean | Set starred flag |
| hidden | boolean | Set hidden flag |
| status | string | Set 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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID |
| category | string | yes | One of: ownership, execution, collaboration, deliverables, monitoring |
| statement | string | yes | The responsibility statement |
| nCount | number | no | Observation 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Responsibility statement |
| category | string | Responsibility category |
| displayOrder | number | Display order |
| status | string | pending, 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):
- Manual items:
{ "action": "deleted", "scope": "agent"|"group"|"org" } - PBD-discovered items:
{ "action": "hidden", "reason": "..." }
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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID |
| responsibilities | array | yes | Array 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:
| Field | Type | Required | Description |
|---|---|---|---|
| ids | string[] (uuid) | yes | Array of responsibility IDs (from any scope) |
| updates | object | yes | Fields to update |
Updates object:
| Field | Type | Description |
|---|---|---|
| starred | boolean | Set starred flag |
| hidden | boolean | Set hidden flag |
| status | string | Set 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:
| Param | Type | Required | Description |
|---|---|---|---|
| category | string | no | Filter by category |
| status | string | no | Filter by status (pending, approved, rejected) |
| starred | boolean | no | Filter by starred |
| hidden | boolean | no | Filter by hidden |
| limit | number | no | Max results |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| category | string | yes | One of: axiom, principle, voice, preference, boundary |
| statement | string | yes | The 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Belief statement |
| category | string | Belief category |
| displayOrder | number | Display order |
| status | string | pending, 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:
| Param | Type | Required | Description |
|---|---|---|---|
| category | string | no | Filter by category |
| status | string | no | Filter by status (pending, approved, rejected) |
| starred | boolean | no | Filter by starred |
| hidden | boolean | no | Filter by hidden |
| limit | number | no | Max results |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| category | string | yes | One of: ownership, execution, collaboration, deliverables, monitoring |
| statement | string | yes | The 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Responsibility statement |
| category | string | Responsibility category |
| displayOrder | number | Display order |
| status | string | pending, 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:
| Param | Type | Required | Description |
|---|---|---|---|
| category | string | no | Filter by category |
| status | string | no | Filter by status (pending, approved, rejected) |
| starred | boolean | no | Filter by starred |
| hidden | boolean | no | Filter by hidden |
| limit | number | no | Max results |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| category | string | yes | One of: axiom, principle, voice, preference, boundary |
| statement | string | yes | The 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Belief statement |
| category | string | Belief category |
| displayOrder | number | Display order |
| status | string | pending, 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:
| Param | Type | Required | Description |
|---|---|---|---|
| category | string | no | Filter by category |
| status | string | no | Filter by status (pending, approved, rejected) |
| starred | boolean | no | Filter by starred |
| hidden | boolean | no | Filter by hidden |
| limit | number | no | Max results |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| category | string | yes | One of: ownership, execution, collaboration, deliverables, monitoring |
| statement | string | yes | The 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):
| Field | Type | Description |
|---|---|---|
| statement | string | Responsibility statement |
| category | string | Responsibility category |
| displayOrder | number | Display order |
| status | string | pending, 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:
| Field | Type | Required |
|---|---|---|
| name | string | yes |
| slug | string | yes |
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):
| Field | Type | Description |
|---|---|---|
| name | string | Organization name |
| autoApproveBeliefs | boolean | Auto-approve new beliefs |
| autoApproveResponsibilities | boolean | Auto-approve new responsibilities |
| autoPublishConversations | boolean | Auto-publish completed conversations as content (default: false) |
| autoPromoteSharedBeliefs | boolean | Automatically promote consensus-detected beliefs to org level |
| autoPromoteSharedResponsibilities | boolean | Automatically promote consensus-detected responsibilities to org level |
| consensusSimilarityThreshold | number | Cosine similarity threshold for consensus detection (0.0–1.0) |
| consensusAgentPercentage | number | Minimum 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:
| Field | Type | Required | Description |
|---|---|---|---|
| confirmSlug | string | yes | Must 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:
| Field | Type | Required |
|---|---|---|
| label | string | no |
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:
| Param | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | no* | Organization ID |
| orgSlug | string | no* | Organization slug |
| limit | number | no | Max results (default: 100) |
| offset | number | no | Skip 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:
| Field | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
| name | string | yes |
| description | string | no |
Response (201): Created group object.
Update Group
PATCH /api/groups/{groupId}
Request Body (all optional):
| Field | Type | Description |
|---|---|---|
| name | string | Group name |
| description | string | Group description |
| displayOrder | number | Display order |
| managerAgentId | string (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:
| Param | Type | Required |
|---|---|---|
| orgId | string (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:
| Field | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
| provider | string | yes |
| modelId | string | yes |
| name | string | yes |
| providerUrl | string | no |
| description | string | no |
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:
| Param | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
Response (200): Array of environment objects ordered by display_order, created_at.
Create Environment
POST /api/environments
Request Body:
| Field | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
| name | string | yes |
| slug | string | yes |
| description | string | no |
| isDefault | boolean | no (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:
| Field | Type | Required |
|---|---|---|
| modelId | string (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:
| Field | Type | Required |
|---|---|---|
| modelId | string (uuid) | yes |
Response (200): { "success": true }
Conversations
Terminology note: The API URLs use "conversations" and route params use
sessionId. The underlying database table isconversation_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:
| Param | Type | Required |
|---|---|---|
| agentId | string (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:
| Field | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | yes | Organization ID |
| title | string | no | Conversation title |
| participants | array | yes | List of participants |
Participant object:
| Field | Type | Required | Description |
|---|---|---|---|
| participantType | string | yes | agent or human |
| displayName | string | yes | Display name |
| agentId | string (uuid) | yes (if agent) | Agent ID |
| userId | string (uuid) | no | User ID (for humans) |
| role | string | no | Role (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):
| Field | Type | Description |
|---|---|---|
| title | string | Conversation title |
| status | string | active, completed, or archived |
| ended_at | string (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:
| Field | Type | Required |
|---|---|---|
| participantId | string (uuid) | yes |
| content | string | yes |
| metadata | object | no |
Mode 2 — Batch messages:
| Field | Type | Required |
|---|---|---|
| messages | array | yes |
Each message: { participantId, content, metadata }
Mode 3 — Format raw transcript:
| Field | Type | Required |
|---|---|---|
| format | boolean (true) | yes |
| rawText | string | yes |
| agentName | string | yes |
| otherParticipantName | string | no |
| otherParticipantType | string | no (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:
| Field | Type | Required |
|---|---|---|
| agentId | string (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:
| Param | Type | Required |
|---|---|---|
| agentId | string (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:
| Field | Type | Required |
|---|---|---|
| rawText | string | yes |
| agentName | string | yes |
| otherParticipantName | string | no (default: Human) |
| otherParticipantType | string | no (default: human) |
| title | string | no |
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:
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
| agentId | string (uuid) | yes | - | Agent to list content for |
| sourceId | string (uuid) | no | - | Filter by content source |
| sourceType | string | no | - | Filter by type: github_commit, github_file, tweet, substack_post, conversation |
| status | string | no | - | Filter by status: imported or hidden |
| limit | number | no | 50 | Max results |
| offset | number | no | 0 | Skip N results |
Response (200):
{ "data": [...], "count": 123 }
Update Content Item
PATCH /api/content-items/{itemId}
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
| status | string | yes | Must 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 association — agentId 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:
| Param | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | no* | Filter by agent |
| orgId | string (uuid) | no* | Filter by organization |
| limit | number | no | Max results (default: 100) |
| offset | number | no | Skip 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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID — all content sources must be associated with an agent |
| orgId | string (uuid) | no | Organization ID (required for org resolution if not using API key auth) |
| platform | string | yes | github, website, twitter, rss, or linkedin |
| config | object | no | Source configuration (platform-specific) |
| githubConnectionId | string (uuid) | no | GitHub connection for private repos |
| xConnectionId | string (uuid) | no | X connection for Twitter sources |
Config object (for platform: "github"):
| Field | Type | Default | Description |
|---|---|---|---|
| repoUrl | string | - | Full GitHub URL (owner/repo extracted automatically) |
| owner | string | - | GitHub owner (alternative to repoUrl) |
| repo | string | - | GitHub repo name (alternative to repoUrl) |
| branch | string | "main" | Branch to import from |
| import_commits | boolean | true | Import commit messages as content |
| import_files | boolean | false | Import file contents as content (prose files only - see below) |
| included_folders | string[] | [] | Only import from these folders (empty = all) |
| excluded_folders | string[] | [] | Exclude these folders from import |
| include_paths | string[] | [] | 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"):
| Field | Type | Default | Description |
|---|---|---|---|
| domain | string | — | Root domain to crawl (e.g., "example.com") |
| discovery | string | "llms_txt" | How to find pages: llms_txt, sitemap, or manual |
| urls | string[] | [] | Explicit URLs to import (required when discovery: "manual") |
| url_pattern | string | — | Regex to filter discovered URLs (e.g., "/blog/.*") |
| max_pages | number | 50 | Maximum pages to import per sync |
| sync_interval_hours | number | 24 | Hours between auto-syncs |
| extract_mode | string | "article" | Content extraction strategy: article, full, or markdown |
Config object (for platform: "twitter"):
| Field | Type | Default | Description |
|---|---|---|---|
| username | string | — | X/Twitter username to sync tweets from |
| exclude_retweets | boolean | true | Exclude retweets |
| exclude_replies | boolean | true | Exclude replies |
| max_pages | number | 3 | Max pages to fetch (100 tweets per page) |
| sync_interval_hours | number | 24 | Hours between auto-syncs |
Config object (for platform: "rss"):
| Field | Type | Default | Description |
|---|---|---|---|
| feed_url | string | — | RSS 2.0 or Atom feed URL (e.g., "https://blog.example.com/feed") |
| max_entries | number | 50 | Maximum entries to import per sync |
| sync_interval_hours | number | 24 | Hours 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):
llms_txt— fetches the site's/llms.txtfile and imports the URLs listed theresitemap— parses/sitemap.xml(orsitemap_index.xml) to discover pagesmanual— imports only the URLs explicitly listed inurls
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):
| Field | Type | Description |
|---|---|---|
| config | object | Platform-specific configuration |
| enabled | boolean | Enable/disable source |
| name | string | Source display name |
| agentId | string (uuid) | Reassign to a different agent |
| githubConnectionId | string (uuid) | Change GitHub connection |
| xConnectionId | string (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:
| Field | Type | Description |
|---|---|---|
| commits_imported | number | New commits imported (GitHub only) |
| files_imported | number | New files imported (GitHub only) |
| pages_imported | number | New pages imported (website only) |
| pages_skipped | number | Pages skipped (already imported or filtered out) |
| pages_blocked | number | Pages blocked by robots.txt or url_pattern |
| tweets_imported | number | New tweets imported (Twitter only) |
| rss_entries_imported | number | New RSS entries imported (RSS only) |
| affected_agent_ids | string[] | All agents that received content |
| scope | string | Source scope (always agent) |
| errors | string[] | 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:
| Param | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
Response (200): Array of connections (token never exposed).
Create GitHub Connection (PAT)
POST /api/github/connections
Request Body:
| Field | Type | Required |
|---|---|---|
| orgId | string (uuid) | yes |
| token | string | yes |
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:
| Param | Type | Required |
|---|---|---|
| orgId | string (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:
| Field | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | no* | Organization ID |
| orgSlug | string | no* | Organization slug |
| label | string | yes | Display label for the connection |
| xUsername | string | yes | X/Twitter username |
| xUserId | string | yes | X/Twitter user ID |
| apiKey | string | yes | X API key |
| apiSecret | string | yes | X API secret |
| accessToken | string | yes | X access token |
| accessTokenSecret | string | yes | X 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:
| Field | Type | Required |
|---|---|---|
| id | string (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:
| Field | Type | Required | Description |
|---|---|---|---|
| file | file | yes | LinkedIn data export ZIP file (max 10MB) |
| orgId | string (uuid) | no* | Organization ID |
| orgSlug | string | no* | Organization slug |
| agentId | string (uuid) | yes | Agent 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:
| Param | Type | Required | Default |
|---|---|---|---|
| agentId | string (uuid) | yes | — |
| contentItemId | string (uuid) | no | — |
| signalId | string (uuid) | no | — |
| limit | number | no | 100 |
| offset | number | no | 0 |
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:
| Param | Type | Required | Default |
|---|---|---|---|
| agentId | string (uuid) | yes | — |
| promoted | string | no | — ("true" or "false") — checks both promoted_to_belief_id AND promoted_to_responsibility_id |
| minNCount | number | no | — |
| predict_category | boolean | no | false — 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):
| Field | Type | Description |
|---|---|---|
| owner | string | GitHub owner (if scraping single repo) |
| repo | string | GitHub 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:
| Field | Type | Required |
|---|---|---|
| agentId | string (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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | yes | Agent ID |
| beliefs | array | no | [{ category, statement, nCount? }, ...] |
| responsibilities | array | no | [{ category, statement, nCount? }, ...] |
| status | string | no | Status 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:
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string (uuid) | no* | Agent to process |
| orgId | string (uuid) | no* | Organization to process (all agents) |
| orgSlug | string | no* | Organization slug (alternative to orgId) |
| force | boolean | no | Re-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:
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
| owner | string | yes | - | GitHub repo owner |
| repo | string | yes | - | GitHub repo name |
| branch | string | no | main | Branch to browse |
| connectionId | string (uuid) | no | - | GitHub connection ID (required for private repos) |
| mode | string | no | folders | folders 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:
| Param | Type | Required | Default |
|---|---|---|---|
| agentId | string (uuid) | no | — |
| orgId | string (uuid) | no | — |
| status | string | no | — (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:
| Field | Type | Required | Description |
|---|---|---|---|
| orgId | string (uuid) | yes | Organization 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:
| Param | Type | Required | Description |
|---|---|---|---|
| dryRun | string | no | true (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" }
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid fields |
| 401 | Unauthorized — missing or invalid auth token |
| 404 | Not found — resource doesn't exist or not accessible |
| 409 | Conflict — duplicate resource (slug, model ID, etc.) |
| 500 | Server 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:
- Content items:
limit=50,offset=0 - Observations:
limit=100,offset=0
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:
PATCH /api/agents/bulk— update up to 50 agents at once (name, location, timezone, jobTitle)POST /api/beliefs/bulk— create up to 100 beliefs at oncePATCH /api/beliefs/bulk— bulk star, hide, or approve beliefs across all scopes (agent, group, org)POST /api/responsibilities/bulk— create up to 100 responsibilities at oncePATCH /api/responsibilities/bulk— bulk star, hide, or approve responsibilities across all scopes (agent, group, org)POST /api/bootstrap/apply— batch upsert beliefs/responsibilities from bootstrap recommendations (updates nCount/status on conflict)
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 Similarity | Action | Speed |
|---|---|---|
| >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 normally | Fast (no LLM) |
When a semantic duplicate is detected, the endpoint returns 200 (not 201) with the existing item plus two extra fields:
_deduplicated: true— signals this was merged, not created_matched_similarity: 0.72— the cosine similarity score of the match
Existing items missing embeddings are backfilled automatically during dedup checks.
Response Code Notes
- Most create operations return
201 Created - Semantic duplicate detection returns
200 OKwith_deduplicated: true(merged into existing item) POST /api/bootstrap/applyreturns200 OK(not 201) despite creating resources — this is because it's a batch operation that may partially succeedPOST /api/pbd/processreturns202 Acceptedwith ajobIdsince processing happens in the background
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.