Studio Backend
Domain Model
Most domain types are defined in promethist-common and promethist-lib, because the engine reads the
same documents the platform writes. The platform adds its own documents for what only Studio needs.
In the Studio and in user-facing text an account is called an account; in code and in the API it is a
tenant.
Hierarchy
Account (Tenant)
├── members and invitations accessControlList, invitation
├── account settings accountSettings
├── billing details billingDetails, usage
└── Project
├── agents (revisions) agent
├── evaluations evaluationDefinition, evaluationInsightDefinition, ...
├── test suites testSuite (+ the bridge's own documents)
├── feedback surveys feedbackDefinition, feedbackQuestionDefinition
└── asset specifications, embedded in the project document:
knowledge, MCP servers, MCP connectors, webhooks, interactive content,
content navigation, skills
An account is the billing and access boundary; a project is a workspace whose agents share its assets.
Accounts
Tenant holds little more than a unique name and an optional logo. Any signed-in user can create one
(POST /api/v1/tenants); TenantService.createTenant then
-
gives the creator an
Ownerrow for the account, -
creates the Stripe customer (
BillingDetails, see Usage and Billing), -
and adds a first project — a copy of the seed project when tenant seeding is configured (
promethist.tenant-seeding.*), otherwise an empty default project. The REST endpoint always asks for one; the AI Assistant’s account creation creates the project in a separate step.
There is no endpoint that deletes an account or a project.
Projects
Project (common) is the document the engine reads to run a project’s agents. Besides name (unique
within the account), a globally unique ref, description, logo and colors it carries:
| Field | Meaning |
|---|---|
|
Terms and conditions (Markdown) the client app asks a user to accept before they can talk to the
project’s agents. The app remembers an acceptance by the exact text, so any change asks every user
again. Edited only through |
|
The project’s main agent — for example the coach of an Empower project. Evaluations can inject their results into it. |
|
The product package the project is built as: |
|
Who may use the project’s agents, see Identity Providers and External Roles. |
|
The project’s assets, embedded as lists in the project document (next section). |
Asset Specifications
Assets live inside the project document rather than in collections of their own, and each is addressed
by a ref that is unique within the project:
| List | Studio name | Content |
|---|---|---|
|
Knowledge |
Knowledge sources: |
|
Integrations (pre- and post-session) |
HTTP calls before a conversation ( |
|
Integrations (MCP) |
A customer’s MCP server the agent can call during a conversation: URL, transport (Streamable HTTP or SSE), an optional authentication header and the selected tools. |
|
Integrations (connectors) |
A connector from the platform’s catalog enabled for the project, with the selected tools. |
|
Interactive content |
Elements an agent can show or ask for during a conversation: |
|
Content navigation |
The menu of the client application: |
|
Skills |
Global skills assigned to the project (assignment is staff-only). |
The isGlobal flag of an asset decides whether every agent of the project gets it, or only agents that
reference it in their instructions with a tool reference such as @knowledge:pricing;,
@multimodal:product-photo;, @skill:negotiation; or, for one tool of an MCP integration,
@MCP:crm_lookupCustomer; (see Referencing
Integrations from Agents). The trailing semicolon is part of the syntax; text generated by the AI
Assistant is repaired deterministically when a model drops it (String.repairToolReferences() in
util/Functions.kt).
Because the specifications are polymorphic and stored with Spring Data’s _class discriminators, they
are read through hand-written Jackson converters in configuration/MongoConfig.kt. They hand Jackson the
stored document as JSON, with a UUID (such as the evaluation id of a navigation condition) as its
canonical string and any other binary value as base64. A new polymorphic specification type, or one
that nests another polymorphic value, needs its converter adjusted — otherwise the write succeeds and
the next read fails: the converter logs the item and puts null in its place. The project read leaves
such items out (Project.sanitized() in
repository/ProjectRepository.kt),
so the project, its asset listings and the Studio pages keep working without them, and the unreadable
items stay stored as they are. That holds because an existing project is never saved whole:
ProjectService
writes only the fields an edit changes (the settings, terms, identity providers or logo), together with
the lastModified and lastModifiedBy audit fields a save would set, and every asset write adds,
replaces or removes its own item.
Agents
Agent (common, collection agent) has three kinds. The kind is chosen at creation and cannot change.
Kind (AgentKind) |
Class | Content |
|---|---|---|
|
The full "digital employee": |
|
|
A single-purpose agent with |
|
|
A simulated user for test suites: a persona and scenario ( |
All kinds share ref, name, title, description, locales, the voice
(realtimeConfigurationId, a realtime configuration), the visual (visualProperties: avatar,
environment, camera preset, animation type), hasMemory, the attached evaluations
(evaluationDefinitionRefs, see Evaluations) and the template they were created from (templateRef,
templateRevision, templateVariables).
Revisions and States
An agent is identified by its ref, which is unique across the platform; each revision is a separate
document with its own id, a revision number and a state — Draft, Preview, Published or
Archived (AgentService).
| Operation | Effect |
|---|---|
Create ( |
Revision |
Edit ( |
Never changes a revision in place: it copies the latest non-archived revision, applies the request
and saves it as a new |
Promote to preview ( |
Only from |
Publish ( |
Only from |
Revert ( |
Makes revision |
Archive ( |
Archives one revision, or every revision of the ref. Agents are never deleted; "deleting" an agent in the Studio or the assistant archives all of its revisions. |
Clone ( |
A new agent (new ref) in |
So an agent has at most one Preview and one Published revision at any time. Client applications
address an agent by its pipeline key, agent:{ref} or agent:{ref}.{revision}.
Templates
Agent templates (RelationalAgentTemplate, BasicAgentTemplate) are global and versioned like agents.
A template presets the content fields, declares variables that are substituted into them, and can lock
individual fields so that agents created from it cannot change them. Everyone sees the published
templates (GET /api/v1/templates); authoring is staff-only (/api/v1/admin/templates), and publishing
a revision archives the previously published one.
Evaluations
An EvaluationDefinition (collection evaluationDefinition) describes what a language model judges
about a conversation. Its output data elements — insights in code, EvaluationInsightDefinition — are
typed:
| Type | Value |
|---|---|
|
Yes or no, with a label for each. |
|
An integer between |
|
Free text. |
|
One or more values from a fixed list — a classification. |
Elements can be put into groups (EvaluationGroupDefinition), which are scored together in one model
call. An evaluation may also define one featured result (a Bool or a Number), the headline figure
of the analytics and the condition of content-navigation items.
Further fields decide when and where an evaluation runs: executionModes (ON_CONVERSATION_END, or
PER_TURN with an optional lookbackWindow), and its destinations — shown to the user (userFacing),
to the Studio (adminFacing), injected into the next session or into the project’s featured agent, or
posted to a webhook. testMode marks the agent evaluations used by test suites, which are kept apart
from the regular ones.
Evaluations are versioned like agents: every revision is its own document with its own groups and
elements, sharing the evaluation’s ref, and moves Draft → Preview → Published → Archived. A
new evaluation is Draft revision 0; a Studio save writes a new Draft, and the assistant’s and the MCP
tools' per-item edits stack on one. An element keeps its ref from revision to revision, so its results
aggregate across revisions; a clone is a new evaluation with refs of its own. Deleting an evaluation
deletes every revision; nothing else removes one, as results point at the revision that produced them.
Agents link evaluations by ref (evaluationDefinitionRefs) and the engine runs the Published revision of
each, so only an evaluation with a Published revision can be attached; the API and the tools still speak
evaluation ids, of any revision, and answer with the Published one. An agent links only its own
project’s evaluations: the engine finds a definition by ref across projects but loads its elements by
the session’s project, so a link to another project’s evaluation would score nothing, and a save that
newly names one is refused. The shared test catalogue is used by copying instead — the MCP tool
copy_catalog_evaluation writes the catalogue evaluation’s Published revision into the caller’s project
as a Published test-mode evaluation of its own, with the master’s element refs kept so results stay
comparable, without its webhook, injection or user-facing settings, and with a line in its description
naming the master revision it holds, which is what makes a second call return the same copy. A testMode
evaluation is refused on any agent that is not a TEST agent. Publishing re-points the
content-navigation conditions on the featured result to the new revision. Such a condition can be met only
while that revision has a featured result of the kind its match reads — Always reads either — so removing
the featured result, switching its kind or deleting the evaluation leaves the item locked for every user
who has not unlocked it yet. Nothing blocks the change: the navigation editor warns about a condition that
cannot be met, and the assistant’s and the MCP tools' delete and publish name the items they leave locked.
The engine writes the results
as EvaluationResult (with the featured outcome) and per-element insight results; the platform reads them for the
analytics and the test-suite verdicts.
Data Saved Before Versioning
EvaluationVersioningMigration brings older data up to date at every start, and once more 15 minutes later,
when no pod of the previous version is left to save data the old way. It writes only what is missing
and deletes nothing:
-
Evaluation definitions saved without
ref,stateorrevisionget them stored — their own id as the ref,Published, revision0— because the engine finds an agent’s evaluations with a query onrefandstate, which does not match a missing field. -
Clones that still share their source’s ref get refs of their own. The
evaluationRefof their results follows, and in a project other than the source’s so do the agents that link the shared ref. -
Agent revisions that link evaluations only by id (
evaluationDefinitionIds) get the refs added; the ids stay. -
The
{ref, state}index, theevaluationIdindexes of elements and groups, and the unique{projectId, ref, revision}index are ensured.
It needs an engine of 2026.35 or newer on the same database, because agents saved now link their evaluations by ref alone. A rollback of the platform does not undo it, and a version from before versioning mishandles the migrated data: it lists every revision as an evaluation of its own, rewrites the revision it saves in place (an Archived one, which results point at, included) and shows no evaluations on agents saved since. After such a rollback, leave evaluations and the evaluations of agents unedited until the platform is forward again.
Members and Invitations
Membership is the set of access control list rows described in Roles and Access Control Lists. People join an account or a project in two ways:
-
Invitation (
invitationcollection) — an Owner invites email addresses; each gets an email rendered from a stored Mailgun template with a link to/invite/{id}. The invitation can be accepted only by the user whose email matches; accepting creates the ACL row. Pending invitations older than seven days are removed by a nightly job (when scheduling is enabled). -
Direct grant (
MemberService,POST /api/v1/{tenant|project}/{id}/members) — the ACL row is created immediately, and a user record is created for an address that has never signed in, so the grant applies on their first login.
Voices and Visuals
A voice is a RealtimeConfiguration (common): the realtime model and speech settings an agent’s
conversations run on, with gender and supported locales. They are global; staff manage them in the
Studio’s Admin → Voices (/api/v1/admin/realtime-configurations), everyone reads the active ones
(GET /api/v1/realtime-configurations).
Visuals — avatars and environments — are not stored by the platform. They come from the public asset
bundle index at https://repository.promethist.ai/public/arc/bundles/default/index.json
(VisualsVoicesFetcher, only entries marked public), and an agent stores the chosen refs in its
visualProperties.
Conversation Records
The engine writes the record of every conversation into the shared database; the platform only reads it.
| Collection | Content |
|---|---|
|
One conversation: agent and revision, user, project, |
|
One exchange of a session: input and output, transcript, locale, |
|
The evaluations of a session. |
|
Answers to feedback surveys. |
|
Usage records of realtime conversations, with their duration, from which billing and the analytics' conversation length are computed. |
The engine also indexes turns and LLM calls into Elasticsearch; the MCP server’s conversation-data tools read those indexes.
Platform-Owned Collections
| Collection | Content |
|---|---|
|
The account privacy settings and the log of every change. |
|
Stripe customer per account, purchasable packages, and the usage pipeline (Usage and Billing). |
|
The extracted chunks of each knowledge source (the first 200) and its generated description. |
|
Test suites (Test Suites). |
|
The AI Assistant’s model memory and the chat history shown in the UI. |
|
Records behind the capability links of exports and uploads; export files themselves are in GridFS. |
|
LiveAvatar API keys of accounts and projects. |
|
Cached session summaries. |