| name | vertesia-api |
| description | Reference for the Vertesia client API (@vertesia/client). Covers available APIs, common operations (objects, workflows, interactions, files), authentication helpers, and query patterns. Use when building tools or UI that call the Vertesia platform. |
Vertesia Client API
The @vertesia/client package provides a typed client for the Vertesia platform.
Getting the Client
const client = await context.getClient();
import { useUserSession } from "@vertesia/ui/session";
const { client, user, project } = useUserSession();
Available APIs
Studio APIs
| API | Description |
|---|
client.projects | Project management |
client.environments | Environment configuration |
client.interactions | Interaction execution and management |
client.prompts | Prompt templates |
client.runs | Run history and analytics |
client.account | Current account operations |
client.analytics | Analytics data |
client.apps | Application management |
client.iam | Identity and access management |
client.users | User management |
client.apikeys | API key management |
client.refs | Reference management |
client.commands | Command execution |
Store APIs
Accessible via client.store.* or shortcuts:
| API | Shortcut | Description |
|---|
client.store.objects | client.objects | Content object CRUD, search, upload |
client.store.files | client.files | File operations |
client.store.types | client.types | Content type definitions |
client.store.workflows | client.workflows | Workflow management, conversations |
client.store.collections | — | Collection management |
client.store.embeddings | — | Embedding operations |
client.store.agents | — | Agent operations |
Object Operations
const object = await client.objects.retrieve(objectId);
const objects = await client.objects.list({ limit: 100, offset: 0 });
const results = await client.objects.search({ query: 'search terms' });
const objects = await client.objects.find({
where: { status: 'active', type: 'article' },
limit: 50,
});
const { count } = await client.objects.count({ where: { status: 'active' } });
await client.objects.upload(payload);
const { url } = await client.objects.getDownloadUrl(fileUri);
const analysis = await client.objects.analyze(objectId);
Workflow / Conversation Operations
const { runs } = await client.store.workflows.listConversations({
interaction: 'app:my-app:collection:interaction-name',
page_size: 20,
});
const run = await client.store.workflows.getRunDetails(runId, workflowId);
const results = await client.store.workflows.searchRuns({
interaction: 'interaction-name',
status: 'completed',
page_size: 10,
});
await client.store.workflows.sendSignal(workflowId, runId, 'signal-name', payload);
Agent Runs (client.agents)
For listing/searching the runs that show up in chat sidebars and conversation pages.
const { items, total_count, next_cursor } = await client.agents.list({
limit: 100,
sort: 'started_at',
order: 'desc',
interaction: 'sys:my_agent',
status: 'completed',
});
const { hits, total } = await client.agents.search({
query: 'invoice review',
interaction: 'sys:my_agent',
status: ['running', 'completed'],
limit: 50,
});
Field-name gotchas (the ones that will bite you)
AgentRunResponse = AgentRun | ProcessRun. Only AgentRun (run_kind === 'agent') has interaction, topic, data. Common mistakes:
| Field | What it is | Trap |
|---|
run.interaction | The interaction code (e.g. "sys:GeneralAgent") — always set on agent runs | This is what the backend filter accepts and what stable IDs should use. |
run.interaction_name | A human-readable display name — optional, often empty | Don't filter or key on this; it'll silently drop most rows. Use it as a label fallback only. |
run.topic | Long topic generated by topic analysis — optional, set asynchronously | Empty for new conversations. |
run.title | Short title — optional, may be empty | Often unset. |
run.data.user_prompt | The user's first message — usually present | Best fallback for a label when topic+title are missing. |
Display-label fallback chain for conversation rows / sidebars:
const isAgent = run.run_kind === 'agent';
const label =
(isAgent ? run.topic : undefined) ||
run.title ||
(isAgent && typeof run.data === 'object' && run.data
? String((run.data as { user_prompt?: unknown }).user_prompt ?? '').trim() || undefined
: undefined) ||
'Untitled conversation';
Agent display + filter values — use interaction as the value, interaction_name || interaction as the label:
const value = isAgent ? run.interaction : undefined;
const label = run.interaction_name || run.interaction;
Sort limitations
agents.list only sorts by 'started_at' | 'updated_at'. To sort by any other column (topic, status, agent code), apply the sort client-side to the loaded page. agents.search doesn't accept a sort parameter at all (results come back by relevance).
Interaction Execution
const result = await client.interactions.execute({
interaction: 'app:my-app:collection:interaction-name',
data: { input: 'some data' },
});
const result = await client.interactions.executeAsync({
type: 'conversation',
interaction: 'app:my-app:collection:interaction-name',
interactive: true,
data: { user_prompt: 'Hello' },
});
Interaction naming convention
Interactions registered by plugins follow this pattern:
app:<plugin-name>:<collection-name>:<interaction-name>
Authentication Helpers
const jwt = await client.getRawJWT();
const payload = await client.getDecodedJWT();
const project = await client.getProject();
const account = await client.getAccount();
Client Initialization (standalone)
When creating the client directly (outside Vertesia host):
import { VertesiaClient } from "@vertesia/client";
const client = new VertesiaClient({
site: 'api.vertesia.io',
apikey: 'your-api-key',
sessionTags: ['your-session-tag'],
});
const client = await VertesiaClient.fromAuthToken(jwtToken);
Query Patterns
Use MongoDB-style query operators in find() and count():
{ where: { status: 'active' } }
{ where: { score: { $gt: 80, $lte: 100 } } }
{ where: { $and: [{ status: 'active' }, { type: 'article' }] } }
{ where: { $or: [{ status: 'draft' }, { status: 'review' }] } }
{ where: { category: { $in: ['news', 'blog'] } } }
Security: Never pass user input directly as query operators. Validate and whitelist allowed operators to prevent injection.
Security
Input Validation
Always validate user inputs before passing to API calls:
if (typeof objectId !== 'string' || !objectId.match(/^[0-9a-f]{24}$/)) {
throw new Error('Invalid object ID');
}
const object = await client.objects.retrieve(objectId);
Query Injection Prevention
Never construct queries from raw user input:
const filter = JSON.parse(userInput);
await client.objects.find({ where: filter });
await client.objects.find({
where: { status: { $eq: validatedStatus }, type: { $eq: validatedType } }
});
Authorization
Check authentication state before accessing protected resources:
const { client, user } = useUserSession();
if (!user) {
throw new Error('Not authenticated');
}
In tool server code, the SDK handles JWT validation automatically. Access the authenticated client:
async run(payload, context) {
const client = await context.getClient();
}
Error Handling
Never expose internal API details to users:
try {
await client.objects.delete(objectId);
} catch (error) {
console.error('Delete failed:', error);
throw new Error('Unable to delete item');
}
Sensitive Data
- Never log JWT tokens, API keys, or user credentials
- Use
VERTESIA_ALLOWED_ORGS to restrict tool server access to specific organizations
- Store secrets in environment variables, never in code