AI Enabler Built on Enterprise Data Models
DSiloed is a powerful AI enablement platform built on comprehensive enterprise data models. It provides everything you need to build intelligent, AI-powered applications with rich memory support, advanced chat features, and deep integration with leading AI providers.
Whether you're building customer service bots, intelligent automation systems, or knowledge management platforms, DSiloed gives you enterprise-grade data infrastructure combined with cutting-edge AI capabilities including persistent memory graphs, real-time chat, document processing, and autonomous agents.
This interactive guide will walk you through getting started with DSiloed, setting up AI models, leveraging memory systems, and building powerful AI-enabled enterprise applications.
DSiloed is built on a flexible, enterprise-grade data model designed for extensibility and integration. Here are the key components:
DSiloed is designed with an AI-first, API-driven approach. Every AI capability—from chat conversations to memory graphs to autonomous agents—is accessible through our comprehensive REST API and WebSocket connections. Build any AI-powered interface on top of our enterprise data foundation.
All sample applications in this demo are built using static JavaScript frameworks that interact with the API, demonstrating how you can create sophisticated AI-enabled applications with standard web technologies.
Ready to build with AI? Head to the Getting Started section to create your first tenant, or jump to Setting up AI to connect your first AI model. Check out Sample Apps to see real-world AI applications in action.
To get started with DSiloed, you need to create a tenant. A tenant is an isolated environment for your application data. Follow these steps to create your first tenant:
Visit the tenant signup page.
Fill in the following information:
Click "Create Tenant" to complete your registration.
Your tenant will be created, and you'll be redirected to the dashboard where you can access all the available applications.
Note: Each tenant is isolated by row based security, so each tenant's data is isolated
After creating your tenant, you'll be directed to the dashboard where you can access all available applications. Here's what you'll find:
{{ app.description }}
DSiloed includes powerful AI capabilities through integration with Large Language Models (LLMs). To enable AI features in your applications, you need to configure LLM models using the LLM Manager application.
This guide will walk you through setting up LLM models from various providers including Anthropic (Claude), OpenAI (GPT), Google (Gemini), xAI (Grok), and local Ollama installations.
DSiloed supports multiple LLM providers. Choose one of the following options based on your needs:
First, you'll need to obtain an API key from Anthropic to access Claude models:
Go to console.anthropic.com and sign up for an account if you don't have one.
Navigate to the API Keys section and create a new key:
Add credits to your Anthropic account to enable API usage. Even a small amount ($5-10) will provide substantial usage for development and testing.
To use OpenAI's GPT models (GPT-3.5, GPT-4), you'll need an OpenAI API key:
Go to platform.openai.com and sign up or log in to your account.
Navigate to API Keys section in your account settings:
Add credits to your OpenAI account. OpenAI uses a pay-as-you-go pricing model with different rates for each model.
To use Google's Gemini models, obtain an API key from Google AI Studio:
Go to makersuite.google.com and sign in with your Google account.
Create a new API key:
Make sure the Generative Language API is enabled in your Google Cloud Console for your project.
To use xAI's Grok models, you'll need an API key from xAI:
Go to console.x.ai and sign up or log in to your account.
Navigate to the API Keys section and create a new key:
xAI provides $25 in free credits every month. You can add additional credits through the billing section if needed. The free tier is generous for development and testing.
Ollama allows you to run LLMs locally on your own hardware, providing privacy and offline capability:
Download and install Ollama from ollama.ai for your operating system.
# macOS/Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows - Download installer from ollama.ai
Pull the models you want to use:
# Popular models
ollama pull llama2
ollama pull mistral
ollama pull codellama
ollama pull mixtral
Ollama runs as a local API server on port 11434. It starts automatically after installation. No API key is needed for local usage.
# Check if Ollama is running
ollama list
Once you have your API key (or Ollama installed), use the LLM Manager application to create an LLM Model by selecting from pre-configured model types:
From your dashboard, click on
In the LLM Manager, click "Create LLM Model" to open the model creation dialog. You'll see provider cards for each available LLM provider. Select a provider to view their available models:
Select a provider to view available models with their specifications:
Available Anthropic Models:
claude-opus-4-20250514claude-opus-4-20241129claude-sonnet-4-20250514claude-3-7-sonnet-20250219claude-3-5-haiku-20241022claude-3-haiku-20240307Available OpenAI Models:
gpt-4ogpt-4o-minio1o1-minio1-previewgpt-4-turbogpt-3.5-turboAvailable Google Models:
gemini-2.5-flash-lite-latestgemini-2.5-flash-latestgemini-2.5-pro-latestAvailable xAI Models:
grok-4-latestgrok-3-latestgrok-3-mini-latestAvailable Ollama Models (Local/Self-Hosted):
llama3.2:1bllama3.2:3bqwen2.5:7bdeepseek-coder-v2:16bmistral:7bmixtral:8x7bAfter selecting your model:
embedding_model_nameConnect Claude Code to your DSiloed tenant using the Model Context Protocol (MCP) to access all your data, conversations, memories, and platform capabilities directly from your development environment.
Claude Code is Anthropic's official CLI for Claude that integrates AI assistance directly into your terminal and development workflow. By connecting it to DSiloed's MCP server, Claude gains access to your tenant's data and can help you manage projects, conversations, documents, and more.
Navigate to your DSiloed dashboard to copy your authentication token:
Add the DSiloed MCP server configuration to your Claude Code settings:
The MCP server configuration is placed in the projects section of your
~/.claude.json file, where each project is keyed by its full directory path.
Each project can have its own mcpServers configuration.
~/.claude.jsonprojects sectionmcpServers object with your DSiloed configurationComplete ~/.claude.json file structure:
{
"projects": {
"/home/username/my-project": {
"mcpServers": {
"harmoniq": {
"command": "npx",
"args": [
"mcp-remote",
"https://dev.dsiloed.com/api/v1/mcp",
"--header",
"Authorization: Bearer YOUR_JWT_TOKEN_HERE"
]
}
}
}
}
}
/home/username/my-project with the full path to your project directoryYOUR_JWT_TOKEN_HERE with the JWT token you copied from the dashboardhttps://www.dsiloed.com/api/v1/mcphttps://dev.dsiloed.com/api/v1/mcpC:/Users/username/my-projectProduction example with actual token (truncated):
{
"projects": {
"/Users/john/projects/my-app": {
"mcpServers": {
"harmoniq": {
"command": "npx",
"args": [
"mcp-remote",
"https://www.dsiloed.com/api/v1/mcp",
"--header",
"Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ..."
]
}
}
}
}
}
Multiple projects with different configurations:
{
"projects": {
"/home/username/project-one": {
"mcpServers": {
"harmoniq": {
"command": "npx",
"args": [
"mcp-remote",
"https://dev.dsiloed.com/api/v1/mcp",
"--header",
"Authorization: Bearer DEV_TOKEN_HERE"
]
}
}
},
"/home/username/project-two": {
"mcpServers": {
"harmoniq": {
"command": "npx",
"args": [
"mcp-remote",
"https://www.dsiloed.com/api/v1/mcp",
"--header",
"Authorization: Bearer PROD_TOKEN_HERE"
]
},
"other-server": {
"command": "some-command",
"args": ["arg1"]
}
}
}
}
}
Test your MCP connection:
Example queries to test:
Claude Code now has full access to your DSiloed tenant through 30+ MCP tools including data management, conversations, memories, agents, file operations, and more.
With Claude Code connected to DSiloed MCP, you can:
After configuring your LLM model, you can test it in several ways:
Look for the chat icon in any application's interface to start a conversation with your AI model.
Use the conversation features in LLM Manager to test model responses directly.
With your LLM model configured, you can now use AI features throughout the platform:
Chat with AI assistants that understand your business context and data.
Get help building custom applications with AI-generated code and guidance.
Upload and interact with documents using the Document Manager application.
AI Agents are sophisticated autonomous systems that can perform complex tasks using AI models, tools, and scheduling. The Visual Agent Builder provides an intuitive interface for creating and configuring these powerful AI agents.
An AI Agent consists of eight key components, each serving a specific purpose in the agent's operation:
Purpose: The AI brain of your agent
Connects to your configured AI model (like Claude) to process information and make decisions. This determines the intelligence and capabilities of your agent.
Purpose: Dual-layer instructions for enhanced AI control
System Prompt: Defines personality, behavior patterns, and operational constraints
User Prompt: Contains specific task instructions and execution objectives
Purpose: Context-based behavioral guidelines
Assigns specific roles (like Software Engineer, Designer) that provide context and persona for execution. Agents can use multiple roles or specific role combinations during execution.
Purpose: Data sources and information access
Provides the agent with access to databases, APIs, documents, and other information sources it needs to complete its tasks effectively.
Purpose: Automated execution timing
Controls when and how often the agent runs. Can be set for one-time execution, recurring schedules, or event-triggered activation.
Purpose: Actions the agent can perform
Equips the agent with specific capabilities like sending emails, creating reports, updating databases, or integrating with external systems.
Purpose: Communication history and context
Maintains conversation history and context, allowing the agent to remember previous interactions and maintain continuity across sessions.
Purpose: Permission-based access control
Controls what capabilities and resources the agent can access. Separate from behavioral roles, these determine API permissions and security constraints.
Follow these steps to create your own AI Agent using the Visual Agent Builder:
Navigate to the LLM Manager application and look for the "Agent Builder" or "Visual Agent Builder" option. This opens the drag-and-drop interface shown in the screenshot above.
Set up the essential components in this order:
Equip your agent with the capabilities it needs:
Configure when your agent should run:
Before activating your agent:
Beyond scheduled automation, agents can be executed manually with fine-grained control over their behavior through role selection and additional instructions.
Click the Execute button on any agent to:
During manual execution:
Each LLM Agent is automatically configured with both Party and User entities, providing comprehensive identity and security management:
The Visual Agent Builder includes two distinct role management systems, each serving different purposes:
Purpose: Define agent personality and expertise
Purpose: Control API and resource access
Security roles are assigned to the agent's User account for fine-grained permission control. Here's how to manage them programmatically:
// Assign a security role to an agent's user account
await apiClient.createSecurityRoleAssignment({
accessor_record_type: 'User',
accessor_record_id: agent.user.id,
security_role_id: securityRoleId
});
// Example: Assign content manager role
const contentManagerRole = await apiClient.getSecurityRoles({
internal_identifier: 'content_manager'
});
await apiClient.createSecurityRoleAssignment({
accessor_record_type: 'User',
accessor_record_id: agent.user.id,
security_role_id: contentManagerRole.security_roles[0].id
});
user object contains security_roles array, while party_roles contains behavioral roles with full role_type details.
The LLM Memory System provides persistent memory capabilities for AI agents and conversations, enabling long-term learning, pattern recognition, and contextual awareness across sessions. Unlike simple chat history, the memory system uses advanced vector embeddings and graph-based relationships to store and retrieve information semantically.
85-100% Accuracy: Advanced semantic similarity matching using OpenAI embeddings
Find memories based on meaning, not just keywords. "What does Ben like?" matches "Ben Koloski loves spaghetti"
Relationship Tracking: Memories are connected through typed relationships
Build knowledge graphs showing how facts, preferences, and insights relate to each other
Background Processing: Daily consolidation jobs identify patterns
Automatically extract insights from related memories and create derived knowledge
Secure by Design: Complete data isolation between tenants
Each tenant's memories are stored separately with capability-based access control
LLM Memory is configured through the memory_management Configuration. The system requires OpenAI's Text Embedding 3 Small
model for vector embeddings, which provides high-quality semantic similarity matching.
true'openai-embeddings'15360.78000'classifier' or null'hybrid'0.90.850classification_model_name enables LLM-based classification
for highly accurate semantic relationships. If null, the system uses fast rule-based heuristics only.
// Step 1: Create OpenAI embedding model in LLM Manager first
// Step 2: Optionally create a classifier model (e.g., GPT-3.5-turbo or GPT-4)
// Step 3: Use internal_identifiers in this configuration
const response = await fetch('/api/v1/configurations', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': tenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
configuration: {
internal_identifier: 'memory_management',
configuration_type_id: 'memory_management',
configuration_values: {
// Vector Embeddings (Required for semantic search)
enable_embeddings: true,
embedding_model_name: 'openai-embeddings', // Your embedding LlmModel
embedding_dimensions: 1536, // Fixed - do not change
embedding_similarity_threshold: 0.7,
max_embedding_length: 8000,
// Relationship Classification (Optional but recommended)
classification_model_name: 'classifier', // Your classifier LlmModel (null = heuristics only)
classification_method: 'hybrid', // 'heuristic', 'llm', or 'hybrid'
classification_llm_threshold_score: 0.9, // Similarity threshold for LLM usage
classification_llm_threshold_importance: 0.8, // Importance threshold for LLM usage
classification_max_llm_calls_per_consolidation: 50 // Cost control limit
}
}
})
});
# Step 1: Create OpenAI embedding model in LLM Manager first
# Step 2: Optionally create a classifier model (e.g., GPT-3.5-turbo or GPT-4)
# Step 3: Use internal_identifiers in this configuration
# Find your tenant
tenant = Tenant.find_by(enterprise_identifier: 'your-tenant')
# Create or update memory_management configuration
config = Configuration.find_or_initialize_by(
tenant: tenant,
internal_identifier: 'memory_management'
)
config.update!(
configuration_values: {
# Vector Embeddings (Required for semantic search)
'enable_embeddings' => true,
'embedding_model_name' => 'openai-embeddings', # Your embedding LlmModel
'embedding_dimensions' => 1536, # Fixed - do not change
'embedding_similarity_threshold' => 0.7,
'max_embedding_length' => 8000,
# Relationship Classification (Optional but recommended)
'classification_model_name' => 'classifier', # Your classifier LlmModel (nil = heuristics only)
'classification_method' => 'hybrid', # 'heuristic', 'llm', or 'hybrid'
'classification_llm_threshold_score' => 0.9, # Similarity threshold for LLM usage
'classification_llm_threshold_importance' => 0.8, # Importance threshold for LLM usage
'classification_max_llm_calls_per_consolidation' => 50 # Cost control limit
}
)
When memories are connected, the system automatically classifies the type of relationship between them using intelligent semantic analysis. This creates meaningful knowledge graphs instead of generic "related to" connections.
Fast, zero-cost pattern matching
Uses keyword detection to identify relationships like "caused_by", "leads_to", "supersedes"
Example: "Fixed bug caused by null pointer" → detects "caused_by" relationship
High-accuracy semantic analysis
Uses configured LLM model for precise relationship classification on important memories
Triggered selectively based on similarity and importance thresholds to manage costs
Once configured, the memory system runs automatic background jobs to maintain and enhance memory quality:
The memory system integrates seamlessly with AI conversations and agents. Memories are automatically loaded and used to provide context for AI responses.
Remember user preferences, work patterns, and communication styles across sessions for natural, personalized interactions
Store solutions, code patterns, and architectural decisions to improve code quality and consistency over time
Remember patient history, treatment preferences, and clinical insights while maintaining HIPAA compliance through multi-tenant isolation
Track customer insights, market trends, and strategic decisions to build organizational knowledge over time
DSiloed includes a powerful, reusable ChatAssistant component that provides AI-powered chat capabilities to any application. The component is framework-independent and features a modern, Discord-like interface with conversation management.
The ChatAssistant component supports intelligent @ai mentions that allow users to interact with specific LLM models within conversations.
// Include the ChatAssistant component
<script src="js/components/ChatAssistant.js?v=1764792677"></script>
// Initialize with your API client
const chatAssistant = new ChatAssistant('YourAppName', {
apiClient: apiClient,
title: 'AI Assistant',
placeholder: 'Ask me anything...',
contextMessage: 'Assistant has access to your data'
});
// Toggle chat visibility
chatAssistant.toggle();
// In your Vue component
methods: {
initChatAssistant() {
this.chatAssistant = new ChatAssistant('MyApp', {
apiClient: this.apiClient,
title: 'MyApp Assistant',
onError: (error) => {
this.showSnackbar('Chat error: ' + error.message, 'error');
}
});
},
toggleChat() {
if (this.chatAssistant) {
this.chatAssistant.toggle();
}
}
},
mounted() {
this.initChatAssistant();
}
// In your React component
import { useEffect, useRef } from 'react';
function MyApp() {
const chatAssistantRef = useRef(null);
useEffect(() => {
chatAssistantRef.current = new ChatAssistant('MyApp', {
apiClient: apiClient,
title: 'MyApp Assistant'
});
}, []);
const toggleChat = () => {
if (chatAssistantRef.current) {
chatAssistantRef.current.toggle();
}
};
return (
<button onClick={toggleChat}>Toggle Chat</button>
);
}
Webhooks allow external systems to send messages directly into conversations without authentication. This enables integration with external applications, chatbots, monitoring systems, and more.
Webhooks can be created through the ChatAssistant Settings interface or via API. Each webhook generates a unique URL that external systems can use to post messages.
# Send a message via webhook
curl -X POST "https://your-domain.com/api/v1/webhooks/your-webhook-key/publish_message" \
-H "Content-Type: application/json" \
-d '{
"message": "Alert: Server CPU usage is at 85%"
}'
# Send a message that triggers AI response
curl -X POST "https://your-domain.com/api/v1/webhooks/your-webhook-key/publish_message" \
-H "Content-Type: application/json" \
-d '{
"message": "@ai Please analyze this error and suggest a solution: Database connection timeout after 30 seconds"
}'
// Create a new webhook for a conversation
const webhookData = {
webhook: {
name: "Server Monitoring",
description: "Webhook for server monitoring alerts",
related_entity_type: "LlmConversation",
related_entity_id: conversationId,
direction: "inbound"
}
};
const response = await fetch('/api/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': tenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify(webhookData)
});
const result = await response.json();
console.log('Webhook URL:', result.webhook.url);
apiClient (required)titleplaceholdercontextMessagepositiononToggleonMessageonErrorModel Context Protocol (MCP) tools provide a standardized interface for AI models to interact with your DSiloed platform. These tools enable LLMs to perform operations, access data, manage agents, handle communications, and more.
All MCP tools are available through the MCP server and can be used by external applications, AI agents, and LLM conversations. This reference lists all 35+ available MCP tools with their descriptions and use cases.
Tools for sending emails and managing conversation invitations.
Send emails with customizable subject, body, recipients, and sender information.
Use Cases:
Key Parameters:
subject - Email subject lineemail_body - HTML email contentto_email - Recipient(s), semicolon-separatedfrom_email - Optional sender addressSend single-use invitation links for one-time feedback in LLM conversations.
Use Cases:
Send guest trial invitations allowing multiple messages before signup.
Use Cases:
Tools for CRUD operations, schema discovery, and file management.
Comprehensive CRUD operations for all data models with advanced filtering, relationships, and aggregations.
Capabilities:
Supported Models:
Party, Individual, Organization, Contact, BizTxnEvent, Project, Task, LlmConversation, and 30+ more
Discover schema information, available columns, required fields, and data types for any model.
Use Cases:
Create text-based documents (Markdown, TXT, CSV, PDF, DOCX) programmatically.
Use Cases:
Update existing documents with new content, titles, or move to different directories.
Read file contents including text files, documents (PDF/DOCX), images, and media files.
Retrieve comprehensive LLM usage statistics, costs, and token consumption by model, user, and conversation.
Apply tracked statuses to entities (Tasks, Projects, BizTxnEvents, etc.) with proper history tracking.
Tools for creating, managing, and executing autonomous AI agents.
Create complete LLM agents with custom tools, resources, prompts, and role assignments.
Modify existing agent configurations, prompts, tools, and settings.
Permanently remove agents from the system.
List all available agents with their capabilities and descriptions.
Execute an agent with specific prompts and monitor execution status.
Check the execution status and output of running agents.
Mark agent runs as complete with success/failure status.
List all tools that can be assigned to agents during creation.
List all resources available for agent configuration.
List configured LLM models available for agent creation.
List available system and user prompts for agents.
Get configuration requirements for specific tools before assigning to agents.
Tools for persistent memory management across AI sessions.
Store facts, insights, decisions, preferences, and context as persistent memories.
Memory Types:
Retrieve relevant memories to provide context for AI conversations.
Retrieval Modes:
Tools for managing LLM conversations and messaging.
Send messages to LLM conversations and trigger AI responses.
Use Cases:
Tools for accessing platform documentation.
Ask questions about the Data Model API platform implementation and features.
Topics Covered:
Access app development documentation for building applications on the platform.
Topics Covered:
Utility and administrative tools for system operations.
Get current date and time with timezone support and multiple formats.
Root tenant only: Tail Rails application logs for debugging and monitoring.
Get external system mappings for MDM integration and data synchronization.
Tools for creating, managing, and executing serverless JavaScript functions.
List available dynamic functions with their status, schedule, and recent execution info.
Filtering Options:
active_only - Only show active functionsscheduled_only - Only show scheduled functionscallbacks_only - Only show callback functionscallback_model - Filter by callback model namename - Filter by function name (partial match)Get full details of a specific function by ID or name, including JavaScript code and execution history.
Key Parameters:
id or name - Function identifierinclude_executions - Include recent execution historyexecution_limit - Number of executions to include (max 20)Execute a dynamic function by ID or name and return results.
Key Parameters:
function_id or function_name - Function to executeparams - Parameters passed to the functionasync - Execute asynchronously (queued)Returns:
success - Execution statusresult - Function return valueexecution_id - For tracking historyexecution_time_ms - DurationCreate, update, or delete dynamic functions with full configuration support.
Actions:
create - Create new function with name, code, schedule, callbacksupdate - Modify existing function (can rename with new_name)delete - Remove functionConfiguration Options:
schedule - Cron expression for scheduled executioncallback_model - Model to trigger callback oncallback_event - Event type (create, update, destroy, all)timeout_seconds - Max execution time (1-300)configuration - Custom config including OAuthView execution history and logs for debugging and monitoring.
Filtering Options:
function_id or function_name - Filter by functionexecution_id - Get specific execution detailstrigger_type - Filter by trigger (manual, api, schedule, mcp_tool, webhook, nested_call, callback)success_only / failed_only - Filter by statuslimit - Number of records (max 100)executeAction(), make HTTP requests with fetch(),
call other functions with executeFunction(), and integrate with external systems via OAuth.
See the Dynamic Functions section for full documentation.
MCP tools can be accessed in multiple ways:
Configure tools when creating agents in LLM Manager. Agents can use these tools autonomously during execution.
LLMs in conversations can invoke tools as needed to answer questions, retrieve data, or perform actions.
Connect external applications via MCP protocol to access all tools programmatically.
Create custom tools in LLM Manager using JavaScript for tenant-specific functionality.
Pro Tip: Combine multiple tools in agent workflows for complex automation. For example,
use general_crud_tool to fetch data, create_llm_file_tool to generate a report,
and send_email_tool to distribute it.
DSiloed provides a comprehensive RESTful API for building applications. All endpoints follow consistent RESTful conventions with JSON responses.
Authentication uses JWT (JSON Web Tokens) passed in the Authorization header.
curl -X GET "https://example-model-api.com/api/v1/parties" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
All API requests should use JSON for request bodies and accept JSON responses.
-H "Content-Type: application/json" \
-H "Accept: application/json"
Responses follow a consistent format with data returned in an object named after the resource.
// Single resource example (e.g., GET /api/v1/users/123)
{
"success": true,
"user": {
"id": 123,
"description": "John Doe",
"created_at": "2025-05-01T12:34:56Z",
"updated_at": "2025-05-02T10:11:12Z"
}
}
// Multiple resources example (e.g., GET /api/v1/users)
{
"success": true,
"total_count": 42,
"users": [
{
"id": 123,
"description": "John Doe",
"created_at": "2025-05-01T12:34:56Z"
},
{
"id": 124,
"description": "Jane Smith",
"created_at": "2025-05-02T08:15:30Z"
}
// ... more users
]
}
Errors return a simple format with success set to false and an error message.
{
"success": false,
"message": "Invalid Access"
}
Parties represent individuals and organizations. The Party model is the foundation of your application's data model.
List all parties
curl -X GET "https://example-model-api.com/api/v1/parties" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
Get a specific party
curl -X GET "https://example-model-api.com/api/v1/parties/123" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
Create an individual
curl -X POST "https://example-model-api.com/api/v1/individuals" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"first_name": "John",
"last_name": "Doe",
"dob": "1990-01-01"
}'
Create an organization
curl -X POST "https://example-model-api.com/api/v1/organizations" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"description": "Acme Corporation"
}'
Contacts represent various contact methods like email addresses, phone numbers, and postal addresses.
List all contacts
curl -X GET "https://example-model-api.com/api/v1/contacts" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
Create an email address
curl -X POST "https://example-model-api.com/api/v1/email_addresses" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"party_id": 123,
"description": "Work Email",
"email_address": "john.doe@example.com"
}'
Create a phone number
curl -X POST "https://example-model-api.com/api/v1/phone_numbers" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"party_id": 123,
"description": "Mobile",
"phone_number": "+15551234567"
}'
Business events track activities and transactions within your application.
List all business events
curl -X GET "https://example-model-api.com/api/v1/biz_txn_events" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
Create a business event
curl -X POST "https://example-model-api.com/api/v1/biz_txn_events" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id" \
-H "Content-Type: application/json" \
-d '{
"biz_txn_event_type_id": 1,
"description": "Order Placed",
"event_date": "2025-05-11T14:30:00Z",
"amount": 99.99,
"payload": {
"order_number": "ORD-12345",
"items": 3
}
}'
Manage security, roles, and capabilities within your application.
List all security roles
curl -X GET "https://example-model-api.com/api/v1/security_roles" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
List all capabilities
curl -X GET "https://example-model-api.com/api/v1/capabilities" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
APIs for managing LLM models, tools, and conversations.
List all LLM models
curl -X GET "https://example-model-api.com/api/v1/llm_models" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
List all LLM tools
curl -X GET "https://example-model-api.com/api/v1/llm_tools" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Tenant-Id: your-tenant-id"
For complete API documentation, refer to the Swagger UI which provides interactive documentation for all available endpoints.
Guest Users allow you to invite new users who have never used the platform before. These "guest users" are created directly in your tenant with scoped access to specific resources, making them ideal for contractors, clients, and consultants who need temporary or limited access.
Create a new user in your tenant with the is_guest: true flag.
// Create guest user account with auto-generated password
const createGuestUser = async (username, email) => {
const response = await fetch('/api/v1/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user: {
first_name: 'John',
last_name: 'Contractor',
username: username,
email: email,
// password is OPTIONAL for guest users - auto-generated if not provided
is_guest: true,
custom_fields: {
invited_by: 'Your Name',
purpose: 'Project collaboration'
}
},
guest_user_security_roles: 'basic_user,project_viewer' // Comma-separated role IIDs
})
});
const data = await response.json();
// Returns: { success: true, user: { id: 456, username, party_id: 789, is_guest: true, has_temp_password: true, ... }}
return data.user;
};
// Create guest user with custom password and email template
const createGuestUserCustom = async (username, email, customPassword) => {
const customTemplate = `
<h2>Welcome to Our Platform, {{firstName}}!</h2>
<p>You've been invited to collaborate on a project.</p>
<h3>Login Information:</h3>
<ul>
<li>Email: {{email}}</li>
<li>Temporary Password: {{temp_password}}</li>
<li>Login URL: {{login_link}}</li>
</ul>
<p>Please change your password after first login.</p>
`;
const response = await fetch('/api/v1/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user: {
first_name: 'John',
last_name: 'Contractor',
username: username,
email: email,
password: customPassword, // Optional: provide custom password
is_guest: true
},
guest_user_security_roles: 'basic_user,project_viewer',
guest_invite_email_template: customTemplate,
guest_invite_email_subject: 'Welcome to Our Project!'
})
});
const data = await response.json();
return data.user;
};
password is omitted, a secure 16-character password is automatically generatedguest_user_security_roles are assigned immediatelyhas_temp_password flag is set, requiring password change on first loginThat's it! No additional steps needed for basic guest user access. The user will receive the email and can log in immediately.
is_guest: true and has_temp_password: true.
The security roles you assign determine their level of access to your system.
The guest user receives an email with their credentials and follows this login workflow:
requires_password_change: true// Guest user login
const response = await fetch('/api/v1/users/login', {
method: 'POST',
headers: {
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'john@example.com',
password: 'temporary_password_from_email'
})
});
const data = await response.json();
// Response: {
// success: true,
// token: 'jwt_token_here',
// user: { id: 456, email: 'john@example.com', is_guest: true, ... },
// requires_password_change: true // Present for guest users with temp passwords
// }
// If requires_password_change is true, show password change form
if (data.requires_password_change) {
showPasswordChangeDialog();
}
// Update password after first login
const changePassword = async (newPassword) => {
const response = await fetch('/api/v1/users/current/update_password', {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
password: newPassword
})
});
const data = await response.json();
// Response: { success: true }
// The has_temp_password flag is automatically cleared
};
has_temp_password flag is automatically cleared.
Future logins will not require a password change.
Note: This step is optional. If the security roles assigned in Step 1 provide sufficient access, you don't need scoped capabilities. Use this approach when you need to restrict guest users to specific records (e.g., only one project or specific documents).
// Create project-scoped capability for guest user
const createProjectCapability = async (projectId) => {
const response = await fetch('/api/v1/capabilities', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability: {
action: 'view',
resource: 'Project',
accessor_record_specific: true, // Auto-delete when assignment removed
description: 'Guest access to specific project',
scope: {
where: { id: projectId },
fields: ['id', 'name', 'description', 'status', 'created_at']
}
}
})
});
const data = await response.json();
return data.capability;
};
// Create task-scoped capability
const createTaskCapability = async (projectId) => {
const response = await fetch('/api/v1/capabilities', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability: {
action: 'view',
resource: 'Task',
accessor_record_specific: true,
description: 'Guest access to project tasks',
scope: {
where: { project_id: projectId }
}
}
})
});
const data = await response.json();
return data.capability;
};
// Assign capability to guest user
const assignCapabilityToGuest = async (capabilityId, guestUserId) => {
const response = await fetch('/api/v1/capability_assignments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability_assignment: {
capability_id: capabilityId,
accessor_record_type: 'User',
accessor_record_id: guestUserId
}
})
});
const data = await response.json();
return data.capability_assignment;
};
// Complete workflow with scoped capabilities
const setupGuestUserWithScoping = async (projectId) => {
// 1. Create guest user (automatically assigns roles and sends email)
const response = await fetch('/api/v1/users', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user: {
first_name: 'John',
last_name: 'Contractor',
username: 'contractor.john',
email: 'john@example.com',
is_guest: true
},
guest_user_security_roles: 'basic_user' // Basic role first
})
});
const guestData = await response.json();
const guest = guestData.user;
// 2. Create scoped capabilities for specific project access
const projectCap = await createProjectCapability(projectId);
const taskCap = await createTaskCapability(projectId);
// 3. Assign scoped capabilities
await assignCapabilityToGuest(projectCap.id, guest.id);
await assignCapabilityToGuest(taskCap.id, guest.id);
// Guest now has:
// - Basic role permissions (from security role)
// - Plus scoped access to specific project and its tasks (from capabilities)
return guest;
};
true to automatically
delete the capability when all assignments are removed. Perfect for temporary collaborations.
For scenarios with multiple guest users (like client portals), use dynamic scoping
with current_user_id to avoid creating per-user capabilities.
// Tag entities with authorized user IDs
const tagProjectForClients = async (projectId, clientUserIds) => {
const response = await fetch(`/api/v1/projects/${projectId}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
project: {
custom_fields: {
client_user_ids: clientUserIds // Array of user IDs
}
}
})
});
return response.json();
};
// Create capability with dynamic filtering
const createDynamicClientCapability = async () => {
const response = await fetch('/api/v1/capabilities', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability: {
action: 'view',
resource: 'Project',
description: 'View projects assigned to current user',
scope: {
where: {
'custom_fields': {
'client_user_ids': {
'in': ['current_user_id'] // Dynamic resolution
}
}
}
}
}
})
});
return response.json();
};
// Create security role for all clients
const setupClientPortal = async () => {
// 1. Create role
const roleResponse = await fetch('/api/v1/security_roles', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
security_role: {
name: 'Client Portal Access',
description: 'Guest client access to their projects'
}
})
});
const role = (await roleResponse.json()).security_role;
// 2. Create dynamic capabilities
const projectCap = await createDynamicClientCapability();
// 3. Assign capability to role
await fetch('/api/v1/capability_assignments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability_assignment: {
capability_id: projectCap.capability.id,
accessor_record_type: 'SecurityRole',
accessor_record_id: role.id
}
})
});
return role;
};
// Assign role to each client
const assignClientToRole = async (roleId, clientUserId) => {
await fetch('/api/v1/security_role_assignments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
security_role_assignment: {
security_role_id: roleId,
accessor_record_type: 'User',
accessor_record_id: clientUserId
}
})
});
};
Guest users have two conversion paths: upgrading to a full user within the same tenant, or converting to their own tenant owner with automatic cross-tenant access.
Upgrade a guest to a regular user within your tenant:
// Convert guest to full user in same tenant
const convertToFullUser = async (userId, sendNotification = false) => {
const response = await fetch(`/api/v1/users/${userId}/convert_to_full_user`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
send_notification: sendNotification
})
});
const data = await response.json();
// Returns: { success: true, user: {...}, message: "Guest user successfully converted to full user" }
return data;
};
Create a new tenant for the guest user with automatic cross-tenant sharing:
// Convert guest to tenant owner with auto-created sharing relationship
const convertToTenant = async (userId, tenantOptions) => {
const response = await fetch(`/api/v1/users/${userId}/convert_to_tenant`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
tenant_options: {
name: tenantOptions.name,
enterprise_identifier: tenantOptions.enterpriseId,
shared_key: tenantOptions.sharedKey, // Optional
product_type_id: tenantOptions.productTypeId, // Optional, defaults to 'free'
pricing_plan_id: tenantOptions.pricingPlanId // Optional, defaults to 'free'
}
})
});
const data = await response.json();
// Returns:
// {
// success: true,
// user: {...},
// tenant: {...},
// sharing_relationship: {...}, // Auto-created for cross-tenant access
// message: "Guest user successfully converted to tenant owner"
// }
return data;
};
The Cross-Tenant User Access system enables controlled user-level access between tenants. Users from one tenant can be granted specific roles and permissions in another tenant, enabling collaboration while maintaining security and data isolation.
Formal relationships between tenants with statuses: pending (awaiting acceptance), active (functional), suspended (temporary pause - maintains roles), revoked (permanent - removes all roles).
Individual users are granted SecurityRoles in the target tenant. Standard RBAC determines what they can view, edit, or delete based on capabilities.
Guest users can switch between their own tenant workspace and host workspaces where they've been granted access.
Step 1: Tenant A requests access for specific users
Step 2: Tenant B accepts the relationship request
Step 3: Tenant B grants SecurityRoles to users from Tenant A
Step 4: Users switch workspaces and access Tenant B's data (within their role permissions)
Request access for users to another tenant.
// Request access for specific users
const requestAccess = async (targetTenantId, userIds, message) => {
const response = await fetch('/api/v1/tenant_sharing/request', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
target_tenant_id: targetTenantId,
description: 'Project collaboration',
request_message: message,
user_ids: userIds // [123, 456] or omit to share only current user
// OR use: share_with_all_users: true
})
});
const data = await response.json();
return data; // Returns relationship with status "pending"
};
Accept an incoming relationship request.
// Accept a relationship request
const acceptRequest = async (relationshipId, message) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/accept`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
acceptance_message: message
})
});
const data = await response.json();
return data; // Returns relationship with status "active"
};
List all tenant relationships for your tenant.
// List all relationships
const listRelationships = async (status = null) => {
let url = '/api/v1/tenant_sharing';
if (status) {
url += `?status=${status}`; // e.g., "party_relationship_active"
}
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId
}
});
const data = await response.json();
return data; // Array of relationships
};
Add more users to an existing relationship (requesting tenant only).
// Add users to relationship
const addUsers = async (relationshipId, userIds) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/add_users`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_ids: userIds // [125, 126]
})
});
const data = await response.json();
return data; // Updated relationship
};
Permanently revoke a tenant relationship and remove all role assignments.
// Revoke a relationship (permanent - requires new invitation to re-establish)
const revokeRelationship = async (relationshipId, reason) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/revoke`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
revocation_reason: reason
})
});
const data = await response.json();
return data; // Removes all SecurityRoleAssignments permanently
};
Temporarily suspend a relationship without removing role assignments.
// Suspend a relationship (temporary - can be reactivated)
const suspendRelationship = async (relationshipId, reason) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/suspend`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
suspension_reason: reason || 'Temporarily suspended'
})
});
const data = await response.json();
return data; // Maintains SecurityRoleAssignments for easy reactivation
};
Reactivate a previously suspended relationship.
// Reactivate a suspended relationship
const reactivateRelationship = async (relationshipId) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/reactivate`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({}) // No body required
});
const data = await response.json();
return data; // Restores access with all previously assigned roles
};
After accepting a relationship, the target tenant must grant SecurityRoles to users.
Get list of users who should be granted access (target tenant only).
// List shareable users
const listShareableUsers = async (relationshipId) => {
const response = await fetch(`/api/v1/tenant_sharing/${relationshipId}/shareable_users`, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId // Must be target tenant
}
});
const data = await response.json();
// Returns:
// {
// shareable_users: [
// {
// id: 123,
// username: "john_doe",
// display_name: "John Doe",
// assigned_roles: [ ... ],
// has_access: true/false
// }
// ],
// share_with_all_users: false
// }
return data;
};
Create a SecurityRole for external users.
// Create a role for partners
const createPartnerRole = async () => {
const response = await fetch('/api/v1/security_roles', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
security_role: {
name: 'Partner Viewer',
description: 'Read-only access for partners'
}
})
});
const data = await response.json();
return data.security_role; // Returns role with ID
};
Assign capabilities to a role.
// Assign capabilities to role
const assignCapabilities = async (roleId, capabilityIds) => {
for (const capId of capabilityIds) {
await fetch(`/api/v1/security_roles/${roleId}/assign_capability`, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
capability_id: capId
})
});
}
};
Assign a role to a user from another tenant.
// Assign role to external user
const grantRoleToUser = async (roleId, userId) => {
const response = await fetch('/api/v1/security_role_assignments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
security_role_assignment: {
security_role_id: roleId,
accessor_record_type: 'User',
accessor_record_id: userId // User ID from requesting tenant
}
})
});
const data = await response.json();
return data.security_role_assignment;
};
Workspaces allow guest users to seamlessly switch between their own tenant's data and host tenants where they have been granted access.
Get all workspaces you can access.
// List all available workspaces
const listWorkspaces = async () => {
const response = await fetch('/api/v1/workspaces', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId
}
});
const data = await response.json();
// HOST TENANT USER - Only sees own workspace:
// {
// workspaces: [
// {
// workspace_tenant_id: "my-org",
// workspace_name: "My Organization",
// relationship_id: null,
// assigned_roles_count: null,
// has_access: true
// }
// ]
// }
// GUEST USER WITH ROLES - Sees own + host workspaces where roles assigned:
// {
// workspaces: [
// {
// workspace_tenant_id: "my-org",
// workspace_name: "My Organization",
// relationship_id: null,
// assigned_roles_count: null,
// has_access: true
// },
// {
// workspace_tenant_id: "partner-org",
// workspace_name: "Partner Organization",
// relationship_id: 789,
// assigned_roles_count: 2,
// has_access: true,
// role: "guest"
// }
// ]
// }
// GUEST USER WITHOUT ROLES - Only sees own workspace:
// (Even if they're in shared_users metadata, without role assignments they don't see host workspace)
// {
// workspaces: [
// {
// workspace_tenant_id: "my-org",
// workspace_name: "My Organization",
// relationship_id: null,
// assigned_roles_count: null,
// has_access: true
// }
// ]
// }
return data.workspaces;
};
View your roles and capabilities in a specific workspace.
// Check what access I have
const checkMyAccess = async (workspaceTenantId) => {
const response = await fetch(`/api/v1/workspaces/${workspaceTenantId}/my_access`, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': currentTenantId
}
});
const data = await response.json();
// Returns:
// {
// roles: [
// {
// role: { id, name, description },
// capabilities: [ ... ]
// }
// ]
// }
return data.roles;
};
Access workspace data by setting the Tenant-Id header.
// Access data in another workspace
const getPartnerTasks = async (partnerTenantId) => {
const response = await fetch('/api/v1/tasks', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token,
'Tenant-Id': partnerTenantId // Use partner's tenant ID
}
});
const data = await response.json();
// Returns tasks from partner tenant (filtered by your capabilities)
return data.tasks;
};
// Important: API enforces your role permissions in that workspace
// You only see/edit what your assigned roles allow
Dynamic Functions provide a serverless JavaScript execution environment within the DSiloed platform. They allow you to create, manage, and execute custom JavaScript functions that can integrate with external APIs, process data, and automate workflows—all without managing any infrastructure.
executeAction.
Query, create, update, and delete records from your functions.
Create a dynamic function using the REST API. Functions receive a context object with parameters, configuration, OAuth tokens, and tenant information.
// Create a function via REST API
POST /api/v1/dynamic_functions
Content-Type: application/json
{
"dynamic_function": {
"name": "hello_world",
"description": "A simple greeting function",
"javascript_code": "return { message: 'Hello, ' + (params.name || 'World') + '!' };",
"active": true,
"timeout_seconds": 30
}
}
Execute the function by ID or name:
// Execute by name
POST /api/v1/dynamic_functions/hello_world/execute
Content-Type: application/json
{
"params": {
"name": "DSiloed"
}
}
// Response:
{
"success": true,
"result": { "message": "Hello, DSiloed!" },
"execution_id": 456,
"execution_time_ms": 12
}
Functions receive a rich context with access to parameters, configuration, and built-in functions for data access and HTTP requests.
// Available context properties
params // Parameters passed when executing
config // Function configuration (excluding secrets)
functionId // Database ID of this function
functionName // Function name
tenantId // Current tenant ID
// OAuth tokens (if connected)
oauth.accessToken // OAuth access token
oauth.tokenType // Token type (usually "Bearer")
// Callback context (only in callback executions)
event // 'create', 'update', or 'destroy'
model_name // Model class name (e.g., 'Task')
record_id // ID of the affected record
record // Full record data
changes // Changed attributes (update only)
// Make HTTP requests to external APIs
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Authorization': `Bearer ${oauth.accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
});
const data = await response.json();
return { fetched: data };
// Query tenant data
var customers = executeAction('Individual', 'list', {
filters: { role_type_id: 'customer' },
limit: 100
});
// Create records
var newContact = executeAction('PhoneNumber', 'create', {
attributes: {
party_id: 123,
phone_number: '555-1234'
}
});
// Update records
executeAction('Individual', 'update', {
id: 456,
attributes: { custom_fields: { synced: true } }
});
// Convenience wrappers also available:
listRecords(modelName, options)
getRecord(modelName, id)
createRecord(modelName, attributes)
updateRecord(modelName, id, attributes)
deleteRecord(modelName, id)
findOrCreateRecord(modelName, findAttrs, createAttrs)
// Execute another DynamicFunction by name (synchronous by default)
const result = executeFunction('helper_function', { key: 'value' });
if (result.success) {
console.log('Helper returned:', result.result);
} else {
console.error('Helper failed:', result.error);
}
// Execute asynchronously (queued for background execution)
const asyncResult = executeFunction('long_running_task', { data: params.input }, true);
// Returns immediately: { success: true, message: '...', execution_id: ... }
// Chain multiple functions together
const validation = executeFunction('validate_data', { data: params.input });
if (!validation.success || !validation.result.valid) {
return { success: false, error: 'Validation failed' };
}
const processed = executeFunction('process_data', {
data: params.input,
validationResult: validation.result
});
return processed.result;
// executeFunction(functionNameOrId, params, async)
// - functionNameOrId: Name or ID of the function
// - params: Parameters object (optional)
// - async: If true, queues for background execution (optional, default: false)
// Look up external ID for a local record
var result = getExternalId('Party', partyId, 'xero', 'xero_user_id');
if (result.found) {
console.log('Xero user ID:', result.external_id);
}
// Find local record by external ID
var projectMapping = findByExternalId('Project', 'xero', xeroProjectId);
if (projectMapping.found) {
var localProject = projectMapping.record;
}
// Store a new mapping
storeExternalMapping('Project', localProjectId, 'xero', xeroProjectId, {
column_name: 'xero_project_id',
table_name: 'xero_projects',
is_primary_key: true
});
Functions can be scheduled to run automatically using cron expressions. The scheduler checks every 30 seconds for functions due to execute.
* * * * *0 * * * *0 9 * * *0 8 * * 10 0 1 * **/15 * * * *run_as_user_id to define
which user's permissions are used during background execution. The user must belong to the same tenant.
// Create a scheduled function
POST /api/v1/dynamic_functions
{
"dynamic_function": {
"name": "daily_sync",
"description": "Sync contacts with CRM every morning",
"javascript_code": "/* sync logic */",
"schedule": "0 9 * * *",
"timeout_seconds": 120,
"run_as_user_id": 123,
"active": true
}
}
Functions can be triggered automatically when records are created, updated, or destroyed. This enables event-driven automation without polling.
run_as_user_id to define
which user's permissions are used during callback execution. The user must belong to the same tenant.
// Create a callback function for task creation
POST /api/v1/dynamic_functions
{
"dynamic_function": {
"name": "on_task_created",
"description": "Notify team when tasks are created",
"callback_model": "Task",
"callback_event": "create",
"run_as_user_id": 123,
"javascript_code": `
// Context includes: event, model_name, record_id, record
console.log('Task created:', record.name);
// Send notification to external system
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + oauth.accessToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
channel: '#tasks',
text: 'New task created: ' + record.name
})
});
return { notified: true };
`
}
}
Dynamic Functions support OAuth 2.0 for integrating with external services. OAuth is configuration-based, meaning any OAuth 2.0 provider can be used without code changes.
{
"oauth": {
"provider": "xero",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"authorize_url": "https://login.xero.com/identity/connect/authorize",
"token_url": "https://identity.xero.com/connect/token",
"scopes": ["openid", "profile", "accounting.transactions.read"]
}
}
/oauth/authorize to get authorization URL
oauth.accessToken
GET /api/v1/dynamic_functionsPOST /api/v1/dynamic_functionsGET /api/v1/dynamic_functions/:idPUT /api/v1/dynamic_functions/:idDELETE /api/v1/dynamic_functions/:idPOST /api/v1/dynamic_functions/:id/executeGET /api/v1/dynamic_functions/:id/exportPOST /api/v1/dynamic_functions/importPOST /api/v1/dynamic_functions/:id/oauth/authorizeGET /api/v1/dynamic_functions/:id/oauth/statusPOST /api/v1/dynamic_functions/:id/oauth/disconnect/api/v1/dynamic_functions/my_function/execute
AI agents can manage and execute Dynamic Functions via five dedicated MCP tools. This enables intelligent automation where AI can create, modify, and run custom code.
run_as_user_id: "current_user" for scheduled/callback functions.
Here's a complete example of a function that syncs time entries to Xero, demonstrating OAuth, data access, and MDM mapping helpers.
// Function: sync_time_entries_to_xero
// Schedule: 0 * * * * (every hour)
// Get unsynced time entries
var entries = listRecords('TimeEntry', {
filters: { status: 'approved' },
limit: 50
});
var synced = 0;
var skipped = 0;
for (var entry of entries.data) {
// Skip if already synced
var existingMapping = getExternalId('TimeEntry', entry.id, 'xero', 'xero_timeentry_id');
if (existingMapping.found) {
skipped++;
continue;
}
// Look up Xero user ID for this party
var userMapping = getExternalId('Party', entry.party_id, 'xero', 'xero_user_id');
if (!userMapping.found) {
console.log('No Xero user mapping for party:', entry.party_id);
continue;
}
// Create time entry in Xero
var xeroResponse = await fetch('https://api.xero.com/projects/timeEntries', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + oauth.accessToken,
'Content-Type': 'application/json',
'Xero-Tenant-Id': config.xero_tenant_id
},
body: JSON.stringify({
userId: userMapping.external_id,
projectId: entry.custom_fields.xero_project_id,
duration: entry.hours * 60,
description: entry.description
})
});
var xeroEntry = await xeroResponse.json();
// Store mapping for future reference
storeExternalMapping('TimeEntry', entry.id, 'xero', xeroEntry.timeEntryId, {
column_name: 'xero_timeentry_id'
});
synced++;
}
return {
success: true,
synced: synced,
skipped: skipped,
message: `Synced ${synced} entries, skipped ${skipped} already synced`
};
DSiloed comes with several sample applications that demonstrate different capabilities of the platform. Each app is built as a static JavaScript application that interacts with the API.
{{ app.description }}
Each sample application follows a similar architecture pattern:
Building your own application on top of DSiloed is straightforward. You can use any modern JavaScript framework that can make HTTP requests to the API.
Create a new project directory with the following structure. You can place this directory anywhere on your system or web server:
# Create the main app directory
mkdir -p myapp
# Create subdirectories for CSS, JavaScript, and documentation
mkdir -p myapp/css
mkdir -p myapp/js
mkdir -p myapp/js/components
mkdir -p myapp/docs
# Create basic files
touch myapp/index.html
touch myapp/css/styles.css
touch myapp/js/app.js
/apps/myapp/.
Next, download these essential components and place them in your application directory structure:
myapp/js/auth.js
myapp/js/components/LoginDialog.js
myapp/docs/
myapp/js/components/ChatAssistant.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App - DSiloed</title>
<!-- Vue and Vuetify -->
<link href="https://cdn.jsdelivr.net/npm/vuetify@3.5.11/dist/vuetify.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<!-- Custom styles -->
<link rel="stylesheet" href="css/styles.css?v=1764792677">
<!-- Auth helper -->
<script src="js/auth.js?v=1764792677"></script>
<script src="js/components/LoginDialog.js?v=1764792677"></script>
<!-- App scripts -->
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.21/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.5.11/dist/vuetify.min.js"></script>
<script src="js/app.js?v=1764792677" defer></script>
</head>
<body>
<div id="app" v-cloak>
<v-app>
<v-app-bar color="primary" app>
<v-app-bar-title>My App</v-app-bar-title>
</v-app-bar>
<v-main>
<v-container>
<h1>My Custom App</h1>
<p>Welcome to my custom DSiloed application!</p>
</v-container>
</v-main>
</v-app>
</div>
</body>
</html>
// Initialize Vue app
document.addEventListener('DOMContentLoaded', function() {
const { createApp } = Vue;
const { createVuetify } = Vuetify;
// Initialize authentication
const authManager = new AuthManager('myapp');
// Create Vuetify instance
const vuetify = createVuetify();
const app = createApp({
data() {
return {
authManager: authManager,
isAuthenticated: false,
user: null,
}
},
methods: {
async initialize() {
// Initialize authentication
const authResult = await this.authManager.initialize();
this.isAuthenticated = authResult.authenticated;
this.user = authResult.user;
// Show login if not authenticated
if (!this.isAuthenticated) {
this.showLogin();
}
},
showLogin() {
const loginDialog = new LoginDialog('myapp');
loginDialog.onLogin = (result) => {
if (result.success) {
this.isAuthenticated = true;
this.user = result.user;
console.log('Logged in as:', this.user.email);
}
};
loginDialog.show();
},
logout() {
this.authManager.logout();
this.isAuthenticated = false;
this.user = null;
}
},
mounted() {
this.initialize();
}
});
app.use(vuetify);
app.mount('#app');
});
Start by creating a new React application using Create React App or your preferred tool:
# Create a new React app
npx create-react-app my-model-api-app
# Navigate to the app directory
cd my-model-api-app
# Install Material UI and other dependencies
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
Create authentication utilities to work with DSiloed:
// src/utils/auth.js
export class AuthManager {
constructor(appName) {
this.appName = appName;
this.authToken = localStorage.getItem('auth_token');
this.tenantId = localStorage.getItem('tenant_id');
this.user = null;
}
isAuthenticated() {
return !!this.authToken;
}
getToken() {
return this.authToken;
}
getTenantId() {
return this.tenantId;
}
async loadUserInfo() {
if (!this.isAuthenticated()) {
return null;
}
try {
const response = await fetch('https://modelapi.russonrails.com/api/v1/users/current', {
headers: {
'Authorization': 'Bearer ' + this.authToken,
'Tenant-Id': this.tenantId || 'root-tenant'
}
});
const data = await response.json();
if (data.success) {
this.user = data.user;
return this.user;
} else {
this.logout();
return null;
}
} catch (error) {
console.error('Error loading user info:', error);
return null;
}
}
async login(email, password, tenantId) {
try {
const response = await fetch('https://modelapi.russonrails.com/api/v1/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Tenant-Id': tenantId
},
body: JSON.stringify({
email: email,
password: password
})
});
const data = await response.json();
if (data.success) {
this.authToken = data.token;
this.tenantId = tenantId;
localStorage.setItem('auth_token', this.authToken);
localStorage.setItem('tenant_id', this.tenantId);
await this.loadUserInfo();
return { success: true, user: this.user };
} else {
return { success: false, message: data.message || 'Invalid email or password' };
}
} catch (error) {
console.error('Login error:', error);
return { success: false, message: 'An error occurred during login. Please try again.' };
}
}
logout() {
this.authToken = null;
this.user = null;
localStorage.removeItem('auth_token');
localStorage.removeItem('tenant_id');
window.location.href = window.location.pathname;
}
async initialize() {
if (this.isAuthenticated()) {
const user = await this.loadUserInfo();
return { authenticated: true, user: user };
}
return { authenticated: false };
}
}
// src/components/LoginDialog.jsx
import React, { useState } from 'react';
import {
Dialog, DialogTitle, DialogContent, DialogActions,
TextField, Button, CircularProgress, Alert
} from '@mui/material';
function LoginDialog({ open, onClose, onLogin, authManager }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [tenantId, setTenantId] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
setLoading(true);
try {
if (!tenantId) {
setError('Tenant ID is required');
setLoading(false);
return;
}
const result = await authManager.login(email, password, tenantId);
if (result.success) {
onLogin(result);
onClose();
} else {
setError(result.message || 'Login failed');
}
} catch (err) {
setError('An unexpected error occurred. Please try again.');
} finally {
setLoading(false);
}
};
return (
);
}
export default LoginDialog;
// src/App.jsx
import React, { useState, useEffect, useRef } from 'react';
import {
Container, AppBar, Toolbar, Typography, Button,
ThemeProvider, createTheme, CssBaseline, Box
} from '@mui/material';
import { AuthManager } from './utils/auth';
import LoginDialog from './components/LoginDialog';
const theme = createTheme({
palette: {
primary: {
main: '#3161FF',
},
secondary: {
main: '#6C63FF',
},
},
});
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [user, setUser] = useState(null);
const [loginOpen, setLoginOpen] = useState(false);
const authManager = useRef(new AuthManager('myapp')).current;
useEffect(() => {
const initializeAuth = async () => {
try {
const authResult = await authManager.initialize();
setIsAuthenticated(authResult.authenticated);
setUser(authResult.user);
} catch (error) {
console.error('Auth initialization error:', error);
}
};
initializeAuth();
}, []);
const handleLogin = (result) => {
if (result.success) {
setIsAuthenticated(true);
setUser(result.user);
}
};
const handleLogout = () => {
authManager.logout();
setIsAuthenticated(false);
setUser(null);
};
return (
My App
{isAuthenticated ? (
<>
{user?.email}
>
) : (
)}
{isAuthenticated ? (
My Custom App
Welcome to my custom DSiloed application!
) : (
Welcome to My App
Please log in to access the application.
)}
setLoginOpen(false)}
onLogin={handleLogin}
authManager={authManager}
/>
);
}
export default App;
// Example API hook (src/hooks/useApi.js)
import { useState, useCallback } from 'react';
export function useApi(authManager) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const headers = useCallback(() => ({
'Authorization': 'Bearer ' + authManager.getToken(),
'Tenant-Id': authManager.getTenantId(),
'Content-Type': 'application/json'
}), [authManager]);
const fetchData = useCallback(async (endpoint, options = {}) => {
if (!authManager.isAuthenticated()) {
setError('Authentication required');
return null;
}
setLoading(true);
setError(null);
try {
const response = await fetch(`https://modelapi.russonrails.com/api/v1/${endpoint}`, {
...options,
headers: {
...headers(),
...(options.headers || {})
}
});
const data = await response.json();
if (!data.success) {
throw new Error(data.error?.message || 'API request failed');
}
return data;
} catch (err) {
setError(err.message);
return null;
} finally {
setLoading(false);
}
}, [authManager, headers]);
return { loading, error, fetchData };
}
// Using the hook in a component:
function PartyList() {
const [parties, setParties] = useState([]);
const authManager = useRef(new AuthManager('myapp')).current;
const { loading, error, fetchData } = useApi(authManager);
const loadParties = useCallback(async () => {
const data = await fetchData('parties');
if (data) {
setParties(data.parties);
}
}, [fetchData]);
useEffect(() => {
if (authManager.isAuthenticated()) {
loadParties();
}
}, [authManager, loadParties]);
if (loading) return ;
if (error) return {error} ;
return (
{parties.map(party => (
))}
);
}
Here's how to make API requests to the DSiloed backend with Vue.js:
// Example method to fetch parties
async fetchParties() {
try {
const response = await fetch('https://modelapi.russonrails.com/api/v1/parties', {
headers: {
'Authorization': 'Bearer ' + this.authManager.getToken(),
'Tenant-Id': this.authManager.getTenantId()
}
});
const data = await response.json();
if (data.success) {
this.parties = data.parties;
} else {
console.error('Error fetching parties:', data.error);
}
} catch (error) {
console.error('API request failed:', error);
}
}
Here's how to make API requests to the DSiloed backend with React:
// Using React hooks and fetch API
import { useState, useEffect, useCallback } from 'react';
function PartyList({ authManager }) {
const [parties, setParties] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchParties = useCallback(async () => {
if (!authManager.isAuthenticated()) return;
setLoading(true);
try {
const response = await fetch('https://modelapi.russonrails.com/api/v1/parties', {
headers: {
'Authorization': 'Bearer ' + authManager.getToken(),
'Tenant-Id': authManager.getTenantId()
}
});
const data = await response.json();
if (data.success) {
setParties(data.parties);
} else {
setError(data.error?.message || 'Failed to load parties');
}
} catch (error) {
setError('API request failed: ' + error.message);
} finally {
setLoading(false);
}
}, [authManager]);
useEffect(() => {
fetchParties();
}, [fetchParties]);
// UI components to display the data...
}
The ChatAssistant component provides a reusable, framework-independent chat interface that you can easily add to any application. It works with Vue.js, React, vanilla JavaScript, or any other framework.
// Include the ChatAssistant component
<script src="js/components/ChatAssistant.js?v=1764792677"></script>
// Initialize in your application
const chatAssistant = new ChatAssistant('MyApp', {
apiClient: apiClient, // Your API client instance
title: 'AI Assistant',
placeholder: 'Ask me anything...',
contextMessage: 'Assistant has access to your application data'
});
// Show/hide the chat
chatAssistant.toggle();
// In your Vue app
methods: {
initChatAssistant() {
this.chatAssistant = new ChatAssistant('MyApp', {
apiClient: apiClient,
title: 'MyApp Assistant',
placeholder: 'Ask about your data...',
contextMessage: 'Assistant can help with your application',
onError: (error) => {
this.showSnackbar('Chat error: ' + error.message, 'error');
}
});
},
toggleChat() {
if (this.chatAssistant) {
this.chatAssistant.toggle();
}
}
},
mounted() {
this.initChatAssistant();
}
import { useEffect, useRef } from 'react';
function MyApp() {
const chatAssistantRef = useRef(null);
useEffect(() => {
// Initialize ChatAssistant
chatAssistantRef.current = new ChatAssistant('MyApp', {
apiClient: apiClient,
title: 'MyApp Assistant',
onError: (error) => {
showErrorToast(error.message);
}
});
// Cleanup on unmount
return () => {
if (chatAssistantRef.current) {
chatAssistantRef.current.destroy();
}
};
}, []);
const toggleChat = () => {
if (chatAssistantRef.current) {
chatAssistantRef.current.toggle();
}
};
return (
<div>
<button onClick={toggleChat}>
Toggle Chat Assistant
</button>
{/* Your app content */}
</div>
);
}
const chatAssistant = new ChatAssistant('AppName', {
// Required
apiClient: apiClientInstance,
// UI Configuration
title: 'AI Assistant',
placeholder: 'Ask me anything...',
contextMessage: 'Assistant has access to your data',
position: 'right', // 'left' or 'right'
width: '400px',
height: '600px',
zIndex: 1000,
// Event Callbacks
onToggle: (isVisible) => {
console.log('Chat toggled:', isVisible);
},
onMessage: (message) => {
console.log('New message:', message);
},
onError: (error) => {
console.error('Chat error:', error);
}
});
The ChatAssistant automatically handles authentication using your existing apiClient instance. It will create conversations, manage message history, and provide a seamless chat experience without additional configuration.
You can use Large Language Models (LLMs) like Claude to help you build applications on top of DSiloed. Here's a prompt template to help you get started:
Use this prompt template with Claude or other AI assistants to help build your application.
For best results, download and extract the docs.zip file to your myapp/docs/ directory
and provide the documentation files to the AI for comprehensive guidance.
Simply modify the prompt template above to specify your application's requirements, then paste it to an LLM like Claude or GPT to get help building your application.
When using LLMs for app development, provide as much context as possible about your specific requirements and the data models you plan to use. This will help the LLM generate more relevant and usable code.
Now that you've explored the DSiloed platform, here are some next steps to continue your journey:
Need help with the DSiloed platform? Here are some resources to assist you:
Access comprehensive documentation for the DSiloed platform.