Agents are configurable AI assistants. Each agent has an LLM provider, a system prompt, and a set of enabled tools.
Key properties:
Property
Description
namespace / name
Unique identifier (e.g., support/ticket-agent)
llm_provider_id
LLM provider to use (null = system default)
model
Model override (null = provider's default)
system_prompt
Jinja2 template for the system message
temperature
Sampling temperature (default: 0.7)
max_tokens
Max token limit for responses
input_schema
JSON Schema for validating chat input variables
output_schema
JSON Schema for validating agent output
initial_messages
Few-shot example messages
enabled_functions
Functions available as tools (list of namespace/name)
function_parameters
Default parameter values per function (supports Jinja2)
enabled_agents
Other agents callable as sub-agents
enabled_skills
Skills available to the agent
enabled_queries
Database queries available as tools
query_parameters
Default query parameter values
enabled_collections
File collections the agent can access. Plain string (readonly) or {"collection": "namespace/name", "access": "readonly|readwrite"}. Readwrite enables write/edit/delete file tools.
enabled_stores
Stores the agent can access. List of {"store": "namespace/name", "access": "readonly"} or {"store": "namespace/name", "access": "readwrite"}
enabled_connectors
Connectors available as tools. List of {"connector": "namespace/name", "operations": [...], "parameters": {"op_name": {"param": "value"}}}. Parameters support Jinja2 templates and are locked (hidden from LLM).
POST /api/v1/agents #Create agentGET /api/v1/agents #List agentsGET /api/v1/agents/{namespace}/{name} # Get agentPUT /api/v1/agents/{namespace}/{name} # Update agentDELETE /api/v1/agents/{namespace}/{name} # Delete agent
POST /api/v1/agents #Create agentGET /api/v1/agents #List agentsGET /api/v1/agents/{namespace}/{name} # Get agentPUT /api/v1/agents/{namespace}/{name} # Update agentDELETE /api/v1/agents/{namespace}/{name} # Delete agent
POST /api/v1/agents #Create agentGET /api/v1/agents #List agentsGET /api/v1/agents/{namespace}/{name} # Get agentPUT /api/v1/agents/{namespace}/{name} # Update agentDELETE /api/v1/agents/{namespace}/{name} # Delete agent
Runtime endpoints (chats):
POST /agents/{namespace}/{name}/invoke #Invoke(sync request/response)POST /agents/{namespace}/{name}/chats #Create chatGET /chats #List user's chats
GET /chats/{id} # Get chat withmessagesPUT /chats/{id} # Update chatDELETE /chats/{id} # Delete chatPOST /chats/{id}/messages #Send messagePOST /chats/{id}/messages/stream #Send message(SSE streaming)GET /chats/{id}/stream/{channel_id} # Reconnect to active streamPOST /chats/{id}/approve-tool/{tool_call_id} # Approve/reject a tool call
POST /agents/{namespace}/{name}/invoke #Invoke(sync request/response)POST /agents/{namespace}/{name}/chats #Create chatGET /chats #List user's chats
GET /chats/{id} # Get chat withmessagesPUT /chats/{id} # Update chatDELETE /chats/{id} # Delete chatPOST /chats/{id}/messages #Send messagePOST /chats/{id}/messages/stream #Send message(SSE streaming)GET /chats/{id}/stream/{channel_id} # Reconnect to active streamPOST /chats/{id}/approve-tool/{tool_call_id} # Approve/reject a tool call
POST /agents/{namespace}/{name}/invoke #Invoke(sync request/response)POST /agents/{namespace}/{name}/chats #Create chatGET /chats #List user's chats
GET /chats/{id} # Get chat withmessagesPUT /chats/{id} # Update chatDELETE /chats/{id} # Delete chatPOST /chats/{id}/messages #Send messagePOST /chats/{id}/messages/stream #Send message(SSE streaming)GET /chats/{id}/stream/{channel_id} # Reconnect to active streamPOST /chats/{id}/approve-tool/{tool_call_id} # Approve/reject a tool call
Invoke endpoint: A synchronous request/response alternative to the two-step chat flow. Intended for integrations (Slack, Telegram, webhooks) that need a simple call-and-response.
# Simple invokecurl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "What is the status of order #123?"}'
# → {"reply":"Your order is on its way...","chat_id":"c_abc123"}
# With session key(conversation continuity)curl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Follow up on that", "session_key": "slack:U09ABC123"}'
# → Same chat_idas previous call withthissession_key
# Simple invokecurl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "What is the status of order #123?"}'
# → {"reply":"Your order is on its way...","chat_id":"c_abc123"}
# With session key(conversation continuity)curl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Follow up on that", "session_key": "slack:U09ABC123"}'
# → Same chat_idas previous call withthissession_key
# Simple invokecurl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "What is the status of order #123?"}'
# → {"reply":"Your order is on its way...","chat_id":"c_abc123"}
# With session key(conversation continuity)curl -X POST https://yourdomain.com/agents/support/helper/invoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Follow up on that", "session_key": "slack:U09ABC123"}'
# → Same chat_idas previous call withthissession_key
session_key: Maps an external identifier (Slack channel, Telegram chat, WhatsApp number) to a persistent Sinas chat. One chat per (agent_id, session_key) pair.
reset: true: Archives the existing session and starts a new conversation.
input: Agent input variables, only used when creating a new chat.
Streams internally, returns assembled reply as a single JSON payload.
How chat works:
Create a chat linked to an agent (optionally with input variables validated against input_schema)
Send a message — Sinas builds the conversation context with the system prompt, preloaded skills, message history, and available tools
The LLM generates a response, possibly calling tools
If tools are called, Sinas executes them (in parallel where possible) and sends results back to the LLM for a follow-up response
The final response is streamed to the client via Server-Sent Events
Ephemeral chats can be created with a TTL by passing expires_in (seconds) when creating the chat. Expired chats are automatically hard-deleted (with all messages) by a scheduled cleanup job:
# Create an ephemeral chat that expiresin1hourcurl -X POST https://yourdomain.com/agents/default/default/chats \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"expires_in": 3600}'
# Create an ephemeral chat that expiresin1hourcurl -X POST https://yourdomain.com/agents/default/default/chats \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"expires_in": 3600}'
# Create an ephemeral chat that expiresin1hourcurl -X POST https://yourdomain.com/agents/default/default/chats \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"expires_in": 3600}'
Chat archiving — Chats can be archived via PUT /chats/{id} with {"archived": true}. Archived chats are hidden from the default list but can be included with ?include_archived=true.
Agent-to-agent calls go through the Redis queue so sub-agents run in separate workers, avoiding recursive blocking. Results stream back via Redis Streams.
Function parameter defaults pre-fill values when an agent calls a function. Supports Jinja2 templates referencing the agent's input variables:
System tools are opt-in platform capabilities beyond the normal function/query toolkit. Enable them via the system_tools property on agents. Each tool is either a simple string (no config needed) or an object with a name and tool-specific configuration.
The databaseIntrospection tool requires a connections list. The agent can only introspect connections listed in its config. This uses the same connection pool as regular queries and includes annotations from the semantic layer (table/column display names and descriptions).
Collection file tools (readwrite access):
When an agent has access: readwrite on an enabled collection, it gets additional tools:
write_file_{ns}_{name}(filename, content) — Write/overwrite a file (creates new version)
edit_file_{ns}_{name}(filename, old_string, new_string) — Surgical edit via exact string replacement
delete_file_{ns}_{name}(filename) — Delete a file
These are in addition to the read tools (search_collection_*, get_file_*) that every enabled collection provides. get_file supports offset and limit parameters for reading specific line ranges of large files.
Functions
Functions are Python code that runs in isolated Docker containers. They can be used as agent tools, triggered by webhooks or schedules, or executed directly.
Key properties:
Property
Description
namespace / name
Unique identifier
code
Python source code
description
Shown to the LLM when used as an agent tool
input_schema
JSON Schema for input validation
output_schema
JSON Schema for output validation
shared_pool
Run in shared container instead of sandbox container (admin-only)
requires_approval
Require user approval when called by an agent
timeout
Per-function timeout in seconds (overrides global FUNCTION_TIMEOUT, default: null)
Function signature
The entry point must be named handler. It receives two arguments — input_data (validated against input_schema) and context (execution metadata):
def handler(input_data,context):
# input_data:dict validated against input_schema
#
# context dict — always present:
# {
# "user_id":str, # IDof the user who triggered execution
# "user_email":str, # Emailof the triggering user
# "access_token":str, # Short-lived JWT forcalling the Sinas API
# "execution_id":str, # Unique execution ID
# "trigger_type":str, # "AGENT" | "API" | "WEBHOOK" | "SCHEDULE" | "CDC" | "HOOK" | "MANUAL"
# "chat_id":str, # Chat ID(when triggered by an agent,empty otherwise)
# "secrets":dict, # Decrypted secrets(shared poolonly):{"NAME":"value"}
# }return{"result":"value"} # Must match output_schema
def handler(input_data,context):
# input_data:dict validated against input_schema
#
# context dict — always present:
# {
# "user_id":str, # IDof the user who triggered execution
# "user_email":str, # Emailof the triggering user
# "access_token":str, # Short-lived JWT forcalling the Sinas API
# "execution_id":str, # Unique execution ID
# "trigger_type":str, # "AGENT" | "API" | "WEBHOOK" | "SCHEDULE" | "CDC" | "HOOK" | "MANUAL"
# "chat_id":str, # Chat ID(when triggered by an agent,empty otherwise)
# "secrets":dict, # Decrypted secrets(shared poolonly):{"NAME":"value"}
# }return{"result":"value"} # Must match output_schema
def handler(input_data,context):
# input_data:dict validated against input_schema
#
# context dict — always present:
# {
# "user_id":str, # IDof the user who triggered execution
# "user_email":str, # Emailof the triggering user
# "access_token":str, # Short-lived JWT forcalling the Sinas API
# "execution_id":str, # Unique execution ID
# "trigger_type":str, # "AGENT" | "API" | "WEBHOOK" | "SCHEDULE" | "CDC" | "HOOK" | "MANUAL"
# "chat_id":str, # Chat ID(when triggered by an agent,empty otherwise)
# "secrets":dict, # Decrypted secrets(shared poolonly):{"NAME":"value"}
# }return{"result":"value"} # Must match output_schema
Legacy: Functions named after the resource name (e.g. def send_email(input_data, context)) still work but log a deprecation warning. Migrate to def handler(...).
The access_token lets functions call back into the Sinas API with the triggering user's identity — useful for reading state, triggering other functions, or accessing any other endpoint.
Trigger-specific input_data
Depending on how the function is invoked, input_data is populated differently:
Trigger
input_data contents
AGENT / API / MANUAL / SCHEDULE
Values matching your input schema, provided by the caller
WEBHOOK
Webhook default_values merged with request body/query params
Functions used as message hooks (configured in agent's hooks field) can return:
Return value
Effect
None or {}
Pass through unchanged
{"content": "..."}
Mutate the message content
{"block": true, "reply": "..."}
Block the pipeline, return reply to client (sync hooks only)
Async hooks run fire-and-forget. For on_assistant_message async hooks, the return value retroactively updates the stored message (not the already-streamed response).
Interactive input (shared containers only)
Functions running in shared containers (shared_pool=true) can call input() to pause and wait for user input:
def handler(input_data,context):name = input("What is your name?") # Pauses executionconfirm = input(f"Confirm {name}?") # Can call multiple timesreturn{"name":name,"confirmed":confirm}
def handler(input_data,context):name = input("What is your name?") # Pauses executionconfirm = input(f"Confirm {name}?") # Can call multiple timesreturn{"name":name,"confirmed":confirm}
def handler(input_data,context):name = input("What is your name?") # Pauses executionconfirm = input(f"Confirm {name}?") # Can call multiple timesreturn{"name":name,"confirmed":confirm}
When input() is called:
The execution status changes to AWAITING_INPUT with the prompt string
The function thread blocks until a resume value is provided
The calling agent or API client resumes execution with the user's response
Multiple input() calls are supported (each triggers a new pause/resume cycle)
In sandbox containers (shared_pool=false), calling input() raises a RuntimeError.
Execution: Functions run in pre-warmed Docker containers from a managed pool. Input is validated before execution, output is validated after. All executions are logged with status, duration, input/output, and any errors.
Write-only credential store. Values are encrypted at rest and never returned via the API. Secrets are available in function context as context['secrets'] — only for shared pool (trusted) functions. Connectors resolve auth from secrets automatically.
Visibility:
shared (default) — global, available to all users and connectors
private — per-user, only used when that user triggers a connector or function
Private secrets override shared secrets with the same name. This enables multi-tenant patterns: admin sets a shared HUBSPOT_API_KEY, individual users can override with their own private key.
Endpoints:
POST /api/v1/secrets #Create or update(upsert byname+visibility)GET /api/v1/secrets #List names and descriptions(no values)GET /api/v1/secrets/{name} # Get metadata(no value)PUT /api/v1/secrets/{name} # Update value or descriptionDELETE /api/v1/secrets/{name} # Delete
POST /api/v1/secrets #Create or update(upsert byname+visibility)GET /api/v1/secrets #List names and descriptions(no values)GET /api/v1/secrets/{name} # Get metadata(no value)PUT /api/v1/secrets/{name} # Update value or descriptionDELETE /api/v1/secrets/{name} # Delete
POST /api/v1/secrets #Create or update(upsert byname+visibility)GET /api/v1/secrets #List names and descriptions(no values)GET /api/v1/secrets/{name} # Get metadata(no value)PUT /api/v1/secrets/{name} # Update value or descriptionDELETE /api/v1/secrets/{name} # Delete
secrets:
- name:SLACK_BOT_TOKEN
value:xoxb-... # omit to skip value update on re-apply
description:Slack bot OAuth token
# visibility defaults to "shared"inYAML config
secrets:
- name:SLACK_BOT_TOKEN
value:xoxb-... # omit to skip value update on re-apply
description:Slack bot OAuth token
# visibility defaults to "shared"inYAML config
secrets:
- name:SLACK_BOT_TOKEN
value:xoxb-... # omit to skip value update on re-apply
description:Slack bot OAuth token
# visibility defaults to "shared"inYAML config
Connectors
Named HTTP client configurations with typed operations. Executed in-process in the backend (no container overhead). Operations are exposed as agent tools. Auth resolved from the Secrets store at call time.
Endpoints:
POST /api/v1/connectors #Create connectorGET /api/v1/connectors #List connectorsGET /api/v1/connectors/{namespace}/{name} # Get connectorPUT /api/v1/connectors/{namespace}/{name} # Update connectorDELETE /api/v1/connectors/{namespace}/{name} # Delete connectorPOST /api/v1/connectors/parse-openapi #Parse OpenAPI spec(no connectorrequired)POST /api/v1/connectors/{namespace}/{name}/import-openapi #Import operations from OpenAPI spec into connectorPOST /api/v1/connectors/{namespace}/{name}/test/{operation} # Test an operation
POST /api/v1/connectors #Create connectorGET /api/v1/connectors #List connectorsGET /api/v1/connectors/{namespace}/{name} # Get connectorPUT /api/v1/connectors/{namespace}/{name} # Update connectorDELETE /api/v1/connectors/{namespace}/{name} # Delete connectorPOST /api/v1/connectors/parse-openapi #Parse OpenAPI spec(no connectorrequired)POST /api/v1/connectors/{namespace}/{name}/import-openapi #Import operations from OpenAPI spec into connectorPOST /api/v1/connectors/{namespace}/{name}/test/{operation} # Test an operation
POST /api/v1/connectors #Create connectorGET /api/v1/connectors #List connectorsGET /api/v1/connectors/{namespace}/{name} # Get connectorPUT /api/v1/connectors/{namespace}/{name} # Update connectorDELETE /api/v1/connectors/{namespace}/{name} # Delete connectorPOST /api/v1/connectors/parse-openapi #Parse OpenAPI spec(no connectorrequired)POST /api/v1/connectors/{namespace}/{name}/import-openapi #Import operations from OpenAPI spec into connectorPOST /api/v1/connectors/{namespace}/{name}/test/{operation} # Test an operation
Auth is resolved from the Secrets store. Private secrets override shared for the calling user — enabling multi-tenant patterns where each user can have their own API key for the same connector.
GET /executions #List executions(filterable by chat_id,trigger_type,status)GET /executions/{execution_id} # Get execution details(includes tool_call_idlink)POST /executions/{execution_id}/continue # Resume a paused execution withuser input
GET /executions #List executions(filterable by chat_id,trigger_type,status)GET /executions/{execution_id} # Get execution details(includes tool_call_idlink)POST /executions/{execution_id}/continue # Resume a paused execution withuser input
GET /executions #List executions(filterable by chat_id,trigger_type,status)GET /executions/{execution_id} # Get execution details(includes tool_call_idlink)POST /executions/{execution_id}/continue # Resume a paused execution withuser input
Executions include a tool_call_id field linking them to the tool call that triggered them, enabling execution tree visualization in the Logs page.
Input schema presets: The function editor includes built-in presets for common input/output schemas. Use the "Load preset" dropdown when editing schemas:
Preset
Use case
Pre-upload filter
Content filtering before file upload (receives file content, returns approved/rejected)
Post-upload
Processing after successful file upload (receives file_id, metadata)
Components are embeddable UI widgets built with JSX/HTML/JS and compiled by Sinas into browser-ready bundles. They can call agents, functions, queries, and access state through proxy endpoints.
Key properties:
Property
Description
namespace / name
Unique identifier
title
Display title
source_code
JSX/HTML/JS source
compiled_bundle
Auto-generated browser-ready JS
input_schema
JSON Schema for component configuration
enabled_agents
Agents the component can call
enabled_functions
Functions the component can call
enabled_queries
Queries the component can execute
enabled_components
Other components it can embed
enabled_stores
Stores the component can access ({"store": "ns/name", "access": "readonly|readwrite"})
css_overrides
Custom CSS
visibility
private, shared, or public
Components use the sinas-ui library (loaded from npm/unpkg) for a consistent look and feel.
Share links allow embedding components outside Sinas with optional expiration and view limits:
POST /api/v1/components/{namespace}/{name}/shares #Create share linkGET /api/v1/components/{namespace}/{name}/shares #List share linksDELETE /api/v1/components/{namespace}/{name}/shares/{token} # Revoke share link
POST /api/v1/components/{namespace}/{name}/shares #Create share linkGET /api/v1/components/{namespace}/{name}/shares #List share linksDELETE /api/v1/components/{namespace}/{name}/shares/{token} # Revoke share link
POST /api/v1/components/{namespace}/{name}/shares #Create share linkGET /api/v1/components/{namespace}/{name}/shares #List share linksDELETE /api/v1/components/{namespace}/{name}/shares/{token} # Revoke share link
Runtime rendering:
GET /components/{namespace}/{name}/render #Renderas full HTML pageGET /components/shared/{token} # Render via share token
GET /components/{namespace}/{name}/render #Renderas full HTML pageGET /components/shared/{token} # Render via share token
GET /components/{namespace}/{name}/render #Renderas full HTML pageGET /components/shared/{token} # Render via share token
Proxy endpoints allow components to call backend resources from the browser securely — the proxy enforces the component's enabled_* permissions:
POST /components/{ns}/{name}/proxy/queries/{q_ns}/{q_name}/execute #Execute queryPOST /components/{ns}/{name}/proxy/functions/{fn_ns}/{fn_name}/execute #Execute functionPOST/components/{ns}/{name}/proxy/states/{state_ns} # Access state
POST /components/{ns}/{name}/proxy/queries/{q_ns}/{q_name}/execute #Execute queryPOST /components/{ns}/{name}/proxy/functions/{fn_ns}/{fn_name}/execute #Execute functionPOST/components/{ns}/{name}/proxy/states/{state_ns} # Access state
POST /components/{ns}/{name}/proxy/queries/{q_ns}/{q_name}/execute #Execute queryPOST /components/{ns}/{name}/proxy/functions/{fn_ns}/{fn_name}/execute #Execute functionPOST/components/{ns}/{name}/proxy/states/{state_ns} # Access state
Queries
Queries are saved SQL templates that can be executed directly or used as agent tools.
Key properties:
Property
Description
namespace / name
Unique identifier
database_connection_id
Which database connection to use
description
Shown to the LLM as the tool description
operation
read or write
sql
SQL with :param_name placeholders
input_schema
JSON Schema for parameter validation
output_schema
JSON Schema for output validation
timeout_ms
Query timeout (default: 5000ms)
max_rows
Max rows returned for read operations (default: 1000)
Agent query parameters support defaults and locking:
query_parameters:"analytics/user_orders":"user_id":
value:"{{user_id}}" # Jinja2 template from agent input
locked:true # Hidden from LLM,always injected"status":
value:"pending"
locked:false # Shown to LLM withdefault,LLM can override
query_parameters:"analytics/user_orders":"user_id":
value:"{{user_id}}" # Jinja2 template from agent input
locked:true # Hidden from LLM,always injected"status":
value:"pending"
locked:false # Shown to LLM withdefault,LLM can override
query_parameters:"analytics/user_orders":"user_id":
value:"{{user_id}}" # Jinja2 template from agent input
locked:true # Hidden from LLM,always injected"status":
value:"pending"
locked:false # Shown to LLM withdefault,LLM can override
Locked parameters prevent the LLM from seeing or modifying security-sensitive values (like user_id).
Contextual parameters: The following parameters are automatically injected into every query execution and can be referenced in SQL:
Parameter
Description
:user_id
UUID of the user who triggered the query
:user_email
Email of the triggering user
These are always available regardless of the query's input_schema. Use them for row-level security:
-- Only returnorders belonging to the calling userSELECT * FROM orders WHERE created_by = :user_id
-- Audit trailINSERT INTO audit_log(action,performed_by)VALUES(:action,:user_email)
-- Only returnorders belonging to the calling userSELECT * FROM orders WHERE created_by = :user_id
-- Audit trailINSERT INTO audit_log(action,performed_by)VALUES(:action,:user_email)
-- Only returnorders belonging to the calling userSELECT * FROM orders WHERE created_by = :user_id
-- Audit trailINSERT INTO audit_log(action,performed_by)VALUES(:action,:user_email)