# Agents API Source: https://docs.junis.ai/api-reference/agents List and invoke AI agents directly without orchestration ## Overview The Agents API provides direct access to individual AI agents in your organization. Unlike the Chat Completions API which routes requests through an orchestrator, this API allows you to invoke specific agents directly for targeted tasks. **Direct Agent Invocation**: Use this API when you know exactly which agent should handle your request. For intelligent routing across multiple agents, use the [Chat Completions API](/api-reference/chat-completions) instead. Direct invocation skips the orchestrator's routing LLM call, so the first message of a new session starts about 2 seconds earlier. The same can be done on the OpenAI-compatible endpoint with `target_agent_id` — see [Low-latency first turn](/api-reference/chat-completions#low-latency-first-turn-direct-routing-warmup-per-request-context). To avoid the one-time agent build on a new user's first call, use [`POST /v1/warmup`](/api-reference/warmup). *** ## Authentication All endpoints require API key authentication with specific scopes: | Endpoint | Required Scope | | - | - | | `GET /agents` | `agents:list` | | `POST /agents/{id}/completions` | `agents:invoke` | | `POST /agents/{id}/completions` with `on_behalf_of` | `agents:invoke`, `users:delegate` | **Default Scopes**: The `agents:list` and `agents:invoke` scopes are enabled by default when creating API keys. No additional configuration is needed. Building your own product on Junis? This endpoint accepts `on_behalf_of` and `session_hidden`, exactly like [Chat Completions](/api-reference/chat-completions), so you can run a single agent as one of your users. See [User Provisioning](/api-reference/user-provisioning). Include your API key in the `X-API-Key` header: ```bash theme={null} X-API-Key: jns_live_YOUR_API_KEY_HERE ``` See [Authentication](/api-reference/authentication) for details. *** ## Agent Types Agents in the system are categorized by their execution pattern: | Type | Description | | - | - | | `LLM_AGENT` | Standard language model agent that processes requests directly | | `SEQUENTIAL_AGENT` | Executes sub-agents in sequence, passing output between them | | `PARALLEL_AGENT` | Executes sub-agents simultaneously and combines results | | `LOOP_AGENT` | Iteratively executes sub-agents based on conditions | **LLM Agents Only**: Only `LLM_AGENT` type agents can be invoked directly via this API. Sequential, Parallel, and Loop agents are container agents that orchestrate other agents. *** ## List Agents Retrieve a list of available agents in your organization. ### Endpoint ``` GET https://api.junis.ai/api/external/agents ``` ### Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `agent_type` | string | No | Filter by type: `LLM_AGENT`, `SEQUENTIAL_AGENT`, `PARALLEL_AGENT`, `LOOP_AGENT` | | `is_active` | boolean | No | Filter by active status (`true` or `false`) | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/agents?agent_type=LLM_AGENT&is_active=true" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.get( "https://api.junis.ai/api/external/agents", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}, params={ "agent_type": "LLM_AGENT", "is_active": True } ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "agents": [ { "agent_id": "brand-analyzer", "name": "Brand Analyzer", "agent_type": "LLM_AGENT", "description": "Analyzes brand data and generates insights", "is_active": true, "model": "anthropic/claude-sonnet-4-6", "has_tools": true, "has_mcp": false }, { "agent_id": "content-writer", "name": "Content Writer", "agent_type": "LLM_AGENT", "description": "Creates marketing content based on brand guidelines", "is_active": true, "model": "anthropic/claude-sonnet-4-6", "has_tools": false, "has_mcp": true } ], "total": 2, "organization_id": "org-abc123" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `agents` | array | List of agent objects | | `total` | integer | Total number of agents matching the filter | | `organization_id` | string | Organization ID | ### Agent Object Fields | Field | Type | Description | | - | - | - | | `agent_id` | string | Unique agent identifier | | `name` | string | Display name of the agent | | `agent_type` | string | Type of agent (`LLM_AGENT`, `SEQUENTIAL_AGENT`, etc.) | | `description` | string | Description of the agent's purpose | | `is_active` | boolean | Whether the agent is currently active | | `model` | string | LLM model used by the agent (null for non-LLM agents) | | `has_tools` | boolean | Whether the agent has tools configured | | `has_mcp` | boolean | Whether the agent has MCP integrations | *** ## Invoke Agent (Non-Streaming) Send a message to a specific agent and receive the complete response. ### Endpoint ``` POST https://api.junis.ai/api/external/agents/{agent_id}/completions ``` ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `agent_id` | string | **Yes** | The agent ID to invoke | ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `message` | string | **Yes** | The message to send to the agent | | `session_id` | string | No | Existing session ID to continue conversation | | `stream` | boolean | No | Set to `false` for non-streaming (default: `false`) | | `temperature` | number | No | Model temperature (0.0-2.0) | | `max_tokens` | integer | No | Maximum tokens in response | | `on_behalf_of` | string | No | Run as a user your organization provisioned. Requires the `users:delegate` scope. Session, message, and memory ownership go to that user; subscription checks and credit charges stay with your API key's own user. | | `session_hidden` | boolean | No | Keep the session out of user-facing session lists — for background or one-off calls. Applied when a session is created, and updated on reuse only when explicitly passed. | | `extra_context` | string | No | Per-request context or instructions for **this turn only** (max 65,536 chars). Appended as a non-persistent trailing block after the conversation, together with the user's memory — the same block the orchestrator would receive. Never stored in the session, never merged into the system instruction. | ### Request Example ```bash cURL theme={null} curl -X POST "https://api.junis.ai/api/external/agents/brand-analyzer/completions" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "message": "Analyze the brand positioning of Nike", "stream": false }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.junis.ai/api/external/agents/brand-analyzer/completions", headers={ "X-API-Key": "jns_live_YOUR_API_KEY_HERE", "Content-Type": "application/json" }, json={ "message": "Analyze the brand positioning of Nike", "stream": False } ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "id": "agentcmpl-550e8400", "agent_id": "brand-analyzer", "agent_name": "Brand Analyzer", "session_id": "sess_abc123def456", "response": "Nike's brand positioning centers on empowerment and athletic excellence...", "created": 1767261600, "model": "anthropic/claude-sonnet-4-6" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `id` | string | Unique response identifier (format: `agentcmpl-{uuid}`) | | `agent_id` | string | ID of the agent that processed the request | | `agent_name` | string | Display name of the agent | | `session_id` | string | Session ID (can be used for follow-up messages) | | `response` | string | The agent's complete response | | `created` | integer | Unix timestamp of response creation | | `model` | string | LLM model used for the response | *** ## Invoke Agent (Streaming) Send a message and receive the response as a real-time stream using Server-Sent Events (SSE). ### Endpoint ``` POST https://api.junis.ai/api/external/agents/{agent_id}/completions ``` ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `message` | string | **Yes** | The message to send to the agent | | `session_id` | string | No | Existing session ID to continue conversation | | `stream` | boolean | **Yes** | Set to `true` for streaming | | `temperature` | number | No | Model temperature (0.0-2.0) | | `max_tokens` | integer | No | Maximum tokens in response | | `on_behalf_of` | string | No | Run as a user your organization provisioned. Requires the `users:delegate` scope. Session, message, and memory ownership go to that user; subscription checks and credit charges stay with your API key's own user. | | `session_hidden` | boolean | No | Keep the session out of user-facing session lists — for background or one-off calls. Applied when a session is created, and updated on reuse only when explicitly passed. | | `extra_context` | string | No | Per-request context or instructions for **this turn only** (max 65,536 chars). Appended as a non-persistent trailing block after the conversation, together with the user's memory — the same block the orchestrator would receive. Never stored in the session, never merged into the system instruction. | ### Request Example ```bash cURL theme={null} curl -X POST "https://api.junis.ai/api/external/agents/brand-analyzer/completions" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "message": "Analyze the brand positioning of Nike", "stream": true }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.junis.ai/api/external/agents/brand-analyzer/completions", headers={ "X-API-Key": "jns_live_YOUR_API_KEY_HERE", "Content-Type": "application/json", "Accept": "text/event-stream" }, json={ "message": "Analyze the brand positioning of Nike", "stream": True }, stream=True ) for line in response.iter_lines(): if line: print(line.decode('utf-8')) ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.junis.ai/api/external/agents/brand-analyzer/completions', { method: 'POST', headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE', 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, body: JSON.stringify({ message: 'Analyze the brand positioning of Nike', stream: true }) } ); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)); if (data.type === 'content') { process.stdout.write(data.content); } } } } ``` ### SSE Response Format The streaming response uses OpenAI-compatible format with `choices` array: | Field | Type | Description | | - | - | - | | `id` | string | Unique chunk identifier | | `object` | string | Always `"agent.completion.chunk"` | | `created` | integer | Unix timestamp | | `agent_id` | string | Agent ID | | `agent_name` | string | Agent display name | | `model` | string | LLM model used | | `choices` | array | Array containing delta content | ### Streaming Response Example ``` data: {"id": "agentcmpl-abc123", "object": "agent.completion.chunk", "created": 1767261600, "agent_id": "brand-analyzer", "agent_name": "Brand Analyzer", "session_id": "sess_abc123", "model": "anthropic/claude-sonnet-4-6", "choices": [{"index": 0, "delta": {"role": "assistant"}, "finish_reason": null}]} data: {"id": "agentcmpl-abc123", "object": "agent.completion.chunk", "created": 1767261600, "agent_id": "brand-analyzer", "agent_name": "Brand Analyzer", "model": "anthropic/claude-sonnet-4-6", "choices": [{"index": 0, "delta": {"content": "Nike's "}, "finish_reason": null}]} data: {"id": "agentcmpl-abc123", "object": "agent.completion.chunk", "created": 1767261600, "agent_id": "brand-analyzer", "agent_name": "Brand Analyzer", "model": "anthropic/claude-sonnet-4-6", "choices": [{"index": 0, "delta": {"content": "brand positioning "}, "finish_reason": null}]} data: {"id": "agentcmpl-abc123", "object": "agent.completion.chunk", "created": 1767261600, "agent_id": "brand-analyzer", "agent_name": "Brand Analyzer", "model": "anthropic/claude-sonnet-4-6", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]} data: [DONE] ``` ### Chunk Object Fields | Field | Type | Description | | - | - | - | | `choices[].index` | integer | Always `0` | | `choices[].delta.role` | string | `"assistant"` (first chunk only) | | `choices[].delta.content` | string | Incremental text content | | `choices[].finish_reason` | string | `null` during streaming, `"stop"` on completion | *** ## Session Continuity You can maintain conversation context by reusing the `session_id` from previous responses: ```python Python theme={null} import requests API_KEY = "jns_live_YOUR_API_KEY_HERE" BASE_URL = "https://api.junis.ai/api/external/agents" # First message response1 = requests.post( f"{BASE_URL}/brand-analyzer/completions", headers={"X-API-Key": API_KEY, "Content-Type": "application/json"}, json={"message": "Tell me about Nike's brand"} ) session_id = response1.json()["session_id"] # Follow-up message using same session response2 = requests.post( f"{BASE_URL}/brand-analyzer/completions", headers={"X-API-Key": API_KEY, "Content-Type": "application/json"}, json={ "message": "How does it compare to Adidas?", "session_id": session_id # Continue conversation } ) print(response2.json()["response"]) ``` *** ## Error Responses ### Common Errors ```json 400 Bad Request - Invalid Agent Type Filter theme={null} { "detail": "Invalid agent_type 'unknown'. Must be one of: ['LLM_AGENT', 'SEQUENTIAL_AGENT', 'PARALLEL_AGENT', 'LOOP_AGENT']" } ``` ```json 400 Bad Request - Inactive Agent theme={null} { "error": { "message": "Agent 'brand-analyzer' is not active", "type": "invalid_request", "code": "agent_inactive" } } ``` ```json 402 Payment Required - Insufficient Credits theme={null} { "error": { "message": "크레딧이 부족합니다. 현재 잔액: $0.50", "message_en": "Insufficient credits. Balance: $0.50", "type": "insufficient_credits", "code": "insufficient_credits" }, "credit_balance": 0.5, "effective_balance": 0.5 } ``` ```json 403 Forbidden - No Subscription theme={null} { "error": { "message": "Basic or Pro subscription required. Please visit /subscription to start a subscription.", "message_ko": "Basic 또는 Pro 구독이 필요합니다. 구독을 시작하려면 /subscription 페이지를 방문하세요.", "type": "subscription_required", "code": "no_subscription" } } ``` ```json 403 Forbidden - Insufficient Scopes theme={null} { "error": "insufficient_scopes", "message": "API key does not have required permissions", "required_scopes": ["agents:invoke"], "missing_scopes": ["agents:invoke"], "available_scopes": ["orchestrator:invoke", "sessions:read"] } ``` ```json 404 Not Found theme={null} { "error": { "message": "Agent 'invalid-agent-id' not found in organization", "type": "not_found", "code": "agent_not_found" } } ``` *** ## Best Practices Use the List Agents endpoint to discover available agents and their capabilities: ```python theme={null} # Get all active LLM agents response = requests.get( "https://api.junis.ai/api/external/agents", headers={"X-API-Key": api_key}, params={"agent_type": "LLM_AGENT", "is_active": True} ) agents = response.json()["agents"] for agent in agents: print(f"{agent['name']}: {agent['description']}") print(f" - Model: {agent['model']}") print(f" - Has Tools: {agent['has_tools']}") print(f" - Has MCP: {agent['has_mcp']}") ``` For streaming responses, implement proper SSE parsing: ```python theme={null} import json import requests response = requests.post( f"{BASE_URL}/agents/{agent_id}/completions", headers={"X-API-Key": api_key, "Content-Type": "application/json"}, json={"message": prompt, "stream": True}, stream=True ) full_response = "" for line in response.iter_lines(): if line: line = line.decode('utf-8') if line.startswith('data: '): try: data = json.loads(line[6:]) if data.get('type') == 'content': full_response += data.get('content', '') elif data.get('type') == 'done': break elif data.get('type') == 'error': raise Exception(data.get('error')) except json.JSONDecodeError: continue ``` Implement comprehensive error handling for production use: ```python theme={null} def invoke_agent(agent_id, message, api_key): try: response = requests.post( f"https://api.junis.ai/api/external/agents/{agent_id}/completions", headers={"X-API-Key": api_key, "Content-Type": "application/json"}, json={"message": message}, timeout=120 ) if response.status_code == 200: return response.json() elif response.status_code == 402: raise PaymentError("Subscription or credits required") elif response.status_code == 403: raise PermissionError("Insufficient API key permissions") elif response.status_code == 404: raise ValueError(f"Agent not found: {agent_id}") else: raise Exception(f"API error: {response.json().get('detail')}") except requests.Timeout: raise TimeoutError("Request timed out after 120 seconds") ``` Efficiently manage sessions for multi-turn conversations: ```python theme={null} class AgentSession: def __init__(self, agent_id, api_key): self.agent_id = agent_id self.api_key = api_key self.session_id = None def send_message(self, message): payload = {"message": message} if self.session_id: payload["session_id"] = self.session_id response = requests.post( f"https://api.junis.ai/api/external/agents/{self.agent_id}/completions", headers={"X-API-Key": self.api_key, "Content-Type": "application/json"}, json=payload ) result = response.json() self.session_id = result.get("session_id") return result["response"] # Usage session = AgentSession("brand-analyzer", api_key) print(session.send_message("Tell me about Nike")) print(session.send_message("Compare it to Adidas")) # Continues conversation ``` *** ## Related Resources Learn about API key scopes and authentication Use orchestrated multi-agent conversations Manage and retrieve conversation sessions Full list of error codes and solutions # Authentication Source: https://docs.junis.ai/api-reference/authentication Secure your API requests with API keys ## Overview The Junis External API uses **API Keys** for authentication. All API requests must include a valid API key in the `X-API-Key` header. Junis has two kinds of API keys: **organization keys** (`jns_live_`) bound to a single organization, and **user keys** (`jns_user_`) that work across all organizations you belong to. See [Organization vs User API Keys](#organization-vs-user-api-keys) below. *** ## API Key Authentication ### Creating an API Key Go to **Dashboard** → **API Keys** in the Junis dashboard Click the **"+ Create API Key"** button Fill in the form: * **Name**: A descriptive name for the key (e.g., "Production API", "Testing") * **Description**: Optional notes about the key's purpose * **Scopes**: Select permissions (default: all external API scopes enabled) * **Rate Limits**: Set automatically (100 requests per minute, 3,000 per hour per key); an admin can change them later through the admin API **IMPORTANT**: Copy the API key and store it securely. Treat it like a password — anyone with the key can act with its permissions. The key format is: `jns_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` (32 random characters) ### API Key Format | Prefix | Purpose | Where to Create | | - | - | - | | `jns_live_` | **Organization key** — bound to one organization | **Admin → API Keys** (admin/owner) | | `jns_user_` | **User key** — works across all your organizations | **Settings → API Keys** (up to 5 per user) | ### Organization vs User API Keys * **Organization keys** (`jns_live_`) always act within the organization they were created in. * **User keys** (`jns_user_`) act as *you*, across every organization you belong to. Select which organization a request targets with the `X-Organization-Id` header; if omitted, your current organization is used. ```bash cURL — user key targeting a specific organization theme={null} curl -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_user_YOUR_API_KEY_HERE" \ -H "X-Organization-Id: YOUR_ORG_ID" \ -H "Content-Type: application/json" \ -d '{"messages": [{"role": "user", "content": "Hello!"}]}' ``` User keys are what [Junis Magic](/guides/junis-magic) uses — one key, all your squads. *** ## Using API Keys Include your API key in the `X-API-Key` header: ```bash cURL theme={null} curl -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "Hello!"} ] }' ``` *** ## API Key Permissions (Scopes) ### Available Scopes API keys can have granular permissions. Each scope grants access to specific API operations: | Scope | Description | Required For | | - | - | - | | `orchestrator:invoke` | Invoke orchestrator with prompts (non-streaming) | Chat Completions (non-streaming) | | `orchestrator:stream` | Stream orchestrator responses (SSE) | Chat Completions with `stream: true` | | `sessions:read` | Read chat sessions | List sessions, Get session status | | `sessions:messages` | Read session messages | Get session messages | | `content:read` | Read generated content | List content, Get content detail | | `content:retry` | Retry failed content generation | Retry content generation | | `agents:list` | List available agents in organization | List agents | | `agents:invoke` | Invoke specific agents directly | Agent completions | | `rag:upload` | Upload files to RAG Datastore | Create DataStore, Upload files | | `rag:read` | Read RAG uploads and DataStores | List DataStores, List uploads, Get upload status | | `rag:delete` | Delete RAG uploads and documents (opt-in, never default) | Delete RAG resources | | `sessions:delete` | Delete a session and its messages (opt-in, never default) | Delete a session | | `admin:read` | Read admin resources (organization-scoped) | Admin endpoints (if accessible) | | `admin:write` | Write admin resources (organization-scoped) | Admin endpoints (if accessible) | | `users:provision` | Provision (create-or-get) users and pending org memberships | [User Provisioning](/api-reference/user-provisioning) | | `users:delegate` | Act on behalf of another organization member | `on_behalf_of` on chat/agent completions and on session read & delete | **Default Scopes**: Organization keys created from the dashboard include: `orchestrator:invoke`, `orchestrator:stream`, `sessions:read`, `sessions:messages`, `agents:list`, `agents:invoke`, `content:read`, `content:retry`. User keys include a broader default set covering management operations (used by Junis Magic). Additional scopes — RAG and delete — are granted by updating the key (ask your organization admin). For partner scopes, see below. **Updating scopes replaces the whole list.** Send every scope the key should keep, not just the ones you are adding. Unknown scope names are rejected so a typo cannot leave a key permanently locked out, and an empty list is refused unless you explicitly confirm it — to disable a key, deactivate it instead of stripping its scopes. **Partner scopes are opt-in only**: `users:provision`, `users:delegate` and `sessions:delete` are **never** included in any default scope set, and are intended for trusted partner services that manage end-user accounts. To get them, create an organization key from **Dashboard → API Keys → New Key** with **"Connect users from my own app"** ticked. A key created without that checkbox does not carry them. See [User Provisioning](/api-reference/user-provisioning). ### Scope Requirements by Endpoint | Endpoint | Required Scopes | | - | - | | `POST /api/external/v1/chat/completions` (non-streaming) | `orchestrator:invoke` | | `POST /api/external/v1/chat/completions` (streaming) | `orchestrator:invoke`, `orchestrator:stream` | | `GET /api/external/sessions` | `sessions:read` | | `GET /api/external/sessions/{id}/messages` | `sessions:messages` | | `GET /api/external/sessions/{id}/status` | `sessions:read` | | `GET /api/external/content` | `content:read` | | `GET /api/external/content/{id}` | `content:read` | | `POST /api/external/content/{id}/retry` | `content:retry` | | `GET /api/external/agents` | `agents:list` | | `POST /api/external/agents/{id}/completions` (non-streaming) | `agents:invoke` | | `POST /api/external/agents/{id}/completions` (streaming) | `agents:invoke` | | `POST /api/external/rag/datastores` | `rag:upload` | | `GET /api/external/rag/datastores` | `rag:read` | | `POST /api/external/rag/upload` | `rag:upload` | | `POST /api/external/management/users` | `users:provision` | | `POST /api/external/v1/chat/completions` with `on_behalf_of` | `orchestrator:invoke`, `users:delegate` | | `GET /api/external/sessions` with `on_behalf_of` | `sessions:read`, `users:delegate` | | `GET /api/external/sessions/{id}/messages` with `on_behalf_of` | `sessions:messages`, `users:delegate` | | `DELETE /api/external/sessions/{id}` | `sessions:delete` | | `DELETE /api/external/sessions/{id}` with `on_behalf_of` | `sessions:delete`, `users:delegate` | | `GET /api/external/rag/uploads` | `rag:read` | | `GET /api/external/rag/uploads/{id}` | `rag:read` | *** ## Rate Limits API keys are subject to rate limits to ensure fair usage and system stability. ### Default Rate Limits | Limit | Organization key | Personal key | | - | - | - | | Requests per minute, per key | 100 | 100 | | Requests per hour, per key | 3,000 | 2,000 | | Requests per minute, whole organization (all keys combined) | 300 | 300 | Per-key limits can be adjusted by an organization admin through the admin API (`PUT /api/admin/api-keys/{key_id}`); the organization-wide total is a platform setting. ### Rate Limit Headers All API responses include rate limit information in headers: ```http theme={null} X-RateLimit-Limit-Minute: 100 X-RateLimit-Remaining-Minute: 85 X-RateLimit-Reset-Minute: 1767261600 X-RateLimit-Limit-Hour: 3000 X-RateLimit-Remaining-Hour: 2950 X-RateLimit-Reset-Hour: 1767265200 X-RateLimit-Limit-Org-Minute: 300 X-RateLimit-Remaining-Org-Minute: 240 ``` See [Rate Limits](/api-reference/rate-limits) for detailed documentation. *** ## Security Best Practices * **Never commit API keys to Git repositories** * Store keys in environment variables or secret management systems (e.g., AWS Secrets Manager, HashiCorp Vault) * Use `.env` files for local development (add to `.gitignore`) * Rotate keys regularly (every 90 days recommended) * All API requests must use HTTPS * Never send API keys over unencrypted HTTP * Our API automatically rejects non-HTTPS requests * Create separate keys for development, staging, and production * Use descriptive names to identify key purposes (e.g., "Prod - Web App", "Dev - Testing") * Revoke unused keys immediately * Check the API Keys page for usage statistics * Review `last_used_at` and `request_count` regularly * Set up alerts for unusual activity (e.g., sudden spike in requests) If an API key is compromised: 1. **Immediately revoke** the key in Admin → API Keys 2. **Create a new key** with a different name 3. **Update all applications** to use the new key 4. **Review logs** for suspicious activity (unusual endpoints, high request counts) * Only grant scopes that are actually needed * For read-only integrations, use `sessions:read` and `sessions:messages` only * Avoid granting `admin:write` unless absolutely necessary *** ## Error Handling ### Common Authentication Errors ```json 401 Unauthorized - Missing API Key theme={null} { "detail": "API key required. Include X-API-Key header with your request." } ``` ```json 401 Unauthorized - Invalid API Key theme={null} { "detail": "Invalid or expired API key" } ``` ```json 403 Forbidden - Insufficient Scopes theme={null} { "error": "insufficient_scopes", "message": "API key does not have required permissions", "required_scopes": ["sessions:delete"], "missing_scopes": ["sessions:delete"], "available_scopes": ["orchestrator:invoke", "sessions:read"] } ``` ```json 429 Too Many Requests theme={null} { "error": { "type": "rate_limit_error", "message": "Rate limit exceeded. Please try again in 60 seconds.", "code": "rate_limit_exceeded", "retry_after": 60 } } ``` ### Handling Errors in Code When you receive an error response, check the HTTP status code and error message to determine the cause. Common error codes: * **401 Unauthorized**: Invalid or missing API key * **403 Forbidden**: Insufficient scopes/permissions * **429 Too Many Requests**: Rate limit exceeded (wait for `retry_after` seconds) *** ## Next Steps Start sending messages to your agents Retrieve and manage chat sessions Understand rate limiting and best practices Full list of error codes and solutions # Chat Completions Source: https://docs.junis.ai/api-reference/chat-completions Send messages to your Junis orchestrator with OpenAI-compatible API ## Overview The Chat Completions API is **fully compatible with OpenAI's API format**, allowing you to use the OpenAI Python SDK, JavaScript SDK, or any OpenAI-compatible client. **Model Parameter (Optional)**: The `model` parameter is optional and primarily for SDK compatibility. Your organization's orchestrator agent automatically uses the configured LLM model. If you're using an OpenAI SDK that requires the `model` parameter, you can pass any value (e.g., `"junis-orchestrator"`) for compatibility. *** ## Prerequisites **Subscription Required**: This API is only available for **Basic** or **Pro** subscribers. If you don't have an active subscription, you'll receive a `403 Forbidden` error. Visit [junis.ai/subscription](https://junis.ai/subscription) to start a plan. *** ## Endpoint ``` POST https://api.junis.ai/api/external/v1/chat/completions ``` ### Authentication Include your API key in the `X-API-Key` header: ```bash theme={null} X-API-Key: jns_live_YOUR_API_KEY_HERE ``` **Required Scope:** `orchestrator:invoke` (`orchestrator:stream` for `stream: true`, `agents:invoke` when `target_agent_id` is set) See [Authentication](/api-reference/authentication) for details. *** ## Request Format ### Basic Request ```bash cURL theme={null} curl -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "What is Junis?"} ] }' ``` ### Request Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `messages` | array | **Yes** | Array of message objects (OpenAI format) | | `stream` | boolean | No | Enable streaming mode (default: `false`) | | `session_id` | string | No | Session ID to continue conversation (creates new if not provided) | | `on_behalf_of` | string | No | Act on behalf of this Junis user ID. Requires the `users:delegate` scope, and the target must be an active or pending member of the key's organization. Session/message/memory ownership goes to the target user; **subscription checks and credit charges stay with the API key's backing user**. Omitted → behavior identical to today. | | `session_hidden` | boolean | No | Create the session as hidden (or update the flag when explicitly passed while reusing a session you own). Hidden sessions are excluded from user-facing session lists (web sidebar, admin session list) but remain fetchable by `session_id`. Omitted → sessions are visible as today. | | `extra_context` | string | No | Per-request context or instructions for **this turn only** (max 65,536 chars). Delivered as a non-persistent trailing block to the orchestrator **and to the agent that answers** — never stored in the session, never merged into the system instruction. Omitted → behavior identical to today. | | `target_agent_id` | string | No | Route this turn **directly** to the given agent (UUID of an active agent in your organization, normally a sub-agent of the orchestrator) and skip the orchestrator's routing LLM call. Requires the `agents:invoke` scope. Unknown or foreign id → `404 agent_not_found`; inactive → `400 agent_inactive`. Later turns may omit it — the session continues with the agent that answered last. See [Low-latency first turn](#low-latency-first-turn-direct-routing-warmup-per-request-context). | **Delegation (`on_behalf_of`)** is designed for partner services that provision end-user accounts via [User Provisioning](/api-reference/user-provisioning). Pending members are allowed so provisioned users work before their first login. ### Message Format Messages follow the OpenAI format: ```json theme={null} { "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}, {"role": "assistant", "content": "Hi! How can I help you?"}, {"role": "user", "content": "Tell me about Junis."} ] } ``` **Role Types:** * `system`: System instructions (optional, rarely needed with Junis) * `user`: User messages * `assistant`: Assistant responses (for conversation context) * `tool`: Tool call results (for function calling) *** ## Response Format ### Non-Streaming Response **Status Code:** `200 OK` **Response Body:** ```json theme={null} { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1767261600, "model": "junis-orchestrator", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Junis is a production-ready Multi-Agent AI platform..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 15, "completion_tokens": 42, "total_tokens": 57 }, "session_id": "550e8400-e29b-41d4-a716-446655440000" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `id` | string | Unique completion ID | | `object` | string | Object type (always `"chat.completion"`) | | `created` | integer | Unix timestamp | | `model` | string | Model used | | `choices` | array | Array of completion choices | | `choices[].message` | object | Generated message | | `choices[].message.role` | string | Always `"assistant"` | | `choices[].message.content` | string | Response text | | `choices[].finish_reason` | string | Reason for completion (`stop`, `length`, `tool_calls`, `content_filter`) | | `usage` | object | Token usage statistics | | `session_id` | string | Session ID for continuing the conversation | *** ## Streaming Mode Enable real-time token-by-token streaming with `stream: true`. ### Streaming Request ```bash cURL theme={null} curl -N -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "Tell me a story"}], "stream": true }' ``` ### Streaming Response Format Server-Sent Events (SSE) format: ``` data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1767261600,"model":"junis-orchestrator","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}],"session_id":"550e8400-e29b-41d4-a716-446655440000"} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1767261600,"model":"junis-orchestrator","choices":[{"index":0,"delta":{"content":"Once"},"finish_reason":null}],"session_id":"550e8400-e29b-41d4-a716-446655440000"} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1767261600,"model":"junis-orchestrator","choices":[{"index":0,"delta":{"content":" upon"},"finish_reason":null}],"session_id":"550e8400-e29b-41d4-a716-446655440000"} ... data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1767261600,"model":"junis-orchestrator","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"session_id":"550e8400-e29b-41d4-a716-446655440000"} data: [DONE] ``` **Key Points:** * Each line starts with `data: ` * First chunk contains `role` * Subsequent chunks contain `content` deltas * Final chunk has `finish_reason` * **Every chunk includes `session_id`** for conversation continuity * Stream ends with `data: [DONE]` * Lines starting with `:` (for example `: keepalive`) are SSE comments sent every 15 seconds while the server is still preparing the answer (including the first-time build of your organization's agents). Standard SSE/OpenAI clients ignore them. *** ## Low-latency first turn: direct routing, warmup, per-request context The first message of a **new session** normally costs one extra LLM call: the orchestrator reads the request and decides which agent should answer (`transfer_to_agent`). Measured in production this routing call takes about 2 seconds (p50) before the answering agent even starts. Later turns in the same session skip it — Junis resumes the agent that answered last. If your application already knows which agent should answer (for example a fixed "counselor" or "support" agent), you can skip the routing call on the first turn too. ### `target_agent_id` — skip the routing call ```bash cURL theme={null} curl -N -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "My child had a hard time at school today"}], "on_behalf_of": "9f1c…", "extra_context": "Child: 7 years old, recently changed schools", "target_agent_id": "785aba85-caf1-4e22-8ca9-841093e611d6", "stream": true }' ``` | Rule | Detail | | - | - | | Value | UUID of an **active** agent in your organization (use the `agent_id` from [`GET /agents`](/api-reference/agents)). Normally a sub-agent of your orchestrator. | | Scope | `agents:invoke` in addition to `orchestrator:invoke` (the same permission as [`POST /agents/{agent_id}/completions`](/api-reference/agents)). Default keys include both. | | Validation | Happens **before** the session is created, so a wrong id never leaves an empty session behind. Unknown/foreign/global id → `404 agent_not_found`, inactive → `400 agent_inactive`. | | What the agent receives | On a `target_agent_id` turn the target agent acts as the orchestrator for that turn, so it receives the trailing block the orchestrator would have received: the user's **memory** plus `extra_context`. | | Later turns | You may omit the field — the session continues with the agent that answered last. Sending it on every turn gives the same result. | | Edge cases | If the target is not part of the orchestrator's tree, the next turn without the field is routed by the orchestrator again. If the last responder is a child of a Sequential/Parallel/Loop agent, every turn is routed by the orchestrator (ADK rule). | `POST /agents/{agent_id}/completions` gives the same effect (no routing call). `target_agent_id` lets you keep the OpenAI-compatible body, `files`, and session rules of this endpoint. ### `extra_context` — context for this turn only `extra_context` is appended as a **non-persistent trailing block** after the conversation, for this request only. It is never written to the session and never merged into the system instruction, so it cannot pollute later turns or break prompt caching. Since October 2026 it is delivered to the agent that actually answers (orchestrator-routed sub-agent, `target_agent_id` target, or `/agents/{agent_id}/completions`), not only to the orchestrator. * Maximum 65,536 characters (`422` if exceeded). * It counts as uncached input tokens on every call that receives it — send what the turn needs, not your whole database. * If your organization prefers the previous behavior (context on the orchestrator call only), contact us and we will turn off delivery to the answering agent for your organization. Sub-agent requests then match the pre-October behavior byte for byte. Note that on `target_agent_id` turns the orchestrator does not run, so with delivery turned off no model call receives `extra_context` (or the user's memory) on those turns — the same as before October. ### Warmup — build the agents before the first message Your organization's agents are built lazily per (organization, user) on the server that receives the request, so the first message of a newly provisioned user pays that build (fractions of a second for small squads, several seconds for squads with many tools). Call [`POST /v1/warmup`](/api-reference/warmup) right after provisioning the user and the first message finds everything ready. *** ## Multi-Agent Events **For Advanced Integrations**: Junis streams additional agent lifecycle events alongside standard OpenAI chunks. These events have no `choices` field, so **standard OpenAI SDKs automatically ignore them** - ensuring 100% backward compatibility. Parse these events to build rich UIs that show which agents are processing your request. ### Event Types | Event | Description | Fields | | - | - | - | | `agent_started` | Agent begins processing | `agent`, `agent_type`, `depth`, `parent_agent` | | `agent_completed` | Agent finishes processing | `agent`, `duration_ms` | | `agent_routing` | Transition between agents | `from_agent`, `to_agent` | | `token_agent` | Which agent produced the token | `agent` | | `tool_started` | Tool execution begins | `tool_name`, `tool_id`, `agent_name` | | `tool_completed` | Tool execution ends | `tool_name`, `tool_id`, `agent_name` | | `payment_required` | Payment needed to continue | `message`, `agent_name`, `nonce`, `network`, `total_amount` | ### SSE Stream Example (Multi-Agent) ``` data: {"event":"agent_started","agent":"researcher","agent_type":"LLM_AGENT","depth":1,"timestamp":"2026-01-01T10:00:00Z"} data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"Based on"}}],"session_id":"..."} data: {"event":"token_agent","agent":"researcher","timestamp":"2026-01-01T10:00:00Z"} data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":" my research"}}],"session_id":"..."} data: {"event":"agent_completed","agent":"researcher","duration_ms":1523,"timestamp":"2026-01-01T10:00:01Z"} data: {"event":"agent_routing","from_agent":"researcher","to_agent":"writer","timestamp":"2026-01-01T10:00:01Z"} data: {"event":"agent_started","agent":"writer","agent_type":"LLM_AGENT","depth":1,"timestamp":"2026-01-01T10:00:01Z"} ... data: [DONE] ``` ### Parsing Agent Events (Python) ```python theme={null} import json import httpx async def stream_with_agent_events(prompt: str, api_key: str): """Stream response and track agent events.""" async with httpx.AsyncClient() as client: async with client.stream( "POST", "https://api.junis.ai/api/external/v1/chat/completions", headers={"X-API-Key": api_key}, json={"messages": [{"role": "user", "content": prompt}], "stream": True} ) as response: current_agent = None agent_durations = {} async for line in response.aiter_lines(): if not line.startswith("data: "): continue data_str = line[6:] if data_str == "[DONE]": break data = json.loads(data_str) # Agent event (no 'choices' field) if "event" in data and "choices" not in data: event = data["event"] if event == "agent_started": current_agent = data["agent"] print(f"🚀 Agent started: {current_agent}") elif event == "agent_completed": agent = data["agent"] duration = data.get("duration_ms", 0) agent_durations[agent] = duration print(f"✅ Agent completed: {agent} ({duration}ms)") elif event == "agent_routing": print(f"🔄 Routing: {data['from_agent']} → {data['to_agent']}") # Standard OpenAI chunk (has 'choices' field) elif "choices" in data: content = data["choices"][0].get("delta", {}).get("content", "") if content: print(content, end="", flush=True) print(f"\n\n📊 Agent performance: {agent_durations}") ``` ### Parallel Agent Support When multiple agents run concurrently (Parallel Agent), use `token_agent` events to correctly attribute tokens: ```python theme={null} agent_buffers = {} # Per-agent content buffers last_token_agent = None for event in stream: if event.get("event") == "token_agent": last_token_agent = event["agent"] elif "choices" in event: content = event["choices"][0].get("delta", {}).get("content", "") if content and last_token_agent: if last_token_agent not in agent_buffers: agent_buffers[last_token_agent] = "" agent_buffers[last_token_agent] += content # Result: {"researcher": "Research content...", "analyst": "Analysis content..."} ``` **When to use agent events:** * Build agent pipeline visualizations * Track per-agent performance metrics * Handle Parallel Agent outputs separately * Debug multi-agent workflows *** ## Session Management Junis supports explicit session management via `session_id` parameter, allowing you to control conversation context precisely. ### How Sessions Work ```mermaid theme={null} sequenceDiagram participant Client participant API participant Database Client->>API: First message (no session_id) API->>Database: Create new session Database-->>API: session_id API-->>Client: Response with session_id Client->>API: Follow-up message (with session_id) API->>Database: Load existing session Database-->>API: session context API-->>Client: Contextual response with session_id ``` ### Session ID Usage **Recommended**: Always capture the `session_id` from the first response and include it in subsequent requests to maintain conversation context. **Behavior:** | Request | Result | | - | - | | No `session_id` | Creates a **new session** and returns `session_id` in response | | With `session_id` (existing) | Uses the **existing session** (same chat room) | | With `session_id` (new ID) | Creates a **new session** with the provided ID | ### Example: Continuing a Conversation ```python theme={null} import requests API_KEY = "jns_live_YOUR_API_KEY_HERE" BASE_URL = "https://api.junis.ai/api/external/v1/chat/completions" # First request - no session_id response1 = requests.post(BASE_URL, headers={"X-API-Key": API_KEY}, json={"messages": [{"role": "user", "content": "Hello!"}]} ) session_id = response1.json()["session_id"] # Capture session_id # Second request - with session_id (continues conversation) response2 = requests.post(BASE_URL, headers={"X-API-Key": API_KEY}, json={ "messages": [{"role": "user", "content": "What did I just say?"}], "session_id": session_id # Use captured session_id } ) # Response will have context from the first message ``` ### Viewing Sessions Retrieve your sessions via the Sessions API: ```bash theme={null} curl -X GET https://api.junis.ai/api/external/sessions \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` See [Sessions API](/api-reference/sessions) for details. *** ## Function Calling (Tool Use) **Current Implementation**: Tool calling uses keyword pattern matching, not LLM-native function calling. Supported tools: * `end_call`: Detects phrases like "안녕히", "감사합니다", "goodbye" * `transfer_call`: Detects phrases like "상담원 연결", "전화 연결", "connect agent" Full LLM-native function calling support is in development. When a supported keyword is detected in the assistant's response, the API returns a `tool_calls` object with `finish_reason: "tool_calls"`. *** ## Error Handling ### Common Errors ```json 400 Bad Request theme={null} { "error": { "message": "Invalid request: 'messages' field is required", "type": "invalid_request_error", "param": "messages", "code": "missing_required_field" } } ``` ```json 401 Unauthorized theme={null} { "error": { "message": "Invalid API key", "type": "authentication_error", "code": "invalid_api_key" } } ``` ```json 402 Payment Required theme={null} { "error": { "message": "Insufficient credits. Current balance: $-5.00. Please add credits to continue.", "type": "insufficient_credits", "code": "insufficient_credits" }, "credit_balance": -5.0, "effective_balance": -5.0, "can_use_api": false } ``` ```json 403 Forbidden (No Subscription) theme={null} { "error": { "message": "Basic or Pro subscription required. Please visit /subscription to start a subscription.", "type": "subscription_required", "code": "no_subscription" }, "has_subscription": false, "required_plans": ["basic_monthly", "pro_monthly"] } ``` ```json 403 Forbidden (Invalid Plan) theme={null} { "error": { "message": "External API is only available for Basic or Pro subscribers.", "type": "invalid_subscription_plan", "code": "invalid_plan" }, "current_plan": "token_30", "required_plans": ["basic_monthly", "pro_monthly"] } ``` ```json 429 Too Many Requests (Minute Limit) theme={null} { "error": "rate_limit_exceeded", "message": "Rate limit exceeded: 60 requests per minute", "limit": 60, "window": "minute", "reset_at": 1767261600, "retry_after": 45 } ``` ```json 429 Too Many Requests (Hour Limit) theme={null} { "error": "rate_limit_exceeded", "message": "Rate limit exceeded: 3000 requests per hour", "limit": 3000, "window": "hour", "reset_at": 1767265200, "retry_after": 1800 } ``` ```json 500 Internal Server Error theme={null} { "error": { "message": "Failed to process request: Internal error", "type": "api_error", "code": "internal_error" } } ``` ### Error Types | Error Type | Status Code | Description | Action | | - | - | - | - | | `invalid_request_error` | 400 | Malformed request | Check request format | | `authentication_error` | 401 | Invalid API key | Verify API key | | `insufficient_credits` | 402 | Credit balance below zero | Add credits at /subscription | | `subscription_required` | 403 | No active subscription | Subscribe to Basic or Pro plan | | `invalid_subscription_plan` | 403 | Plan doesn't include API access | Upgrade to Basic or Pro plan | | `rate_limit_error` | 429 | Too many requests | Implement exponential backoff | | `api_error` | 500 | Server error | Retry with exponential backoff | *** ## Best Practices * **Use Streaming**: Enable `stream: true` for long responses to provide real-time feedback * **Handle Rate Limits**: Implement exponential backoff when hitting rate limits (see error codes above) * **Keep Message History Concise**: Trim old messages to avoid token limits (keep last 10-20 messages) * **Secure API Keys**: Never hardcode API keys; use environment variables * **Log Requests**: Log requests and responses for debugging and monitoring *** ## Response Parameters ### Temperature Guide | Use Case | Recommended Temperature | | - | - | | Factual Q\&A | 0.0 - 0.3 | | General chat | 0.5 - 0.7 | | Creative writing | 0.8 - 1.0 | | Code generation | 0.0 - 0.2 | *** ## Next Steps Retrieve and manage conversation sessions Complete error reference Understand rate limiting # Generated Content Source: https://docs.junis.ai/api-reference/content Access and manage AI-generated content (images, videos, audio, 3D models) ## Overview The Generated Content API provides access to content generated through Junis AI agents using [Replicate](https://replicate.com) models. This includes images, videos, audio files, and 3D models (GLB format). **Content Generation**: Content is generated through AI agents that use the `gen_content` tool. This API provides read-only access to view and retry generated content. *** ## Authentication All endpoints require API key authentication with specific scopes: | Endpoint | Required Scope | | - | - | | `GET /content` | `content:read` | | `GET /content/{id}` | `content:read` | | `POST /content/{id}/retry` | `content:retry` | **Default Scopes**: The `content:read` and `content:retry` scopes are **not** enabled by default when creating API keys. You must explicitly enable these scopes during key creation. Include your API key in the `X-API-Key` header: ```bash theme={null} X-API-Key: jns_live_YOUR_API_KEY_HERE ``` See [Authentication](/api-reference/authentication) for details. *** ## Content Types Generated content is categorized into the following types: | Type | Description | Example Models | | - | - | - | | `image` | Static images (PNG, JPG, WebP) | SDXL, Flux, Stable Diffusion | | `video` | Video files (MP4, WebM) | Runway Gen-3, AnimateDiff | | `audio` | Audio files (MP3, WAV) | MusicGen, Bark | | `glb` | 3D models (GLB format) | Point-E, Shap-E | | `other` | Other content types | Various | *** ## Content Status Content goes through the following status lifecycle: | Status | Description | | - | - | | `pending` | Generation request received, waiting to be processed | | `processing` | Content is being generated by Replicate | | `completed` | Generation successful, content available | | `failed` | Generation failed (can be retried) | *** ## List Content Retrieve a paginated list of generated content for your organization. ### Endpoint ``` GET https://api.junis.ai/api/external/content ``` ### Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `page` | integer | No | Page number (default: `1`, min: `1`) | | `page_size` | integer | No | Items per page (default: `20`, max: `100`) | | `content_type` | string | No | Filter by type: `image`, `video`, `audio`, `glb`, `other` | | `status` | string | No | Filter by status: `pending`, `processing`, `completed`, `failed` | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/content?page=1&page_size=10&status=completed" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.get( "https://api.junis.ai/api/external/content", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}, params={ "page": 1, "page_size": 10, "status": "completed" } ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "items": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "content_type": "image", "status": "completed", "prompt": "A serene mountain landscape at sunset", "replicate_version": "stability-ai/sdxl", "output_url": "https://replicate.delivery/...", "storage_url": "https://.blob.core.windows.net//generation/...", "error_message": null, "processing_time_ms": 12500, "credit_cost": 0.05, "created_at": "2026-01-01T10:30:00Z", "completed_at": "2026-01-01T10:30:12Z" } ], "total": 42, "page": 1, "page_size": 10, "has_more": true } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `items` | array | List of content items | | `total` | integer | Total number of items matching the filter | | `page` | integer | Current page number | | `page_size` | integer | Number of items per page | | `has_more` | boolean | Whether more pages are available | ### Content Item Fields | Field | Type | Description | | - | - | - | | `id` | string | Unique content ID (UUID) | | `content_type` | string | Type of content (`image`, `video`, `audio`, `glb`, `other`). `null` until generation completes. | | `status` | string | Current status | | `prompt` | string | Generation prompt (may be truncated in list view) | | `replicate_version` | string | Replicate model version used | | `output_url` | string | Direct URL to generated content (Replicate CDN) | | `storage_url` | string | Permanent URL (Junis object storage) | | `error_message` | string | Error description if failed | | `processing_time_ms` | integer | Generation time in milliseconds | | `credit_cost` | number | Credit cost for this generation (6 decimal places) | | `created_at` | string | Creation timestamp (ISO 8601) | | `completed_at` | string | Completion timestamp (ISO 8601) | *** ## Get Content Detail Retrieve detailed information about a specific content item. ### Endpoint ``` GET https://api.junis.ai/api/external/content/{content_id} ``` ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `content_id` | string | **Yes** | Content ID (UUID format) | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/content/550e8400-e29b-41d4-a716-446655440000" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "organization_id": "org-abc123", "user_id": "user-xyz789", "content_type": "image", "status": "completed", "prompt": "A serene mountain landscape at sunset with dramatic clouds and golden light reflecting off a calm lake in the foreground", "replicate_version": "stability-ai/sdxl", "replicate_prediction_id": "xyz123abc456", "replicate_input": { "prompt": "A serene mountain landscape at sunset...", "negative_prompt": "blurry, low quality", "width": 1024, "height": 1024, "num_inference_steps": 50 }, "output_url": "https://replicate.delivery/...", "storage_path": "generation/image/550e8400_20260101_103000.png", "storage_url": "https://.blob.core.windows.net//generation/...", "error_message": null, "retry_count": 0, "max_retries": 3, "credit_cost": 0.05, "processing_time_ms": 12500, "created_at": "2026-01-01T10:30:00Z", "started_at": "2026-01-01T10:30:01Z", "completed_at": "2026-01-01T10:30:12Z" } ``` ### Additional Detail Fields | Field | Type | Description | | - | - | - | | `organization_id` | string | Organization that owns this content | | `user_id` | string | User who initiated the generation | | `replicate_prediction_id` | string | Replicate's internal prediction ID | | `replicate_input` | object | Full input parameters sent to Replicate | | `storage_path` | string | Path in cloud storage bucket | | `retry_count` | integer | Number of retry attempts made | | `max_retries` | integer | Maximum allowed retries (default: 3) | | `started_at` | string | Processing start timestamp | *** ## Retry Failed Content Retry a failed content generation. This re-queues the generation with the original parameters. ### Endpoint ``` POST https://api.junis.ai/api/external/content/{content_id}/retry ``` ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `content_id` | string | **Yes** | Content ID of failed generation (UUID format) | ### Prerequisites * Content must have status `failed` * `retry_count` must be less than `max_retries` (default: 3) ### Request Example ```bash cURL theme={null} curl -X POST "https://api.junis.ai/api/external/content/550e8400-e29b-41d4-a716-446655440000/retry" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.post( "https://api.junis.ai/api/external/content/550e8400-e29b-41d4-a716-446655440000/retry", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"} ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "success": true, "message": "Content generation retry queued (attempt 1/3)", "generation_id": "550e8400-e29b-41d4-a716-446655440000" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether retry was queued successfully | | `message` | string | Human-readable status message | | `generation_id` | string | Content ID being retried | *** ## Error Responses ### Common Errors ```json 400 Bad Request - Invalid Filter theme={null} { "detail": "Invalid content_type 'unknown'. Must be one of: ['image', 'video', 'audio', 'glb', 'other']" } ``` ```json 400 Bad Request - Cannot Retry theme={null} { "detail": "Cannot retry content with status 'completed'. Only failed content can be retried." } ``` ```json 400 Bad Request - Max Retries Reached theme={null} { "detail": "Maximum retry attempts (3) reached for this content." } ``` ```json 403 Forbidden - Insufficient Scopes theme={null} { "error": "insufficient_scopes", "message": "API key does not have required permissions", "required_scopes": ["content:read"], "missing_scopes": ["content:read"], "available_scopes": ["orchestrator:invoke", "sessions:read"] } ``` ```json 404 Not Found theme={null} { "detail": "Content not found: 550e8400-e29b-41d4-a716-446655440000" } ``` *** ## Best Practices When content is in `pending` or `processing` status, poll the detail endpoint to check for completion: ```python theme={null} import time import requests def wait_for_content(content_id, api_key, timeout=300, interval=5): """Poll until content is completed or failed.""" start = time.time() while time.time() - start < timeout: response = requests.get( f"https://api.junis.ai/api/external/content/{content_id}", headers={"X-API-Key": api_key} ) data = response.json() if data["status"] == "completed": return data elif data["status"] == "failed": raise Exception(f"Generation failed: {data.get('error_message')}") time.sleep(interval) raise TimeoutError("Content generation timed out") ``` Use content type filters to retrieve specific types of generated content: ```bash theme={null} # Get only images curl "https://api.junis.ai/api/external/content?content_type=image" # Get only videos curl "https://api.junis.ai/api/external/content?content_type=video" # Get failed content for monitoring curl "https://api.junis.ai/api/external/content?status=failed" ``` Generated content has two URLs: * **`output_url`**: Replicate's CDN URL (may expire) * **`storage_url`**: Permanent Junis object storage URL Always prefer `storage_url` for persistent access to content. The `credit_cost` field shows the credit deducted for each generation. Use this for: * Billing reconciliation * Usage monitoring * Cost optimization Note: Credit costs support 6 decimal places for precision. *** ## Related Resources Learn about API key scopes and authentication Generate content through AI conversations Invoke specific agents directly Full list of error codes and solutions # Error Codes Source: https://docs.junis.ai/api-reference/error-codes Comprehensive guide to Junis External API error codes and responses ## Overview The Junis External API uses conventional HTTP status codes to indicate the success or failure of a request. Error responses include a structured JSON body with details about the error, making it easy to debug and handle errors programmatically. **Base URL**: `https://api.junis.ai` *** ## Error Response Format ### Standard Error Response All error responses follow this consistent structure: ```json theme={null} { "detail": "Human-readable error message" } ``` ### Structured Error Response For more complex errors (like rate limiting or scope validation), the API returns additional context: ```json theme={null} { "error": "error_code", "message": "Human-readable error message", "code": "SPECIFIC_ERROR_CODE", "details": { // Additional error-specific details } } ``` *** ## HTTP Status Codes | Status Code | Name | Description | | - | - | - | | **200** | OK | Request succeeded. | | **201** | Created | Resource successfully created. | | **202** | Accepted | Request accepted for processing (async operations). | | **204** | No Content | Request succeeded with no response body. | | Status Code | Name | Description | | - | - | - | | **400** | Bad Request | Invalid request parameters or malformed JSON. | | **401** | Unauthorized | Missing or invalid API key. | | **403** | Forbidden | Authenticated but insufficient permissions (scopes). | | **404** | Not Found | Resource does not exist. | | **413** | Payload Too Large | Request body exceeds size limit. | | **429** | Too Many Requests | Rate limit exceeded. | | Status Code | Name | Description | | - | - | - | | **500** | Internal Server Error | Unexpected server error. | | **502** | Bad Gateway | Upstream service unavailable. | | **503** | Service Unavailable | Server temporarily overloaded or down. | | **504** | Gateway Timeout | Upstream service timeout. | *** ## Common Error Codes ### Authentication Errors (401) **Description**: No API key provided in request headers. **Causes**: * Missing `X-API-Key` header * Empty `X-API-Key` header value **Response Example**: ```json theme={null} { "detail": "API key required. Include X-API-Key header with your request." } ``` **Response Headers**: ``` HTTP/1.1 401 Unauthorized WWW-Authenticate: ApiKey ``` **Solution**: ```bash theme={null} # Include X-API-Key header curl https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY" ``` **Description**: API key not found, revoked, or expired. **Causes**: * API key deleted by organization admin * API key revoked * Wrong API key format * API key expired (if expiration was set) **Response Example**: ```json theme={null} { "detail": "Invalid or expired API key" } ``` **Solution**: * Verify API key in **Admin → API Keys** * Generate new API key if revoked or expired * Check API key format: `jns_live_` prefix + 32 characters *** ### Authorization Errors (403) **Description**: API key lacks required scopes for the operation. **Causes**: * API key created without required scopes * API key missing `orchestrator:invoke` scope (required for chat completions) * API key missing `sessions:read` or `sessions:messages` scope **Response Example**: ```json theme={null} { "error": "insufficient_scopes", "message": "API key does not have required permissions", "required_scopes": ["orchestrator:invoke"], "missing_scopes": ["orchestrator:invoke"], "available_scopes": ["sessions:read", "sessions:messages"] } ``` **Solution**: * Check API key scopes in **Admin → API Keys** * Create new API key with required scopes: * `orchestrator:invoke` - Chat completions (both streaming and non-streaming) * `sessions:read` - List sessions and get session status * `sessions:messages` - Get session messages **Description**: API key belongs to a different organization. **Causes**: * Trying to access sessions created by another organization * Trying to access resources not owned by your organization **Response Example**: ```json theme={null} { "detail": "Access denied. Resource belongs to a different organization." } ``` **Solution**: * Verify you're using the correct API key for your organization * Each API key can only access its own organization's resources *** ### Validation Errors (400) **Description**: Request body validation failed. **Causes**: * Missing required fields * Invalid field types * Failed Pydantic validation **Response Example**: ```json theme={null} { "detail": [ { "loc": ["body", "messages"], "msg": "field required", "type": "value_error.missing" }, { "loc": ["body", "model"], "msg": "str type expected", "type": "type_error.str" } ] } ``` **Solution**: * Check API documentation for required fields * Verify data types match schema * Example valid request: ```json theme={null} { "messages": [ {"role": "user", "content": "Hello"} ] } ``` **Description**: Malformed JSON in request body. **Causes**: * Syntax error in JSON * Unescaped special characters * Trailing commas **Response Example**: ```json theme={null} { "detail": "Expecting property name enclosed in double quotes" } ``` **Solution**: * Validate JSON syntax using a linter * Escape special characters properly * Remove trailing commas *** ### Resource Errors (404) **Description**: Chat session does not exist. **Causes**: * Session deleted * Wrong session\_id * Session belongs to different organization **Response Example**: ```json theme={null} { "detail": "Session not found" } ``` **Solution**: * Verify session\_id is correct * Create new session via Chat Completions API * List available sessions: `GET /api/external/sessions` **Description**: Message does not exist in session. **Causes**: * Invalid message index * Session has no messages yet **Response Example**: ```json theme={null} { "detail": "Message not found in session" } ``` **Solution**: * Check session messages: `GET /api/external/sessions/{id}/messages` * Verify message index is within range *** ### Rate Limit Errors (429) **Description**: Too many requests in time window. **Response Example**: ```json theme={null} { "error": "rate_limit_exceeded", "message": "Rate limit exceeded: 100 requests per minute", "limit": 100, "window": "minute", "reset_at": 1767261600, "retry_after": 45 } ``` **Response Headers**: ``` HTTP/1.1 429 Too Many Requests X-RateLimit-Limit-Minute: 100 X-RateLimit-Remaining-Minute: 0 X-RateLimit-Reset-Minute: 1767261600 X-RateLimit-Limit-Hour: 3000 X-RateLimit-Remaining-Hour: 2950 X-RateLimit-Reset-Hour: 1767261600 Retry-After: 45 ``` **Solution**: * Wait for `retry_after` seconds before retrying * Implement exponential backoff * Monitor `X-RateLimit-Remaining-*` headers * Request higher rate limits in **Admin → API Keys** * Cache responses to reduce request frequency See [Rate Limits](/api-reference/rate-limits) for more details. *** ### Payload Errors (413) **Description**: Request body exceeds size limit. **Causes**: * Large message content * Too many messages in array * Maximum payload: 10MB **Response Example**: ```json theme={null} { "detail": "Request payload too large. Maximum size: 10MB" } ``` **Solution**: * Split large requests into smaller chunks * Summarize long messages * Use pagination for message history *** ### Server Errors (500) **Description**: Unexpected server error occurred. **Causes**: * Database connection failure * External service timeout * Unhandled exception **Response Example**: ```json theme={null} { "detail": "Internal server error" } ``` **Solution**: * Retry request after a few seconds with exponential backoff * Check [Status Page](https://status.junis.ai) for incidents * Contact support if error persists: [contact@junis.ai](mailto:contact@junis.ai) **Description**: LLM provider (Anthropic, OpenAI) unavailable. **Causes**: * Anthropic API outage * OpenAI API rate limits * Network timeout to LLM provider **Response Example**: ```json theme={null} { "detail": "LLM service temporarily unavailable. Please try again." } ``` **Solution**: * Retry request with exponential backoff * Check [Anthropic Status](https://status.anthropic.com) * Wait a few minutes and retry **Description**: Agent orchestration failed. **Causes**: * Agent not found * Agent configuration error * Tool execution failure **Response Example**: ```json theme={null} { "detail": "Orchestrator processing error. Please try again." } ``` **Solution**: * Retry request * Simplify prompt if issue persists * Contact support with session\_id for debugging *** ## Error Handling Best Practices ### Retry Logic Implement exponential backoff for transient errors (429, 500+ status codes): * **429 Rate Limit**: Use the `Retry-After` header value to wait before retrying * **5xx Server Errors**: Use exponential backoff (1s, 2s, 4s, etc.) * **Max Retries**: Limit to 3-5 attempts to avoid infinite loops * **Client Errors (4xx)**: Do not retry, fix the request instead ### Error Logging Log errors with sufficient context for debugging: * **Include**: Timestamp, status code, error message, request URL, method * **Exclude**: Full API key (log only prefix like `jns_live_abc...`) * **Structured Logging**: Use JSON format for easier parsing ### User-Friendly Error Messages Present errors to users in a helpful way: * **401 Unauthorized**: "Invalid API key. Please check your credentials." * **403 Forbidden**: "Permission denied. Check your API key scopes." * **429 Rate Limit**: "Too many requests. Please wait seconds." * **5xx Server Errors**: "Our servers are experiencing issues. Please try again." * **Generic Fallback**: Use the `detail` field from the error response *** ## Debugging Tools ### Check API Status ```bash theme={null} curl https://api.junis.ai/api/health ``` **Healthy Response**: ```json theme={null} { "status": "healthy", "version": "1.0.0", "timestamp": "2026-01-01T10:00:00Z" } ``` ### Test API Key Authentication ```bash theme={null} curl https://api.junis.ai/api/external/sessions \ -H "X-API-Key: jns_live_YOUR_API_KEY" ``` **Success Response** (200 OK): ```json theme={null} { "sessions": [], "total": 0, "limit": 50, "offset": 0 } ``` **Error Response** (401 Unauthorized): ```json theme={null} { "detail": "Invalid or expired API key" } ``` ### Inspect Rate Limits ```bash theme={null} curl -I https://api.junis.ai/api/external/v1/chat/completions \ -X POST \ -H "X-API-Key: jns_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"role":"user","content":"test"}]}' ``` **Response Headers**: ``` HTTP/1.1 200 OK X-RateLimit-Limit-Minute: 100 X-RateLimit-Remaining-Minute: 55 X-RateLimit-Reset-Minute: 1767261600 X-RateLimit-Limit-Hour: 3000 X-RateLimit-Remaining-Hour: 2950 X-RateLimit-Reset-Hour: 1767261600 ``` *** ## Monitoring and Alerts ### Track Error Rates Monitor these metrics for your API integration: Track 2xx responses vs total requests **Target**: > 99% success rate Client errors (auth, validation, rate limits) **Alert**: > 5% of requests Server errors (outages, timeouts) **Alert**: Any occurrence P50, P95, P99 latency **Target**: P95 \< 2 seconds ### Sample Monitoring Code ```python theme={null} import prometheus_client as prom # Define metrics api_requests = prom.Counter('api_requests_total', 'Total API requests', ['status']) api_latency = prom.Histogram('api_latency_seconds', 'API request latency') # Track request with api_latency.time(): response = requests.post(url, headers=headers, json=payload) api_requests.labels(status=response.status_code).inc() ``` *** ## Need Help? Check real-time API status and incidents Learn about rate limiting and optimization Review API key authentication Contact support for assistance # External API Overview Source: https://docs.junis.ai/api-reference/external-overview Integrate Junis AI agents into your applications ## Introduction The External API allows you to integrate Junis AI agents into your own applications. Use your API key to invoke agents, manage sessions, and build powerful AI-powered features. External API requires an active subscription (Basic or Pro). Credits are consumed for each API call. *** ## Base URL ``` https://api.junis.ai/api/external ``` For OpenAI-compatible endpoints: ``` https://api.junis.ai/api/external/v1 ``` *** ## Authentication All External API requests require an API key in the `X-API-Key` header: ```bash theme={null} curl -H "X-API-Key: jns_live_xxxxxxxxxxxxxxxx" \ https://api.junis.ai/api/external/v1/chat/completions ``` Never expose your API key in client-side code. Use server-side calls or a backend proxy. *** ## API Key Scopes API keys can be created with specific scopes to limit access: | Scope | Description | | - | - | | `orchestrator:invoke` | Invoke the main orchestrator via chat completions | | `orchestrator:stream` | Required in addition to `orchestrator:invoke` when `stream: true` | | `agents:list` | List available agents in organization | | `agents:invoke` | Invoke individual agents directly | | `sessions:read` | Read session history and messages | | `rag:upload` | Upload files to RAG Datastore | | `rag:read` | Read RAG uploads and DataStores | *** ## Available Endpoints ### OpenAI-Compatible `POST /v1/chat/completions` - OpenAI-compatible chat API `POST /v1/warmup` - Build the orchestrator (and optionally one agent) before the first message — no LLM call ### Agent Management `GET /agents` - List available agents `POST /agents/{agent_id}/completions` - Invoke specific agent ### Session Management `GET /sessions` - List chat sessions `GET /sessions/{session_id}/messages` - Get session messages ### RAG Knowledge Base `POST /rag/datastores` - Create new DataStore `POST /rag/upload` - Upload files to DataStore `GET /rag/uploads` - List upload history `GET /rag/datastores` - List DataStores *** ## Quick Start ### 1. Get Your API Key 1. Go to **Admin > API Keys** 2. Click **Create API Key** 3. Select required scopes 4. Copy and securely store the key (shown only once) ### 2. Make Your First Call ```bash theme={null} curl -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "model": "junis-orchestrator", "messages": [{"role": "user", "content": "Hello, how are you?"}], "stream": false }' ``` ### 3. Parse the Response ```json theme={null} { "id": "cmpl-abc123", "object": "chat.completion", "created": 1767261600, "model": "junis-orchestrator", "choices": [{ "index": 0, "message": { "role": "assistant", "content": "Hello! I'm doing well, thank you for asking..." }, "finish_reason": "stop" }], "usage": { "prompt_tokens": 10, "completion_tokens": 25, "total_tokens": 35 } } ``` *** ## SDK Support ### OpenAI Python SDK ```python theme={null} import openai client = openai.OpenAI( api_key="jns_live_your_api_key", base_url="https://api.junis.ai/api/external/v1" ) response = client.chat.completions.create( model="junis-orchestrator", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ### OpenAI Node.js SDK ```javascript theme={null} import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: 'jns_live_your_api_key', baseURL: 'https://api.junis.ai/api/external/v1' }); const response = await openai.chat.completions.create({ model: 'junis-orchestrator', messages: [{ role: 'user', content: 'Hello!' }] }); console.log(response.choices[0].message.content); ``` *** ## Streaming Enable streaming for real-time responses: ```python theme={null} stream = client.chat.completions.create( model="junis-orchestrator", messages=[{"role": "user", "content": "Tell me a story"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` *** ## Rate Limits | Limit | Default | | - | - | | Requests per minute, per key | 100 | | Requests per hour, per key | 3,000 (organization key) · 2,000 (personal key) | | Requests per minute, whole organization (all keys combined) | 300 | Rate limit headers are included in all responses: `X-RateLimit-Limit-Minute`, `X-RateLimit-Remaining-Minute`, `X-RateLimit-Reset-Minute`, their `-Hour` equivalents, and `X-RateLimit-Limit-Org-Minute` / `X-RateLimit-Remaining-Org-Minute`. See [Rate Limits](/api-reference/rate-limits). *** ## Error Handling All errors follow a consistent format: ```json theme={null} { "error": { "message": "Invalid API key", "type": "authentication_error", "code": "invalid_api_key" } } ``` ### Common Error Codes | Code | Status | Description | | - | - | - | | `invalid_api_key` | 401 | API key is invalid or revoked | | `insufficient_credits` | 402 | Not enough credits for request | | `rate_limit_exceeded` | 429 | Too many requests | | `scope_not_allowed` | 403 | API key lacks required scope | | `agent_not_found` | 404 | Specified agent doesn't exist | *** ## Related OpenAI-compatible chat API Individual agent invocation # RAG Knowledge Base Source: https://docs.junis.ai/api-reference/rag Upload and manage documents for RAG-powered search ## Overview The RAG (Retrieval-Augmented Generation) API allows you to upload documents and create knowledge bases that your AI agents can search. Documents are uploaded to Google Cloud Storage and indexed in Vertex AI Search for semantic retrieval. **Workflow**: First create a DataStore using `POST /rag/datastores`, then upload files to that DataStore using `POST /rag/upload`. Files are indexed asynchronously via background tasks. *** ## Authentication All RAG endpoints require API key authentication with specific scopes: | Endpoint | Required Scope | | - | - | | `POST /rag/datastores` | `rag:upload` | | `GET /rag/datastores` | `rag:read` | | `GET /rag/datastores/{id}/files` | `rag:read` | | `DELETE /rag/datastores/{id}` | `rag:delete` | | `DELETE /rag/datastores/{id}/files/{filename}` | `rag:delete` | | `POST /rag/upload` | `rag:upload` | | `GET /rag/uploads` | `rag:read` | | `GET /rag/uploads/{upload_id}` | `rag:read` | **Scopes Required**: The `rag:upload`, `rag:read`, and `rag:delete` scopes must be explicitly enabled when creating API keys. They are not enabled by default. Include your API key in the `X-API-Key` header: ```bash theme={null} X-API-Key: jns_live_YOUR_API_KEY_HERE ``` See [Authentication](/api-reference/authentication) for details. *** ## Supported File Types The following file types can be uploaded to RAG Datastores: | MIME Type | Extension | Description | | - | - | - | | `application/pdf` | `.pdf` | PDF documents | | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | `.docx` | Microsoft Word documents | | `application/vnd.openxmlformats-officedocument.presentationml.presentation` | `.pptx` | Microsoft PowerPoint presentations | | `text/plain` | `.txt` | Plain text files | | `text/html` | `.html` | HTML documents | | `application/json` | `.json` | JSON files | | `application/x-ndjson` | `.jsonl` | Newline-delimited JSON | | `text/markdown` | `.md`, `.markdown` | Markdown documents | **File Limits**: Maximum 10MB per file, maximum 10 files per upload request. *** ## Create DataStore Create a new DataStore to organize uploaded documents. ### Endpoint ``` POST https://api.junis.ai/api/external/rag/datastores ``` ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `display_name` | string | **Yes** | Human-readable name for the DataStore (e.g., "Company Policies") | | `description` | string | No | Optional description of the DataStore's purpose | ### Request Example ```bash cURL theme={null} curl -X POST "https://api.junis.ai/api/external/rag/datastores" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "display_name": "Company Policies", "description": "HR policies and employee handbook" }' ``` ```python Python theme={null} import requests response = requests.post( "https://api.junis.ai/api/external/rag/datastores", headers={ "X-API-Key": "jns_live_YOUR_API_KEY_HERE", "Content-Type": "application/json" }, json={ "display_name": "Company Policies", "description": "HR policies and employee handbook" } ) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.junis.ai/api/external/rag/datastores', { method: 'POST', headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE', 'Content-Type': 'application/json' }, body: JSON.stringify({ display_name: 'Company Policies', description: 'HR policies and employee handbook' }) }); const data = await response.json(); console.log(data); ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "success": true, "datastore_id": "company-policies-a1b2c3d4", "display_name": "Company Policies", "description": "HR policies and employee handbook", "message": "DataStore 'Company Policies' created successfully" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether creation was successful | | `datastore_id` | string | Unique ID for the DataStore (use this for uploads) | | `display_name` | string | Human-readable name | | `description` | string | DataStore description (null if not provided) | | `message` | string | Success message | ### Error Responses ```json 409 Conflict - DataStore Already Exists theme={null} { "detail": { "error": { "message": "DataStore with name 'Company Policies' already exists", "type": "conflict", "code": "datastore_exists", "existing_datastore_id": "company-policies-a1b2c3d4" } } } ``` *** ## Upload Files Upload files to an existing DataStore. Files are encoded as Base64 and indexed asynchronously. ### Endpoint ``` POST https://api.junis.ai/api/external/rag/upload ``` **DataStore Required**: You must create a DataStore first using `POST /rag/datastores`. File uploads to non-existent DataStores will fail with a 404 error. ### Request Body | Field | Type | Required | Description | | - | - | - | - | | `datastore_id` | string | **Yes** | ID of existing DataStore (from create response) | | `files` | array | **Yes** | Array of files to upload (min: 1, max: 10) | | `files[].filename` | string | **Yes** | Filename with extension (e.g., "policy.pdf") | | `files[].content` | string | **Yes** | Base64-encoded file content | | `files[].content_type` | string | No | MIME type (auto-detected from extension if not provided) | ### Request Example ```bash cURL theme={null} # First, encode your file as Base64 BASE64_CONTENT=$(base64 -i policy.pdf) curl -X POST "https://api.junis.ai/api/external/rag/upload" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "datastore_id": "company-policies-a1b2c3d4", "files": [ { "filename": "policy.pdf", "content": "'"${BASE64_CONTENT}"'" } ] }' ``` ```python Python theme={null} import base64 import requests # Read and encode file with open("policy.pdf", "rb") as f: content = base64.b64encode(f.read()).decode() response = requests.post( "https://api.junis.ai/api/external/rag/upload", headers={ "X-API-Key": "jns_live_YOUR_API_KEY_HERE", "Content-Type": "application/json" }, json={ "datastore_id": "company-policies-a1b2c3d4", "files": [ { "filename": "policy.pdf", "content": content } ] } ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "success": true, "datastore_id": "company-policies-a1b2c3d4", "datastore_name": "Company Policies", "uploads": [ { "upload_id": "550e8400-e29b-41d4-a716-446655440000", "filename": "policy.pdf", "status": "processing" } ], "message": "1 file(s) upload started" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether upload was initiated | | `datastore_id` | string | Target DataStore ID | | `datastore_name` | string | Target DataStore display name | | `uploads` | array | Status of each uploaded file | | `uploads[].upload_id` | string | Unique upload ID (UUID) | | `uploads[].filename` | string | Original filename | | `uploads[].status` | string | Upload status (`processing`) | | `message` | string | Summary message | ### Error Responses ```json 404 Not Found - DataStore Not Found theme={null} { "detail": { "error": { "message": "DataStore not found: invalid-datastore-id. Create it first using POST /rag/datastores", "type": "not_found", "code": "datastore_not_found" } } } ``` ```json 400 Bad Request - Unsupported File Type theme={null} { "detail": { "error": { "message": "Unsupported file extension: .exe", "type": "validation_error", "code": "unsupported_file_type" } } } ``` ```json 400 Bad Request - File Too Large theme={null} { "detail": { "error": { "message": "File size exceeds 10MB limit", "type": "validation_error", "code": "file_too_large" } } } ``` ```json 400 Bad Request - Invalid Base64 theme={null} { "detail": { "error": { "message": "Base64 decoding failed: Invalid padding", "type": "validation_error", "code": "invalid_base64" } } } ``` *** ## List DataStores Retrieve all DataStores in your organization. ### Endpoint ``` GET https://api.junis.ai/api/external/rag/datastores ``` ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/rag/datastores" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.get( "https://api.junis.ai/api/external/rag/datastores", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"} ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "datastores": [ { "datastore_id": "company-policies-a1b2c3d4", "display_name": "Company Policies", "description": "HR policies and employee handbook", "document_count": 5, "created_at": "2026-01-01T10:00:00Z", "is_active": true }, { "datastore_id": "product-docs-e5f6g7h8", "display_name": "Product Documentation", "description": null, "document_count": 12, "created_at": "2026-01-01T08:30:00Z", "is_active": true } ], "total": 2 } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `datastores` | array | List of DataStores | | `datastores[].datastore_id` | string | Unique DataStore ID | | `datastores[].display_name` | string | Human-readable name | | `datastores[].description` | string | DataStore description (null if not set) | | `datastores[].document_count` | integer | Number of completed uploads | | `datastores[].created_at` | string | Creation timestamp (ISO 8601) | | `datastores[].is_active` | boolean | Whether DataStore is active | | `total` | integer | Total number of DataStores | *** ## List Uploads Retrieve a paginated list of file uploads for your organization. ### Endpoint ``` GET https://api.junis.ai/api/external/rag/uploads ``` ### Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `limit` | integer | No | Maximum items to return (default: `50`, max: `100`) | | `offset` | integer | No | Number of items to skip (default: `0`) | | `status` | string | No | Filter by status: `pending`, `processing`, `indexing`, `completed`, `failed` | | `datastore_id` | string | No | Filter by DataStore ID | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/rag/uploads?limit=10&status=completed" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.get( "https://api.junis.ai/api/external/rag/uploads", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}, params={ "limit": 10, "status": "completed" } ) print(response.json()) ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "uploads": [ { "upload_id": "550e8400-e29b-41d4-a716-446655440000", "filename": "policy.pdf", "file_size": 102400, "file_type": "application/pdf", "datastore_id": "company-policies-a1b2c3d4", "datastore_name": "Company Policies", "status": "completed", "uploaded_at": "2026-01-01T10:00:00Z", "indexed_at": "2026-01-01T10:05:00Z", "error_message": null } ], "total": 42, "limit": 10, "offset": 0 } ``` ### Upload Status Values | Status | Description | | - | - | | `pending` | Upload received, waiting for processing | | `processing` | File being uploaded to GCS | | `indexing` | File being indexed in Vertex AI Search | | `completed` | Indexing complete, document is searchable | | `failed` | Upload or indexing failed | ### Response Fields | Field | Type | Description | | - | - | - | | `uploads` | array | List of uploads | | `uploads[].upload_id` | string | Unique upload ID (UUID) | | `uploads[].filename` | string | Original filename | | `uploads[].file_size` | integer | File size in bytes | | `uploads[].file_type` | string | MIME type | | `uploads[].datastore_id` | string | Target DataStore ID | | `uploads[].datastore_name` | string | Target DataStore name | | `uploads[].status` | string | Current status | | `uploads[].uploaded_at` | string | Upload timestamp (ISO 8601) | | `uploads[].indexed_at` | string | Indexing completion timestamp (null if not indexed) | | `uploads[].error_message` | string | Error description if failed | | `total` | integer | Total matching uploads | | `limit` | integer | Items per page | | `offset` | integer | Current offset | *** ## Get Upload Status Retrieve detailed status of a specific upload. ### Endpoint ``` GET https://api.junis.ai/api/external/rag/uploads/{upload_id} ``` ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `upload_id` | string | **Yes** | Upload ID (UUID format) | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/rag/uploads/550e8400-e29b-41d4-a716-446655440000" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "upload_id": "550e8400-e29b-41d4-a716-446655440000", "filename": "policy.pdf", "file_size": 102400, "file_type": "application/pdf", "status": "completed", "ready": true, "datastore_id": "company-policies-a1b2c3d4", "datastore_name": "Company Policies", "uploaded_at": "2026-01-01T10:00:00Z", "indexed_at": "2026-01-01T10:05:00Z", "error_message": null } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `upload_id` | string | Unique upload ID (UUID) | | `filename` | string | Original filename | | `file_size` | integer | File size in bytes | | `file_type` | string | MIME type | | `status` | string | Current status | | `ready` | boolean | `true` if document is searchable (`status === completed`) | | `datastore_id` | string | Target DataStore ID | | `datastore_name` | string | Target DataStore name | | `uploaded_at` | string | Upload timestamp (ISO 8601) | | `indexed_at` | string | Indexing completion timestamp (null if not indexed) | | `error_message` | string | Error description if failed | ### Error Responses ```json 404 Not Found theme={null} { "detail": { "error": { "message": "Upload not found: 550e8400-e29b-41d4-a716-446655440000", "type": "not_found", "code": "upload_not_found" } } } ``` *** ## List DataStore Files Retrieve all files in a specific DataStore with their indexing status. ### Endpoint ``` GET https://api.junis.ai/api/external/rag/datastores/{datastore_id}/files ``` ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `datastore_id` | string | **Yes** | DataStore ID | ### Request Example ```bash cURL theme={null} curl -X GET "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.get( "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"} ) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files', { headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' } } ); const data = await response.json(); console.log(data); ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "datastore_id": "company-policies-a1b2c3d4", "datastore_name": "Company Policies", "files": [ { "name": "employee-handbook.pdf", "size": 2048576, "content_type": "application/pdf", "uploaded_at": "2026-01-01T10:00:00Z", "status": "completed" }, { "name": "vacation-policy.docx", "size": 102400, "content_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "uploaded_at": "2026-01-01T10:05:00Z", "status": "indexing" } ], "total_files": 2, "indexed_files": 1 } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `datastore_id` | string | DataStore ID | | `datastore_name` | string | DataStore display name | | `files` | array | List of files in the DataStore | | `files[].name` | string | Filename | | `files[].size` | integer | File size in bytes | | `files[].content_type` | string | MIME type | | `files[].uploaded_at` | string | Upload timestamp (ISO 8601) | | `files[].status` | string | Indexing status (`pending`, `indexing`, `completed`, `failed`) | | `total_files` | integer | Total number of files | | `indexed_files` | integer | Number of fully indexed files | ### Error Responses ```json 404 Not Found theme={null} { "detail": { "error": { "message": "DataStore not found: invalid-datastore-id", "type": "not_found", "code": "datastore_not_found" } } } ``` *** ## Delete DataStore Delete a DataStore and all its contents (files and indexed documents). ### Endpoint ``` DELETE https://api.junis.ai/api/external/rag/datastores/{datastore_id} ``` **Destructive Operation**: This permanently deletes all files and indexed documents in the DataStore. This action cannot be undone. ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `datastore_id` | string | **Yes** | DataStore ID to delete | ### Query Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `force` | boolean | No | If `true`, delete even if DataStore is connected to agents (default: `false`) | ### Request Example ```bash cURL theme={null} curl -X DELETE "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4?force=true" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"}, params={"force": True} ) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4?force=true', { method: 'DELETE', headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' } } ); const data = await response.json(); console.log(data); ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "success": true, "datastore_id": "company-policies-a1b2c3d4", "message": "DataStore 'Company Policies' deleted successfully" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether deletion was successful | | `datastore_id` | string | Deleted DataStore ID | | `message` | string | Success message | ### Error Responses ```json 404 Not Found theme={null} { "detail": { "error": { "message": "DataStore not found: invalid-datastore-id", "type": "not_found", "code": "datastore_not_found" } } } ``` ```json 409 Conflict - DataStore In Use theme={null} { "detail": { "error": { "message": "DataStore is connected to agents. Use force=true to delete anyway.", "type": "conflict", "code": "datastore_in_use", "connected_agents": ["brand-analyzer", "content-writer"] } } } ``` *** ## Delete File Delete a single file from a DataStore while preserving the DataStore itself. ### Endpoint ``` DELETE https://api.junis.ai/api/external/rag/datastores/{datastore_id}/files/{filename} ``` **Partial Deletion**: Only the specified file is removed. The DataStore and other files remain intact. ### Path Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `datastore_id` | string | **Yes** | DataStore ID | | `filename` | string | **Yes** | Name of the file to delete (URL-encoded if contains special characters) | ### Request Example ```bash cURL theme={null} curl -X DELETE "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf" \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` ```python Python theme={null} import requests response = requests.delete( "https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf", headers={"X-API-Key": "jns_live_YOUR_API_KEY_HERE"} ) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch( 'https://api.junis.ai/api/external/rag/datastores/company-policies-a1b2c3d4/files/old-policy.pdf', { method: 'DELETE', headers: { 'X-API-Key': 'jns_live_YOUR_API_KEY_HERE' } } ); const data = await response.json(); console.log(data); ``` ### Response **Status Code:** `200 OK` ```json theme={null} { "success": true, "datastore_id": "company-policies-a1b2c3d4", "filename": "old-policy.pdf", "remaining_files": 4, "message": "File 'old-policy.pdf' deleted from 'Company Policies'" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `success` | boolean | Whether deletion was successful | | `datastore_id` | string | DataStore ID | | `filename` | string | Deleted filename | | `remaining_files` | integer | Number of files remaining in the DataStore | | `message` | string | Success message | ### Error Responses ```json 404 Not Found - DataStore theme={null} { "detail": { "error": { "message": "DataStore not found: invalid-datastore-id", "type": "not_found", "code": "datastore_not_found" } } } ``` ```json 500 Internal Server Error - File Not Found theme={null} { "detail": { "error": { "message": "Failed to delete file: File not found in DataStore", "type": "deletion_error", "code": "file_deletion_failed" } } } ``` *** ## Best Practices When an upload is in `processing` or `indexing` status, poll the status endpoint to check for completion: ```python theme={null} import time import requests def wait_for_upload(upload_id, api_key, timeout=300, interval=5): """Poll until upload is completed or failed.""" start = time.time() while time.time() - start < timeout: response = requests.get( f"https://api.junis.ai/api/external/rag/uploads/{upload_id}", headers={"X-API-Key": api_key} ) data = response.json() if data["ready"]: return data elif data["status"] == "failed": raise Exception(f"Upload failed: {data.get('error_message')}") time.sleep(interval) raise TimeoutError("Upload indexing timed out") ``` Upload multiple related files in a single request (up to 10 files): ```python theme={null} import base64 import requests import os def upload_directory(datastore_id, directory_path, api_key): """Upload all supported files from a directory.""" files = [] for filename in os.listdir(directory_path): filepath = os.path.join(directory_path, filename) if os.path.isfile(filepath): with open(filepath, "rb") as f: content = base64.b64encode(f.read()).decode() files.append({ "filename": filename, "content": content }) response = requests.post( "https://api.junis.ai/api/external/rag/upload", headers={"X-API-Key": api_key, "Content-Type": "application/json"}, json={"datastore_id": datastore_id, "files": files[:10]} # Max 10 ) return response.json() ``` Create separate DataStores for different topics or document types: * **By Department**: "HR Policies", "Engineering Docs", "Sales Materials" * **By Project**: "Project Alpha", "Project Beta" * **By Type**: "Legal Contracts", "Technical Specs", "Meeting Notes" This helps agents retrieve more relevant information by searching specific DataStores. For large documents: * Split large PDFs into smaller chapters * Remove unnecessary images from documents * Use text formats (TXT, MD) when formatting isn't important * Compress images before including in documents *** ## Related Resources Learn about API key scopes and authentication User guide for RAG features Use RAG in conversations Full list of error codes and solutions # Rate Limits Source: https://docs.junis.ai/api-reference/rate-limits Understanding API key rate limiting and optimization strategies ## Overview The Junis External API implements rate limiting to ensure fair usage and maintain service quality for all users. Rate limits are applied **per API key**, and all keys of an organization also share one **organization-wide** per-minute total. Per-key limits can be adjusted by an organization admin. **Base URL**: `https://api.junis.ai` Each API key has its own per-minute and per-hour counters. On top of that, all keys of an organization share one per-minute total (300 requests by default), so creating more keys does not raise the organization's total. *** ## Rate Limiting Architecture The External API uses **Redis-based fixed-window counters** with three windows (per key per minute, per key per hour, per organization per minute) for distributed rate limiting across multiple server instances. **Storage**: Redis with distributed support across all API servers **Windows**: Three fixed windows aligned to the clock * **Per key, per minute**: resets at the start of every minute * **Per key, per hour**: resets at the start of every hour * **Per organization, per minute**: all keys of the organization combined, resets at the start of every minute **Algorithm**: Fixed-window counters with atomic increments (one Redis round trip per request) **Check Before Request**: Rate limits are checked before processing your request **Headers Included**: Every response includes current rate limit status **429 on Exceed**: Returns `429 Too Many Requests` if limit exceeded **Automatic Reset**: Counters automatically reset at the end of each window *** ## Default Rate Limits When you create a new API key, the following default limits apply: | Limit | Organization key (Admin → API Keys) | Personal key (Settings → API Keys) | | - | - | - | | **Requests per minute, per key** | 100 | 100 | | **Requests per hour, per key** | 3,000 | 2,000 | | **Requests per minute, whole organization** | 300 (all keys combined) | 300 (all keys combined) | Per-key limits of an existing key can be raised or lowered by an organization admin through the admin API (`PUT /api/admin/api-keys/{key_id}` with `rate_limit_per_minute` 1–1,000 and/or `rate_limit_per_hour` 1–100,000). The change applies from the next request, with no redeploy. The organization-wide total is a platform setting; contact Junis if your integration needs more. *** ## Rate Limit Headers Every API response includes headers that show your current rate limit status. ### Response Headers ```http theme={null} X-RateLimit-Limit-Minute: 100 X-RateLimit-Remaining-Minute: 55 X-RateLimit-Reset-Minute: 1767261600 X-RateLimit-Limit-Hour: 3000 X-RateLimit-Remaining-Hour: 2950 X-RateLimit-Reset-Hour: 1767261600 X-RateLimit-Limit-Org-Minute: 300 X-RateLimit-Remaining-Org-Minute: 240 ``` | Header | Description | | - | - | | `X-RateLimit-Limit-Minute` | Maximum requests allowed per minute for this key | | `X-RateLimit-Remaining-Minute` | Requests remaining for this key in the current minute window | | `X-RateLimit-Reset-Minute` | Unix timestamp when the minute window resets | | `X-RateLimit-Limit-Hour` | Maximum requests allowed per hour for this key | | `X-RateLimit-Remaining-Hour` | Requests remaining for this key in the current hour window | | `X-RateLimit-Reset-Hour` | Unix timestamp when the hour window resets | | `X-RateLimit-Limit-Org-Minute` | Maximum requests per minute for all keys of your organization combined | | `X-RateLimit-Remaining-Org-Minute` | Requests remaining for the whole organization in the current minute window (resets with the minute window) | Responses also carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Window` without a suffix. Those belong to a platform-level flood guard in front of the key limiter and are not your key's quota — read the suffixed headers above. Always monitor `X-RateLimit-Remaining-*` headers to avoid hitting rate limits. Consider pausing requests when remaining count is low. *** ## 429 Too Many Requests When you exceed your rate limit, the API returns a `429` status code. ### Response Format **HTTP Status**: ``` HTTP/1.1 429 Too Many Requests Retry-After: 45 ``` **Headers**: ``` X-RateLimit-Limit-Minute: 100 X-RateLimit-Remaining-Minute: 0 X-RateLimit-Reset-Minute: 1767261600 X-RateLimit-Limit-Hour: 3000 X-RateLimit-Remaining-Hour: 2950 X-RateLimit-Reset-Hour: 1767261600 X-RateLimit-Limit-Org-Minute: 300 X-RateLimit-Remaining-Org-Minute: 180 Retry-After: 45 ``` **Body**: ```json theme={null} { "error": "rate_limit_exceeded", "message": "Rate limit exceeded: 100 requests per minute", "limit": 100, "window": "minute", "reset_at": 1767261600, "retry_after": 45 } ``` | Field | Description | | - | - | | `error` | Error type: `rate_limit_exceeded` | | `message` | Human-readable error message | | `limit` | The rate limit that was exceeded | | `window` | Which window was exceeded: `minute`, `hour` (this key) or `org_minute` (all keys of the organization) | | `reset_at` | Unix timestamp when the window resets | | `retry_after` | Seconds to wait before retrying | When the organization-wide total is exceeded, `window` is `org_minute`, `limit` is the organization total and `message` reads `Organization rate limit exceeded: 300 requests per minute across all API keys`. The per-key windows are checked first. *** ## Handling Rate Limits ### Best Practices * **Monitor Headers**: Always check `X-RateLimit-Remaining-Minute`, `X-RateLimit-Remaining-Hour` and `X-RateLimit-Remaining-Org-Minute` headers to track your usage * **Implement Exponential Backoff**: When you receive a 429 response, use the `Retry-After` header value to wait before retrying * **Cache Responses**: Implement caching (TTL: 5 minutes recommended) to reduce API calls for frequently accessed data * **Batch Requests**: Use paginated endpoints instead of individual requests (e.g., `GET /api/external/sessions?limit=100`) * **Use Streaming**: For real-time chat, use `stream: true` parameter instead of polling to avoid repeated API calls *** ## Rate Limit Optimization ### Identify Bottlenecks 1. **Log Rate Limit Headers**: Track `X-RateLimit-Remaining-*` headers in your application logs 2. **Analyze Request Patterns**: Identify high-frequency endpoints that may benefit from caching or batching * Repeated session fetches → Add caching * Individual message fetches → Use paginated list endpoints * Polling for updates → Switch to streaming 3. **Implement Caching**: Use Redis or in-memory cache with 5-minute TTL for frequently accessed data 4. **Monitor & Alert**: Set up alerts when rate limit usage exceeds 90% *** ## Testing Rate Limits ### Local Testing Test your rate limit handling before deploying to production: ```bash theme={null} # Test the per-key minute limit (default 100 per minute — send 105 requests in 1 minute) for i in {1..105}; do curl -s -o /dev/null -w "%{http_code}\n" \ https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"role":"user","content":"test"}]}' sleep 0.5 done ``` *** ## Increasing Rate Limits Per-key limits are stored on the key itself, so they can be changed without redeploying anything: 1. An organization admin calls `PUT /api/admin/api-keys/{key_id}` (JWT authentication) with `rate_limit_per_minute` (1–1,000) and/or `rate_limit_per_hour` (1–100,000). 2. The new values apply from the next request made with that key. Only organization admins can modify API key rate limits. Raising a key above the organization-wide total (300 requests per minute across all keys by default) does not add capacity on its own — the organization total still applies. Contact Junis to raise the organization total. Set rate limits based on your actual usage patterns. Start conservative and increase as needed. *** ## FAQs **Answer**: Both. Each key has its own per-minute and per-hour limits, and all keys of the organization share one per-minute total (300 by default). Example: If your organization has 2 API keys: * API Key 1: 100 requests/minute * API Key 2: 100 requests/minute * Organization total: 300 requests/minute — the keys together can never exceed it, so adding a third key does not add capacity beyond the total **Response**: `429 Too Many Requests` with `Retry-After` header **Action**: Wait for the number of seconds specified in `Retry-After` or until the next window reset **No penalties**: No account suspension or additional charges **Per key**: Yes — an organization admin updates the key through the admin API (`PUT /api/admin/api-keys/{key_id}`), and the change applies from the next request **Organization total**: Contact Junis **Answer**: Streaming responses count as **1 request**, regardless of how long the stream lasts. **Example**: * Non-streaming: 1 request = 1 complete response * Streaming: 1 request = entire conversation stream Streaming is more efficient for real-time chat. **Method 1**: Check response headers (`X-RateLimit-Remaining-*`) **Method 2**: Set up logging in your application (see Best Practices) **Method 3**: Monitor 429 error rates in your application metrics *** ## Next Steps Learn about API keys and scopes Handle 429 and other API errors Start building with streaming Manage chat sessions efficiently # Sessions API Source: https://docs.junis.ai/api-reference/sessions Retrieve conversation sessions and messages ## Overview The Sessions API allows you to **retrieve conversation history** and access message details. All sessions are scoped to your organization and the API key creator. Sessions are automatically created when you send messages via [Chat Completions API](/api-reference/chat-completions). Building your own product on Junis? Add `on_behalf_of` to read the sessions of a user you provisioned, and use `DELETE` to purge their data when they leave. See [User Provisioning](/api-reference/user-provisioning). ### Required Scopes All Sessions API endpoints require specific API key scopes: | Endpoint | Required Scope | | - | - | | `GET /api/external/sessions` | `sessions:read` | | `GET /api/external/sessions/{session_id}/messages` | `sessions:messages` | | `GET /api/external/sessions/{session_id}/status` | `sessions:read` | | `DELETE /api/external/sessions/{session_id}` | `sessions:delete` | Adding `on_behalf_of` to any of them also requires `users:delegate`. `sessions:delete` is opt-in and is never part of a default key. Grant it from **Dashboard → API Keys → New Key** by ticking **"Connect users from my own app"**. When creating an API key in the Admin panel, ensure you enable the appropriate scopes. Without the required scopes, requests will return a `403 Forbidden` error. *** ## Endpoints | Method | Endpoint | Description | | - | - | - | | `GET` | `/api/external/sessions` | List all sessions (paginated) | | `GET` | `/api/external/sessions/{session_id}/messages` | Get session messages (paginated) | | `GET` | `/api/external/sessions/{session_id}/status` | Get session processing status | | `DELETE` | `/api/external/sessions/{session_id}` | Permanently delete a session and its messages | *** ## List Sessions Retrieve all sessions created by your API key. ### Request ```bash theme={null} GET /api/external/sessions ``` **Query Parameters:** | Parameter | Type | Default | Description | | - | - | - | - | | `page` | integer | 1 | Page number (1-indexed) | | `page_size` | integer | 20 | Items per page (max: 100) | | `on_behalf_of` | string | — | List the sessions of a user your organization provisioned. Requires `users:delegate`. Omit for your own key's sessions. | | `include_hidden` | boolean | `true` | Set to `false` to leave out sessions created with `session_hidden` — useful when the list is shown to the user. | Sessions are automatically sorted by `last_update_time` (most recent first). Custom sorting is not currently supported. ### Response **Status Code:** `200 OK` ```json theme={null} { "sessions": [ { "session_id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2026-01-01T10:00:00Z", "last_update_time": "2026-01-01T15:30:00Z", "is_active": true, "message_count": 12 }, { "session_id": "660f9511-f39c-52e5-b827-557766551111", "created_at": "2026-01-01T14:20:00Z", "last_update_time": "2026-01-01T16:45:00Z", "is_active": false, "message_count": 8 } ], "total": 45, "page": 1, "page_size": 20, "has_next": true } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `sessions` | array | Array of session objects | | `sessions[].session_id` | string | Unique session ID (UUID) | | `sessions[].created_at` | string | ISO 8601 timestamp | | `sessions[].last_update_time` | string | Last activity timestamp | | `sessions[].is_active` | boolean | Whether session is active | | `sessions[].message_count` | integer | Number of messages in session | | `total` | integer | Total number of sessions | | `page` | integer | Current page number | | `page_size` | integer | Items per page | | `has_next` | boolean | Whether there are more pages | ### cURL Example ```bash theme={null} curl -X GET 'https://api.junis.ai/api/external/sessions?page=1&page_size=20' \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` *** ## Get Session Messages Retrieve all messages from a specific session. ### Request ```bash theme={null} GET /api/external/sessions/{session_id}/messages ``` **Path Parameters:** | Parameter | Type | Required | Description | | - | - | - | - | | `session_id` | string | Yes | Session UUID | **Query Parameters:** | Parameter | Type | Default | Description | | - | - | - | - | | `page` | integer | 1 | Page number (1-indexed) | | `page_size` | integer | 50 | Items per page (max: 200) | | `on_behalf_of` | string | — | Read a session belonging to a user your organization provisioned. Requires `users:delegate`. | ### Response **Status Code:** `200 OK` ```json theme={null} { "session_id": "550e8400-e29b-41d4-a716-446655440000", "messages": [ { "event_id": "evt_001", "event_type": "user_message", "content": "What is Junis?", "agent": null, "created_at": "2026-01-01T10:00:00Z", "visibility": "visible" }, { "event_id": "evt_002", "event_type": "assistant_message", "content": "Junis is a production-ready Multi-Agent AI platform...", "agent": "Orchestrator", "created_at": "2026-01-01T10:00:05Z", "visibility": "visible" }, { "event_id": "evt_003", "event_type": "user_message", "content": "How do I create an agent?", "agent": null, "created_at": "2026-01-01T10:05:00Z", "visibility": "visible" }, { "event_id": "evt_004", "event_type": "assistant_message", "content": "To create an agent, follow these steps...", "agent": "Orchestrator", "created_at": "2026-01-01T10:05:08Z", "visibility": "visible" } ], "total": 12, "page": 1, "page_size": 50, "has_next": false } ``` ### Message Object | Field | Type | Description | | - | - | - | | `event_id` | string | Unique message ID | | `event_type` | string | Message type (`user_message`, `assistant_message`) | | `content` | string | Message text content | | `agent` | string or null | Agent name (for assistant messages) | | `created_at` | string | ISO 8601 timestamp | | `visibility` | string | Message visibility (`visible`, `hidden`) | Messages are always returned in chronological order (oldest first). ### cURL Example ```bash theme={null} curl -X GET 'https://api.junis.ai/api/external/sessions/550e8400-e29b-41d4-a716-446655440000/messages?page=1&page_size=50' \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` *** ## Get Session Processing Status Check if a session is currently processing a message. ### Request ```bash theme={null} GET /api/external/sessions/{session_id}/status ``` **Path Parameters:** | Parameter | Type | Required | Description | | - | - | - | - | | `session_id` | string | Yes | Session UUID | **Query Parameters:** | Parameter | Type | Default | Description | | - | - | - | - | | `on_behalf_of` | string | — | Check a session belonging to a user your organization provisioned. Requires `users:delegate`. | ### Response **Status Code:** `200 OK` ```json theme={null} { "session_id": "550e8400-e29b-41d4-a716-446655440000", "is_processing": true, "started_at": "2026-01-01T15:30:00Z", "current_agent": "Orchestrator", "elapsed_seconds": 2.5 } ``` **When not processing:** ```json theme={null} { "session_id": "550e8400-e29b-41d4-a716-446655440000", "is_processing": false } ``` **When timed out (30 minutes):** ```json theme={null} { "session_id": "550e8400-e29b-41d4-a716-446655440000", "is_processing": false, "timed_out": true, "message": "Request took too long and was automatically cancelled" } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `session_id` | string | Session UUID | | `is_processing` | boolean | Whether session is currently processing | | `started_at` | string (optional) | ISO 8601 timestamp (if processing) | | `current_agent` | string (optional) | Active agent name (if processing) | | `elapsed_seconds` | float (optional) | Time elapsed (if processing) | | `timed_out` | boolean (optional) | Whether request timed out (if timeout occurred) | | `message` | string (optional) | Status message (if timeout occurred) | Sessions automatically timeout after 30 minutes of processing. ### cURL Example ```bash theme={null} curl -X GET 'https://api.junis.ai/api/external/sessions/550e8400-e29b-41d4-a716-446655440000/status' \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" ``` *** ## Delete Session Permanently delete a session together with its messages and stored state. This cannot be undone. There is no soft delete and no recovery window. ### Request ```bash theme={null} DELETE /api/external/sessions/{session_id} ``` **Path Parameters:** | Parameter | Type | Required | Description | | - | - | - | - | | `session_id` | string | Yes | Session UUID | **Query Parameters:** | Parameter | Type | Default | Description | | - | - | - | - | | `on_behalf_of` | string | — | Delete a session belonging to a user your organization provisioned. Requires `users:delegate`. | ### Response **Status Code:** `200 OK` ```json theme={null} { "session_id": "550e8400-e29b-41d4-a716-446655440000", "deleted": true } ``` ### cURL Example ```bash theme={null} curl -X DELETE \ "https://api.junis.ai/api/external/sessions/550e8400-e29b-41d4-a716-446655440000" \ -H "X-API-Key: jns_live_YOUR_API_KEY" ``` Deletions are recorded in your organization's audit log. *** ## Error Handling ### Common Errors ```json 404 Not Found theme={null} { "detail": "Session not found or access denied" } ``` ```json 400 Bad Request theme={null} { "detail": { "page_size": ["ensure this value is less than or equal to 100"] } } ``` ## Best Practices * **Use Pagination**: Always use pagination with appropriate `page_size` (max: 100 for sessions, 200 for messages) to avoid timeouts * **Cache Session Data**: Implement caching to reduce API calls (recommended TTL: 5 minutes) * **Poll Status for Long-Running Requests**: Use the status endpoint to check if a session is still processing * **Handle Errors**: Check for 404 (session not found) and 400 (invalid parameters) responses *** ## Next Steps Send messages and create sessions Learn about API key authentication Understand rate limiting # User Provisioning Source: https://docs.junis.ai/api-reference/user-provisioning Provision end-user accounts and organization memberships for partner integrations ## Overview If you are building your own product on Junis, your users should each get their own Junis account — so their conversations, history, and memory stay separate — without anyone having to sign up for Junis. User Provisioning does exactly that: you create an account for each of your users, run everything on their behalf, and pay for all of it from one place. You never share your API key with your users, and they never see Junis. ### The lifecycle One call with their email. Idempotent, so you can call it every login. Push API keys straight in, or hand the person a **connect link** for anything that needs their sign-in (Gmail, Slack). Your agents then act as that user. Add `on_behalf_of` to your normal calls. Sessions and memory belong to them. The same parameter works on the session read endpoints. Purge their conversations permanently, so you can honour your own deletion promise. **Billing never moves.** Subscription checks and credit charges always stay with the user behind your API key, no matter whose name a call runs under. Your users do not need a Junis subscription — or a Junis account they ever log into. Provisioned accounts do not receive the signup credit bonus and have no memory profile until the person actually signs in with Google themselves. If they ever do, their Google account links to the one you created and their membership activates — nothing is lost or duplicated. *** ## Getting a key Create the key from **Dashboard → API Keys → New Key** and tick **"Connect users from my own app"** before generating it. That checkbox is what turns an ordinary key into a provisioning key. | Checkbox | Scopes granted | | - | - | | Unchecked (default) | The 8 standard scopes — `orchestrator:*`, `sessions:*`, `agents:*`, `content:*` | | Checked | The 8 standard scopes **plus** `users:provision`, `users:delegate`, `sessions:delete` and `users:credentials` | | Scope | What it lets you do | | - | - | | `users:provision` | Create accounts for your users | | `users:delegate` | Run and read as one of those users | | `sessions:delete` | Delete their conversations | | `users:credentials` | Store API credentials on their behalf (write-only), issue/revoke connect links, and read connect status | Scopes are frozen into a key when it is created. If you already have a provisioning key from before `users:credentials` existed, generate a new one to get it — existing keys are not upgraded. Only organization owners and admins can open that page, which is also the requirement for using these scopes — so a key created there is always eligible. Provisioning keys are named `provisioning-key-…` and carry a **User provisioning** label in the key list, so you can tell them apart later. This key can create accounts in your organization, run chats as its members, and delete conversations. Keep it on your server — never ship it in a browser, a mobile app, or a public repository. *** ## Endpoint ``` POST https://api.junis.ai/api/external/management/users ``` ### Authentication Include your API key in the `X-API-Key` header: ```bash theme={null} X-API-Key: jns_live_YOUR_API_KEY_HERE ``` ### Request Parameters | Parameter | Type | Required | Description | | - | - | - | - | | `email` | string | **Yes** | Email of the user to provision (normalized to lowercase) | | `name` | string | No | Display name (defaults to the email local part) | | `organization_id` | string | **Yes** | Must match the organization your API key resolves to | ### Example Request ```bash theme={null} curl -X POST https://api.junis.ai/api/external/management/users \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "email": "alice@example.com", "name": "Alice", "organization_id": "org-uuid" }' ``` ### Response ```json theme={null} { "user_id": "550e8400-e29b-41d4-a716-446655440000", "email": "alice@example.com", "created": true, "delegable": true, "membership_status": "pending" } ``` | Field | Type | Description | | - | - | - | | `user_id` | string | Junis user ID — usable as `on_behalf_of` in Chat Completions | | `email` | string | Normalized email | | `created` | boolean | `true` if a new account was created, `false` if it already existed | | `delegable` | boolean | `true` if you can act on behalf of this account (see below) | | `membership_status` | string | `pending` (awaiting first login) or `active` | ### When the Email Belongs to an Existing Junis User If the email already belongs to a **real Junis account** (someone who has signed in themselves), the account is **not** attached to your organization and cannot be delegated: ```json theme={null} { "email": "existing-user@example.com", "created": false, "delegable": false } ``` Only the email is echoed back — no user ID or membership information is returned. Handle this case in your integration: invite the user to your organization from the dashboard instead, or skip Junis-linked features for that user. *** ## Behavior If a user with the email already exists, it is returned unchanged (`created: false`). Otherwise a new account is created with `google_id = NULL` — no signup bonus, no memory profile (both happen on the real first login). If the organization has no membership row for this email, a `pending` invite with the `member` role is created — identical to inviting the user from the dashboard. When the user signs in with Google using that email, the account is linked to their Google identity, the pending membership is activated, and the invited organization becomes their current organization. The endpoint is **idempotent** — calling it repeatedly for the same email returns the same user and membership state. *** ## Errors | Status | Code | Description | | - | - | - | | `403` | `insufficient_scopes` | API key lacks the `users:provision` scope | | `403` | — | Key's organization does not match `organization_id`, or the backing user is not an org admin/owner | | `422` | — | Invalid email address | *** ## Storing API Credentials for a User Most useful automations need to call something on the person's behalf — their Notion, their Google Sheets, their store. Those calls need *their* key, not yours. Push the credential into their account once, and every agent that runs for them uses it automatically. They never paste a key into a chat, and the key never reaches the model. ### Endpoints ``` GET https://api.junis.ai/api/external/management/users/{user_id}/secrets PUT https://api.junis.ai/api/external/management/users/{user_id}/secrets/{NAME} DELETE https://api.junis.ai/api/external/management/users/{user_id}/secrets/{NAME} ``` Requires the `users:credentials` scope and an owner/admin key. ### Store or rotate `PUT` is idempotent — the same call creates a credential the first time and rotates it every time after. ```bash theme={null} curl -X PUT \ "https://api.junis.ai/api/external/management/users/USER_ID/secrets/NOTION_TOKEN" \ -H "X-API-Key: jns_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "value": "secret_xxx", "description": "Customer Notion token" }' ``` ```json theme={null} { "success": true, "user_id": "USER_ID", "name": "NOTION_TOKEN", "scope": "private" } ``` ### Check what they have Returns names only — never values. Use it to work out what is still missing and prompt for it in your own onboarding UI. ```bash theme={null} curl "https://api.junis.ai/api/external/management/users/USER_ID/secrets" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` ```json theme={null} { "user_id": "USER_ID", "secrets": [ { "id": 12, "name": "NOTION_TOKEN", "description": "Customer Notion token", "created_at": "2026-08-21T04:10:00+00:00", "updated_at": "2026-08-21T04:10:00+00:00" } ] } ``` ### Delete Do this when the person leaves, alongside deleting their conversations. ```bash theme={null} curl -X DELETE \ "https://api.junis.ai/api/external/management/users/USER_ID/secrets/NOTION_TOKEN" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` **Stored values can never be read back.** There is no endpoint, on any key, that returns a credential's value — listing gives you names only. If your key leaks, the credentials already stored with it cannot be pulled out. **Only accounts you provisioned.** If the email already belonged to a real Junis user (`delegable: false`), this returns `403`. Nobody — not even an organization owner — can write into a real person's private vault. That rule is what makes the vault trustworthy, so it has no exceptions. ### Rules | Rule | Value | | - | - | | Name format | Uppercase letters, digits, underscore. Must start with a letter — `NOTION_TOKEN` | | Value size | 8 KB max (fits PEM keys and service-account JSON) | | Count | 200 credentials per user, per organization | | Rotation | Always allowed, even at the count limit | | Organizations | Separate vaults — the same person can hold a different key in each org | ### Errors | Status | Code | Description | | - | - | - | | `400` | — | Invalid name — must be uppercase letters, digits, underscore | | `400` | — | The 200-credential limit is reached (rotating an existing name still works) | | `403` | `insufficient_scopes` | Key lacks `users:credentials` | | `403` | — | Target is a real account, not one your organization provisioned | | `403` | — | Target was provisioned by a different organization | | `404` | — | User not found, or (on `DELETE`) no credential with that name | | `422` | — | Body validation failed — empty `value`, or a value longer than 8 KB | *** ## Listing Your Provisioned Users Reconcile against your own database, or build an internal admin view. **Only accounts your organization provisioned appear** — never real Junis users. ```bash theme={null} curl "https://api.junis.ai/api/external/management/users?limit=50&offset=0" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` ```json theme={null} { "users": [ { "user_id": "usr_1", "email": "customer@example.com", "name": "Customer", "created_at": "2026-08-21T04:00:00+00:00" } ], "total": 1, "limit": 50, "offset": 0 } ``` Requires `users:provision`. *** ## Connect Links — let the person connect what the squad needs An API key you can push in directly (§ above). **Gmail, Slack, Google Calendar and other OAuth apps you cannot** — those credentials only exist after the person clicks "Allow" on the provider's own consent screen. And even for plain API keys, it is better when the person's key never passes through your servers. A **connect link** solves both with one flow: you request a link for a squad, open it in a webview, and the person connects (or disconnects) everything on one page. OAuth redirects, key entry and encryption are handled for you — your app needs no callback page and no OAuth code. The page carries no branding other than what the person is connecting to. One call with the squad's `agent_id`. The page shows only what **that squad** needs. The person connects each item. OAuth apps redirect out and come back automatically. Via your `return_url` — even if they only connected some items. ### What the page shows Pass the squad's root agent (`agent_id`, from `GET /management/agents`). The page is built from what is configured on that agent and its sub-agents: | Section | Items | | - | - | | **Accounts** | Personal Composio integrations linked to the squad (`connection_type: "personal"`) — Gmail, Notion, … | | **Credentials** | MCP platforms linked to the squad that need a personal credential. Platforms your organization already provides a key for are not shown | | **API keys** | Personal keys the squad's instructions reference as `{cred:private:NAME}` | If the squad changes later, existing links follow the new configuration. ### Check what is needed — and what is done Use the same endpoint before issuing a link (is there anything to connect?) and after (did they finish?). It is generated by the same code as the page, so what you see is what the person sees. ```bash theme={null} curl "https://api.junis.ai/api/external/management/users/USER_ID/connect-status?agent_id=AGENT_ID" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` ```json theme={null} { "user_id": "USER_ID", "agent_id": "AGENT_ID", "all_connected": false, "items": [ { "kind": "composio", "id": "3f9c…", "name": "Gmail", "auth_scheme": "OAUTH2", "connected": false, "fields": [] }, { "kind": "mcp", "id": "notion-mcp", "name": "Notion", "connected": true, "fields": [ { "name": "NOTION_TOKEN", "type": "text", "required": true } ] }, { "kind": "secret", "id": "HUNTER_API_KEY", "name": "HUNTER_API_KEY", "connected": false, "description": "Used by hunter_api", "fields": [ { "name": "value", "required": true } ] } ], "skipped": [ { "kind": "mcp", "id": "replicate-global", "name": "Replicate", "reason": "provided_by_system" } ] } ``` `skipped` explains what is **not** on the page and why: `provided_by_organization` / `provided_by_system` (already works without the person doing anything), or `no_credential_schema` (the platform does not declare which values it needs — the squad's admin adds that in the platform settings and the item appears), or `too_many_items` (more than 20 of one kind; trim the squad). An item with no declared fields stays hidden **even when already connected**: the page cannot store that value, so it does not offer to remove it either — that would be a one-way door. Items we provision automatically never appear at all, since there is nothing for the person to enter. Stored values are never returned — only `connected` and field **names**. ### Request a link ```bash theme={null} curl -X POST \ "https://api.junis.ai/api/external/management/users/USER_ID/connect-sessions" \ -H "X-API-Key: jns_live_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "agent_id": "AGENT_ID", "return_url": "myapp://connect-done", "ttl_minutes": 30 }' ``` ```json theme={null} { "url": "https://junis.ai/connect/xNq7...", "expires_at": "2026-09-07T05:00:00+00:00", "user_id": "USER_ID", "agent_id": "AGENT_ID", "items": { "composio": [], "mcp": [] } } ``` | Field | Notes | | - | - | | `agent_id` | The squad's root agent. **Recommended.** | | `composio` / `mcp` | Explicit mode: list catalog ids yourself (`GET /management/composio/integrations` → `id`, `GET /management/mcp-platforms` → `platform_id`). Cannot be combined with `agent_id` (`400`). Kept for compatibility | | `return_url` | Optional. `https://…` or a custom app scheme like `myapp://done`. `http://` and `javascript:`/`data:`/`file:` are rejected | | `ttl_minutes` | Default `30`, max `1440` | Requires `users:credentials`, and the target must be an account you provisioned. If the squad has nothing for the person to connect, the request returns `400` with the same `skipped` list as `connect-status`. **The token in `url` is shown once.** Only its hash is stored, so the link cannot be looked up again — request a new one instead. Treat the URL like a password: it is the only thing standing between a stranger and that user's connect page. ### Open it in a webview Fetch the link **at the moment the person taps your "Connect" button**, then open it. Do not pre-generate links — a 30 minute lifetime is what keeps a leaked URL harmless. ``` [ Connect accounts ] → POST …/connect-sessions → open url in webview ``` | Item | What the person sees | | - | - | | Gmail, Slack (OAuth) | **Connect** → provider consent → back to the page, marked connected | | Notion, Airtable (API key) | A key field → **Save** | | MCP credentials | One field per value the platform declares, by name → **Save** | | API keys | A key field → **Save** | | Anything connected | **Disconnect** | The page shows **All set** when everything is connected, and a button back to your app whenever you set `return_url` — including when only some items are done, so the person can come back and finish later. ### Revoke links When a person leaves your app, or you suspect a link leaked, invalidate every live link for that account at once. Idempotent. ```bash theme={null} curl -X DELETE \ "https://api.junis.ai/api/external/management/users/USER_ID/connect-sessions" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` ```json theme={null} { "user_id": "USER_ID", "revoked": 1 } ``` ### What the page can and cannot do The link grants exactly one thing: **connecting and disconnecting that one user's items for that one squad.** It cannot read data, cannot reach other users, cannot touch items outside the squad, and cannot reveal any stored value. The page never shows your organization, the user's id, or their email. * Expired, revoked and non-existent links all return the same response — a link cannot be probed for validity. * Reusable until it expires, because an OAuth round trip has to come back to it. * Connecting or disconnecting refreshes that user's agent tools immediately. ### Errors | Status | Code | Description | | - | - | - | | `400` | — | `agent_id` combined with `composio`/`mcp`, nothing to connect (see `skipped`), more than 20 explicit items, bad `return_url`, or an unknown toolkit id | | `403` | `insufficient_scopes` | Key lacks `users:credentials` | | `403` | — | Target is a real account, or was provisioned by another organization | | `404` | — | User not found, or `agent_id` is not an active agent of your organization (global agents are accepted) | *** ## Using a Stored Credential in an Agent Reference the credential by **name** in the agent's instruction. Junis substitutes the calling user's own value at request time and sends it as an HTTP header — it never enters the prompt, the model's context, or the conversation log. ``` [CUSTOM_API_CONFIG] { "apis": [{ "name": "notion", "endpoint": "https://api.notion.com/v1/search", "method": "POST", "api_key_header": "Authorization", "api_key": "Bearer {cred:private:NOTION_TOKEN}", "headers": { "Notion-Version": "2022-06-28" } }] } [/CUSTOM_API_CONFIG] ``` | Syntax | Resolves to | | - | - | | `{cred:private:NAME}` | The **calling user's** own credential | | `{cred:NAME}` | A single organization-wide credential shared by everyone | The two never substitute for each other. If a user has no credential with that name, the call is **not sent** — the tool returns an error naming what is missing, instead of quietly falling back to an organization key. The config is read from the instruction of **the agent that owns the `custom_api_call` tool** — not from the orchestrator. If a sub-agent makes the call, the block belongs in that sub-agent's instruction. Giving one dedicated agent the tool and the config keeps it out of your orchestrator's context on every turn. ### Writing it into an agent Owner/admin only, with a **user-level** key carrying `agents:manage`. Read the current instruction, append your block, and write the whole thing back — `instruction` is replaced wholesale, not appended to. ```bash theme={null} # 1. Read the current instruction curl "https://api.junis.ai/api/external/management/agents/AGENT_ID" \ -H "X-API-Key: jns_user_YOUR_KEY" # 2. Write back the combined instruction (and the tool, if not already assigned) curl -X PUT "https://api.junis.ai/api/external/management/agents/AGENT_ID" \ -H "X-API-Key: jns_user_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "instruction": "\n\n[CUSTOM_API_CONFIG]...[/CUSTOM_API_CONFIG]", "tools": ["custom_api_call"] }' ``` `instruction` and `tools` are both replaced in full. Fetch, merge, then write — and have only one process doing it, or two concurrent writers will overwrite each other. An organization-level provisioning key **cannot** do this — it does not carry `agents:manage`. Use a user-level key created by an owner or admin from **Settings → API Keys**. The two keys have different jobs: the provisioning key manages accounts and their credentials, the user-level key manages agents. *** ## Users Managing Their Own Credentials If the person signs into Junis themselves, they manage their own credentials — you do not need to push anything. * **In the app:** **Team → MCP & Keys → My Keys**. Open to every member, including those with the MEMBER role. * **Over the API:** with their own user-level key carrying `secrets:manage`. ``` GET https://api.junis.ai/api/external/management/me/secrets PUT https://api.junis.ai/api/external/management/me/secrets/{NAME} DELETE https://api.junis.ai/api/external/management/me/secrets/{NAME} ``` ```bash theme={null} curl -X PUT "https://api.junis.ai/api/external/management/me/secrets/NOTION_TOKEN" \ -H "X-API-Key: jns_user_THEIR_KEY" \ -H "Content-Type: application/json" \ -d '{ "value": "secret_xxx" }' ``` These endpoints accept **user-level keys only**. An organization-level key is rejected with `403`, because such a key resolves to whoever created it — accepting it would let every holder of that key edit that person's private vault. Same rules as above: names only on read, 8 KB per value, 200 per organization, and no way to read a value back. *** ## Running as a Provisioned User Take the `user_id` from the provisioning response and pass it as `on_behalf_of`. It works on both invocation endpoints and needs the `users:delegate` scope: | Endpoint | Use it for | | - | - | | [`POST /v1/chat/completions`](/api-reference/chat-completions) | Your main assistant — routed through the orchestrator | | [`POST /agents/{id}/completions`](/api-reference/agents) | A single agent, called directly | ```json theme={null} { "model": "junis", "messages": [{ "role": "user", "content": "How did I sleep last week?" }], "on_behalf_of": "USER_ID" } ``` Sessions, messages, and memory belong to that user. Subscription checks and credit charges stay with your API key's own user. Delegation works before their first login, so you can start using an account the moment you create it. ### Keeping one-off calls out of their history Both endpoints accept `session_hidden: true`. Use it for calls that are part of your product's machinery rather than a conversation the user should see later — scoring, classification, background analysis: ```json theme={null} { "message": "Classify this entry", "on_behalf_of": "USER_ID", "session_hidden": true } ``` Hidden sessions still belong to the user and still bill to you; they are simply left out when you list a user's conversations with `include_hidden=false`. *** ## Showing a User Their History The same `on_behalf_of` parameter works on the session read endpoints, so you can build a history view inside your own product: ```bash theme={null} # Their conversations, newest first curl "https://api.junis.ai/api/external/sessions?on_behalf_of=USER_ID&include_hidden=false" \ -H "X-API-Key: jns_live_YOUR_KEY" # One conversation, in full curl "https://api.junis.ai/api/external/sessions/SESSION_ID/messages?on_behalf_of=USER_ID" \ -H "X-API-Key: jns_live_YOUR_KEY" # Is a reply still being generated? curl "https://api.junis.ai/api/external/sessions/SESSION_ID/status?on_behalf_of=USER_ID" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` `include_hidden=false` leaves out the one-off calls you marked with `session_hidden`, so the list matches what your user would expect to see. It defaults to `true`. Omit `on_behalf_of` entirely and you get your own key's sessions, exactly as before. **Reading is narrower than running.** On these endpoints `on_behalf_of` only accepts accounts **your organization provisioned**. Chat completions also let you run as a real member of your organization, but reading or deleting their conversations never works — a provisioning key can't pull a real person's chat history. *** ## Deleting a User's Data When someone deletes their account in your product, delete their Junis conversations too. This is what lets you honour a "we delete everything" promise. Requires the `sessions:delete` scope, granted by the same checkbox. ```bash theme={null} curl -X DELETE \ "https://api.junis.ai/api/external/sessions/SESSION_ID?on_behalf_of=USER_ID" \ -H "X-API-Key: jns_live_YOUR_KEY" ``` ```json theme={null} { "session_id": "abc123", "deleted": true } ``` The session, its messages, and its stored state are removed permanently — there is no recovery. To purge a user completely, list their sessions with `include_hidden=true` (the default, so you catch the hidden ones too) and delete each one. Every deletion is recorded in your organization's audit log with who deleted it, whose session it was, and whether the call was delegated. ### Delegation errors | Status | Code | Description | | - | - | - | | `403` | `insufficient_scopes` | Key lacks `users:delegate` (read) or `sessions:delete` (delete) | | `403` | `delegation_target_not_found` | `on_behalf_of` user does not exist or is inactive | | `403` | `delegation_target_not_member` | Target is not an active or pending member of your organization | | `403` | `delegation_target_not_provisioned` | Target is a real account, not one your organization provisioned | | `404` | — | Session does not exist, or does not belong to the target user | # Warmup Source: https://docs.junis.ai/api-reference/warmup Build an organization/user orchestrator (and optionally one agent) ahead of the first message ## Overview Junis builds your organization's agents lazily, per **(organization, user)**, on the server that receives the request, and keeps them in memory while they are in use. The first message of a newly provisioned user therefore pays that build before the first token: a fraction of a second for a small squad, several seconds for squads with many tools or integrations. `POST /v1/warmup` performs that build **without any LLM call** (no tokens, no charge) so the first message finds everything ready. Requests that carry the same API key are routed to the same server, so the warmed instance is the one your next message uses. **When to call it**: right after provisioning a user ([User Provisioning](/api-reference/user-provisioning)), or after a long idle period. Warmed instances are released after about an hour of inactivity and after server restarts or deployments; calling warmup again is always safe. *** ## Endpoint ``` POST https://api.junis.ai/api/external/v1/warmup ``` ### Authentication | Scope | When | | - | - | | `orchestrator:invoke` | always | | `users:delegate` | when `on_behalf_of` is set | | `agents:invoke` | when `agent_id` is set | ### Request Body The body is optional. `curl -X POST …/v1/warmup` with no body (or `{}`) warms the orchestrator for the API key's own user. | Field | Type | Required | Description | | - | - | - | - | | `on_behalf_of` | string | No | Warm the orchestrator for this provisioned user (same rules as in Chat Completions: active or pending member of your organization). Omit to warm for the API key's own user. | | `agent_id` | string | No | Also pre-build this agent (UUID of an active agent in your organization). Useful before the first [`POST /agents/{agent_id}/completions`](/api-reference/agents) call or the first `target_agent_id` turn. Unknown/foreign → `404 agent_not_found`, inactive → `400 agent_inactive`. | ### Request Example ```bash cURL theme={null} curl -X POST https://api.junis.ai/api/external/v1/warmup \ -H "X-API-Key: jns_live_YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "on_behalf_of": "9f1c…", "agent_id": "785aba85-caf1-4e22-8ca9-841093e611d6" }' ``` ### Response ```json theme={null} { "status": "built", "took_ms": 412, "organization_id": "603da21e-…", "user_id": "9f1c…", "sub_agents": 8, "tools": 18, "agent_id": "785aba85-caf1-4e22-8ca9-841093e611d6", "agent_ready": true } ``` | Field | Type | Description | | - | - | - | | `status` | string | `built` — the build ran now (or joined a build already in progress); `warm` — already cached | | `took_ms` | integer | Time spent building (after validation), in milliseconds | | `organization_id` | string | Your organization | | `user_id` | string | The user the orchestrator was warmed for | | `sub_agents` | integer | Sub-agents attached to the orchestrator | | `tools` | integer | Tools attached to the orchestrator | | `agent_id` | string | The pre-built agent, when requested | | `agent_ready` | boolean | Whether the requested agent was built and cached | ### Errors | Status | Code | Meaning | | - | - | - | | `403` | `insufficient_scopes` | Missing `users:delegate` (with `on_behalf_of`) or `agents:invoke` (with `agent_id`) | | `403` | `no_subscription` / `invalid_plan` | Same subscription gate as `POST /v1/chat/completions` (Basic or Pro required for the key's user) | | `402` | `insufficient_credits` | Same credit gate as `POST /v1/chat/completions` (billing user's effective balance is negative) | | `404` | `agent_not_found` | `agent_id` is not an agent of your organization | | `400` | `agent_inactive` | The agent exists but is inactive | | `429` | `warmup_rate_limited` | Too many **cold** builds for this API key (see below). The response carries `Retry-After` (seconds). Nothing is lost — the user's first message builds normally. | | `500` | `internal_error` | The build failed (the next message will try again) | Warmup is rate-limited like every other External API call. It is idempotent: calling it for an already-warm user returns `status: "warm"` immediately. ### Cold-build limits A cold build is the expensive part of warmup, so each API key may run at most **2 cold builds at the same time** and **30 cold builds per minute** by default. Calls for users that are already warm never count and are never rejected. Warm users right before their first message (for example when they open your app) rather than warming every provisioned account in a batch — idle instances are cleaned up after a while, so a batch warmup mostly builds instances that expire unused. # Core Concepts Source: https://docs.junis.ai/concepts Understanding the Junis platform architecture and key concepts ## What is the Junis platform architecture? Junis is an AI workforce platform built on a **multi-agent architecture**: every organization runs its own orchestrator that routes requests to specialized agents, which use tools, MCP integrations, RAG knowledge bases, and persistent memory to complete tasks. This page explains the core concepts behind that architecture — agents, organizations, sessions, tools, and the systems that connect them. *** ## 1. Agents **Agents** are the fundamental building blocks of Junis. Think of them as AI workers with specific skills and responsibilities. ### Agent Types Individual AI worker that processes tasks using a language model * Has its own instructions and tools * Can call sub-agents * Specialized for specific tasks Executes sub-agents one after another in order * Step-by-step processing * Output of one becomes input of next * Perfect for pipelines Runs multiple sub-agents simultaneously * Concurrent execution * Aggregates results * Faster for independent tasks Repeats sub-agents until a condition is met * Iterative processing * Configurable max iterations * Useful for retry logic ### Agent Hierarchy ```mermaid theme={null} graph TD Orchestrator[Orchestrator
Entry point for all requests] Orchestrator -->|Routes to| Sequential[Sequential Agent
Data Pipeline] Orchestrator -->|Routes to| Parallel[Parallel Agent
Multi-task] Sequential --> Agent1[LLM Agent 1
Data Collection] Sequential --> Agent2[LLM Agent 2
Data Analysis] Sequential --> Agent3[LLM Agent 3
Report Generation] Parallel --> Agent4[LLM Agent 4
Task A] Parallel --> Agent5[LLM Agent 5
Task B] Parallel --> Agent6[LLM Agent 6
Task C] ``` **Orchestrator** is a special agent that serves as the entry point for all requests. It routes messages to appropriate sub-agents based on the user's intent. *** ## 2. Multi-Tenant System Junis is a **multi-tenant platform** where each organization has isolated data and configurations. ### Organization Isolation ```mermaid theme={null} graph LR System[Junis Platform] System --> Org1[Organization A] System --> Org2[Organization B] System --> Org3[Organization C] Org1 --> A1[Agents] Org1 --> A2[Sessions] Org1 --> A3[RAG Data] Org2 --> B1[Agents] Org2 --> B2[Sessions] Org2 --> B3[RAG Data] ``` **Key Points**: * Each organization has its own **Orchestrator** * Agents are **scoped to organizations** * Sessions and data are **completely isolated** * Users can belong to multiple organizations ### Organization Roles | Role | Permissions | | - | - | | **OWNER** | Full organization control, manage all settings | | **ADMIN** | Manage agents, tools, schedules, members | | **MEMBER** | Use agents, create sessions, upload RAG documents | Platform-level roles (SUPER\_ADMIN, SUPPORT) are managed separately for system administration purposes. *** ## 3. Sessions A **session** represents a conversation between a user and the Junis system. Sessions maintain context across multiple messages. ### Session Lifecycle ```mermaid theme={null} sequenceDiagram participant User participant Frontend participant API participant Database User->>Frontend: Send first message Frontend->>API: POST /chat (no session_id) API->>Database: Create new session Database-->>API: Return session_id API-->>Frontend: Stream response + session_id Frontend->>Frontend: Store session_id User->>Frontend: Send follow-up message Frontend->>API: POST /chat (with session_id) API->>Database: Load session context API-->>Frontend: Stream response with context ``` **Session Properties**: * **Persistent**: Stored in PostgreSQL database * **Contextual**: Agents remember previous messages * **Searchable**: Full conversation history available via API * **Resumable**: Can continue sessions across disconnections *** ## 4. Tools **Tools** are functions that agents can call to perform actions or retrieve information. ### Tool Categories **Python Functions** registered in the system: * `various_of_tools`: Useful Tools **External services** via Model Context Protocol: * **GitHub**: `create_repository`, `create_issue`, `list_commits` * **Firecrawl**: `scrape_url`, `crawl_website`, `map_website` * **Remote MCPs**: `-` * **Local MCPs**: `-` Dynamic toolsets from MCP servers **Document search** tools powered by Vertex AI: * Auto-generated per knowledge base * Semantic search across uploaded documents * Returns relevant excerpts with citations * Fully managed by the system ### Tool Calling Flow ```mermaid theme={null} sequenceDiagram participant Agent participant Google ADK participant Tool participant External Service Agent->>Google ADK: Generate response Google ADK->>Google ADK: Detect tool call needed Google ADK->>Tool: Call tool with parameters Tool->>External Service: Execute action External Service-->>Tool: Return result Tool-->>Google ADK: Return formatted result Google ADK-->>Agent: Continue with result ``` *** ## 5. Model Context Protocol (MCP) **MCP** is a standard protocol for connecting AI agents to external services. Junis supports both **Remote** and **Local** MCP servers. ### MCP Architecture ```mermaid theme={null} graph TB Agent[LLM Agent] Loader[Dynamic Agent Loader] Integration[MCP Integration Layer] Agent -->|Load MCP| Loader Loader -->|Initialize| Integration Integration -->|HTTP/SSE| Remote[Remote MCP Server] Integration -->|Subprocess| Custom[Custom MCP Server] Remote --> GitHub[GitHub API] Remote --> Firecrawl[Firecrawl API] subgraph "Remote MCP" Remote GitHub Firecrawl end subgraph "Custom MCP" Custom end ``` ### Authentication Hierarchy MCP credentials support **multi-level authentication**. Users can configure credentials at different levels, with automatic fallback to appropriate defaults. For detailed authentication configuration, including priority systems and credential management, refer to the MCP Integration Guide in the Admin documentation. *** ## 6. RAG (Retrieval-Augmented Generation) **RAG** enhances agents with knowledge from your documents. When a user asks a question, the system: 1. **Searches** relevant documents using semantic search 2. **Retrieves** the most relevant excerpts 3. **Augments** the agent's prompt with this context 4. **Generates** a response informed by your documents ### RAG Workflow ```mermaid theme={null} sequenceDiagram participant User participant Agent participant RAG Tool participant Vertex AI Search participant GCS User->>Agent: Ask question Agent->>Agent: Detect need for knowledge Agent->>RAG Tool: Call rag_datastore_id() RAG Tool->>Vertex AI Search: Semantic search Vertex AI Search->>GCS: Fetch document chunks GCS-->>Vertex AI Search: Return chunks Vertex AI Search-->>RAG Tool: Return relevant excerpts RAG Tool-->>Agent: Formatted context Agent-->>User: Answer with citations ``` ### DataStore Structure ``` Organization: Acme Corp ├── DataStore: salesforce_brands │ ├── brands_2026_q1.pdf │ ├── brands_2026_q2.pdf │ └── brands_pricing.xlsx ├── DataStore: marketing_assets │ ├── campaign_brief.docx │ └── creative_guidelines.pdf └── DataStore: product_docs ├── api_reference.md └── user_manual.pdf ``` *** ## 7. Memory System **Memory** gives your AI agents the ability to remember users across conversations, building a personalized understanding over time. ### Memory Tiers ```mermaid theme={null} graph LR Chat[Conversations] -->|After each session| D1[Daily Memory] D1 -->|Monthly| D30[Monthly Summary] D30 -->|Quarterly| D90[Quarterly Summary] D90 -->|Yearly| D365[Yearly Narrative] Chat -->|Key facts| D0[Eternal Memory] User[User Profile] -.->|Editable| Agent[Agent Context] D0 -.-> Agent D1 -.-> Agent D30 -.-> Agent ``` | Tier | What It Stores | How It's Updated | | - | - | - | | **User Profile** | Name, role, preferences, expertise | Agent or user updates anytime | | **Eternal Memory** | Permanent facts and standing instructions | Agent stores on user request | | **Daily Memory** | Conversation summaries | Automatic after each session | | **Monthly/Quarterly/Yearly** | Consolidated patterns and narratives | AI-powered auto-consolidation | **Key Properties**: * **Automatic**: Memory is collected and consolidated without manual action * **Per-User**: Each user has isolated memory — no cross-user visibility * **Contextual Injection**: Agents automatically load relevant memories at the start of each conversation * **AI Consolidation**: Memories are periodically reviewed and summarized, mimicking human memory For details on managing memory and available tools, see the [Memory System Guide](/guides/memory-system). *** ## 8. Soul & Identity **Soul & Identity** defines your AI team's personality at the organization level. It controls how agents think and communicate. ### Two Files **Philosophy & Thinking** — Core values, decision-making principles, reasoning style **Expression & Persona** — Communication tone, language patterns, personality traits ### How It Works ```mermaid theme={null} graph TD Soul[soul.md] --> Inject[Auto-Injection] Identity[identity.md] --> Inject Inject --> Every[Every Agent Conversation] ``` * **Organization-scoped**: All agents inherit the same personality * **Automatic injection**: Loaded into every conversation without manual configuration * **Admin-only editing**: Only Admin/Owner roles can modify * **Instant effect**: Changes apply to the next conversation Soul & Identity defines *who* your AI is. Agent instructions define *what* each agent does. See the [Soul & Identity Guide](/guides/soul-identity). *** ## 9. Streaming Junis supports **real-time streaming** for better user experience. ### Streaming Modes **REST API streaming** via `/api/external/v1/chat/completions` * HTTP with `stream: true` * OpenAI-compatible format * Token-by-token delivery **Persistent connection** via `/ws/chat` * Bidirectional communication * Pipeline events * File uploads * Payment flows ### Event Types (WebSocket) | Event Type | Description | Example | | - | - | - | | `stream_token` | Individual text token | `{"type": "stream_token", "token": "Hello"}` | | `agent_routing` | Agent handoff | `{"type": "agent_routing", "from": "A", "to": "B"}` | | `agent_started` | Agent begins processing | `{"type": "agent_started", "agent": "Analyzer"}` | | `agent_completed` | Agent finishes | `{"type": "agent_completed", "duration_ms": 1234}` | | `stream_complete` | Entire response complete | `{"type": "stream_complete"}` | *** ## 10. Performance Optimization Junis uses **intelligent caching and optimization** to ensure fast, responsive agent interactions. ### Caching System The platform implements a multi-tier caching strategy: * **Agent Configuration**: Automatically cached for rapid access * **Session Context**: Persisted for conversation continuity * **External Integrations**: Connection pooling and result caching * **Database Queries**: Query-result caching layer ### Cache Invalidation Caches are automatically updated when: * Agent configuration changes (Admin UI) * Agent relationships modified (Admin UI) * Organization settings updated * External service credentials change For detailed information about caching strategy, performance tuning, and optimization techniques, refer to the Operations Guide in the Admin documentation. *** ## 11. Scheduled Tasks **Scheduled Tasks** enable automation by running agents at specific times or intervals. ### Schedule Types Run at a specific date/time Repeat every N minutes Repeat every N hours Repeat every day at specified hour Run on specific weekdays Execute monthly on specific day ### Schedule Execution ```mermaid theme={null} sequenceDiagram participant Celery Beat participant Celery Worker participant Orchestrator participant Database Celery Beat->>Celery Beat: Check due tasks Celery Beat->>Celery Worker: Trigger task Celery Worker->>Database: Load task config Celery Worker->>Orchestrator: Send prompt Orchestrator-->>Celery Worker: Response Celery Worker->>Database: Save result ``` **Key Features**: * Timezone-aware scheduling * Session reuse or new session per run * Execution history and logs * Enable/disable without deletion *** ## Next Steps Now that you understand the core concepts, explore these guides: Learn how to combine agent types Manage your platform from Claude Desktop Persistent AI memory across conversations Define your AI team's personality Connect MCP and Composio platforms Integrate programmatically # Junis AX 프로그램 Source: https://docs.junis.ai/consulting/overview 전사 AI 전환(AX). 조직의 두뇌를 만들고 에이전트 스쿼드를 배치합니다. 보수는 성과에서만 받는 파트너십. Junis AX는 기업의 AI 전환을 설계부터 운영까지 책임지는 파트너십 프로그램입니다. 도구를 알려주는 교육이 아니라, 회사의 데이터·지식·업무 위에 실제로 일하는 AI 조직을 만들어 함께 운영합니다. 보수는 성과에서만 받습니다. ## AX란 무엇인가: 두 개의 축 AI 전환(AX)은 두 축이 함께 갈 때 완성됩니다. 하나만 하면 반쪽입니다. 팀원 각자가 Claude Code 같은 AI 도구로 자기 일을 빠르게 하는 단계. 필요하지만, 개인의 생산성에서 멈춥니다. 사람이 나가면 노하우도 나갑니다. 회사의 데이터와 지식으로 전사 두뇌를 만들고, 그 위에 에이전트 스쿼드를 배치하는 단계. 지식이 조직에 쌓이고, 일이 사람 없이도 돌아갑니다. 비즈니스 10x는 여기서 나옵니다. Junis AX는 **축 2를 만드는 프로그램**입니다. 축 1은 그 과정에서 자연히 따라옵니다. ## 무엇을 하나요? 레거시 파악부터 운영까지, 전 과정을 저희가 직접 수행합니다. | | 단계 | 내용 | | - | - | - | | 1 | **레거시 파악 · 설계** | 현재 시스템·업무 흐름·데이터 자산을 전수 분석하고 전환 설계도를 그립니다 | | 2 | **데이터 온톨로지** | 흩어진 데이터를 하나의 온톨로지로 통합하고, 자동 업데이트와 에이전트 연결까지 구축합니다 | | 3 | **지식 체계** | RAG · LLM Wiki · 온톨로지로 회사가 아는 모든 것을 AI가 쓸 수 있는 형태로 만듭니다 | | 4 | **에이전트 스쿼드 배치** | 재무 분석, 시장 인사이트, 콘텐츠 생성, 고객사 발굴, 세일즈, 신제품 기획, 위기 대응까지, 사람이 하던 일마다 전담 스쿼드를 구성합니다 | | 5 | **레거시 툴 연결** | Slack · Google Drive · Gmail · Linear 등 이미 쓰는 도구에 그대로 연결합니다 | | 6 | **대시보드** | 경영진과 실무진이 보는 지표를 실시간 대시보드로 구성합니다 | | 7 | **보안 체계** | 권한 · 크레덴셜 격리 · 감사 로그까지 조직 기준에 맞게 설계합니다 | | 8 | **운영 · 개선 · 교육** | 배치가 끝이 아닙니다. 함께 운영하며 개선하고, 팀이 스스로 다루도록 교육합니다 | ## 비용: 성과로만 과금합니다 Junis AX의 보수는 시스템이 만든 **수익의 일부**입니다. 성과가 없으면 보수도 없습니다. 저희가 성과에 집착하는 이유입니다. 시작 시 합리적 수준의 미니멈 개런티가 있습니다. **전액이 성과 수수료에서 차감**되는 구조로, 별도 컨설팅비가 아니라 성과에 대한 선지급입니다. 요약하면, 성과가 나면 컨설팅 비용은 사실상 없고, 성과가 부족하면 저희가 손해를 봅니다. 리스크는 저희 쪽에 있습니다. ## 연 매출 1,000억 기업에서 이미 돌아가고 있습니다 연 매출 1,000억 원대의 글로벌 소비재 기업에서 Junis 에이전트 스쿼드가 상시 운영되고 있습니다. 아래는 실제 운영 중인 구성입니다. * **리뷰 분석**: 고객 리뷰를 루프로 상시 분석해 제품별 인사이트를 만듭니다 * **주간 인텔리전스**: 시장 · 자사 리포트가 매주 Slack으로 배달됩니다 * **트렌드 수집**: 카테고리 트렌드와 소셜 시그널을 상시 추적합니다 * **글로벌 채널**: 해외 마켓플레이스 인사이트와 메일 대응을 담당합니다 * **콘텐츠 발행**: 소셜 채널별 콘텐츠를 만들어 발행합니다 * **업무 연결**: 메일 · 보고 · 문서화를 잇고, 사내 지식 질의응답과 종합 비즈니스 분석까지 맡습니다 ERP · 물류(WMS) · 커머스 스토어 · 운영 DB 네 시스템을 **채팅 한 줄로 조회**합니다. 재고 · 주문 · 정산 질문에 즉답하고, 전용 동기화 파이프라인이 계정 · 상품 · BOM 데이터를 자동으로 맞춥니다. 잠재 고객사와 담당자를 자동 발굴해 온톨로지 DB에 적재하고, 매주 스케줄로 스스로 실행됩니다. 신규 파트너 후보도 상시 탐색합니다. 실시간 사용량 · 비용 대시보드, 커머스 컨트롤타워, 주간 인텔리전스 리포트. 경영진이 보는 화면까지가 구축 범위입니다. ## 시작하기 메일 한 통이면 됩니다. 현재 시스템과 목표를 알려주시면, 저희가 먼저 분석해서 찾아갑니다. 회사명과 해결하고 싶은 문제 한 줄이면 충분합니다. Junis를 만드는 TOOEASY는 Strong Ventures · Mashup Angels · Google for Startups의 투자·지원을 받았으며, 정부 TIPS 프로그램에 선정되었습니다. # Admin Interface Guide Source: https://docs.junis.ai/getting-started/admin-interface Master the Junis Admin Panel - manage agents, workflows, and team settings ## Welcome to the Admin Panel The Junis Admin Panel is your **central command center** for managing AI agents, workflows, knowledge bases, and team settings. This no-code interface allows you to build sophisticated multi-agent systems without writing a single line of code. **What You'll Learn:** * Navigate the Admin interface * Manage agents and workflows * Configure RAG knowledge bases * Connect MCP integrations * Monitor usage and performance *** ## 📍 Accessing the Admin Panel ### Navigation Navigate to [https://junis.ai](https://junis.ai) and sign in with your account From the top navigation bar, ensure you've selected the correct organization (if you're a member of multiple organizations) Open the **"Team Management"** dropdown in the top navigation bar. Admin features live under its menu items — for example **"Dashboard"** (usage dashboard) and **"Agent Orchestration"** (agent management) **Two surfaces, one admin experience**: Most admin pages (Agent Orchestration, RAG Knowledge, MCP & Keys, Skills, Members, Audit, etc.) live under the **Team Management** dropdown in the top navigation bar. The usage **Dashboard** described in this guide is also opened from that same dropdown. Other guides that say "go to Agent Orchestration" mean **Team Management → Agent Orchestration**. ### Permission Levels Different features are available based on your role: | Feature | Member | Admin | Owner | | - | - | - | - | | View Dashboard | ✅ | ✅ | ✅ | | Manage API Keys | ✅ | ✅ | ✅ | | View Agents | ❌ | ✅ | ✅ | | Create/Edit/Delete Agents | ❌ | ✅ | ✅ | | Organization Settings | ❌ | ✅ | ✅ | | Rename / Delete Organization, Billing | ❌ | ❌ | ✅ | | System Management | ❌ | ❌ | ❌ | **Role Hierarchy**: Owner > Admin > Member. Higher roles inherit all permissions from lower roles. Owner additionally controls organization-level settings such as renaming or deleting the organization and billing. *** ## 📊 Dashboard Overview The Dashboard is the first page you see when entering the Admin panel. It provides **real-time insights** into your organization's AI usage. ### Quick Stats The top section displays four key metrics: Number of unique users in the last 24 hours Total chat sessions created today Currently connected WebSocket sessions (real-time) Average response time in milliseconds (last 24 hours) ### Real-Time Metrics The metrics section shows **live updates** via WebSocket: * **Active Sessions**: Users currently chatting with agents * **Recent Messages**: Message rate per minute * **Agent Performance**: Response times by agent * **Error Rate**: System health monitoring Metrics refresh automatically every few seconds. Look for the **green indicator** to confirm live connection status. ### Recent Activity (Admin Only) Admins and Super Admins can see: * Latest chat sessions * Which agents were used * Session duration and message count * User information *** ## 🤖 Agent Management The heart of Junis - where you create, configure, and visualize your AI agent workflows. ### Two Views: List and Orchestration **Table View** - See all agents in a sortable, hierarchical list **Columns:** * **Name**: Agent identifier (Orchestrator shown first) * **Type**: LLM, Sequential, Parallel, or Loop (color-coded badges) * **SubAgents**: Visual chips showing connected agents with order numbers * **Description**: Agent purpose and function * **Actions**: Edit, Relationships, Delete buttons **Sorting**: Agents are automatically sorted by hierarchy, starting with the Orchestrator, then its sub-agents in execution order. **Visual Workflow Builder** - Interactive node graph showing agent relationships **Features:** * Drag-and-drop node positioning * Color-coded by agent type: * **Blue**: LLM Agent * **Purple**: Sequential Agent * **Green**: Parallel Agent * **Orange**: Loop Agent * Connection lines show execution flow * Zoom and pan controls * Minimap for navigation ### Creating a New Agent From the Agent Management page, click the **"+ New Agent"** button (top right) Select how to create the agent: * **Create New**: Build from scratch * **Use Global Agent**: Import a public template from the Junis marketplace ```yaml Basic Settings theme={null} Name: My New Agent Agent Type: LLM_AGENT Description: What this agent does Status: Active ✅ ``` ```yaml Model Configuration theme={null} Model: anthropic/claude-sonnet-5 # Or gpt, gemini, etc. Temperature: 0.7 Max Output Tokens: 16000 Streaming: Enabled ✅ ``` ```yaml System Prompt theme={null} You are a helpful assistant that... Your responsibilities: 1. Answer user questions clearly 2. Use tools when appropriate 3. Be concise and professional Important: Always verify information before responding. ``` * **Regular Tools**: Select Python functions (search\_database, send\_email, etc.) * **RAG Tools**: Select knowledge bases (rag\_product\_docs, rag\_support\_kb) * **MCP Platforms**: Select integrations (GitHub, Firecrawl) * **Output Key**: Variable name for Sequential Agent data passing * **Payment Settings**: Enable paid usage (wallet address, price per use) * **Web Search**: Enable real-time web search (Gemini 2.5+/3.x, Claude, OpenRouter models) * **Extended Thinking**: Enable reasoning tokens (Claude, Gemini 3.x, and other reasoning-capable models) Click **"Create"** - your agent will be instantly available! **Auto-Refresh**: The cache is automatically refreshed when you create or update agents. No manual reload needed! ### Editing an Agent 1. Click the **Edit (pencil)** icon next to the agent name 2. Modify settings in the dialog 3. Click **"Save"** to apply changes **Toggle Switches** (instant save): * **Active/Inactive**: Enable or disable the agent * **Streaming**: Turn real-time streaming on/off **Global Agents** (marked with a globe icon) **cannot be edited**. These are public templates provided by Junis. To customize, create a copy instead. ### Managing Agent Relationships **Agent Relationships** define which sub-agents a parent agent can call. Click the **GitBranch icon** next to the agent name See two sections: * **Sub-Agents**: Agents this one calls (with order index) * **Parent Agents**: Agents that call this one 1. Click **"+ Add Sub-Agent"** 2. Select an agent from the dropdown 3. Set the **order index** (1 = first, 2 = second, etc.) 4. Click **"Add"** For **Sequential Agents**, order matters! * Click the **up/down arrows** to change execution order * Changes save immediately Click the **trash icon** next to a sub-agent relationship **For Sequential Agents**: Sub-agents execute in order (1, 2, 3...). Each agent passes its output to the next via the `output_key` variable. **For Parallel Agents**: Order doesn't matter. All sub-agents run simultaneously, and results are combined. ### Visual Workflow Builder The **Orchestration tab** provides an interactive graph view of your agent system. **Node Colors**: * **Blue Gradient**: LLM Agent (performs tasks) * **Purple Gradient**: Sequential Agent (runs agents in order) * **Green Gradient**: Parallel Agent (runs agents simultaneously) * **Orange Gradient**: Loop Agent (repeats until condition met) **Connection Lines**: * **Solid**: Direct parent-child relationship * **Dotted (Purple)**: Sequential execution flow (left to right) * **Dotted (Green)**: Parallel execution flow (top to bottom) **Legend** (top of the page): * Shows agent type meanings * Indicates execution flow patterns **Unconnected Agents Panel** (right side): * Lists agents not connected to the Orchestrator * Displays active/inactive status * Tool Agents are automatically hidden **Pro Tip**: Use the minimap (bottom right) to navigate large workflows. Click and drag the viewport box to move around. ### Deleting an Agent **Deletion is permanent!** Make sure you're not deleting an agent that other agents depend on. Before deleting, open the **Relationships dialog** to see if other agents use this one If the agent has **parent agents**, remove those relationships first Click the **trash icon**, confirm the deletion **Orchestrator Protection**: You cannot delete the Orchestrator agent. It's the root of your organization's agent system. *** ## 📚 RAG Knowledge Base **RAG (Retrieval-Augmented Generation)** allows your agents to query uploaded documents for accurate, context-aware responses. ### How It Works ```mermaid theme={null} graph LR A[User Question] --> B[Agent receives query] B --> C{Has RAG tool?} C -->|Yes| D[Search Knowledge Base] C -->|No| E[Direct LLM response] D --> F[Retrieve relevant documents] F --> G[LLM generates answer with context] G --> H[Response to user] E --> H ``` ### Uploading Documents Navigate to **Team > RAG Knowledge** in the sidebar **Supported Formats**: * ✅ PDF (.pdf) - Max 50MB * ✅ Microsoft Word (.docx) - Max 50MB * ✅ PowerPoint (.pptx) - Max 10MB * ✅ Plain Text (.txt) - Max 10MB * ✅ Markdown (.md, .markdown) - Max 10MB * ✅ HTML (.html, .htm) - Max 10MB * ✅ JSON (.json, .jsonl) - Max 10MB Documents are uploaded to Google Cloud Storage and indexed by Vertex AI Search. Indexing typically takes **5-60 minutes** depending on file count. Click **"Upload Document"** and select your file The system will: 1. Upload to Google Cloud Storage 2. Create a Vertex AI Search DataStore 3. Index the document (JSONL conversion) 4. Generate a RAG tool ID (format: `rag_{datastore_id}`) Watch the upload progress bar. Status will change: * **Uploading** → **Indexing** → **Completed** ### Assigning RAG Tools to Agents Open the agent edit dialog and scroll to the **RAG Tools** section Check the boxes for the knowledge bases this agent should access Example: * ✅ `rag_product_documentation` * ✅ `rag_support_tickets_history` * ☐ `rag_marketing_materials` The agent can now query these documents during conversations! ### RAG Best Practices * **Clean formatting**: Remove unnecessary headers, footers * **Consistent structure**: Use headings, bullet points * **Clear language**: Avoid jargon unless necessary * **Chunk size**: Keep documents under 10MB for faster indexing * Separate by **topic** (product docs, support tickets, policies) * Separate by **audience** (internal vs customer-facing) * Agents can use multiple DataStores simultaneously * Include **keywords** in your documents * Use **semantic search** (Vertex AI Search handles this automatically) * Test queries with the agent to verify retrieval accuracy *** ## 🔌 MCP Integrations **MCP (Model Context Protocol)** connects your agents to external platforms like GitHub, Firecrawl, and custom services. ### Available Platforms Manage repos, issues, and PRs Web scraping and crawling **Supported platforms**: GitHub, Firecrawl, and custom MCP servers. Easily extensible for any service with MCP implementation. See the [MCP Integration Guide](/guides/mcp/overview) for details. ### Setting Up MCP Credentials **Shared credentials for all team members** Go to **Team > MCP** in the sidebar Click **"Connect"** next to a platform (e.g., GitHub, Firecrawl) * **API Key**: Enter the API key/token * **OAuth**: Click "Authorize" to connect via OAuth **Shared Access**: All agents in your organization can now use this platform (if granted permission). **Your personal credentials (e.g., personal GitHub)** Go to **Settings > User Profile** (click your avatar, then "Settings") Click the **"MCP Credentials"** tab * Click **"+ Add Platform"** * Select the platform (e.g., GitHub) * Enter your **personal API key** or authorize via OAuth Your personal credentials **override** organization credentials for that platform **Use Case**: Personal GitHub credentials ensure agents access **your repositories**, not organization repos. User-level credentials override organization credentials. ### Enabling MCP for an Agent Open the agent edit dialog and scroll to the **MCP Platforms** section Check the boxes for platforms this agent should access: * ✅ GitHub * ✅ Firecrawl * ☐ Custom MCP (if configured) Click **"Save"** to enable MCP integration for this agent *** ## 🔑 API Keys Generate API keys to integrate Junis agents into external applications. ### Creating an API Key Navigate to **Admin > Dashboard**, then click the **"API Keys"** tab Click **"+ New API Key"** ```yaml API Key Settings theme={null} Name: My Integration Key Description: For mobile app integration Permissions: Read/Write Expiration: 90 days (optional) ``` **⚠️ Important**: The key is shown **only once**. Copy it immediately! Format: `jns_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` (32 characters after prefix) ### Using an API Key **Authentication Header**: ```bash theme={null} X-API-Key: jns_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` **Example Request** (OpenAI-compatible): ```bash theme={null} curl https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: jns_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "model": "orchestrator", "messages": [{"role": "user", "content": "Hello!"}] }' ``` **OpenAI SDK Compatible**: Use the OpenAI Python SDK with `base_url="https://api.junis.ai/api/external/v1"` for seamless integration. ### Managing API Keys * See all API keys for your organization * Check **last used** timestamp * View **permissions** and **expiration** Click **"Revoke"** next to a key to immediately disable it (cannot be undone) Best practice: **Rotate keys every 90 days** 1. Create a new key 2. Update your applications 3. Revoke the old key *** ## ⚙️ Organization Settings **Admin/Owner Only**: This section is only accessible to Admins and Super Admins. Navigate to **Admin > Dashboard > Settings tab** to configure organization-wide options. ### Team Settings * **Name**: Your organization's display name * **Slug**: URL-friendly identifier (e.g., `acme-corp`) * **Logo**: Upload a logo image (PNG, JPG, max 2MB) * **Description**: Public description (shown on showcase page) * **Enable Public Profile**: Make your organization discoverable on junis.ai/showcase * **Welcome Suggestions**: Customize suggested prompts for visitors * **Featured Agents**: Select which agents to showcase publicly * **Company Wallet**: Ethereum wallet address for receiving agent usage payments * **Default Agent Pricing**: Set default price per use (USDC micro) * View current usage statistics * Set monthly limits (Pro/Enterprise plans) * Configure billing alerts ### User Management **Invite Team Members**: Navigate to **Admin > Dashboard > Users** tab Click the **"+ Invite User"** button Type the email address of the person you want to invite * **Member**: Can use agents, view dashboard * **Support**: + Can view agent configurations * **Admin**: + Can create/edit agents, manage settings Click **"Send Invite"** - the user will receive an email invitation **Change User Roles**: * Find the user in the list * Click the **role dropdown** * Select the new role **Remove Users**: * Click the **trash icon** next to a user * Confirm removal *** ## 🔍 Advanced Features ### Scheduled Tasks **Automate agent execution** with schedules (cron jobs). Go to **Admin > Schedules** (if available in your plan) Click **"+ New Schedule"** ```yaml Schedule Configuration theme={null} Name: Daily Report Generator Prompt: Generate today's summary report Schedule: Daily at 9:00 AM Timezone: America/New_York Session: Create new session each time ``` Click **"Run Now"** to test before saving * **Once**: Run at a specific date/time * **Daily**: Every day at X time * **Weekly**: Specific days of the week * **Monthly**: Specific day of the month * **Cron**: Advanced cron expression (e.g., `0 9 * * 1-5` = weekdays at 9 AM) ### Workflow Builder (Coming Soon) Visual drag-and-drop interface for building complex agent workflows. **Features** (planned): * Drag agents from a palette * Draw connections between agents * Set conditional logic (if/else branches) * Test workflows in real-time * Export/import workflow templates *** ## 💡 Tips for Effective Admin Management * Begin with a **single LLM Agent** to handle basic tasks * Test thoroughly before adding complexity * Add Sequential/Parallel agents only when you need multi-step workflows * Monitor performance before scaling up **Naming Convention**: * `orchestrator` - Main router * `customer_support_agent` - Customer-facing * `research_analyst` - Data analysis * `email_sender` - Action agents **Groups**: * **Front-line**: Customer support, sales * **Back-office**: Data processing, reporting * **Specialized**: Legal, technical documentation **Good**: ``` You are a customer support agent for Acme Corp. Your responsibilities: 1. Answer questions about products A, B, C 2. Help troubleshoot common issues (login, billing) 3. Escalate to human support for complex cases Important: Be friendly, concise, and professional. Always verify user identity before sharing account info. ``` **Bad**: ``` You are a helpful assistant. ``` * Check **Dashboard metrics** weekly * Review **Recent Activity** to see how agents are being used * Update agent instructions based on user feedback * Test edge cases regularly * **Rotate API keys** every 90 days * Use **user-level MCP credentials** for personal platforms (e.g., personal GitHub) * Set **payment thresholds** for paid agents * Review **user permissions** quarterly * Enable **2FA** for all admin users (if available) *** ## 🆘 Common Issues and Solutions **Possible Causes**: * Agent is **inactive** (check the toggle) * Agent is **not connected to Orchestrator** (check relationships) * **Model API key** is missing (verify in SQLAdmin) **Solution**: 1. Check agent status (green toggle) 2. Verify Orchestrator relationships 3. Test with a simple message 4. Check Dashboard > Recent Activity for errors **Possible Causes**: * Document **still indexing** (wait 1-2 minutes) * **RAG tool not assigned** to agent * Query doesn't match document content **Solution**: 1. Check DataStore status (should be "Completed") 2. Verify agent has the RAG tool enabled 3. Test with a query that matches document keywords 4. Check Vertex AI Search console for indexing errors **Possible Causes**: * **Credentials expired or invalid** * Platform not enabled for agent * **Rate limiting** from external platform **Solution**: 1. Re-authenticate the MCP platform 2. Check agent MCP platforms list 3. Verify API key permissions on the external platform 4. Check error logs in Dashboard > Recent Activity **Possible Causes**: * Browser cache issue * Relationships not saved correctly **Solution**: 1. Hard refresh the page (Ctrl+Shift+R or Cmd+Shift+R) 2. Switch to List view to verify relationships 3. Re-save the relationships if needed *** ## 📖 Next Steps Connect external platforms via Model Context Protocol Register Python functions as agent tools Integrate Junis into your applications *** ## 🎓 Admin Interface Certification Congratulations! You now know how to: ✅ Navigate the Admin dashboard and interpret metrics ✅ Create, edit, and delete agents with confidence ✅ Manage agent relationships and build workflows ✅ Upload RAG documents and assign to agents ✅ Configure MCP integrations (org-level and user-level) ✅ Generate and manage API keys securely ✅ Configure organization settings and team permissions ✅ Troubleshoot common issues independently **Need More Help?** Check the [API Reference](/api-reference/authentication) for programmatic management or contact us at [contact@junis.ai](mailto:contact@junis.ai). # Understanding the Agent System Source: https://docs.junis.ai/getting-started/agent-system Master the four agent types and build complex AI workflows ## Agent System Overview The Junis agent system is a set of **four composable agent types** — LLM, Sequential, Parallel, and Loop — that you combine into AI teams without writing code. An orchestrator routes every request to the right agent, and each agent can call tools, MCP integrations, and sub-agents. This guide explains each type, when to use it, and how to combine them. *** ## What are the four agent types? Uses language models to generate responses, make decisions, and execute tasks Executes agents in order, passing results from one to the next Runs multiple agents simultaneously, combining their results Repeats agent execution until a condition is met or max iterations reached *** ## LLM Agent ### What It Does LLM Agents are the **workhorses** of your AI system. They use language models (like Claude, gpt, or Gemini) to: * Generate natural language responses * Analyze and summarize text * Make decisions based on input * Call tools and APIs * Query knowledge bases (RAG) ### When to Use ✅ Answering user questions ✅ Content generation (blogs, emails, summaries) ✅ Data analysis and insights ✅ Decision-making tasks ✅ Any task requiring language understanding ### Configuration Options ```yaml LLM Agent Settings theme={null} name: My Agent agent_type: LLM_AGENT model: anthropic/claude-sonnet-5 # Or gpt, gemini, etc. # Model Parameters temperature: 0.7 # Creativity (0 = deterministic, 1 = creative) max_output_tokens: 16000 # Response length limit max_llm_calls: 3 # Max tool call iterations # Instructions (System Prompt) instruction: | You are a helpful assistant that answers questions about our product. Be concise, friendly, and accurate. # Tools & Integrations tools: - search_database - send_email rag_tools: - rag_product_docs mcp_platforms: - github - firecrawl # Advanced streaming_enabled: true # Real-time response streaming web_search_enabled: false # Enable web search capabilities output_key: null # Extract specific field from response # Extended Thinking (Claude models only) enable_thinking: false # Enable extended thinking mode thinking_budget_tokens: 2048 # Token budget for thinking (min: 1024) reasoning_effort: none # "none", "low", "medium", "high" ``` ### Example: Customer Support Agent ```yaml theme={null} name: Support Agent agent_type: LLM_AGENT model: anthropic/claude-sonnet-5 temperature: 0.5 instruction: | You are a customer support agent for Acme Corp. Responsibilities: 1. Answer product questions using the knowledge base 2. Troubleshoot common issues 3. Escalate complex problems to human agents Always be empathetic and solution-oriented. rag_tools: - rag_product_documentation - rag_faq_database ``` **User**: How do I reset my password? **Support Agent**: I'd be happy to help you reset your password! Here's how: 1. Go to the login page at acme.com/login 2. Click "Forgot Password?" below the login button 3. Enter your registered email address 4. Check your email for a reset link (arrives within 5 minutes) 5. Click the link and create a new password If you don't receive the email, check your spam folder or contact us at [support@acme.com](mailto:support@acme.com). Is there anything else I can help you with? *** ## Sequential Agent ### What It Does Sequential Agents execute a **series of agents in order**, where each agent's output becomes the next agent's input. Think of it as a pipeline or assembly line. ```mermaid theme={null} graph LR A[Input] --> B[Agent 1] B --> C[Agent 2] C --> D[Agent 3] D --> E[Final Output] ``` ### When to Use ✅ Multi-step workflows (research → analyze → report) ✅ Data transformation pipelines ✅ Quality assurance (generate → review → refine) ✅ Complex decision trees ✅ When output of one task informs the next ### Configuration Options ```yaml Sequential Agent Settings theme={null} name: My Sequential Workflow agent_type: SEQUENTIAL_AGENT # Sub-Agents (executed in order) sub_agents: - agent_id: researcher order_index: 1 description: Gathers information from multiple sources - agent_id: analyzer order_index: 2 description: Analyzes and synthesizes research findings - agent_id: writer order_index: 3 description: Writes final report based on analysis # Optional: Define output format output_key: final_report ``` ### Example: Content Creation Pipeline ```yaml theme={null} name: Blog Post Generator agent_type: SEQUENTIAL_AGENT sub_agents: # Step 1: Research - agent: Topic Researcher order: 1 role: Search web and knowledge base for relevant information # Step 2: Outline - agent: Content Outliner order: 2 role: Create structured outline from research # Step 3: Writing - agent: Blog Writer order: 3 role: Write engaging blog post following outline # Step 4: SEO Optimization - agent: SEO Optimizer order: 4 role: Add keywords, meta description, headings ``` **Input**: "Write a blog post about AI safety" **Step 1 (Topic Researcher)**: * Searches web for latest AI safety news * Queries knowledge base for expert opinions * Output: Comprehensive research notes (2000 words) **Step 2 (Content Outliner)**: * Reads research notes from Step 1 * Creates structured outline with sections * Output: 5-section outline with key points **Step 3 (Blog Writer)**: * Follows outline from Step 2 * Writes engaging 1500-word blog post * Output: Draft blog post with intro, body, conclusion **Step 4 (SEO Optimizer)**: * Analyzes draft from Step 3 * Adds SEO metadata and formatting * **Final Output**: Publication-ready blog post Each step only sees the output from the previous step, keeping context focused and efficient. ### Best Practices * Each sub-agent should have ONE clear responsibility * Don't try to do too much in a single step * Break complex tasks into 3-5 discrete steps **Bad Example**: "Research, analyze, and write report" (one agent) **Good Example**: Research Agent → Analysis Agent → Writing Agent (three agents) When you need specific data from a step, use `output_key` to extract it: ```yaml theme={null} - agent: Data Extractor order: 1 output_key: extracted_data # Only pass this field to next step ``` This prevents information overload in later steps. Insert validation agents to ensure quality: ```yaml theme={null} sub_agents: - agent: Content Generator order: 1 - agent: Quality Checker # ← Validates output order: 2 - agent: Formatter order: 3 ``` *** ## Parallel Agent ### What It Does Parallel Agents run **multiple agents simultaneously** and combine their results. Perfect for gathering information from multiple sources or performing independent tasks concurrently. ```mermaid theme={null} graph TD A[Input] --> B[Agent 1] A --> C[Agent 2] A --> D[Agent 3] B --> E[Combined Output] C --> E D --> E ``` ### When to Use ✅ Data collection from multiple sources ✅ Independent analyses that can run concurrently ✅ Speeding up workflows with parallelization ✅ Comparing different approaches ✅ Aggregating diverse perspectives ### Configuration Options ```yaml Parallel Agent Settings theme={null} name: My Parallel Workflow agent_type: PARALLEL_AGENT # Sub-Agents (executed simultaneously) sub_agents: - agent_id: source_a_collector description: Fetches data from Source A - agent_id: source_b_collector description: Fetches data from Source B - agent_id: source_c_collector description: Fetches data from Source C # Results are automatically combined # Order doesn't matter for parallel execution ``` ### Example: Multi-Source Data Aggregator ```yaml theme={null} name: Market Research Aggregator agent_type: PARALLEL_AGENT sub_agents: # All run simultaneously - agent: Competitor Analysis Agent role: Analyze competitor websites and pricing - agent: Social Media Insights Agent role: Gather sentiment from Twitter, Reddit, LinkedIn - agent: Industry News Agent role: Summarize latest industry news and trends - agent: Customer Feedback Agent role: Analyze recent customer reviews and support tickets ``` ``` t=0s: All agents start simultaneously ├─ Competitor Analysis (15s) ├─ Social Media Insights (20s) ├─ Industry News (10s) └─ Customer Feedback (18s) t=20s: All agents complete Results are combined into single report Total Time: 20 seconds (vs 63 seconds if sequential) ``` Parallel execution requires sufficient API rate limits and resources. Monitor usage to avoid throttling. The Parallel Agent automatically combines results: ```json theme={null} { "competitor_analysis": "Our pricing is 15% lower than competitors...", "social_sentiment": "Positive sentiment at 68%, trending topics include...", "industry_news": "New regulations announced today...", "customer_feedback": "Top feature requests: mobile app, API access..." } ``` You can add a **Compiler Agent** after the Parallel Agent to synthesize results into a cohesive narrative. ### Best Practices Parallel agents should NOT depend on each other's results: **Bad Example**: Agent A calculates total, Agent B calculates percentage of total **Good Example**: Agent A analyzes Dataset 1, Agent B analyzes Dataset 2 (independent) If one parallel agent fails, others should still complete: ```yaml theme={null} # Enable fault tolerance parallel_agent: continue_on_error: true # Don't stop if one agent fails timeout: 30 # Max wait time per agent (seconds) ``` After parallel execution, add a Sequential step to combine results: ```mermaid theme={null} graph TD A[Input] --> B[Parallel Agent] B --> C[Agent 1] B --> D[Agent 2] B --> E[Agent 3] C --> F[Compiler Agent] D --> F E --> F F --> G[Unified Output] ``` The Compiler Agent synthesizes parallel results into a coherent response. *** ## Loop Agent ### What It Does Loop Agents **repeat execution** until a condition is met or maximum iterations reached. Useful for iterative refinement, retry logic, and self-correcting workflows. ```mermaid theme={null} graph TD A[Input] --> B[Agent Execution] B --> C{Condition Met?} C -->|No| B C -->|Yes| D[Final Output] C -->|Max Iterations| E[Exit with Error] ``` ### When to Use ✅ Iterative refinement (generate → evaluate → improve) ✅ Retry logic with exponential backoff ✅ Quality assurance loops ✅ Self-correcting systems ✅ Tasks requiring multiple attempts ### Configuration Options ```yaml Loop Agent Settings theme={null} name: My Loop Workflow agent_type: LOOP_AGENT max_llm_calls: 5 # Maximum iterations (prevents infinite loops) # Sub-Agent to Loop sub_agents: - agent_id: iterative_improver description: Improves output each iteration # Exit Conditions (implicit) # - Condition evaluates to true # - Max iterations reached # - Agent returns success signal ``` ### Example: Code Review and Refinement ```yaml theme={null} name: Code Quality Loop agent_type: LOOP_AGENT max_llm_calls: 3 sub_agents: - agent: Code Generator role: Generate Python function based on requirements - agent: Code Reviewer role: | Review code for: - Correctness - Edge cases - Best practices - Security issues If issues found, provide specific feedback for next iteration. If code is acceptable, respond with "APPROVED". ``` **Iteration 1**: * Generator creates initial code * Reviewer finds bug: "Missing null check on input" * Loop continues **Iteration 2**: * Generator adds null check * Reviewer finds issue: "Function doesn't handle empty lists" * Loop continues **Iteration 3**: * Generator handles empty lists * Reviewer responds: "APPROVED - Code meets quality standards" * **Loop exits** with final code Code quality improved through automated iterative review! If max iterations (3) reached without approval: ```json theme={null} { "status": "max_iterations_reached", "iterations": 3, "final_output": "Latest code version", "reviewer_feedback": "Still has issues with error handling", "action": "Escalate to human review" } ``` Always set `max_llm_calls` to prevent infinite loops. Recommended: 3-5 iterations. ### Best Practices The sub-agent should explicitly signal when to exit: ```yaml theme={null} instruction: | Review the output. If quality is acceptable, respond with: "EXIT: Quality standards met" Otherwise, provide specific improvement suggestions. ``` * **2-3 iterations**: Quick refinement tasks * **3-5 iterations**: Moderate complexity * **5-10 iterations**: Complex optimization (rare) Higher values increase cost and latency. Most tasks converge in 2-3 iterations. Pass iteration count to the agent for context: ```yaml theme={null} instruction: | This is iteration {{iteration_count}} of {{max_iterations}}. Previous feedback: {{previous_feedback}} Improve the output based on feedback. ``` Handle max iterations gracefully: ```yaml theme={null} # After loop exits if iterations >= max_llm_calls: escalate_to_human_review() log_warning("Loop did not converge") return best_attempt ``` *** ## Combining Agent Types The real power of Junis comes from **combining different agent types** to build sophisticated workflows. ### Example: Full Blog Post System ```mermaid theme={null} graph TD A[User Request] --> B[Orchestrator] B --> C[Sequential: Content Pipeline] C --> D[Parallel: Research Team] D --> E[Web Researcher] D --> F[Doc Researcher] D --> G[Expert Interview Agent] E --> H[Compiler] F --> H G --> H H --> I[Outline Agent] I --> J[Loop: Writer + Reviewer] J --> K{Approved?} K -->|No| J K -->|Yes| L[SEO Optimizer] L --> M[Final Blog Post] ``` User says: "Write a blog post about quantum computing" Orchestrator recognizes this as a content creation task and routes to **Content Pipeline** (Sequential Agent). Content Pipeline executes three stages: 1. Research (Parallel Agent) 2. Writing (Loop Agent) 3. Optimization (LLM Agent) Research Team runs three agents simultaneously: * **Web Researcher**: Latest quantum computing news * **Doc Researcher**: Academic papers from knowledge base * **Expert Interview Agent**: Queries interview transcripts Results feed into **Compiler Agent** that synthesizes a research brief. **Writer + Reviewer Loop**: * Writer drafts blog post from research * Reviewer checks quality, accuracy, readability * Loop continues until approved (max 3 iterations) SEO Optimizer adds: * Meta description * Keywords * Heading structure * Internal links **Output**: Publication-ready blog post delivered in \~45 seconds. *** ## Which agent type should you use? Not sure which agent type to use? Follow this decision tree: ```mermaid theme={null} graph TD A[Start] --> B{Multiple steps?} B -->|Yes| C{Steps independent?} B -->|No| D{Need iteration?} C -->|Yes| E[Parallel Agent] C -->|No| F[Sequential Agent] D -->|Yes| G[Loop Agent] D -->|No| H[LLM Agent] ``` | Scenario | Agent Type | Reason | | - | - | - | | Answer a question | LLM Agent | Single-step task | | Research → Analyze → Report | Sequential | Multi-step, order matters | | Check 3 databases simultaneously | Parallel | Independent, concurrent | | Generate → Review → Improve | Loop | Iterative refinement | | Route to appropriate handler | Orchestrator | Dynamic dispatching | **Start Simple**: Begin with LLM Agents **Add Complexity As Needed**: 1. **LLM Agent** (Day 1): Get basic functionality working 2. **Sequential Agent** (Week 1): Add multi-step workflows 3. **Parallel Agent** (Week 2): Optimize with parallelization 4. **Loop Agent** (Week 3): Add self-correction and quality loops 80% of use cases can be solved with LLM + Sequential Agents. Add Parallel and Loop only when needed. *** ## Common Patterns **Use Case**: Generate insights from data ```yaml theme={null} Sequential Agent: - Parallel Research: Gather data from multiple sources - LLM Analysis: Identify trends and patterns - LLM Report Writer: Create executive summary ``` **Use Case**: Quality assurance ```yaml theme={null} Loop Agent: - LLM Generator: Create output - LLM Validator: Check against criteria - If invalid: Provide feedback and retry - If valid: Exit loop ``` **Use Case**: Smart routing ```yaml theme={null} Sequential Agent: - LLM Triage: Classify request - Orchestrator: Route to specialist agent - Specialist LLM: Handle request ``` **Use Case**: Evaluate multiple approaches ```yaml theme={null} Parallel Agent: - LLM Approach A: Conservative solution - LLM Approach B: Creative solution - LLM Approach C: Balanced solution Then: - LLM Evaluator: Pick best approach ``` *** ## Performance Considerations * **LLM Agent**: 2-10 seconds * **Sequential Agent**: Sum of sub-agents * **Parallel Agent**: Max of sub-agents * **Loop Agent**: Iterations × agent time * **LLM Agent**: 1 LLM call * **Sequential Agent**: N LLM calls * **Parallel Agent**: N LLM calls (concurrent) * **Loop Agent**: Up to max\_iterations × calls ### Optimization Tips * **Development**: Gemini Flash (fast, cheap) * **Production**: Claude Sonnet (balanced) * **Complex Tasks Only**: Claude Opus / GPT-5.5 Switching from Opus to a faster model like Gemini 3.7 Flash can substantially reduce latency and cut per-token costs by roughly two-thirds. ```yaml theme={null} streaming_enabled: true # Show tokens as they arrive ``` Improves perceived performance even if total time is the same. * **2-3 parallel agents**: Optimal for most cases * **4-5 parallel agents**: Acceptable with monitoring * **6+ parallel agents**: Risk rate limiting and high costs Consider Sequential + Parallel hybrid instead of large Parallel groups. ```yaml theme={null} Loop Agent: max_llm_calls: 3 # Rarely need more than 3 iterations ``` Most loops converge in 2-3 iterations. Higher values rarely improve output quality. *** ## Next Steps Connect agents to APIs and external services Integrate GitHub, Firecrawl, and custom services Let agents query your documents Let one squad's orchestrator ask another squad and relay the answer Give every agent a shared second brain that persists across conversations *** **Questions?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) to discuss agent architectures and get expert guidance. # Introduction to Junis Source: https://docs.junis.ai/getting-started/introduction Build powerful multi-agent AI systems without writing code ## Welcome to Junis Junis is an AI workforce platform where organizations build, orchestrate, and deploy teams of AI agents without writing code. Each team combines 4 agent types (LLM, Sequential, Parallel, Loop) with MCP integrations, 200+ Composio OAuth apps, a shared organization wiki, persistent memory, an embeddable chat widget, and an OpenAI-compatible API. *** ## What is Junis? Junis transforms how you build with AI by providing: Orchestrate multiple AI agents that collaborate to solve complex tasks Build sophisticated AI workflows through an intuitive visual interface Multi-tenant architecture with organization-level isolation and security Connect to external tools, databases, and APIs seamlessly *** ## What are Junis's key features? ### 🤖 Dynamic Agent System Create four types of agents to handle any workflow: | Agent Type | Purpose | Use Case | | - | - | - | | **LLM Agent** | Executes AI tasks using language models | Chat, analysis, content generation | | **Sequential Agent** | Runs agents in order, step by step | Multi-stage workflows (research → analyze → report) | | **Parallel Agent** | Runs multiple agents simultaneously | Data collection from multiple sources | | **Loop Agent** | Repeats agent execution until condition met | Iterative refinement, quality checks | ### 🔧 Powerful Integrations Connect to external services including GitHub and Firecrawl via Model Context Protocol. Add custom MCP servers for your internal tools. Upload documents and create searchable knowledge bases. Your agents can query PDFs, Word docs, spreadsheets, and more. Register Python functions as agent tools. Call APIs, process data, or integrate with any service. Enable real-time web search for agents to access the latest information beyond their training data. ### 🧠 Second Brain & Collaboration A shared second brain every agent reads and writes — say "save this to the wiki" once and your whole AI team knows it One squad's orchestrator asks another squad a question and relays the answer — chain depth 3, loops blocked Put your AI team on any website with a single iframe — visitors chat without logging in OpenAI-compatible API plus user provisioning to build your own product on top of Junis ### 💬 Seamless Chat Experience * **Real-time Streaming**: See agent responses as they're generated * **Multi-turn Conversations**: Maintain context across multiple messages * **File Attachments**: Upload images, documents, and files for analysis * **Voice Input**: Record and transcribe voice messages ### 💳 Flexible Billing Monthly plans with included credits for AI agent usage Pay-per-use credits for flexible consumption tracking Manage your subscription and monitor usage from the **Subscription** page. Credits are automatically deducted based on agent activity. ### 🔐 Enterprise Security Each organization has its own agents, data, and configurations Fine-grained permissions: Super Admin, Admin, Support, Member Generate and manage API keys for external integrations Track all actions and changes for compliance *** ## How does Junis work? ```mermaid theme={null} graph LR A[User Message] --> B[Orchestrator] B --> C{Agent Selection} C --> D[LLM Agent] C --> E[Sequential Agent] C --> F[Parallel Agent] D --> G[Tools & MCP] E --> G F --> G G --> H[Response] H --> A ``` 1. **User sends a message** through the chat interface or API 2. **Orchestrator analyzes** the request and selects the appropriate agent 3. **Agent executes** using LLMs, tools, RAG, or MCP integrations 4. **Response streams** back to the user in real-time 5. **Context persists** across the conversation session *** ## Who Uses Junis? Build customer support chatbots, onboarding assistants, and internal tools without engineering resources. Automate data analysis, generate reports, and query multiple databases simultaneously. Generate blog posts, social media content, and marketing copy with multi-agent orchestrations. Integrate AI capabilities into your apps using OpenAI-compatible APIs and webhooks. *** ## How does Junis compare to other platforms? | Feature | Junis | LangChain | AutoGPT | | - | - | - | - | | **No-Code Interface** | ✅ Visual workflow builder | ❌ Code-based | ❌ Code-based | | **Multi-Agent System** | ✅ 4 agent types | ✅ Limited | ✅ Limited | | **Real-time Streaming** | ✅ WebSocket + SSE | ⚠️ Basic | ❌ Polling | | **Multi-Tenant** | ✅ Organization isolation | ❌ Self-hosted only | ❌ Single user | | **MCP Integration** | ✅ GitHub, Firecrawl + Custom | ❌ Manual setup | ❌ Not supported | | **API Compatibility** | ✅ OpenAI-compatible | ❌ Custom | ❌ Custom | | **Deployment** | ✅ Cloud + On-premise | ⚠️ Self-hosted | ⚠️ Self-hosted | *** ## Quick Start Ready to build your first AI agent? Create your first chatbot in 5 minutes Learn about the four agent types Integrate Junis into your applications *** ## Support & Community Comprehensive guides and tutorials Contact our team for assistance *** **New to AI agents?** Don't worry! Our [Quick Start Guide](/getting-started/quickstart) walks you through creating your first agent step-by-step, with no prior knowledge required. # Quick Start Guide Source: https://docs.junis.ai/getting-started/quickstart Create your first AI agent in 5 minutes ## What will you build? Junis is an AI workforce platform where you create, orchestrate, and deploy teams of AI agents through a web interface — no code required. In this tutorial, you sign in, configure your first LLM agent, connect it to your organization's orchestrator, and test it in a live chat, all in about 5 minutes. In this tutorial, you'll create a **Customer Support Agent** that can: * Answer common questions about your product * Route complex issues to the right team * Maintain context across conversations **Time Required**: 5 minutes **Prerequisites**: Google account (for login) *** ## Step 1: Sign In Navigate to [https://junis.ai](https://junis.ai) and click **"Sign in with Google"** Grant Junis permission to access your Google account. We only use this for authentication. Junis never accesses your Gmail, Drive, or other Google services without explicit permission. You'll be prompted to: * Create or join an AI Team * Set your display name * Choose your role For this tutorial, select **"Create New AI Team"** and name it anything you like (e.g., "Acme Support"). *** ## Step 2: Create Your First Agent After logging in, you'll see the main dashboard. Click **"Team Management"** dropdown in the Navigation Bar, then select **"Agent Orchestration"**. In the Orchestration tab, you'll see a pre-configured **Orchestrator** agent (don't modify this yet). The Orchestrator is the "brain" that routes user messages to the right agents. Every AI Team has one. Click the **"New Agent"** button. *** ## Step 3: Configure Your Agent Fill in the agent creation form: ```yaml Basic Info theme={null} Name: Customer Support Agent Description: Helps customers with common questions and routes complex issues Agent Type: LLM Agent Status: Active ✅ ``` ```yaml Model Settings theme={null} Model: Claude Sonnet 5 (default) Temperature: 0.7 Max Output Tokens: 1000 ``` ```yaml Instructions theme={null} You are a helpful customer support agent for Acme Corp. Your responsibilities: 1. Answer common questions about our product 2. Help troubleshoot basic issues 3. Route complex technical problems to the engineering team 4. Be friendly, professional, and concise Our product: Acme Widgets - cloud-based widget management software Common questions: - Pricing: $29/month for Pro, $99/month for Enterprise - Free trial: 14 days, no credit card required - Support hours: 9 AM - 6 PM EST, Monday-Friday - Contact: support@acme.com or live chat If you cannot help, politely ask the user to contact support@acme.com. ``` * **Agent Type**: LLM Agent uses a language model to generate responses * **Model**: Claude Sonnet 5 is fast and cost-effective for most tasks * **Temperature**: 0.7 balances creativity and consistency (0 = deterministic, 1 = creative) * **Max Output Tokens**: Limits response length to control costs * **Instructions**: The "system prompt" that defines the agent's behavior Click **"Save"** to create your agent. *** ## Step 4: Connect Agent to Orchestrator Go back to the Agents list and click **"Edit"** on the **Orchestrator** agent. Scroll to the **"Sub-Agents"** section and click **"+ Add Sub-Agent"**. Select **"Customer Support Agent"** from the dropdown and set: * **Order**: 1 (execution priority) * **Description**: "Handles customer support questions" In the **Instructions** field, add: ```yaml theme={null} You are the Orchestrator for Acme Corp. Route user messages to the appropriate agent: - Customer support questions → Customer Support Agent - General conversation → Respond directly Always be helpful and professional. ``` Click **"Save"**. *** ## Step 5: Test Your Agent Click **"Chat"** in the sidebar, then click **"+ New Chat"**. Try these example messages: ```text Example 1 theme={null} How much does Acme Widgets cost? ``` ```text Example 2 theme={null} What's your free trial policy? ``` ```text Example 3 theme={null} I'm having trouble logging in to my account ``` Watch the **Agent Pipeline Viewer** at the bottom of the chat: 1. **Orchestrator** receives your message 2. Routes to **Customer Support Agent** 3. Response streams back in real-time The agent should correctly answer pricing and trial questions based on the instructions you provided. *** ## Congratulations! 🎉 You've created your first AI agent! Here's what you learned: ✅ How to create and configure an LLM Agent ✅ How to write effective agent instructions ✅ How to connect agents to the Orchestrator ✅ How to test agents in the chat interface *** ## Next Steps Connect to APIs, databases, and external services Chain multiple agents together Integrate GitHub, Firecrawl, and custom MCP servers *** ## Tips for Better Agents * Be specific about the agent's role and responsibilities * Provide examples of expected inputs and outputs * Include edge cases (e.g., "If you cannot help, say X") * Use bullet points for readability * Begin with basic functionality * Test thoroughly with real user messages * Add complexity (tools, RAG, sub-agents) gradually * Monitor agent performance and refine instructions * **Gemini 3.7 Flash**: Fast, good for simple tasks ($1.95 input / $9.75 output per 1M tokens) * **Claude Sonnet 5**: Balanced speed and quality — the platform default ($3.50 input / $17.30 output per 1M tokens) * **Claude Opus 4.x**: Most capable, best reasoning ($5.75 input / $28.75 output per 1M tokens) * **GPT-5.5**: Strong for structured outputs and function calling ($3.13 input / $18.75 output per 1M tokens) Try these scenarios: * Ambiguous questions * Out-of-scope requests * Multi-turn conversations * Requests in different languages * Adversarial inputs (jailbreaks, prompt injections) *** ## Common Issues **Possible causes:** * Agent is inactive (check Status toggle) * Orchestrator didn't route to your agent (check Agent Pipeline Viewer) * API key missing for the selected model **Solution:** * Ensure agent Status is **Active** (green toggle) * Check Orchestrator instructions include your agent * Verify API keys in Settings > Organization **Possible causes:** * Instructions are too vague * Temperature is too high (> 0.9) * Agent doesn't have enough context **Solution:** * Rewrite instructions with specific examples * Lower temperature to 0.5-0.7 for consistency * Add relevant information to the system prompt **Possible causes:** * Using a slower, heavier model (e.g., Claude Opus, GPT-5.4 Pro) * Agent is calling multiple tools/sub-agents * Network latency **Solution:** * Switch to Gemini Flash or Sonnet for faster responses * Optimize tool calls (reduce number of API requests) * Use streaming for better perceived performance *** ## Need Help? Contact our team directly Integrate with OpenAI-compatible API **Pro Tip**: Explore **Public Agents** in Agent Orchestration > New Agent > Public Agent to discover pre-built templates you can clone and customize for your AI Team. # Credit System Guide Source: https://docs.junis.ai/guides/billing/credits Understanding how credits work in Junis ## Overview Credits are the currency for AI agent usage in Junis. Every AI model interaction consumes credits based on the model's complexity and token usage. **1 Credit ≈ \$1 USD** equivalent in AI model usage. Actual consumption varies by model. *** ## How Credits Work ### Credit Consumption When you send a message or run an agent workflow: 1. **Input Processing**: Credits are calculated based on input tokens 2. **Model Execution**: The AI model processes your request 3. **Output Generation**: Additional credits are consumed for generated content 4. **Credit Deduction**: Total usage is deducted from your balance ### Cost Factors | Factor | Impact | | - | - | | **Model Selection** | Premium models (Claude Opus, GPT-5.5) cost more than efficient models (Gemini Flash) | | **Input Length** | Longer prompts and context consume more credits | | **Output Length** | Longer responses cost more | | **Tool Usage** | External tool calls may add to credit consumption | *** ## Checking Your Balance ### From the Dashboard Your current credit balance is displayed in the top navigation bar after logging in. ### From the Subscription Page For detailed balance information: 1. Go to `/subscription` 2. View your **Credit Balance** card 3. See total credits and recent usage ### From the Usage Page For historical usage data: 1. Go to `/usage` 2. View credit consumption over time 3. See breakdown by agent and model *** ## Credit Sources ### Subscription Credits Monthly subscriptions include credits that refresh each billing cycle: | Plan | Monthly Credits | | - | - | | Basic | 20 credits | | Pro | 195 credits | Subscription credits do not roll over. Unused credits expire at the end of each billing period. ### One-Time Purchases Credits purchased individually: | Package | Credits | | - | - | | Token 30 | 30 credits | | Token 60 | 60 credits | | Token 95 | 95 credits | One-time purchase credits **never expire**. Use them whenever you need extra capacity. *** ## AI Model Pricing Different AI models have different credit costs. Generally: **Gemini Flash** Fast, affordable for simple tasks **Claude Sonnet** Best value for most use cases **Claude Opus** Maximum capability for complex tasks **Pro Tip**: Use Claude Sonnet for most tasks. It offers an excellent balance of quality and cost. *** ## Optimizing Credit Usage ### Best Practices * Use **Gemini Flash** for simple Q\&A and classification * Use **Sonnet** for general tasks and content generation * Reserve **Opus** for complex reasoning and analysis * Be clear and concise in your instructions * Avoid unnecessary context * Use system prompts to set agent behavior once * Design efficient Sequential/Parallel flows * Avoid redundant agent calls * Cache results when possible (via agent instructions) * Check the Usage page weekly * Identify high-consumption agents * Adjust configurations as needed *** ## Low Balance Alerts When your credit balance runs low: 1. **Warning**: Yellow indicator when below 10% of monthly allocation 2. **Critical**: Red indicator when below 5% 3. **Depleted**: Agent executions may be limited Enable email notifications in Settings to receive low balance alerts. *** ## Credit FAQ Agent executions may be paused or limited. You can purchase additional credits at any time or wait for your next subscription renewal. Subscription credits are non-refundable. One-time purchases may be eligible for refund within 7 days if unused. Contact support for assistance. No, credits are tied to the user account. However, your credits work across all organizations you belong to. Credit balance updates in real-time. There may be a brief delay (1-2 seconds) during active agent execution. *** ## Related Guides Manage your subscription plan Learn about agent types and optimization # Subscription & Payment Guide Source: https://docs.junis.ai/guides/billing/subscription Manage your Junis subscription and payment options ## Overview Junis offers flexible subscription plans to power your AI agent workflows. Choose a plan that fits your team's needs and scale as you grow. All payments are processed securely through Lemon Squeezy. Your payment information is never stored on Junis servers. *** ## Subscription Plans **\$20/month** (₩33,300) * 20 AI credits monthly * Access to all AI models * Up to 3 AI teams * Team member management * API development access * Unlimited chat sessions * Basic support **\$195/month** (₩299,000) * 195 AI credits monthly * Access to all AI models * Unlimited AI teams * Advanced team controls * Priority API access * Unlimited everything * Priority support * **10% platform savings** **Pro Plan Value**: The Pro plan offers a 10% discount compared to purchasing credits individually, plus priority support and unlimited teams. *** ## Getting Started ### Step 1: Navigate to Subscription Page From any page in Junis, click your profile icon and select **"Subscription"**, or go directly to `/subscription`. ### Step 2: Choose Your Plan Review the available plans and click **"Subscribe"** on your preferred option. ### Step 3: Complete Payment You'll be redirected to a secure Lemon Squeezy checkout page. Enter your payment details and complete the purchase. **Immediate Access**: Your credits are available immediately after successful payment. *** ## Managing Your Subscription ### View Current Plan The Subscription page displays: * Your current plan (Basic, Pro, or None) * Subscription status (Active, Cancelled, Past Due) * Next renewal date * Available credits ### Cancel Subscription Navigate to `/subscription` or click Subscription in your profile menu. Find the **"Cancel Subscription"** button in your current plan section. Your subscription will remain active until the end of the current billing period. Unused credits do not roll over after cancellation. Use your credits before your plan ends. ### Resume Cancelled Subscription If you've cancelled but your subscription hasn't expired yet, you can resume: 1. Go to the Subscription page 2. Click **"Resume Subscription"** 3. Your subscription will continue with the next billing cycle ### Upgrade or Downgrade To change your plan: 1. Cancel your current subscription 2. Wait for the billing period to end 3. Subscribe to your desired new plan **Coming Soon**: In-app plan switching with prorated billing is planned for a future release. *** ## One-Time Credit Purchases Need more credits without a subscription? Purchase credits individually: | Package | Price | Credits | Best For | | - | - | - | - | | Token 30 | \$30 (₩49,900) | 30 credits | Light usage | | Token 60 | \$60 (₩99,900) | 60 credits | Most popular | | Token 95 | \$95 (₩155,500) | 95 credits | Heavy usage | One-time purchases are perfect for trying Junis before subscribing or for occasional extra capacity. *** ## Billing Cycle * **Monthly Plans**: Billed on the same day each month * **Credits**: Added to your balance immediately upon renewal * **Unused Credits**: Do not roll over to the next month (subscription plans) * **One-Time Purchases**: Credits never expire *** ## Payment Methods Junis accepts: * Credit/Debit Cards (Visa, Mastercard, American Express) * PayPal (where available) * Local payment methods (varies by region) *** ## Subscription Status Reference | Status | Description | | - | - | | `active` | Subscription is current and active | | `on_trial` | Trial period active, not yet charged | | `paused` | Subscription temporarily paused | | `past_due` | Payment failed, action required | | `unpaid` | Multiple payment failures, service may be limited | | `cancelled` | Cancelled but still active until period end | | `expired` | Subscription has ended | *** ## Troubleshooting **Possible causes:** * Insufficient funds * Card expired or declined * Bank security block **Solution:** * Check your payment method is valid * Contact your bank if the issue persists * Try a different payment method **Possible causes:** * Payment processing delay * Page not refreshed **Solution:** * Wait 1-2 minutes and refresh the page * Check your email for payment confirmation * Contact support if credits don't appear within 10 minutes **Possible causes:** * Already cancelled (pending expiration) * Technical issue **Solution:** * Check subscription status shows "Active" * Try logging out and back in * Contact support for assistance *** ## Support Need help with billing? Contact our billing team Learn about credit usage # AI Content Generation Guide Source: https://docs.junis.ai/guides/content/generation Generate images, videos, audio, and 3D models using AI ## Overview Junis integrates with Replicate AI to provide powerful content generation capabilities. Create images, videos, audio, and 3D models through natural language prompts. Content generation consumes credits based on the model used and processing time. Check your credit balance before generating. *** ## Supported Content Types SDXL, Flux, and more AI video generation Music and sound effects GLB format models *** ## Generating Content ### Through Chat Ask your AI agent to generate content using natural language: ``` "Generate an image of a sunset over mountains" "Create a 10-second video of ocean waves" "Make background music for a podcast intro" ``` ### Through Team Content Page 1. Navigate to **Team > Content** 2. View all generated content for your organization 3. Filter by type, status, or date 4. Retry failed generations or delete content *** ## Content Status | Status | Description | | - | - | | `pending` | Waiting to be processed | | `processing` | Generation in progress | | `completed` | Successfully generated | | `failed` | Generation failed (can be retried) | Processing time varies by content type. Images typically complete in 10-30 seconds, while videos may take several minutes. *** ## Managing Content ### Viewing Content From the Team Content page, you can: * **Preview** generated content directly in the browser * **Download** content to your device * **View details** including prompt, model used, and processing time ### Retrying Failed Content If content generation fails: 1. Find the failed item in the content list 2. Click **Retry** 3. The system will attempt to regenerate the content ### Deleting Content To remove content: 1. Select the content item 2. Click **Delete** 3. Confirm the deletion Deleted content cannot be recovered. Storage space is freed immediately. *** ## Technical Details This section is for administrators and developers who need to understand how the `generate_content` tool works internally. ### Tool Parameters The `gen_content` tool accepts the following parameters: | Parameter | Type | Required | Description | | - | - | - | - | | `version` | string | **Yes** | Replicate model identifier (e.g., `stability-ai/sdxl`) | | `input_values` | object | **Yes** | Model-specific input parameters (must include `prompt` for most models) | | `prefer_wait` | boolean | No | Wait for completion inline (default: `false`) | ### Generation Modes **`prefer_wait=false`** - Queue and return immediately The task is queued in the background via Celery workers. This is the default mode when generating content through chat. **Return Structure:** ```json theme={null} { "success": true, "generation_id": "550e8400-e29b-41d4-a716-446655440000", "message": "Content generation started. Results: https://junis.ai/content/550e8400...", "model": "stability-ai/sdxl" } ``` **`prefer_wait=true`** - Wait for completion (polling) The tool polls every 10 seconds until completion, with a maximum timeout of 15 minutes. Useful for integrations that need immediate results. **Return Structure (on success):** ```json theme={null} { "success": true, "generation_id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "output_url": "https://replicate.delivery/...", "storage_url": "https://.blob.core.windows.net//generation/...", "content_type": "image", "processing_time_ms": 12500, "model": "stability-ai/sdxl" } ``` ### Popular Models | Category | Model | Example Use | | - | - | - | | **Image** | `stability-ai/sdxl` | High-quality images | | **Image** | `black-forest-labs/flux-schnell` | Fast image generation | | **Video** | `stability-ai/stable-video-diffusion` | Short video clips | | **Audio** | `meta/musicgen` | Music generation | | **3D** | `abiruyt/text-to-3d` | 3D model generation (GLB) | ### Credit Cost Lookup Credit costs are determined by a priority-based lookup system: 1. **Exact Match**: `stability-ai/sdxl` → matches pattern `stability-ai/sdxl` 2. **Provider Wildcard**: `stability-ai/sdxl` → matches pattern `stability-ai/*` 3. **Global Wildcard**: Any model → matches pattern `*` 4. **Default**: If no pattern matches, defaults to 1 credit Credit costs are managed by administrators in the System Admin panel (ReplicateModelCredit table). *** ## Credit Costs Content generation costs vary by type and complexity: | Content Type | Typical Cost | | - | - | | Image (standard) | 0.01-0.05 credits | | Image (high-res) | 0.05-0.15 credits | | Video (short) | 0.10-0.50 credits | | Audio | 0.05-0.20 credits | | 3D Model | 0.20-0.50 credits | Check your credit balance in the Subscription page before generating large amounts of content. *** ## Best Practices Be specific about what you want: * Include style, mood, and subject details * Specify dimensions or duration if needed * Mention any constraints or requirements Test with smaller/faster generations before creating final content: * Use lower resolution for image tests * Create shorter video clips first * Iterate on prompts before final generation Keep track of content generation: * Check the Content page regularly * Review credit consumption * Clean up unused content *** ## Troubleshooting **Possible causes:** * High system load * Complex content request * Large output size **Solution:** * Wait for processing to complete (timeout: 15 minutes) * Check content status in Team > Content * Retry if the request times out **Possible causes:** * Invalid prompt * Model temporarily unavailable * Insufficient credits **Solution:** * Check error message in content details * Modify your prompt and retry * Verify credit balance **Possible causes:** * Still processing * Page needs refresh **Solution:** * Refresh the page * Check the processing status * Wait a few moments and check again *** ## Related Guides Understand credit consumption Learn about AI agents # Junis Magic Source: https://docs.junis.ai/guides/junis-magic Manage your entire Junis platform from Claude Desktop, Cursor, or any MCP-compatible client ## What is Junis Magic? **Junis Magic** is Junis's official MCP (Model Context Protocol) server that gives AI assistants complete access to manage your Junis platform. Instead of navigating the web dashboard, you can build, configure, and orchestrate AI agent teams directly from tools like **Claude Desktop**, **Cursor**, or any MCP-compatible client. > Build and manage entire AI teams with natural language — no code, no dashboard clicking. **One-Line Setup**: Just add the Junis Magic MCP server URL and your API key to your MCP client configuration. That's it. *** ## Why Junis Magic? Let AI assistants build your agent teams. Describe what you need in natural language, and your assistant creates, configures, and wires everything together. A comprehensive toolset across 16 categories — from creating agents to uploading documents, managing schedules, and configuring OAuth integrations. Manage multiple organizations from a single MCP connection. Switch between teams effortlessly. No API calls to write. No dashboard to navigate. Just tell your AI what you want. *** ## Getting Started ### Step 1: Get Your API Key 1. Log in to [junis.ai](https://junis.ai) 2. Go to **Settings > API Keys** 3. Create a **User API Key** (starts with `jns_user_`) **User API Keys** are recommended — they work across all your organizations with a single key. You can create up to 5 user API keys. ### Step 2: Configure Your MCP Client Add to your Claude Desktop configuration file: **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ```json theme={null} { "mcpServers": { "junis-magic": { "url": "https://mcp.junis.ai/mcp", "transport": "streamable-http", "headers": { "Authorization": "Bearer jns_user_YOUR_API_KEY" } } } } ``` Add to `.cursor/mcp.json` in your project: ```json theme={null} { "mcpServers": { "junis-magic": { "url": "https://mcp.junis.ai/mcp", "transport": "streamable-http", "headers": { "Authorization": "Bearer jns_user_YOUR_API_KEY" } } } } ``` Use these connection details for any MCP-compatible client: | Setting | Value | | - | - | | **URL** | `https://mcp.junis.ai/mcp` | | **Transport** | Streamable HTTP | | **Auth Header** | `Authorization: Bearer jns_user_YOUR_API_KEY` | ### Step 3: Start Building Once connected, ask your AI assistant to manage your Junis platform: ``` "Switch to my organization and show me all my agents." "Create a customer support agent with web search enabled." "Build a research team with a web scraper and a report writer working in sequence." ``` **Build-first**: For clear build requests, your assistant builds immediately with smart defaults and reports afterward — it only asks for confirmation on large builds (10+ agents), destructive changes, or high-cost operations. *** ## Available Tool Categories Junis Magic provides a comprehensive toolset organized in 16 categories: | Category | Key Capabilities | | - | - | | **Organization Management** | List, create, and switch between organizations | | **Agent Management** | Create, update, clone agents (LLM, Sequential, Parallel, Loop) | | **Sub-Agent Relationships** | Wire parent-child agent hierarchies, reorder execution | | **MCP Platform Management** | Connect and configure MCP integrations for agents | | **RAG Knowledge Base** | Create datastores, upload documents, track indexing | | **Sessions** | List conversations, view message history, manage sessions | | **Chat** | Send messages to agents, OpenAI-compatible completions | | **Schedules** | Create and manage automated task schedules | | **Skills** | Import prompt-template skills from GitHub or npm | | **Composio OAuth** | Connect 200+ OAuth integrations (Gmail, Slack, GitHub, etc.) | | **Team Builder** | Batch operations for creating entire agent teams at once | | **Content Management** | Track AI-generated images, videos, and audio | | **Soul & Identity** | Define your organization's AI personality and communication style | | **Memory Management** | View and update user memory profiles | | **RAG Upload** | Upload and manage knowledge base documents | | **Device Management** | Connect and manage local devices for desktop agent control | *** ## Example Workflows ### Build a Complete Agent Team ``` You: "Create a content team with these agents: 1. A research agent that can search the web 2. A writer agent that creates articles 3. A reviewer agent that checks quality Set them up as a sequential pipeline." AI Assistant: 1. Creates 3 LLM agents with appropriate instructions 2. Creates a Sequential parent agent 3. Wires sub-agent relationships in order 4. Links web search capability to the research agent Done! Team ready to use. ``` ### Set Up a Knowledge Base ``` You: "Create a knowledge base called 'Product Docs' and upload the product manual." AI Assistant: 1. Creates a new RAG DataStore 2. Uploads the document 3. Monitors indexing progress 4. Reports when the knowledge base is ready for search ``` ### Configure OAuth Integrations ``` You: "Connect Gmail to my support agent so it can read and send emails." AI Assistant: 1. Lists available Composio integrations 2. Links Gmail integration to the agent 3. Provides the OAuth URL for you to authorize 4. Confirms connection status ``` ### Manage Soul & Identity ``` You: "Set our AI personality to be professional but approachable, with a focus on data-driven recommendations." AI Assistant: 1. Reads current soul and identity configuration 2. Updates soul.md with reasoning principles 3. Updates identity.md with communication style 4. Changes take effect on next conversation ``` *** ## Team Builder: Batch Operations For complex setups, the **Team Builder** tool lets you create entire agent architectures in a single call: ``` You: "Build me an e-commerce support team with: - Product FAQ agent (with RAG knowledge base) - Order tracking agent (with Composio for CRM) - Escalation agent (routes complex issues) All orchestrated through a main support agent." AI Assistant uses apply_team_changes() to: 1. Create all 4 agents simultaneously 2. Configure each with appropriate models and instructions 3. Wire the orchestration hierarchy 4. Link MCP platforms and integrations ``` **Pro Tip**: The Team Builder is the fastest way to set up complex agent architectures. It handles all the wiring automatically. *** ## Supported Models Junis Magic lets you configure agents with various AI models: | Model | Best For | | - | - | | **Claude Sonnet 5** | Default — general purpose, strong tool calling, balanced cost/performance | | **Claude Sonnet 4.6** | Stable Claude alternative | | **Claude Opus 4.8** | Flagship — complex reasoning, advanced analysis | | **Gemini 3.5 Flash / 3.1 Flash Lite** | Cost-effective, fast; Gemini is **required** for RAG agents | | **GPT-5.x** | Alternative general purpose (e.g. GPT-5.5, GPT-5.4) | **RAG Agents**: Must use Gemini models (Vertex AI Search requirement). Other models cannot use RAG tools. Models are validated against the platform's live model registry — if you request an unavailable model, your assistant will suggest the closest supported one. Ask *"what models can I use?"* for the current list. *** ## Best Practices **Recommended**: * Always call `switch_organization` first when starting a session * Use `list_agents` and `list_tools` to understand your current setup before making changes * Use the Team Builder for creating multiple agents at once * Use User API Keys for cross-organization management * Test agents with `send_message` after creation **Important Notes**: * Agent names should use lowercase with hyphens (e.g., `my-agent`) * RAG agents must use Gemini models * Changes take effect on the next conversation with the agent *** ## Related Guides Learn about agent types and orchestration Configure MCP platforms Connect OAuth-based services Programmatic access via REST API *** **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for assistance with Junis Magic setup and configuration. # Knowledge Graph Widget Source: https://docs.junis.ai/guides/knowledge-graph-embed Embed the interactive knowledge graph from your Knowledge base on any website — read-only, domain-restricted, optionally locked with a signed token The knowledge graph widget puts the **ontology graph control tower** from **Team → Knowledge** on any website with one iframe. Visitors explore the entities and relations your organization has published — search, filter by type, and inspect a node's neighbours — without logging in. **Prerequisites**: * **OWNER** or **ADMIN** role in your Junis organization * At least one **ready** source in **Team → Knowledge** (files, URLs, a JSON API, or a database) * A website where you can paste an ` ``` Add `?theme=light` to the `src` for a light theme. The widget shows the same explorer as the admin control tower: a left panel (type and relation filters, focus on one entity's local graph, layout and how many entities to show) and, when a node is clicked, a right panel with that entity's relations. Everything is computed from the data already sent to the browser; summaries, source passages and citations are never sent. Add `?panels=0` to hide the left panel for small placements. On frames narrower than 820px the panel starts collapsed and can be expanded with the › button. Your knowledge graph is live on your website. To verify the domain restriction, open the same snippet from a site that is not on the list — it must show *This site is not allowed to embed the knowledge graph.* Keep `referrerpolicy` as generated. With `no-referrer` the browser hides your page's origin from the widget, the domain check cannot run, and the widget shows "not allowed". *** ## What should my website do? Three settings on the hosting page keep the embed both working and safe: 1. **Allow the frame** — if your page sends a Content Security Policy, include `https://junis.ai` in `frame-src`. 2. **Do not let others wrap your page** — send `Content-Security-Policy: frame-ancestors 'self'` on the hosting page. The widget also verifies the *entire* chain of parent frames, so a page that is itself embedded by a foreign site is refused. 3. **Keep the attributes** — `sandbox="allow-scripts allow-same-origin"` and `allow=""` limit the frame to what the graph needs. *** ## How does the domain check work? The widget reads the origins of **all** its parent frames as reported by the visitor's browser and sends them to Junis. Every one of them must be on your allowed-domains list. In addition: * The browser itself refuses to render the widget on other sites — the embed page is served with `frame-ancestors` set to your allowed domains. * Requests that do not come from the widget's own page (address-bar navigation, scripts on other sites) are refused. * The chat widget and the knowledge graph widget share the same allowed-domains list. The domain check relies on what the visitor's **browser** reports. Anyone who knows your organization slug and an allowed domain can still fetch the published graph with a non-browser client. If that matters for your data, require a signed embed token (next section). *** ## Require a signed embed token (recommended for non-public data) With a signed token, the graph is served **only** to frames that present a token your own server obtained through the API — even a request that claims an allowed domain is refused without it. Call the External API with an API key that has the `brain:read` scope. Never call it from the browser. `brain:read` is **not** part of the default scope set. A key you already use for chat completions will get `403` here — create a key with `brain:read` selected in **Organization Settings → API Keys**, or add the scope to an existing key. ```bash theme={null} curl -X POST https://api.junis.ai/api/external/brain/embed-token \ -H "X-API-Key: $JUNIS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"embed_domain": "https://example.com"}' ``` ```json theme={null} { "token": "eyJhbGciOi...", "expires_in": 300, "embed_domain": "https://example.com", "embed_path": "/embed/your-org-slug/graph", "embed_url": "https://junis.ai/embed/your-org-slug/graph#t=eyJhbGciOi..." } ``` * `embed_domain` must be one of your allowed domains (otherwise `403 embed_domain_not_allowed`). * The graph widget must be switched on (otherwise `409 graph_widget_off`). * Tokens live **5 minutes** by default (`ttl_seconds`, 30–600) and can be exchanged **once**. Use `embed_url` as the iframe `src`. The token sits after `#` — a fragment is never sent to servers, never appears in referrers or logs. The widget removes it from the address as soon as it has read it. Alternatively send it from the hosting page after the frame loads: ```js theme={null} iframe.contentWindow.postMessage({ type: 'junis:embed-token', token }, 'https://junis.ai') ``` Once your server issues tokens, enable **Require a signed embed token** in the graph widget card. From then on, requests without a valid token are refused with `403 token_required`. The graph is now served only through tokens your server issued. Behind the scenes the widget exchanges the token for a 30-minute viewing session and sends that session in a request header — nothing sensitive stays in the URL. Reloading the page must fetch a **new** token; a reused token is refused. *** ## Why does the widget show a message instead of the graph? | Message | Cause | Fix | | - | - | - | | *This site is not allowed to embed the knowledge graph.* | The page origin — or one of its parent frames — is not on the allowed-domains list | Add the exact origin; check for `www`, `http` vs `https`, ports; make sure no other site wraps your page | | *The embedding page hides its address (referrerpolicy)…* | The iframe uses `referrerpolicy="no-referrer"` or the parent page hides its origin | Use the snippet as generated (`strict-origin-when-cross-origin`) | | *This knowledge graph requires a signed embed token…* | **Require a signed embed token** is on and the frame presented no token | Issue a token on your server and put it in the URL fragment | | *The embed token is invalid or has expired.* | Token older than its lifetime, already used, issued for another domain, or tampered | Fetch a fresh token for every page view | | *The knowledge graph is temporarily busy.* | Request limit or a short service hiccup | Retry in a moment | | *Nothing is published yet.* | No source is marked **Public in widget** | Mark at least one ready source | *** ## Next Steps Let visitors chat with your AI team on the same site API keys, scopes, and rate limits for the token endpoint Grow the knowledge your graph is built from Widget settings require OWNER or ADMIN *** **Questions?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for help embedding your knowledge graph. # Build Your Own MCP Server Source: https://docs.junis.ai/guides/mcp/custom Create custom MCP integrations for your proprietary APIs and internal systems ## What You'll Learn This guide shows you how to build your own MCP (Model Context Protocol) server to integrate custom APIs, internal tools, or proprietary systems with Junis. You'll learn: * MCP protocol fundamentals * Building MCP servers in Python and Node.js * Implementing tools and resources * Authentication and security * Deploying and testing your MCP server * Registering with Junis **Prerequisites**: * Familiarity with REST APIs * Python (3.9+) or Node.js (18+) knowledge * Understanding of [MCP Overview](/guides/mcp/overview) * Docker (optional, for deployment) *** ## Why Build a Custom MCP Server? Connect agents to your company's proprietary APIs, microservices, or legacy systems. Implement custom workflows, calculations, or processes specific to your business. Aggregate data from multiple internal sources into a unified interface. Keep sensitive data within your infrastructure without exposing it to third-party MCPs. *** ## MCP Protocol Basics ### MCP Architecture ```mermaid theme={null} graph LR A[Junis Agent] -->|MCP Protocol| B[Your MCP Server] B -->|Internal API| C[Your Backend] B -->|Database Query| D[(Your Database)] B -->|External API| E[Third-Party Service] ``` ### MCP Request/Response Flow **1. Tool Discovery**: ```json theme={null} // Request from Junis { "method": "list_tools", "params": {} } // Response from your MCP server { "tools": [ { "name": "get_customer_data", "description": "Retrieve customer information by ID", "parameters": { "customer_id": {"type": "string", "required": true} } } ] } ``` **2. Tool Execution**: ```json theme={null} // Request from Junis { "method": "call_tool", "params": { "tool_name": "get_customer_data", "arguments": {"customer_id": "CUST-12345"} } } // Response from your MCP server { "result": { "customer_id": "CUST-12345", "name": "Acme Corp", "tier": "Enterprise", "mrr": 5000 } } ``` *** ## Quick Start: Python MCP Server ### Project Setup ```bash theme={null} mkdir my-mcp-server cd my-mcp-server # Create files touch server.py requirements.txt README.md # Project structure my-mcp-server/ ├── server.py ├── requirements.txt ├── tools/ │ ├── __init__.py │ └── customer_tools.py └── README.md ``` **requirements.txt**: ```txt theme={null} fastapi==0.104.1 uvicorn==0.24.0 pydantic==2.5.0 httpx==0.25.1 python-dotenv==1.0.0 ``` Install: ```bash theme={null} pip install -r requirements.txt ``` **server.py**: ```python theme={null} from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional, Dict, Any, List import os app = FastAPI(title="My Custom MCP Server") # Tool definitions TOOLS = [ { "name": "get_customer_data", "description": "Retrieve customer information by ID", "parameters": { "type": "object", "properties": { "customer_id": { "type": "string", "description": "Customer ID (e.g., CUST-12345)" } }, "required": ["customer_id"] } }, { "name": "calculate_discount", "description": "Calculate discount based on customer tier", "parameters": { "type": "object", "properties": { "amount": {"type": "number"}, "tier": {"type": "string", "enum": ["basic", "premium", "enterprise"]} }, "required": ["amount", "tier"] } } ] class MCPRequest(BaseModel): method: str params: Optional[Dict[str, Any]] = {} @app.post("/") async def handle_mcp_request(request: MCPRequest): """Main MCP endpoint""" if request.method == "list_tools": return {"tools": TOOLS} elif request.method == "call_tool": tool_name = request.params.get("tool_name") arguments = request.params.get("arguments", {}) if tool_name == "get_customer_data": return await get_customer_data(arguments["customer_id"]) elif tool_name == "calculate_discount": return await calculate_discount( arguments["amount"], arguments["tier"] ) else: raise HTTPException(status_code=404, detail=f"Tool {tool_name} not found") else: raise HTTPException(status_code=400, detail=f"Unknown method: {request.method}") async def get_customer_data(customer_id: str): """Tool implementation: Get customer data""" # TODO: Replace with your actual API call mock_data = { "CUST-12345": { "customer_id": "CUST-12345", "name": "Acme Corp", "tier": "enterprise", "mrr": 5000, "industry": "Technology" } } if customer_id in mock_data: return {"result": mock_data[customer_id]} else: raise HTTPException(status_code=404, detail="Customer not found") async def calculate_discount(amount: float, tier: str): """Tool implementation: Calculate discount""" discount_rates = { "basic": 0.05, "premium": 0.10, "enterprise": 0.20 } discount = amount * discount_rates.get(tier, 0) final_amount = amount - discount return { "result": { "original_amount": amount, "discount_rate": discount_rates[tier], "discount": discount, "final_amount": final_amount } } @app.get("/health") async def health_check(): """Health check endpoint""" return {"status": "healthy", "tools_count": len(TOOLS)} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8766) ``` ```bash theme={null} python server.py ``` Server will start on: `http://localhost:8766` ```bash theme={null} # List tools curl -X POST http://localhost:8766/ \ -H "Content-Type: application/json" \ -d '{"method": "list_tools", "params": {}}' # Call a tool curl -X POST http://localhost:8766/ \ -H "Content-Type: application/json" \ -d '{ "method": "call_tool", "params": { "tool_name": "get_customer_data", "arguments": {"customer_id": "CUST-12345"} } }' ``` *** ## Quick Start: Node.js MCP Server ### Project Setup ```bash theme={null} mkdir my-mcp-server-js cd my-mcp-server-js npm init -y npm install express body-parser axios dotenv ``` **server.js**: ```javascript theme={null} const express = require('express'); const bodyParser = require('body-parser'); require('dotenv').config(); const app = express(); app.use(bodyParser.json()); // Tool definitions const TOOLS = [ { name: 'get_product_info', description: 'Retrieve product information by SKU', parameters: { type: 'object', properties: { sku: { type: 'string', description: 'Product SKU code' } }, required: ['sku'] } } ]; // Main MCP endpoint app.post('/', async (req, res) => { const { method, params } = req.body; try { if (method === 'list_tools') { res.json({ tools: TOOLS }); } else if (method === 'call_tool') { const { tool_name, arguments: args } = params; if (tool_name === 'get_product_info') { const result = await getProductInfo(args.sku); res.json({ result }); } else { res.status(404).json({ error: `Tool ${tool_name} not found` }); } } else { res.status(400).json({ error: `Unknown method: ${method}` }); } } catch (error) { res.status(500).json({ error: error.message }); } }); // Tool implementation async function getProductInfo(sku) { // TODO: Replace with your actual API call const mockData = { 'SKU-001': { sku: 'SKU-001', name: 'Premium Widget', price: 99.99, stock: 150, category: 'Electronics' } }; if (mockData[sku]) { return mockData[sku]; } else { throw new Error('Product not found'); } } // Health check app.get('/health', (req, res) => { res.json({ status: 'healthy', tools_count: TOOLS.length }); }); const PORT = process.env.PORT || 8766; app.listen(PORT, () => { console.log(`MCP Server running on port ${PORT}`); }); ``` *** ## Adding Authentication ### API Key Authentication ```python theme={null} from fastapi import FastAPI, HTTPException, Header from typing import Optional app = FastAPI() # Simple API key validation VALID_API_KEYS = { "sk-test-123": "TestOrg", "sk-prod-456": "ProdOrg" } async def verify_api_key(authorization: Optional[str] = Header(None)): if not authorization: raise HTTPException(status_code=401, detail="Missing API key") # Extract key from "Bearer " if not authorization.startswith("Bearer "): raise HTTPException(status_code=401, detail="Invalid authorization format") api_key = authorization.replace("Bearer ", "") if api_key not in VALID_API_KEYS: raise HTTPException(status_code=401, detail="Invalid API key") return VALID_API_KEYS[api_key] # Returns organization name @app.post("/") async def handle_mcp_request( request: MCPRequest, organization: str = Depends(verify_api_key) ): # Use 'organization' to scope data access print(f"Request from organization: {organization}") # ... rest of handler ``` ```javascript theme={null} const VALID_API_KEYS = { 'sk-test-123': 'TestOrg', 'sk-prod-456': 'ProdOrg' }; // Authentication middleware function verifyApiKey(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json({ error: 'Missing API key' }); } if (!authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Invalid authorization format' }); } const apiKey = authHeader.replace('Bearer ', ''); if (!VALID_API_KEYS[apiKey]) { return res.status(401).json({ error: 'Invalid API key' }); } req.organization = VALID_API_KEYS[apiKey]; next(); } // Apply middleware to all routes app.use(verifyApiKey); app.post('/', async (req, res) => { console.log(`Request from organization: ${req.organization}`); // ... rest of handler }); ``` ### OAuth 2.0 Authentication For production systems, implement OAuth 2.0: ```python theme={null} from fastapi import Depends, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import jwt security = HTTPBearer() async def verify_oauth_token( credentials: HTTPAuthorizationCredentials = Depends(security) ): token = credentials.credentials try: # Verify JWT token payload = jwt.decode( token, os.getenv("JWT_SECRET"), algorithms=["HS256"] ) return payload # Contains user/org info except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token") ``` *** ## Advanced Tool Implementation ### Streaming Responses For long-running operations, implement streaming: ```python theme={null} from fastapi.responses import StreamingResponse import asyncio async def stream_large_dataset(): """Stream data incrementally""" for chunk in fetch_data_chunks(): yield json.dumps(chunk) + "\n" await asyncio.sleep(0.1) # Simulate processing @app.post("/stream-tool") async def handle_stream_tool(): return StreamingResponse( stream_large_dataset(), media_type="application/x-ndjson" ) ``` ### Error Handling Implement comprehensive error handling: ```python theme={null} from enum import Enum class MCPErrorCode(str, Enum): TOOL_NOT_FOUND = "tool_not_found" INVALID_PARAMETERS = "invalid_parameters" INTERNAL_ERROR = "internal_error" RATE_LIMIT_EXCEEDED = "rate_limit_exceeded" class MCPError(Exception): def __init__(self, code: MCPErrorCode, message: str, details: dict = None): self.code = code self.message = message self.details = details or {} @app.exception_handler(MCPError) async def mcp_error_handler(request, exc: MCPError): return JSONResponse( status_code=400, content={ "error": { "code": exc.code, "message": exc.message, "details": exc.details } } ) ``` ### Parameter Validation Use Pydantic for strict parameter validation: ```python theme={null} from pydantic import BaseModel, Field, validator class GetCustomerParams(BaseModel): customer_id: str = Field(..., regex="^CUST-\d{5}$") @validator('customer_id') def validate_customer_id(cls, v): if not v.startswith("CUST-"): raise ValueError("Customer ID must start with CUST-") return v async def get_customer_data(params: GetCustomerParams): # params.customer_id is validated pass ``` *** ## Deployment ### Docker Deployment **Dockerfile**: ```dockerfile theme={null} FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8766 CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8766"] ``` **Build and Run**: ```bash theme={null} docker build -t my-mcp-server . docker run -d -p 8766:8766 --name mcp-server my-mcp-server ``` ### Production Considerations **✅ Production Checklist**: * [ ] HTTPS/TLS enabled (use reverse proxy like Nginx) * [ ] Rate limiting implemented * [ ] Logging and monitoring configured * [ ] Error tracking (Sentry, Rollbar) * [ ] Health check endpoint exposed * [ ] Secrets managed via environment variables * [ ] API versioning implemented * [ ] Documentation generated (OpenAPI/Swagger) *** ## Registering with Junis ### Step 1: Deploy Your MCP Server Ensure your server is publicly accessible or within your VPN: ``` https://mcp.yourcompany.com/ ``` ### Step 2: Add Platform in Junis **Admin Panel**: 1. Go to **Team > MCP** 2. Click **"Connect"** 3. Fill in details: * **Platform Name**: `Your Company MCP` * **MCP Server URL**: `https://mcp.yourcompany.com/` * **Transport Type**: `Streamable HTTP` * **Auth Type**: `API Key` or `OAuth2` ### Step 3: Add Credentials **Organization-Level**: * Admin > MCP > \[Your Platform] > Add Auth * Enter API key or OAuth credentials **User-Level** (if applicable): * User Settings > MCP Credentials > \[Your Platform] * Complete authentication flow ### Step 4: Enable for Agents 1. Admin > Agents > \[Your Agent] 2. MCP Platforms > Check your custom platform 3. Save ### Step 5: Test Ask your agent: ``` "Use [your tool name] to [perform action]" ``` *** ## Examples & Templates ### Example 1: Internal CRM Integration **Use Case**: Query customer data from internal CRM **Tools**: * `crm_search_customers` - Search by name, email, or company * `crm_get_customer` - Get full customer profile * `crm_get_interactions` - List recent interactions * `crm_create_note` - Add note to customer record **Reference**: See [MCP Server Examples](https://github.com/modelcontextprotocol/servers) for similar implementations ### Example 2: Data Warehouse Query **Use Case**: Run analytics queries on data warehouse **Tools**: * `dw_execute_query` - Run SQL query * `dw_list_tables` - List available tables * `dw_get_schema` - Get table schema * `dw_export_results` - Export results to CSV **Reference**: See [PostgreSQL MCP Implementation](https://github.com/modelcontextprotocol/servers/tree/main/src/postgres) for reference ### Example 3: Workflow Automation **Use Case**: Trigger internal workflows **Tools**: * `workflow_start` - Start workflow with parameters * `workflow_status` - Check workflow status * `workflow_cancel` - Cancel running workflow * `workflow_list_templates` - List available workflow templates **Reference**: Implement based on your internal workflow engine API *** ## Best Practices **✅ DO**: * Document all tools with clear descriptions * Implement comprehensive error handling * Use typed parameters (JSON Schema) * Log all requests for debugging * Implement rate limiting per API key * Version your MCP API (/v1/, /v2/) * Provide health check endpoint * Test with realistic data **❌ DON'T**: * Expose sensitive data without authentication * Return unbounded result sets (always paginate) * Make blocking calls (use async) * Trust input without validation * Log sensitive data (passwords, API keys) * Deploy without monitoring * Skip documentation *** ## What's Next? Deep dive into MCP protocol details See how other platforms implement MCP Connect to GitHub repositories Browse open-source MCP servers *** ## Additional Resources * **MCP Protocol Spec**: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/) * **Example Servers**: [https://github.com/modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) * **FastAPI Documentation**: [https://fastapi.tiangolo.com/](https://fastapi.tiangolo.com/) * **Express.js Documentation**: [https://expressjs.com/](https://expressjs.com/) **Need Help?** Check the [MCP Protocol Specification](https://modelcontextprotocol.io/) and [MCP Server Examples](https://github.com/modelcontextprotocol/servers) for implementation guidance and best practices. # Firecrawl MCP Setup Source: https://docs.junis.ai/guides/mcp/firecrawl Enable web scraping, crawling, and structured data extraction for your agents ## What You'll Learn This guide shows you how to integrate Firecrawl with Junis using the Firecrawl MCP Server. Your agents will be able to: * Scrape single web pages or batch process multiple URLs * Crawl entire websites recursively * Search the web and extract content from results * Extract structured data using JSON schemas * Map website structures and discover all pages **Prerequisites**: * Firecrawl account and API key ([Get one here](https://www.firecrawl.dev/app/api-keys)) * Admin role in Junis (for organization-level setup) * Basic understanding of web scraping concepts *** ## Quick Setup (5 Minutes) 1. Visit [Firecrawl Dashboard](https://www.firecrawl.dev/app/api-keys) 2. Sign up or log in 3. Click **"Create API Key"** 4. Copy your API key (starts with `fc-`) Navigate to **Team > MCP** in Junis and find the Firecrawl card. If Firecrawl is already configured (globe icon 🌍), skip to Step 3. Otherwise, click **"Connect"** and fill in: * **Platform Name**: `Firecrawl` * **MCP Server URL**: `https://api.firecrawl.dev/mcp/` * **Transport Type**: `Streamable HTTP` Click **"Add Auth"** on the Firecrawl card and paste your API key. Click **"Test Connection"** to verify it works. Go to **Admin > Agents**, edit the agent you want to connect, and check **"Firecrawl"** in the MCP Platforms section. Save and test by asking: *"Scrape the homepage of [https://example.com](https://example.com)"* *** ## Available Tools (8 Tools) Firecrawl MCP provides 8 powerful web research tools: ### Content Extraction **Single Page Scraping** Extract content from a single URL in markdown or HTML format. *Example*: "Scrape the content of [https://blog.example.com/post](https://blog.example.com/post)" **Batch Scraping** Scrape multiple URLs simultaneously. *Example*: "Scrape these 5 product pages: URL1, URL2, ..." ### Site Discovery **Website Mapping** Discover all indexed URLs on a website. *Example*: "Map all pages on example.com" **Web Search + Extract** Search the web and extract content from top results. *Example*: "Search for 'AI trends 2026' and summarize top 5 results" ### Advanced Operations **Recursive Crawling** Crawl an entire website recursively. *Example*: "Crawl all product pages on example.com" **Structured Data Extraction** Extract data using JSON schemas for consistent formatting. *Example*: "Extract price, stock, and reviews from this product page" ### Status Monitoring **Batch Status** Check progress of batch scraping jobs. **Crawl Status** Monitor progress of crawling operations. *** ## Common Workflows ### 1. Single Page Analysis **Use Case**: Analyze a blog post or article **Workflow**: ``` 1. User: "Analyze the content of this blog post: [URL]" 2. Agent uses firecrawl_scrape to get the content 3. Agent summarizes and analyzes the text ``` **Agent Instruction**: ``` When asked to analyze a web page: 1. Use firecrawl_scrape to get the content 2. Focus on the main content (set onlyMainContent: true) 3. Provide a concise summary and key insights ``` ### 2. Competitor Research **Use Case**: Analyze multiple competitor websites **Workflow**: ``` 1. User: "Research our top 5 competitors' pricing pages" 2. Agent uses firecrawl_batch_scrape with 5 URLs 3. Agent uses firecrawl_extract to get structured pricing data 4. Agent compares and creates a report ``` **Agent Instruction**: ``` For competitor research: 1. Use batch_scrape for multiple URLs 2. Extract key information like pricing, features, target audience 3. Create a comparison table 4. Highlight our advantages and areas for improvement ``` ### 3. Content Discovery **Use Case**: Find all blog posts on a website **Workflow**: ``` 1. User: "Find all blog posts on example.com" 2. Agent uses firecrawl_map to discover all URLs 3. Agent filters URLs containing "/blog/" 4. Agent uses batch_scrape to get content 5. Agent categorizes and summarizes posts ``` **Agent Instruction**: ``` To discover content on a website: 1. Use firecrawl_map to get all URLs 2. Filter relevant URLs based on patterns 3. Use batch_scrape for multiple pages 4. Organize content by topic or date ``` ### 4. Structured Data Collection **Use Case**: Collect product information from e-commerce sites **Workflow**: ``` 1. User: "Extract product details from these pages" 2. Agent uses firecrawl_extract with JSON schema: { "name": "string", "price": "number", "stock": "boolean", "rating": "number" } 3. Agent returns structured data ``` **Agent Instruction**: ``` For structured data extraction: 1. Define a clear JSON schema for the data 2. Use firecrawl_extract with the schema 3. Validate extracted data for completeness 4. Format results in a table or JSON ``` *** ## Tool Parameters Explained ### firecrawl\_scrape | Parameter | Type | Description | Example | | - | - | - | - | | `url` | string | **Required**. URL to scrape | `https://example.com` | | `formats` | array | Output format(s) | `[{"type": "json", "prompt": "..."}]` (recommended) | | `onlyMainContent` | boolean | Extract only main content | `true` | | `includeTags` | array | HTML tags to include | `["article", "main"]` | | `excludeTags` | array | HTML tags to exclude | `["nav", "footer"]` | ### firecrawl\_batch\_scrape | Parameter | Type | Description | Example | | - | - | - | - | | `urls` | array | **Required**. List of URLs | `["url1", "url2", "url3"]` | | `options` | object | Scraping options | Same as `firecrawl_scrape` | ### firecrawl\_search | Parameter | Type | Description | Example | | - | - | - | - | | `query` | string | **Required**. Search query | `"AI trends 2026"` | | `limit` | number | Max results to return | `5` | | `lang` | string | Language code | `"en"` or `"ko"` | | `country` | string | Country code | `"US"` or `"KR"` | ### firecrawl\_crawl | Parameter | Type | Description | Example | | - | - | - | - | | `url` | string | **Required**. Starting URL | `https://example.com` | | `maxDepth` | number | Max crawl depth | `3` | | `limit` | number | Max pages to crawl | `100` | | `allowExternalLinks` | boolean | Follow external links | `false` | | `includePaths` | array | URL patterns to include | `["/blog/*"]` | | `excludePaths` | array | URL patterns to exclude | `["/admin/*"]` | ### firecrawl\_extract | Parameter | Type | Description | Example | | - | - | - | - | | `urls` | array | **Required**. URLs to extract from | `["url1", "url2"]` | | `prompt` | string | Extraction instructions | `"Extract product details"` | | `schema` | object | JSON Schema for data | `{"name": "string", "price": "number"}` | | `enableWebSearch` | boolean | Use web search | `false` | *** ## Example Use Cases ### Web Research Agent **Agent Prompt**: ``` You are a web research specialist. When users ask you to research a topic: 1. Use firecrawl_search to find relevant sources 2. Use firecrawl_scrape or batch_scrape to get full content 3. Analyze and synthesize information 4. Provide citations with URLs 5. Summarize key findings in bullet points ``` **Connected MCP**: Firecrawl + Notion (for saving research) **Example Interaction**: ``` User: "Research the latest developments in quantum computing" Agent: [Uses firecrawl_search("quantum computing 2026", limit=10)] [Uses firecrawl_batch_scrape on top 5 results] "Here are the latest developments in quantum computing: - IBM announced a 1000+ qubit processor (Source: [URL]) - Google achieved quantum advantage in optimization problems (Source: [URL]) - New error correction methods show promise (Source: [URL]) ..." ``` ### Content Monitoring Agent **Agent Prompt**: ``` You monitor competitor websites and news sources for updates. - Check specific URLs daily - Compare new content with previous versions - Highlight changes and new information - Alert team if significant updates detected ``` **Connected MCP**: Firecrawl + Slack (for notifications) **Example Interaction**: ``` User: "Check if competitor X has updated their pricing page" Agent: [Uses firecrawl_scrape on competitor pricing URL] [Compares with previously stored data] "Changes detected on competitor X pricing page: - Basic plan increased from $49 to $59/month - New Enterprise tier added at $299/month - Free trial extended from 7 to 14 days" ``` ### E-commerce Data Collector **Agent Prompt**: ``` You collect product data from e-commerce websites. - Extract product name, price, availability, ratings - Use structured extraction (firecrawl_extract) - Store data in a consistent format - Track price changes over time ``` **Connected MCP**: Firecrawl + PostgreSQL (for storage) **Example Interaction**: ``` User: "Collect product data from these 20 URLs" Agent: [Uses firecrawl_extract with schema: { "product_name": "string", "price": "number", "currency": "string", "in_stock": "boolean", "rating": "number", "review_count": "number" }] "Extracted data from 20 products: [Displays formatted table with all product information]" ``` *** ## Troubleshooting **Symptom**: Connection fails with authentication error **Cause**: Invalid or expired Firecrawl API key **Solution**: 1. Check your API key at [https://www.firecrawl.dev/app/api-keys](https://www.firecrawl.dev/app/api-keys) 2. Generate a new key if needed 3. Update credentials in Junis (Team > MCP > Firecrawl) **Symptom**: Scraping operations fail after many requests **Cause**: Firecrawl account has run out of credits **Solution**: 1. Check your usage at [https://www.firecrawl.dev/app](https://www.firecrawl.dev/app) 2. Purchase additional credits or upgrade plan 3. Implement rate limiting in your agent logic **Symptom**: firecrawl\_crawl or batch\_scrape timeout **Cause**: Large websites or many URLs **Solution**: * Reduce `maxDepth` or `limit` parameters * Use `includePaths` to filter specific sections * Check status periodically with check\_crawl\_status * Break large jobs into smaller batches **Symptom**: firecrawl\_extract misses some fields **Cause**: JSON schema doesn't match page structure **Solution**: 1. Test with `firecrawl_scrape` first to see raw content 2. Refine your JSON schema based on actual page structure 3. Use more descriptive `prompt` to guide extraction 4. Try extracting fewer fields per request **Symptom**: Agent connected but Firecrawl tools don't appear **Solution**: 1. Verify connection: Team > MCP > Firecrawl > Test Connection 2. Check agent configuration: Admin > Agents > \[Your Agent] > MCP Platforms 3. Restart agent (edit and save without changes) 4. Check logs: Admin > Dashboard > Recent Activity *** ## Performance Tips **✅ Optimization Best Practices**: * Use `formats: [{"type": "json", "prompt": "..."}]` for structured JSON output * End every prompt with "반드시 JSON 형식으로 반환해" for consistent formatting * Filter URLs with `includePaths` and `excludePaths` before crawling * Batch process multiple URLs instead of sequential single scrapes * Cache scraped content to avoid redundant API calls * Set reasonable `limit` and `maxDepth` for crawling operations * Use `firecrawl_map` first to plan your scraping strategy * Maximum **3 concurrent Firecrawl calls** per agent turn to prevent context overflow *** ## Rate Limits & Costs ### Firecrawl Pricing Tiers | Tier | Credits/Month | Best For | | - | - | - | | **Free** | 500 credits | Testing and small projects | | **Starter** | 10,000 credits | Regular scraping tasks | | **Growth** | 50,000 credits | Medium-scale operations | | **Enterprise** | Custom | Large-scale data collection | ### Credit Usage * **Scrape**: 1 credit per page * **Batch Scrape**: 1 credit per page * **Map**: 5 credits per site * **Search**: 2 credits per query * **Crawl**: 1 credit per page discovered * **Extract**: 2 credits per page Monitor your usage at [https://www.firecrawl.dev/app](https://www.firecrawl.dev/app) to avoid unexpected credit depletion. *** ## Advanced Configuration ### Extract Mode Configuration (Recommended) ```json theme={null} { "url": "https://example.com/page", "formats": [ { "type": "json", "prompt": "Describe the data to collect in detail. 반드시 JSON 형식으로 반환해." } ], "onlyMainContent": false, "removeBase64Images": true, "blockAds": true, "waitFor": 3000, "timeout": 60000, "excludeTags": [ "script", "style", "noscript", "nav", "header", "footer", "aside", ".sidebar", ".ad", ".advertisement" ] } ``` ### Crawling Strategy For large websites, use a phased approach: **Phase 1: Discovery** ``` Use firecrawl_map to get all URLs → Filter relevant sections ``` **Phase 2: Selective Crawling** ``` Use firecrawl_crawl with includePaths: ["/products/*", "/blog/*"] ``` **Phase 3: Data Extraction** ``` Use firecrawl_extract on discovered URLs with structured schema ``` *** ## What's Next? Build your own MCP integrations Connect to GitHub repositories Back to MCP Integration overview *** ## Additional Resources * **Firecrawl Dashboard**: [https://www.firecrawl.dev/app](https://www.firecrawl.dev/app) * **API Documentation**: [https://docs.firecrawl.dev/](https://docs.firecrawl.dev/) * **MCP Server GitHub**: [https://github.com/firecrawl/firecrawl-mcp-server](https://github.com/firecrawl/firecrawl-mcp-server) * **MCP Protocol**: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/) **Pro Tip**: Combine Firecrawl with Notion MCP to automatically save research findings, or with Slack MCP to get real-time alerts when monitored websites change. # GitHub MCP Setup Source: https://docs.junis.ai/guides/mcp/github Connect your agents to GitHub repositories, issues, and pull requests ## What You'll Learn This guide shows you how to integrate GitHub with Junis using the official GitHub MCP Server. Your agents will be able to: * Browse and search repositories * Read and create issues * Manage pull requests * View commits and branches * Create and update files **Prerequisites**: * GitHub account with repository access * Admin role in Junis (for organization-level setup) * Basic understanding of [GitHub Personal Access Tokens](https://github.com/settings/tokens) *** ## Quick Setup (5 Minutes) 1. Go to [GitHub Settings → Tokens](https://github.com/settings/tokens) 2. Click **"Generate new token (classic)"** 3. Select scopes based on your needs: * `repo` - Full repository access (recommended) * `read:org` - Read organization info * `read:user` - Read user info 4. Click **"Generate token"** and copy it immediately (shown only once!) Navigate to **Team > MCP** in Junis and find the GitHub card. If GitHub is already configured (globe icon 🌍), skip to Step 3. Otherwise, click **"Connect"** and fill in: * **Platform Name**: `GitHub` * **MCP Server URL**: `https://api.githubcopilot.com/mcp/` * **Transport Type**: `Streamable HTTP` ⚠️ **Important: Must be HTTP, not SSE!** Click **"Add Auth"** on the GitHub card and paste your Personal Access Token. Click **"Test Connection"** to verify it works. Go to **Admin > Agents**, edit the agent you want to connect, and check **"GitHub"** in the MCP Platforms section. Save and test by asking: *"List repositories in the JunisAI organization"* *** ## Authentication Levels GitHub MCP supports all three authentication levels: **Use Case**: Access your personal repositories and private GitHub account **Setup**: User Settings > MCP Credentials > GitHub **Example**: * Your personal projects * Private repos under your account * Creating issues in repos you own **Scopes Needed**: `repo`, `read:user` **Use Case**: Access company/team repositories shared by all team members **Setup**: Admin > MCP > GitHub > Organization Credentials (Admin only) **Example**: * Company repositories * Team projects * Shared organization resources **Scopes Needed**: `repo`, `read:org`, `read:user` **Use Case**: Fallback authentication when no user/org credentials exist **Setup**: Automatically configured by Junis Super Admins **Example**: * Public repositories * Trial/demo purposes **Scopes Needed**: `public_repo` (read-only) **Priority**: User > Organization > System (highest to lowest) *** ## GitHub PAT Scopes Explained ### Minimal Scopes (Read-Only) ``` ✅ repo:status - Repository status ✅ public_repo - Public repositories only ``` **Good for**: Browsing public repos, reading issues (no write access) ### Recommended Scopes (Read + Write) ``` ✅ repo - Full repository access (includes private repos) ✅ read:org - Read organization information ✅ read:user - Read user profile ``` **Good for**: Full agent capabilities (create issues, PRs, commit files) ### Optional Scopes (Advanced) ``` ✅ workflow - Manage GitHub Actions workflows ✅ admin:repo_hook - Manage repository webhooks ✅ delete_repo - Delete repositories (⚠️ Use with caution!) ``` **Good for**: Advanced automation and repository management **Security Best Practice**: Only grant the minimum scopes your agents need. You can always create a new token with more scopes later. *** ## Available GitHub Tools Your agents can use these GitHub MCP tools: ### Repositories List all repositories for a user or organization Get detailed information about a specific repository Search repositories by keyword or criteria Create a new repository ### Issues List issues in a repository Get details of a specific issue Create a new issue Update an existing issue (title, body, labels, assignees) ### Pull Requests List pull requests in a repository Get details of a specific PR Create a new pull request Merge a pull request ### File Operations Read file contents from a repository Create or update a file in a repository Push multiple files in a single commit ### Commits & Branches View commit history Get details of a specific commit List all branches Create a new branch *** ## Example Use Cases ### DevOps Automation Agent **Agent Prompt**: ``` You are a DevOps automation agent. Monitor GitHub repositories for: - New issues and pull requests - Failed CI/CD builds - Pending code reviews When you detect issues, create reports and notify the team. ``` **Connected MCP**: GitHub + Slack **Example Interaction**: ``` User: "Check if there are any failed builds in the main branch" Agent: [Uses github_list_commits + GitHub Actions status] "Found 3 failed builds in the last 24 hours. Creating a report..." ``` ### Code Review Assistant **Agent Prompt**: ``` You are a code review assistant. When given a pull request URL: 1. Fetch the PR details and changed files 2. Analyze code quality and potential issues 3. Leave constructive review comments ``` **Connected MCP**: GitHub only **Example Interaction**: ``` User: "Review PR #123 in the junis-ai repo" Agent: [Uses github_get_pull_request, github_get_file_contents] "Reviewed 5 changed files. Here are my suggestions: - Add error handling in api.py (line 45) - Consider using async/await in database.py ..." ``` ### Issue Triage Agent **Agent Prompt**: ``` You help triage GitHub issues by: - Reading new issues - Analyzing content and assigning labels - Assigning to appropriate team members - Prioritizing based on severity ``` **Connected MCP**: GitHub + Notion (for team member database) **Example Interaction**: ``` User: "Triage all new issues from the last 3 days" Agent: [Uses github_list_issues, github_update_issue] "Triaged 12 new issues: - 5 bugs (assigned to engineering team) - 4 feature requests (added to backlog) - 3 documentation updates (assigned to tech writers)" ``` *** ## Troubleshooting **Symptom**: Connection fails with `405 Method Not Allowed for url 'https://api.githubcopilot.com/mcp/'` **Cause**: Transport type is set to `sse` instead of `streamable-http` **Solution**: 1. Go to Admin > MCP > GitHub 2. Edit the platform configuration 3. Change Transport Type to **"Streamable HTTP"** 4. Save and test connection **Symptom**: Tools load but API calls fail with 401 errors **Cause**: Invalid or expired Personal Access Token **Solution**: 1. Generate a new PAT at [https://github.com/settings/tokens](https://github.com/settings/tokens) 2. Update your MCP credentials in Junis 3. Test connection again **Symptom**: Some operations fail with 403 errors **Cause**: PAT lacks necessary scopes (permissions) **Solution**: 1. Check which operation failed (e.g., creating issues) 2. Generate a new PAT with additional scopes: * Issues: `repo` scope * Private repos: `repo` scope * Organization: `read:org` scope 3. Update credentials and retry **Symptom**: Agent is connected to GitHub but no tools appear **Possible Causes**: * MCP connection failed * Agent cache not refreshed * Transport type misconfigured **Solution**: 1. Check logs: Admin > Dashboard > Recent Activity 2. Look for error messages mentioning "GitHub" or "MCP" 3. Verify connection in Team > MCP > GitHub > Test Connection 4. If all looks good, restart the agent (edit and save without changes) **Symptom**: API calls start failing after many requests **Cause**: GitHub API rate limit reached (5,000 requests/hour for authenticated users) **Solution**: * **Short-term**: Wait 1 hour for rate limit reset * **Long-term**: Implement caching in your agent logic to reduce API calls * **Check status**: Ask agent "What's my GitHub API rate limit status?" *** ## Security Best Practices **✅ DO**: * Use user-level credentials for personal repositories * Use organization-level credentials for shared team resources * Rotate PATs every 90 days * Grant only the minimum required scopes * Store PATs in a password manager **❌ DON'T**: * Share your Personal Access Token with anyone * Commit PATs to code repositories * Use admin-level tokens for routine operations * Grant `delete_repo` scope unless absolutely necessary *** ## Advanced Configuration ### Using GitHub Enterprise Server If your organization uses GitHub Enterprise Server: ```json theme={null} { "mcp_server_url": "https://github.your-company.com/api/mcp/", "transport_type": "streamable-http", "additional_auth_config": { "headers": { "Authorization": "Bearer ghp_your_enterprise_token" } } } ``` ### Rate Limit Monitoring Create an agent that monitors GitHub API rate limits: **Agent Instruction**: ``` When asked about rate limits, use the github_get_rate_limit tool to check remaining API calls and reset time. ``` *** ## What's Next? Web scraping and crawling Build your own MCP integrations Back to MCP Integration overview *** ## Additional Resources * **GitHub MCP Server**: [https://github.com/github/github-mcp-server](https://github.com/github/github-mcp-server) * **GitHub PAT Guide**: [https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) * **GitHub API Docs**: [https://docs.github.com/en/rest](https://docs.github.com/en/rest) * **MCP Protocol**: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/) **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for assistance with GitHub automation workflows and integration support. # MCP Integration Overview Source: https://docs.junis.ai/guides/mcp/overview Connect your AI agents to external services with Model Context Protocol - an open standard for AI integration ## What is MCP? **MCP (Model Context Protocol)** is an open standard protocol that allows AI agents to securely connect to external tools and services. Think of it as a **universal adapter** that lets your agents interact with platforms like GitHub, Firecrawl, and custom services. ### Why Use MCP? One protocol to connect to multiple platforms - no custom code for each service Encrypted credentials with user/organization-level isolation Configure integrations through the Admin UI - zero programming required Agents can call external APIs and get responses in milliseconds *** ## How MCP Works in Junis ```mermaid theme={null} graph LR A[User Message] --> B[Junis Agent] B --> C{MCP Platform} C -->|GitHub| D[GitHub API] C -->|Firecrawl| E[Firecrawl API] C -->|Custom| F[Custom Service] D --> H[Agent Response] E --> H F --> H ``` **Flow**: 1. User sends a message to your agent 2. Agent identifies the need for external data (e.g., "list GitHub issues") 3. Agent calls the appropriate MCP tool (e.g., `github_list_issues`) 4. MCP protocol handles authentication and API communication 5. External service returns data 6. Agent processes the data and responds to the user *** ## Supported Integrations Junis supports a wide range of MCP integrations: Full platform management — build agent teams, manage configurations, and control your organization from any MCP client. [Learn more](/guides/junis-magic) Repository management, issues, PRs, and commits Web scraping and crawling OAuth integrations for Gmail, Slack, Jira, Salesforce, and more via [Composio](/integrations/composio) ### Desktop Agent (npx junis) Connect your local device to the Junis platform with [`npx junis`](/guides/npx-junis). Agents can control files, execute commands, and automate browsers on your machine. ### Custom MCP Servers You can also connect **custom MCP servers** to integrate any service that provides an MCP implementation. See the [Custom MCP guide](/guides/mcp/custom) for details. **Need another platform?** MCP is an open standard - you can integrate any service that provides an MCP server. Check the [MCP Protocol website](https://modelcontextprotocol.io/) for community-contributed servers. *** ## Authentication Priority System Junis supports **three levels of authentication** for MCP platforms, with a clear priority order: ``` 👤 User-Level > 🏢 Organization-Level > 🌍 System-Level ``` ### User-Level (Highest Priority) **Your personal credentials** for platforms like personal GitHub accounts. **Use Case**: Individual authentication (e.g., accessing your personal GitHub repositories) **Setup**: User Settings > MCP Credentials **Example**: * You set your personal GitHub token * When agents query repositories, they use **your** GitHub account * Other team members cannot access your GitHub credentials ### Organization-Level (Medium Priority) **Team-shared credentials** for platforms like company GitHub or Firecrawl accounts. **Use Case**: Shared team resources (e.g., querying company GitHub repositories) **Setup**: Admin > MCP > Organization Credentials (Admin only) **Example**: * Admin sets the company GitHub PAT (Personal Access Token) * All team members can query company repositories * Each user doesn't need their own GitHub token ### System-Level (Lowest Priority) **Global default credentials** provided by Junis for public integrations. **Use Case**: Trial/demo purposes or when no user/org credentials exist **Setup**: Automatically configured by Junis Super Admins **Example**: * Junis provides a default Firecrawl API key for testing * Users can override with their own keys for production use *** ## MCP Types: Remote vs Local MCP servers can be deployed in two ways: ### Remote MCP (HTTP/SSE) **How it works**: Your agent connects to an external MCP server via HTTP or Server-Sent Events (SSE). **Characteristics**: * No package installation required * Server runs independently (cloud-hosted) * Authentication via HTTP headers * Always-on availability **Examples**: * **GitHub MCP**: [https://api.githubcopilot.com/mcp/](https://api.githubcopilot.com/mcp/) * **Firecrawl MCP**: Firecrawl API endpoint * **Custom APIs**: Your company's internal MCP server **Setup Complexity**: ⭐⭐ (Easy - just provide URL and API key) ### Local MCP (Subprocess) **How it works**: A process (NPX, Python script) runs locally on the Junis server for each session. **Characteristics**: * Requires package installation (`requirements.txt`) * Process spawned per agent session * Authentication via environment variables * On-demand execution **Examples**: * **Python-based MCPs**: Custom Python package * **NPX-based MCPs**: `npx @some/mcp-server` * **Custom Scripts**: Your Python/Node.js MCP implementation **Setup Complexity**: ⭐⭐⭐⭐ (Moderate - requires deployment) **Deployment Required**: Local MCPs require adding the package to `requirements.txt` and redeploying the backend. Remote MCPs can be added instantly without deployment. *** ## Quick Start Guide ### Option 1: Connect a Remote MCP (5 minutes) Go to **Team > MCP** in the sidebar Find the platform you want to connect (e.g., GitHub) and click **"Connect"** Provide the required information: * **MCP Server URL**: (e.g., `https://api.githubcopilot.com/mcp/`) * **Transport Type**: SSE or HTTP (check platform docs) * **API Key/Token**: Your authentication credentials Click **"Test"** to verify the connection works Go to **Admin > Agents**, edit your agent, and check the platform in the **"MCP Platforms"** section ### Option 2: Use Pre-Configured Global Platform Many popular platforms are **already configured** at the system level! Go to **Team > MCP** - platforms with a globe icon (🌍) are pre-configured Click **"Add Auth"** and provide your personal or organization credentials The agent can immediately start using the platform! *** ## What's Next? Connect GitHub repos, issues, and PRs Web scraping and crawling Build and integrate your own MCP servers Learn how to configure agents with MCP *** ## Key Concepts to Remember * **MCP Platform**: The service definition (name, URL, authentication method) * **MCP Credentials**: Your actual API keys/tokens for that platform **Example**: GitHub is a platform. Your GitHub Personal Access Token (PAT) is the credential. **Flexibility and Security**: * **User-Level**: Personal accounts (personal GitHub repos) * **Organization-Level**: Shared resources (company GitHub, team Firecrawl) * **System-Level**: Fallback/demo purposes Your user credentials **always override** organization credentials for enhanced privacy. **Server-Sent Events (SSE)**: * One-way streaming from server to client * Better for real-time updates **Streamable HTTP**: * Request-response with streaming support * More reliable for most APIs **Important**: Check the platform's documentation! Using the wrong type causes errors. **Yes!** MCP is an open protocol. You can: 1. Build an MCP server for your internal APIs 2. Host it on your infrastructure 3. Add it to Junis as a Remote MCP 4. Your agents can now access your custom services See the [MCP Protocol Spec](https://modelcontextprotocol.io/docs) for implementation details. *** ## Common Use Cases ### Development Automation Agent **Platforms**: GitHub * Monitor GitHub issues and PRs * Automate code reviews * Manage repository workflows ### Research & Analysis Agent **Platforms**: Firecrawl, Custom MCP * Scrape competitor websites with Firecrawl * Extract and analyze web content * Integrate with custom data processing services ### Content Discovery Agent **Platforms**: Firecrawl, GitHub * Crawl documentation sites * Index technical resources * Track industry trends *** ## Security & Best Practices **✅ DO**: * Use **user-level** credentials for personal accounts (private GitHub) * Use **organization-level** credentials for shared team resources * Rotate API keys regularly (every 90 days recommended) * Test connections before deploying to production * Review MCP platform permissions (grant least privilege) **❌ DON'T**: * Share personal API keys with team members (use org-level instead) * Store credentials in code or logs (Junis encrypts them automatically) * Grant excessive permissions (only enable what agents need) * Use production databases for testing (use test environments) *** ## Troubleshooting Quick Reference | Issue | Possible Cause | Solution | | - | - | - | | 405 Method Not Allowed | Wrong transport type (SSE vs HTTP) | Check platform docs, update transport type | | 401 Unauthorized | Invalid/expired API key | Generate new API key, update credentials | | 403 Forbidden | Insufficient permissions | Check API key scopes/permissions | | Tools not loading | Agent not connected to platform | Edit agent, enable MCP platform | | Connection timeout | MCP server down or unreachable | Test connection, check server status | For detailed troubleshooting guides, see each platform's specific documentation page. *** ## Platform-Specific Guides Full platform management from any MCP client Repository management, issues, and PRs Web scraping and crawling 200+ OAuth app integrations Device control with npx junis Build your own MCP server *** ## FAQs **Unlimited!** An agent can connect to as many MCP platforms as needed. For example, a research agent might use Firecrawl (web scraping) + GitHub (code analysis) + Custom MCP (data storage) simultaneously. **It depends**. MCP is just the connection protocol. You need active accounts/subscriptions for each service: * **Free**: GitHub (public repos) * **Paid**: Firecrawl (API credits), custom services **Yes!** Organization-level credentials are shared across all agents in your organization. The MCP Session Manager optimizes by reusing connections when possible. Agents will fail to connect and return authentication errors. Update the credentials in **Team > MCP** and the connection will resume immediately (no restart needed). **Absolutely!** Scheduled tasks can call agents with MCP integrations. Example: Daily report agent that scrapes data with Firecrawl and posts results to GitHub issues at 9 AM. *** ## Additional Resources * **MCP Protocol Specification**: [https://modelcontextprotocol.io/](https://modelcontextprotocol.io/) * **Community MCP Servers**: [https://github.com/topics/mcp-server](https://github.com/topics/mcp-server) * **Junis API Documentation**: [Authentication & API Keys](/api-reference/authentication) * **GitHub Discussions**: Share your MCP integrations! **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) to ask questions about MCP integration and get expert support. # Memory System Source: https://docs.junis.ai/guides/memory-system Persistent, evolving memory that helps your AI agents remember and learn over time ## Overview Junis provides a **hierarchical memory system** that enables AI agents to remember users across conversations. Unlike traditional chatbots that forget everything after each session, Junis agents build a growing understanding of each user over time. > Your AI agents don't just respond — they remember, learn, and evolve with every conversation. **Automatic**: Memory collection and consolidation happen automatically. No manual setup required — just start chatting. *** ## How It Works Key information from every conversation is captured and stored automatically. Daily memories are consolidated into monthly, quarterly, and yearly summaries by AI. Agents use stored memories to provide contextual, personalized interactions for each user. Users can view and update their own profile. Memory is scoped per-user with full privacy. *** ## Memory Architecture Junis organizes memory into a **hierarchical tier system**, inspired by how human memory works — from detailed daily notes to high-level life narratives: ### Memory Tiers | Tier | Description | Updated | | - | - | - | | **User Profile** | Name, preferences, communication style, expertise | Anytime (by agent or user) | | **Eternal Memory** | Core truths and permanent preferences | Anytime (by agent) | | **Daily Memory** | Conversation summaries per day | After each conversation | | **Monthly Summary** | Consolidated patterns and themes | Automatically, 1st of each month | | **Quarterly Summary** | Growth trajectories and milestones | Automatically, every quarter | | **Yearly Narrative** | Autobiographical narrative of the year | Automatically, January 1st | ### Consolidation Flow ```mermaid theme={null} graph LR Conv[Conversations] -->|After each chat| Daily[Daily Memory] Daily -->|Monthly AI review| Monthly[Monthly Summary] Monthly -->|Quarterly AI review| Quarterly[Quarterly Summary] Quarterly -->|Yearly AI review| Yearly[Yearly Narrative] Conv -->|Key insights extracted| Eternal[Eternal Memory] ``` **AI-Powered Consolidation**: An AI model reviews and consolidates memories at each tier, preserving important patterns while letting trivial details naturally fade — mimicking how human memory works. *** ## What Gets Remembered ### Automatic Memory (No Action Needed) The system automatically captures: * **Conversation summaries** from each session * **User preferences** mentioned naturally in conversation * **Key decisions** and outcomes * **Recurring topics** and interests ### Explicit Memory Commands Users can explicitly ask agents to remember things: ``` "Remember that I prefer concise responses." "Don't forget that our quarterly reviews are on Fridays." "Always format reports with bullet points for me." ``` Agents store these as **Eternal Memory** — persistent facts that are always available. ### User Profile Updates When agents learn about users, they automatically update the profile: ``` "My name is Sarah and I'm a marketing director." → Agent updates User Profile with name and role "I prefer Korean for internal docs and English for client-facing work." → Agent updates communication preferences ``` *** ## Memory in Action ### How Agents Use Memory Every time an agent starts a conversation, it automatically loads the user's memory context: 1. **User Profile** — Who the user is 2. **Eternal Memory** — Permanent preferences and instructions 3. **Recent Daily Memories** — What happened in recent conversations 4. **Latest Summaries** — Monthly/quarterly/yearly patterns This context is automatically injected into the agent's awareness, so it can reference past interactions naturally. ### Example Interaction ``` Day 1: User: "I'm working on the Q4 marketing campaign for product launch." Agent: [Stores in daily memory] Day 5: User: "How's the project going?" Agent: "Based on our conversation earlier this week about the Q4 marketing campaign for the product launch, here's where we can pick up..." → Agent references past context without being asked ``` ### Cross-Session Continuity ``` Week 1: User discusses project goals and timelines Week 2: User mentions team members and responsibilities Week 3: User provides budget details Monthly Summary (auto-generated): "User is managing a Q4 product launch campaign with a team of 5. Key priorities: timeline adherence, budget of $50K, and cross-department coordination." ``` *** ## Managing Memory ### View Your Memory You can ask any agent to show your stored memories: ``` "Show me my profile." → Agent displays your current User Profile "What do you remember about me?" → Agent reads and summarizes your memory context ``` ### Update Your Profile Ask an agent to update your information: ``` "Update my profile: I've moved to the product team." → Agent updates your role "Remove the note about my old email address." → Agent reads eternal memory, removes the entry, saves ``` ### Memory via API & MCP Access memory programmatically through the [Junis Magic](/guides/junis-magic) MCP tools: | Tool | Purpose | | - | - | | `get_memory_directory` | List all memory files for a user | | `get_memory_content` | Read specific memory content | | `update_memory_content` | Update the user profile | *** ## Memory Tools for Agents Junis automatically equips all orchestrator agents with two memory tools: ### `update_user_memory` Read or update the user's profile. **When agents use this tool**: * User shares their name, role, or job title * Communication preferences are revealed * Professional expertise becomes apparent * User explicitly asks to see their profile **Actions**: `read` (view profile), `update` (save changes) ### `save_to_eternal_memory` Store permanent facts and preferences. **When agents use this tool**: * User says "remember this" or "don't forget" * User wants a standing instruction ("always do it this way") * User wants to edit or delete existing memories **Actions**: `read` (view), `add` (append), `update` (replace) **Automatic**: These tools are available to all orchestrator agents by default. No configuration needed. *** ## Privacy & Security Memory is strictly scoped to individual users. No user can access another user's memories, even within the same organization. Memory is completely isolated between organizations. Switching organizations means working with a separate memory context. Users can view, update, and manage their own memory at any time. Agents are designed to never store sensitive information like passwords, financial data, or API keys in memory. Memory consolidation (monthly/quarterly/yearly) is processed only for users with active subscriptions, ensuring fair resource usage. *** ## Related Guides Define your AI's personality (organization-level) Learn about agent types and orchestration Manage memory via MCP tools Understand usage costs *** **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for questions about the memory system. # Junis Desktop Agent Source: https://docs.junis.ai/guides/npx-junis Give your AI agents full control over your device with a single command ## What is Junis Desktop Agent? **Junis Desktop Agent** (`npx junis`) is a CLI tool that connects your local device to the Junis platform, enabling AI agents to control your computer. File management, terminal commands, browser automation, and more — all accessible to your AI agents through a secure connection. > One command. One login click. Full device control for your AI agents. ```bash theme={null} npx junis ``` **Zero Configuration**: Run the command, click login, and your device is connected. AI agents can immediately start working on your machine. *** ## Why Use Junis Desktop Agent? AI agents can read/write files, execute commands, automate browsers, and interact with your OS — all through your Junis agents. No complex configuration. Run `npx junis`, authenticate once, and you're connected. OAuth-based authentication with encrypted WebSocket relay. Your device connects outbound — no open ports required. Use **Cloud Mode** for remote AI access or **Local Mode** for direct integration with Claude Desktop. *** ## Getting Started ### Prerequisites * **Node.js 18+** installed on your machine * A **Junis account** (sign up at [junis.ai](https://junis.ai)) ### Quick Start Open your terminal and run: ```bash theme={null} npx junis ``` A browser window opens automatically. If you're already logged in to Junis, simply click **"Approve"** to authorize the device. The CLI confirms the connection. Your device is now accessible to your Junis agents. ``` Connected: My MacBook Pro WebSocket relay active Listening for agent commands... ``` *** ## Available Tools Once connected, your AI agents gain access to powerful device tools: ### File & Shell Tools | Tool | Description | | - | - | | `execute_command` | Run terminal commands on the device | | `read_file` | Read files (supports multiple formats) | | `write_file` | Create or overwrite files | | `list_directory` | Browse directory contents | | `search_code` | Search through codebases | | `list_processes` / `kill_process` | Process management | ### Browser Automation Tools | Tool | Description | | - | - | | `browser_navigate` | Navigate to any URL | | `browser_click` | Click elements on web pages | | `browser_type` | Type text into form fields | | `browser_screenshot` | Capture screenshots | | `browser_snapshot` | Get page accessibility tree | | `browser_evaluate` | Execute JavaScript in the browser | *** ## Operating Modes ### Cloud Relay Mode ```bash theme={null} npx junis ``` Your device connects to the Junis cloud via WebSocket. AI agents on the Junis platform can send commands to your device remotely. **Best for**: Remote AI agent workflows, team collaboration, automated pipelines **Features**: * Persistent connection with auto-reconnect * 30-second heartbeat for reliability * Exponential backoff on disconnection * Device appears in your Junis dashboard ### Local-Only Mode **Coming Soon**: Local Mode is currently in development. Cloud Mode is fully available today. ```bash theme={null} npx junis --local ``` Runs a local MCP server without cloud connectivity. Perfect for direct integration with Claude Desktop or other local MCP clients. **Best for**: Privacy-focused use, offline work, direct Claude Desktop integration **Features**: * No cloud connection required * No Junis account needed * Local MCP server on your machine * Full tool access without relay *** ## CLI Commands | Command | Description | | - | - | | `npx junis` | Start in cloud mode (default) | | `npx junis --local` | Start in local-only mode | | `npx junis --port 3000` | Specify a custom port | | `npx junis --reset` | Reset authentication | | `npx junis status` | Check connection status | | `npx junis logout` | Log out and clear credentials | | `npx junis --help` | Display help | *** ## Linking Devices to Agents After your device is connected, link it to specific agents: Go to **Admin > Agents** and edit the agent you want to give device access. In the **Devices** section, select your connected device from the list. Chat with the agent and ask it to perform a device action, like listing files in your home directory. You can also link devices programmatically via [Junis Magic](/guides/junis-magic) MCP tools: `list_devices` and `link_agent_device`. *** ## Cross-Platform Support | Platform | Status | | - | - | | **macOS** | Fully supported | | **Windows** | Fully supported | | **Linux** | Fully supported | *** ## Security * **OAuth Device Flow**: Industry-standard authentication * Tokens stored locally in `~/.junis/config.json` * Tokens can be revoked anytime via `npx junis logout` * **Outbound-only connection**: Your device connects out — no incoming ports needed * **Encrypted WebSocket**: All communication is encrypted via WSS * **Per-request authorization**: Each agent command is verified against your permissions * Only agents explicitly linked to your device can send commands * You control which agents have access via the dashboard * Disconnect anytime by stopping the CLI *** ## Use Cases ### Development Automation ``` You: "Run the test suite and fix any failing tests." Agent (via your device): 1. Executes `npm test` on your machine 2. Reads test output and identifies failures 3. Opens and reads relevant source files 4. Applies fixes and reruns tests ``` ### Browser-Based Research ``` You: "Go to competitor.com, take screenshots of their pricing page, and compile a comparison report." Agent (via your device): 1. Opens browser and navigates to the URL 2. Captures screenshots 3. Extracts pricing information 4. Generates a comparison document ``` ### File Organization ``` You: "Organize the downloads folder — sort files by type into subfolders." Agent (via your device): 1. Lists all files in ~/Downloads 2. Categorizes by file type 3. Creates organized subdirectories 4. Moves files into appropriate folders ``` *** ## Related Guides Manage devices via MCP tools Learn about agent types Connect external services All available tools *** **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for assistance with device setup and configuration. # Agent Orchestration Overview Source: https://docs.junis.ai/guides/orchestration/overview Build complex multi-agent orchestrations with prompt-based configuration - no code required # Agent Orchestration Overview ## 🎯 What is Agent Orchestration? **Agent Orchestration** is how Junis coordinates multiple AI agents into one working team: an orchestrator receives every request, routes it to the right specialized agent or workflow (Sequential, Parallel, or Loop), and streams the result back. In Junis, orchestration is **entirely prompt-based** — you configure agent behavior through system prompts in the Admin UI, without writing any code. Orchestration also reaches **beyond a single team**: with [Squad Collaboration](/guides/orchestration/squad-collaboration), one squad's orchestrator can ask another squad a question mid-conversation and relay the answer back. *** ## 🏗️ Architecture ``` ┌─────────────────────────────────────────────┐ │ User Request │ └──────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Orchestrator Agent │ │ (Analyzes & routes to appropriate agent) │ └──┬──────────┬──────────┬────────────────────┘ │ │ │ ▼ ▼ ▼ ┌─────┐ ┌─────┐ ┌─────────┐ │Agent│ │Agent│ │Workflow │ │ A │ │ B │ │ (Multi) │ └─────┘ └─────┘ └────┬────┘ │ ┌──────┴──────┐ ▼ ▼ ┌─────┐ ┌─────┐ │Agent│ │Agent│ │ C │ │ D │ └─────┘ └─────┘ ``` *** ## 🧩 Key Components ### 1. Orchestrator **Role**: Top-level router that receives user requests and delegates to appropriate agents. **Configuration**: System prompt with routing rules. **Example**: * User: "Send an email to John" * Orchestrator: Routes to `EmailAgent` ### 2. Sub-Agents **Role**: Specialized agents that handle specific tasks. **Types**: * **LLM Agent**: Single AI task execution * **Sequential Agent**: Multi-step workflow (A → B → C) * **Parallel Agent**: Concurrent execution (A + B + C simultaneously) * **Loop Agent**: Iterative refinement **Example**: * `EmailAgent`: Handles email composition and sending * `DataAgent`: Handles data retrieval and analysis ### 3. Tools & MCP **Role**: External capabilities agents can use. **Tools**: Python functions (e.g., `search_database`, `generate_pdf`) **MCP**: External platform integrations (e.g., GitHub, Firecrawl) **Example**: * Agent calls `search_github_repo` tool → Code retrieved via GitHub MCP ### 4. RAG Knowledge Base **Role**: Domain-specific knowledge agents can query. **Configuration**: Upload documents → Create DataStore → Link to agent **Example**: * Agent queries company policies → RAG returns relevant documents *** ## 🔄 Workflow Types ### Sequential Workflow **Pattern**: A → B → C (one after another) **Use Case**: Multi-step processes where each step depends on the previous. **Example**: Brand Factbook Generation ``` User Request → DataCollector (gather data) → Analyzer (process data) → ReportGenerator (create PDF) ``` ### Parallel Workflow **Pattern**: A + B + C (simultaneously) **Use Case**: Independent tasks that can run concurrently. **Example**: Multi-Source Data Collection ``` User Request ├── DataCollector (get DataCollector data) ├── CRCollector (get CR data) └── SalesforceCollector (get CRM data) → Aggregator (combine results) ``` ### Loop Workflow **Pattern**: Repeat until condition met **Use Case**: Iterative refinement or retry logic. **Example**: Content Improvement ``` User Request → DraftWriter (create v1) → Reviewer (check quality) → IF quality < threshold: DraftWriter (improve) → Reviewer → ELSE: Return final draft ``` ### Hybrid Workflow **Pattern**: Combination of above **Example**: Complex Business Process ``` Orchestrator → VerificationAgent (check inputs) → DataWorkflow (Sequential) ├── Collector1 (Parallel) ├── Collector2 (Parallel) └── Collector3 (Parallel) → Aggregator (combine) → AnalysisAgent (process) → ReportAgent (generate output) ``` *** ## ✨ Core Capabilities ### A. Agent Routing **What**: Direct requests to specific agents based on keywords. **How**: Mention agent names in Orchestrator's system prompt. **Example**: ```markdown theme={null} 이메일 요청 → EmailAgent 전송 데이터 분석 요청 → DataAgent 전송 ``` ### B. Tool & MCP Calls **What**: Agents call external functions and APIs. **How**: List tools/MCP in agent config, mention in prompt. **Example**: ```markdown theme={null} 사용 가능한 도구: - send_email: 이메일 발송 - search_crm: CRM 검색 ``` ### C. RAG Queries **What**: Agents retrieve information from uploaded documents. **How**: Upload docs → Link DataStore → Mention knowledge domains in prompt. **Example**: ```markdown theme={null} 회사 정책 문서에서 휴가 규정 검색 ``` ### D. Data Passing **What**: Pass structured data between agents in workflows. **How**: Set `output_key` in parent, `include_contents` in child. **Example**: ``` DataCollector (outputs: collected_data) → Analyzer (receives: collected_data) ``` ### E. Flexible Patterns **What**: Conditional logic and dynamic behavior. **How**: Write if/else logic in system prompts. **Example**: ```markdown theme={null} IF 긴급 요청: → UrgentAgent ELSE: → NormalAgent ``` *** ## 🛠️ Configuration Methods ### 1. Admin UI (Recommended) **Easiest way** to configure agents - no code required. **Steps**: 1. **Agent Management** → Create agents 2. **Edit Agent** → Set system prompt, tools, MCP, RAG 3. **Relationships** → Link agents together 4. **Test** → Send requests and monitor ### 2. API (Programmatic) Use Junis API for automation or bulk operations. **Endpoints**: * `POST /api/admin/agents` - Create agent * `PUT /api/admin/agents/{id}` - Update agent * `POST /api/admin/agents/{id}/relationships` - Link agents **Documentation**: See [API Reference](/api-reference/authentication) *** ## 📊 Monitoring & Optimization ### Usage Tracking **View in Admin UI**: * **Usage** → Agent usage statistics * Session logs and conversation history * Agent routing flow visualization ### Performance Optimization **Best Practices**: 1. **Cache frequently-used agents** (automatic in Junis) 2. **Use Parallel workflows** for independent tasks 3. **Optimize prompts** for clarity and conciseness 4. **Monitor token usage** and adjust as needed *** ## 🚀 Quick Start ### 1. Simple Routing Example **Goal**: Create Orchestrator that routes to 2 agents. **Steps**: 1. Create 3 agents: * `Orchestrator` (LLM\_AGENT) * `EmailAgent` (LLM\_AGENT) * `DataAgent` (LLM\_AGENT) 2. Set Orchestrator prompt: ```markdown theme={null} 이메일 요청 → EmailAgent 전송 데이터 요청 → DataAgent 전송 ``` 3. Link agents: Orchestrator → EmailAgent, DataAgent 4. Test: "Send email" → Routes to EmailAgent ### 2. Multi-Step Workflow **Goal**: Create Sequential workflow (A → B). **Steps**: 1. Create 3 agents: * `Orchestrator` (LLM\_AGENT) * `DataWorkflow` (SEQUENTIAL\_AGENT) * `CollectorAgent` (LLM\_AGENT) * `AnalyzerAgent` (LLM\_AGENT) 2. Link: DataWorkflow → CollectorAgent (order 1), AnalyzerAgent (order 2) 3. Link: Orchestrator → DataWorkflow 4. Test: "Analyze data" → CollectorAgent → AnalyzerAgent ### 3. Tool Integration **Goal**: Agent that calls a tool. **Steps**: 1. Create `SearchAgent` (LLM\_AGENT) 2. Add tool in config: `["search_database"]` 3. Set prompt: ```markdown theme={null} 도구: search_database 사용자 검색 요청 시 search_database 호출 ``` 4. Test: "Search for X" → Calls tool *** ## 📖 Next Steps **Start Here**: Learn to configure agents with prompts Understand agent types and architecture Create custom tools for agents Connect external platforms (GitHub, Firecrawl, etc.) Let one squad's orchestrator ask another squad and relay the answer *** ## ❓ FAQ **No!** Junis orchestration is entirely prompt-based. Configure agents through the Admin UI with system prompts. Code is only needed for custom tools or advanced integrations. No hard limit. However, we recommend: * **Orchestrator**: 1 per organization * **Sub-agents**: 5-20 for most use cases * **Nested workflows**: Max 3-4 levels deep **Yes!** Use **output keys** and **include contents** to pass data between agents in Sequential workflows. See [Prompt Engineering Guide](/guides/orchestration/prompt-engineering#d-output-format--key-passing). * **Automatic retry**: Junis retries failed tool/MCP calls once * **Error handling**: Agents receive error messages and can handle gracefully * **Monitoring**: View errors in Usage logs * **Recovery**: Update agent prompts to handle edge cases 1. **Edit agent** → Save (cache auto-refreshes) 2. **Send test message** in Chat UI 3. **Check Usage logs** for routing and tool calls 4. **Iterate** on prompts based on results *** ## 🎓 Learning Path **Beginner**: 1. Read this overview 2. Follow [Quick Start](#quick-start) examples 3. Try [Prompt Engineering Guide](/guides/orchestration/prompt-engineering) **Intermediate**: 1. Build Sequential and Parallel workflows 2. Integrate tools and MCP 3. Use RAG for knowledge retrieval **Advanced**: 1. Create complex hybrid workflows 2. Optimize performance with caching 3. Build custom tools and MCP integrations *** **Ready to build?** Start with the [Prompt Engineering Guide](/guides/orchestration/prompt-engineering) → 🚀 # Prompt Engineering for Agent Orchestration Source: https://docs.junis.ai/guides/orchestration/prompt-engineering Master agent routing, tool calls, MCP, RAG, and data passing through system prompts - Production-tested patterns # Prompt Engineering for Agent Orchestration ## 🎯 Overview Junis uses **prompt-based orchestration** where you control agent behavior entirely through system prompts (instruction templates). No code changes needed - just configure your prompts in the Admin UI. This guide provides **production-tested patterns** that work directly in your Junis system. *** ## 🧠 Core Concepts ### How Junis Routing Works ``` User Message → Orchestrator (analyzes) → Routes to Sub-Agent → Returns Response ``` **Key Point**: Orchestrator decides routing based on **keywords in its system prompt**. ### Where to Configure 1. **Admin UI** → **Agent Management** → Select Agent 2. **Edit Agent** → **Instruction Template** field 3. Paste your prompt → Save 4. **Done!** Agent automatically reloads *** ## 🔀 A. Sub-Agent Routing ### How It Works **Mention sub-agent names in your Orchestrator's instruction template**, and the LLM will route requests to them based on keywords. ### ✅ Working Example (From Production) This is the **actual default Orchestrator prompt** in Junis: ```markdown theme={null} 당신은 Junis의 메인 오케스트레이터입니다. 사용자 요청을 분석하고 적절하게 응답하세요: 1. 이메일 관련 요청 → EmailWorkflow 에이전트로 전송 - "이메일 작성해줘", "메일 답장", "이메일 템플릿" 등 - 예: "이메일 작성을 도와드리겠습니다. EmailWorkflow 에이전트로 연결합니다." 2. 일반 대화 → 직접 응답 - 인사, 일상 대화, 간단한 질문 등 - 친근하고 전문적인 톤으로 직접 응답하세요. ``` ### 📋 Pattern Breakdown **Structure**: ``` [Condition Description] → [Sub-Agent Name] 에이전트로 전송 - [Keyword 1], [Keyword 2], ... - 예: [Example response] ``` **Why It Works**: * ✅ **Explicit agent names** (EmailWorkflow, etc.) * ✅ **Clear keywords** for pattern matching * ✅ **Examples** guide the LLM's response style ### 🛠️ How to Set Up #### Step 1: Create Sub-Agents 1. **Admin UI** → **Agent Management** → **Create Agent** 2. Create your sub-agents: * `EmailWorkflow` (LLM\_AGENT) * `DataWorkflow` (SEQUENTIAL\_AGENT) #### Step 2: Link to Orchestrator 1. Select your Orchestrator agent 2. **Relationships** tab → **Add Relationship** 3. Add each sub-agent with order index (1, 2, 3...) #### Step 3: Configure Orchestrator Prompt 1. Edit Orchestrator → **Instruction Template** 2. Paste routing logic (see example above) 3. **Save** → Cache automatically refreshes ### ✨ Advanced Routing Patterns #### Multi-Language Routing ```markdown theme={null} You are a multilingual orchestrator. Route based on request type: 1. Code requests → CodeAssistant agent - "write code", "debug", "refactor", "implement" - Example: "I'll connect you to CodeAssistant for coding help." 2. Data analysis → DataAnalyst agent - "analyze data", "create chart", "statistics", "trend" - Example: "Routing to DataAnalyst for data insights." 3. General chat → Respond directly - Greetings, small talk, questions - Friendly and professional tone. ``` #### Priority-Based Routing ```markdown theme={null} 우선순위 기반 라우팅: 1. 긴급 요청 (최우선) → UrgentSupport 에이전트 - "긴급", "빠르게", "지금 당장", "urgent" 등 2. 기술 문제 → TechSupport 에이전트 - "에러", "버그", "오류", "작동하지 않음" 등 3. 일반 문의 → GeneralSupport 에이전트 - 그 외 모든 요청 ``` *** ## 🛠️ B. Tool Calls & MCP Integration ### How It Works **Mention tool/MCP names in your agent's instruction**, and the LLM will call them when needed. ### ✅ Working Example (Tool Call) ```markdown theme={null} 당신은 브랜드 데이터 검색 전문가입니다. 사용 가능한 도구: 1. search_brand_in_systems - 여러 시스템에서 브랜드 정보 검색 2. get_bbpi_data - BBPI 데이터 조회 3. get_cr_segments - CR 세그먼트 데이터 조회 작업 절차: 1. 사용자가 브랜드 이름을 언급하면 search_brand_in_systems 도구 사용 2. 브랜드가 확인되면 get_bbpi_data로 상세 정보 조회 3. 결과를 친절하게 요약하여 제공 예시: - 사용자: "아모레퍼시픽 정보 알려줘" - 행동: search_brand_in_systems("아모레퍼시픽") 실행 ``` ### 📋 Pattern Breakdown **Structure**: ``` 사용 가능한 도구: 1. [tool_name] - [tool_description] 2. [tool_name] - [tool_description] 작업 절차: 1. [Condition] → [tool_name] 사용 2. [Next step] → [tool_name] 사용 ``` ### 🔧 How to Configure Tools #### Step 1: Register Tool in Admin UI 1. **Admin UI** → **Tools Management** 2. Check available tools (system admin only) 3. Note the **exact function name** #### Step 2: Add to Agent Config 1. **Agent Management** → Select agent → **Edit** 2. **Tools** field → Add tool names: ```json theme={null} ["search_brand_in_systems", "get_bbpi_data"] ``` 3. **Save** #### Step 3: Update Instruction Template Include tool names and usage patterns in the instruction (see example above). ### ✨ MCP Integration Example ```markdown theme={null} You are a development assistant with GitHub integration. Available MCP platforms: 1. GitHub - Create issues, manage repositories, and track PRs Tasks you can perform: - "create an issue for X" → Use GitHub MCP to create issue - "list open PRs" → Use GitHub MCP to retrieve pull requests - "get repository info" → Use GitHub MCP to fetch repo details Example workflow: 1. User: "Create an issue for bug in authentication" 2. Action: Call GitHub MCP create_issue function 3. Response: "Issue #123 created successfully in repository" ``` ### 🔧 How to Configure MCP #### Step 1: Enable MCP Platform 1. **Admin UI** → **MCP Platforms** 2. Check available platforms (GitHub, Firecrawl, etc.) 3. Configure credentials (API keys, OAuth tokens) #### Step 2: Link MCP to Agent 1. **Agent Management** → Select agent → **MCP** tab 2. **Add MCP Platform** → Select platform 3. **Priority Order**: Lower number = higher priority #### Step 3: Update Instruction Template Mention MCP platform capabilities in your prompt (see example above). *** ## 📚 C. RAG Datastore Queries ### How It Works **Mention data types or topics in your instruction**, and the LLM will query your RAG Datastore when needed. ### ✅ Working Example ```markdown theme={null} 당신은 회사 문서 검색 전문가입니다. 사용 가능한 지식 베이스: - 회사 정책 및 규정 문서 - 제품 매뉴얼 및 사양서 - 고객 FAQ 및 문의 이력 작업 방식: 1. 사용자 질문 분석 2. 관련 문서 자동 검색 (RAG) 3. 검색 결과 기반 답변 생성 예시: - 사용자: "휴가 정책 알려줘" - 행동: 회사 정책 문서에서 휴가 관련 내용 검색 → 답변 생성 ``` ### 📋 Pattern Breakdown **Structure**: ``` 사용 가능한 지식 베이스: - [Knowledge domain 1] - [Knowledge domain 2] 작업 방식: 1. 질문 분석 2. 자동 검색 (RAG) 3. 답변 생성 ``` ### 🗂️ How to Set Up RAG #### Step 1: Create DataStore 1. **Admin UI** → **Knowledge Base** → **Create DataStore** 2. **Name**: `company-policies` (use lowercase with hyphens) 3. **Upload documents**: PDFs, DOCX, TXT files #### Step 2: Add RAG Tool to Agent 1. **Agent Management** → Select agent → **Edit** 2. **RAG Tools** field → Add: ```json theme={null} ["rag_company-policies"] ``` **Format**: `rag_` prefix + DataStore name 3. **Save** #### Step 3: Update Instruction Template Mention knowledge domains in your prompt (see example above). ### ⚠️ Important Notes * **Model Requirement**: RAG only works with **Gemini models** (e.g., `gemini/gemini-2.5-flash`) * **DataStore Location**: Must be in `global` location (not regional) * **Naming**: Use lowercase, hyphens only (e.g., `rag_brand-knowledge`) *** ## 🔄 D. Output Format & Key Passing ### How It Works **Define output format in parent agent**, then **reference output key in child agent** to receive structured data. ### ✅ Working Example (Parent Agent) ```markdown theme={null} 당신은 데이터 수집 코디네이터입니다. 작업: 1. 여러 소스에서 브랜드 데이터 수집 2. 결과를 JSON 형식으로 출력 출력 형식 (중요): { "brand_name": "브랜드명", "bbpi_data": { BBPI 상세 정보 }, "cr_data": { CR 세그먼트 정보 }, "salesforce_data": { Salesforce CRM 정보 } } 이 출력은 ReportCompiler 에이전트가 사용합니다. ``` **DB Configuration**: * **Output Key**: `collected_brand_data` ### ✅ Working Example (Child Agent) ```markdown theme={null} 당신은 데이터 분석 보고서 컴파일러입니다. 입력 데이터: - 이전 에이전트의 collected_data 출력을 받습니다. 작업: 1. collected_data에서 핵심 정보 추출 2. 분석 보고서 형식으로 재구성 3. 마크다운 또는 HTML로 최종 보고서 생성 출력 형식: # [데이터 이름] 분석 보고서 ## 1. 데이터 분석 [collected_data.analysis 내용] ## 2. 세그먼트 정보 [collected_data.segments 내용] ## 3. CRM 정보 [collected_data.crm_data 내용] ``` **DB Configuration**: * **Include Contents**: `collected_brand_data` ### 📋 Pattern Breakdown **Parent Agent**: ``` 출력 형식 (중요): { "key1": "value description", "key2": { nested object }, ... } 이 출력은 [ChildAgent] 에이전트가 사용합니다. ``` **Child Agent**: ``` 입력 데이터: - 이전 에이전트의 [output_key] 출력을 받습니다. 작업: 1. [output_key]에서 [data] 추출 2. [Process] ``` ### 🔧 How to Configure #### Step 1: Parent Agent Setup 1. **Agent Management** → Select parent agent → **Edit** 2. **Instruction Template**: Define output format 3. **Output Key** field: `collected_brand_data` 4. **Output Schema** (optional): Define JSON schema 5. **Save** #### Step 2: Child Agent Setup 1. **Agent Management** → Select child agent → **Edit** 2. **Instruction Template**: Mention input data source 3. **Include Contents** field: `collected_brand_data` 4. **Save** #### Step 3: Link Agents 1. Create **Sequential Agent** (e.g., `DataWorkflow`) 2. Add relationships: * Order 1: `DataCollector` (parent) * Order 2: `ReportCompiler` (child) ### ✨ Advanced: Schema Validation **Define output schema for strict validation**: ```json theme={null} { "type": "object", "properties": { "brand_name": { "type": "string" }, "bbpi_data": { "type": "object" }, "cr_data": { "type": "object" } }, "required": ["brand_name", "bbpi_data"] } ``` Add this to **Output Schema** field in Admin UI. *** ## 🎭 E. Flexible Application Patterns ### Conditional Logic ```markdown theme={null} 당신은 스마트 라우터입니다. 조건부 라우팅: IF 사용자 요청에 "긴급" 또는 "urgent" 포함: → UrgentHandler 에이전트로 즉시 전달 ELSE IF 사용자가 파일 첨부: → FileProcessor 에이전트로 전달 ELSE IF 질문이 회사 정책 관련: → 지식 베이스 검색 수행 ELSE: → 일반 응답 제공 ``` ### Multi-Step Workflows ```markdown theme={null} 당신은 워크플로우 관리자입니다. 단계별 작업 프로세스: 1단계: 데이터 검증 - 사용자 입력 확인 - 필수 필드 체크 - 유효성 검사 2단계: 외부 시스템 조회 - Salesforce에서 고객 정보 조회 (MCP) - DataLab에서 거래 이력 조회 (Tool) 3단계: 분석 및 보고서 생성 - 수집된 데이터 통합 - 인사이트 도출 - PDF 보고서 생성 (Tool: claude_pdf_processor) 4단계: 결과 전달 - GitHub 이슈 생성 또는 커밋 - 사용자에게 완료 알림 ``` ### Dynamic Tool Selection ```markdown theme={null} 당신은 유연한 도구 사용자입니다. 상황에 맞는 도구 선택: 데이터 소스 우선순위: 1. 먼저 내부 데이터베이스 검색 (get_internal_data 툴) 2. 없으면 Salesforce 조회 (Salesforce MCP) 3. 그래도 없으면 웹 검색 수행 (Web Search 활성화 필요) 항상 가장 정확한 데이터 소스를 먼저 시도하세요. ``` ### Error Handling ```markdown theme={null} 당신은 안정적인 에이전트입니다. 에러 처리: IF 도구 호출 실패: → 사용자에게 친절히 설명 → 대체 방법 제안 IF 데이터 없음: → "해당 정보를 찾을 수 없습니다" 명확히 전달 → 추가 정보 요청 또는 다른 접근 방법 제시 IF 외부 시스템 오류: → 재시도 1회 → 실패 시 수동 대응 안내 ``` *** ## ✅ Best Practices ### 1. Be Explicit ❌ **Bad**: "Handle email requests" ✅ **Good**: "이메일 작성, 답장, 전달 요청 → EmailWorkflow 에이전트로 전송" ### 2. Provide Examples Include example user inputs and agent responses in your prompts. ### 3. Use Clear Formatting Structure your prompts with: * Numbered lists * Clear headers * Example blocks ### 4. Test Incrementally 1. Test Orchestrator routing first 2. Add one sub-agent at a time 3. Verify each tool/MCP call individually 4. Test complete workflow last ### 5. Monitor & Iterate * Check **Usage Logs** in Admin UI * Review agent routing events * Refine prompts based on real usage *** ## 🐛 Troubleshooting ### Agent Not Routing **Problem**: Orchestrator responds directly instead of routing. **Solution**: * ✅ Check sub-agent **name** matches exactly in prompt * ✅ Verify agent **relationship** exists in Admin UI * ✅ Ensure sub-agent is **active** (not disabled) ### Tool Not Called **Problem**: Agent doesn't call the tool. **Solution**: * ✅ Verify tool is added to **Tools** field in agent config * ✅ Mention tool **name** and **usage** in instruction * ✅ Check tool is registered in system (Tools Management) ### MCP Call Fails **Problem**: MCP platform error or timeout. **Solution**: * ✅ Verify MCP credentials in **MCP Platforms** settings * ✅ Check MCP platform is **linked** to agent (MCP tab) * ✅ Test MCP connection manually in Admin UI ### RAG Returns No Results **Problem**: RAG query returns empty or irrelevant results. **Solution**: * ✅ Verify DataStore has **indexed documents** * ✅ Check agent uses **Gemini model** (RAG only works with Gemini) * ✅ Ensure RAG tool name format: `rag_[datastore-name]` * ✅ Improve query phrasing in prompt ### Output Key Not Passed **Problem**: Child agent doesn't receive parent output. **Solution**: * ✅ Parent agent must set **Output Key** field * ✅ Child agent must set **Include Contents** field with same key * ✅ Agents must be in **Sequential** flow (not Parallel) *** ## 📖 Related Documentation Learn agent types and architecture Create custom tools Connect external platforms *** ## 🎯 Summary **You now know how to**: ✅ **A. Route to sub-agents** by mentioning agent names in prompts ✅ **B. Call tools & MCP** by listing available functions ✅ **C. Query RAG Datastores** by mentioning knowledge domains ✅ **D. Pass data with output keys** between agents ✅ **E. Apply flexible patterns** with conditional logic **Next Steps**: 1. Open **Admin UI** → **Agent Management** 2. Create a simple Orchestrator with 2 sub-agents 3. Test routing with different user inputs 4. Gradually add tools, MCP, and RAG **Remember**: No code changes needed - just configure prompts and save! 🚀 # Squad Collaboration Source: https://docs.junis.ai/guides/orchestration/squad-collaboration Let your squads talk to each other — one orchestrator can ask another squad and bring back the answer ## What is Squad Collaboration? In Junis, each organization is a **squad** — an AI team with its own orchestrator, agents, knowledge, and personality. Squad Collaboration lets one squad's orchestrator **discover and message another squad** during a conversation, wait for its answer, and bring the result back to you. > "우리 마케팅 스쿼드에 이번 캠페인 일정 물어봐줘" — your current squad asks the marketing squad and relays the answer, all in one chat. **Zero setup**: Squad collaboration is automatically available to every orchestrator. There is nothing to configure. *** ## How It Works Your orchestrator can list the squads you have access to — squads where you are an **active member**, plus **public** squads. The orchestrator sends your request to the target squad's orchestrator, which answers using its own agents, tools, knowledge base, and wiki. The response comes back into your current conversation, with the source squad clearly attributed. ### Example ``` You: "Ask our Data squad what last month's churn rate was." Agent: 1. Lists your accessible squads, finds "Data" 2. Sends the question to the Data squad's orchestrator 3. Data squad answers using its own tools and knowledge 4. Your agent replies: "According to the Data squad, last month's churn was 2.3%..." ``` *** ## What You Should Know You can only reach squads where you are an **active member**, or squads that are **public**. A squad cannot call itself. Each cross-squad call creates a new conversation in the target squad, visible in that squad's session list, and the incoming request is labeled with its origin (e.g. `[스쿼드 호출: 'Marketing' 조직에서 전달된 요청]`). Nothing happens invisibly. Usage from a cross-squad call is billed under the **target squad's** billing rules — exactly as if you had chatted with that squad directly. Squads can chain calls (A → B → C) up to a **maximum depth of 3**, and circular calls (A → B → A) are automatically blocked. Your agent waits up to a few minutes for the target squad to answer. If the target squad needs longer, you get the session reference so you can open that squad's conversation and read the full answer there. Very long messages and responses are truncated at generous limits to keep conversations responsive. For large deliverables, ask the target squad directly in its own chat. *** ## When to Use It "물류 스쿼드에 재고 현황 물어봐줘" — pull answers from the squad that owns the data. Route a legal, finance, or design question to the squad built for it, without leaving your chat. Query public squads that offer their expertise to everyone. Chain up to 3 squads for pipelines like research → analysis → summary. If your agent doesn't know which squad to ask, just say "어떤 스쿼드에 물어볼 수 있어?" — it will list the squads you can reach. *** ## What's Next? How multi-agent orchestration works Managing multiple squads Each squad's shared knowledge base Members, roles, and permissions # Multi-Organization Setup Guide Source: https://docs.junis.ai/guides/organization/multi-org Manage multiple organizations, switch contexts, and organize your workspaces ## What You'll Learn This guide covers managing multiple organizations in Junis: * **Why Multiple Organizations**: Use cases and benefits * **Creating Additional Organizations**: Step-by-step setup * **Organization Switching**: Seamless context switching * **Cross-Organization Features**: What's shared and what's isolated * **Common Scenarios**: Agency, enterprise, dev/prod separation * **Best Practices**: Naming, structure, and organization strategies **Who This Is For**: Users who need to manage multiple projects, clients, or environments within Junis. *** ## Why Multiple Organizations? ### Common Use Cases **Scenario**: Manage separate workspaces for each client **Benefits**: * Complete data isolation per client * Separate billing and feature limits * Client-specific branding and settings * Easy client handoff (transfer ownership) **Example**: * `acme-corp` (client A) * `globex-industries` (client B) * `initech-consulting` (your internal workspace) **Scenario**: Separate organizations for different departments **Benefits**: * Department-level autonomy * Isolated agent configurations * Department-specific analytics * Compliance and governance per department **Example**: * `acme-marketing` * `acme-sales` * `acme-engineering` * `acme-data-science` **Scenario**: Separate environments for development lifecycle **Benefits**: * Test changes safely in dev * Isolated data per environment * Different feature flags per environment * Production safety **Example**: * `mycompany-dev` * `mycompany-staging` * `mycompany-production` **Scenario**: Separate personal projects from work **Benefits**: * Work-life separation * Different billing accounts * Personal experimentation space * Professional client work **Example**: * `john-doe-personal` * `john-doe-freelance` * `acme-corp` (employer) ### Single vs Multiple Organizations | Aspect | Single Organization | Multiple Organizations | | - | - | - | | **Data Isolation** | All data in one workspace | Complete isolation per organization | | **Billing** | One bill for everything | Separate billing per organization | | **Team Management** | One team roster | Separate teams per organization | | **Agent Sharing** | Agents shared across projects | Agents isolated per organization | | **Use Case** | Small teams, single project | Agencies, enterprises, multi-project | **When to Use Multiple Organizations**: * Managing multiple clients * Separating dev/staging/prod environments * Different departments with separate budgets * Projects with different compliance requirements * Testing new features in isolation **When NOT to Use Multiple Organizations**: * Single project with multiple sub-projects (use agent tagging instead) * Small team working on one product * Just to separate different agent types (use agent naming/folders instead) *** ## Creating Additional Organizations ### Prerequisites * Existing Junis account (verified email) * At least one organization already created (from onboarding) * Organization approval pending or approved ### Step-by-Step Creation Click the **organization name** in the top-left corner of the dashboard. Or navigate to **Settings > Organizations** in the sidebar. In the organization switcher dropdown, click **"+ Create New Organization"** at the bottom. **Required Fields**: * **Organization Name**: Display name (e.g., "Acme Corp - Marketing") * **Slug**: URL-friendly identifier (e.g., "acme-marketing") * Auto-generated from name * Can be customized * Must be unique across all Junis organizations **Optional Fields**: * **Description**: Organization purpose or description * **Logo**: Upload organization logo (PNG, JPG, max 2MB) * **Welcome Suggestions**: Custom chat suggestions for this organization **Organization Type**: (Auto-selected: STANDARD) **Initial Features**: * Max Agents: 10 (default, can be increased) * Max Members: 50 (default) * Max Sessions/Day: 1,000 (default) * RAG Enabled: Yes (default) Feature limits can be adjusted later by administrators. Click **"Create Organization"**. **What Happens Next**: 1. Organization created with `PENDING` status 2. You become the OWNER automatically 3. Default orchestrator agent created (`orchestrator-{slug}`) 4. Approval notification sent to Junis administrators **Approval Timeline**: 1-2 business days **While PENDING**: * Organization appears in your organization list * Yellow "Pending Approval" badge displayed * You can view organization settings but cannot use agents **After APPROVAL**: * Status changes to `ACTIVE` * Green "Active" badge displayed * All features unlocked * You can invite team members and create agents ### Organization Creation via API **Endpoint**: `POST /api/organizations` **Request Body**: ```json theme={null} { "name": "Acme Corp - Marketing", "slug": "acme-marketing", "description": "Marketing department workspace", "logo_url": "https://example.com/logo.png", "welcome_suggestions": [ "Analyze this campaign performance", "Generate social media content ideas", "Create a content calendar" ] } ``` **Response**: ```json theme={null} { "id": "org-uuid-1234", "name": "Acme Corp - Marketing", "slug": "acme-marketing", "status": "PENDING", "org_type": "STANDARD", "created_at": "2026-01-01T10:00:00Z", "created_by": "user-uuid-5678", "features": { "max_agents": 10, "max_members": 50, "max_sessions_per_day": 1000, "rag_enabled": true } } ``` *** ## Switching Between Organizations ### Using the Organization Switcher **Location**: Top-left corner of the dashboard (next to Junis logo) **Steps**: 1. Click the **current organization name** (e.g., "Acme Marketing") 2. Dropdown shows all your organizations 3. Click the organization you want to switch to 4. Page reloads with new organization context **Keyboard Shortcut**: `Cmd+K` (Mac) / `Ctrl+K` (Windows) → Type organization name → Enter **Location**: Hamburger menu (☰) → "Switch Organization" **Steps**: 1. Tap the **hamburger menu** (☰) in the top-left 2. Tap **"Switch Organization"** 3. Select organization from list 4. App reloads with new context **Endpoint**: `POST /api/organizations/{organization_id}/switch` **Request**: ```bash theme={null} curl -X POST https://api.junis.ai/api/organizations/org-uuid-1234/switch \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Response**: ```json theme={null} { "message": "Switched to organization: Acme Marketing", "organization_id": "org-uuid-1234", "organization_name": "Acme Marketing", "new_token": "eyJhbGciOi..." // New JWT with org_id } ``` **Important**: Store the new token! It includes the organization context. ### What Changes When You Switch **Visible Changes**: * Organization name in header updates * Logo changes (if custom logo set) * Welcome suggestions update * Sidebar navigation shows organization-specific items **Dashboard**: * Agents list filtered to current organization * Sessions filtered to current organization * Analytics show current organization data only * Team members list shows current organization roster **JWT Token Updated**: ```json theme={null} { "user_id": "user-uuid-5678", "organization_id": "org-uuid-1234", // ← Updated "role": "OWNER", "permissions": { /* org-specific permissions */ }, "exp": 1767261600 } ``` **All API Requests**: * Automatically scoped to current organization * Cannot access other organization's data * WebSocket connections tied to organization **What You See**: * Only agents from current organization * Only sessions from current organization * Only team members from current organization * Only RAG Datastores from current organization **What You DON'T See**: * Other organizations' agents (even if you're a member) * Other organizations' sessions * Other organizations' data ### Organization Context Persistence **Session Persistence**: Your last selected organization is remembered across browser sessions. **How It Works**: 1. Organization choice stored in JWT token 2. JWT token saved in browser localStorage 3. On next visit, last organization is auto-selected 4. You can switch anytime using the organization switcher *** ## Managing Multiple Organizations ### Organization List View **Location**: Click organization name → "Manage Organizations" **What You See**: **List of Organizations You Own or Are a Member Of**: | Organization | Status | Role | Members | Agents | Last Active | | - | - | - | - | - | - | | Acme Marketing | 🟢 Active | OWNER | 12 | 25 | 2 hours ago | | Acme Sales | 🟢 Active | ADMIN | 8 | 15 | 1 day ago | | Personal Workspace | 🟡 Pending | OWNER | 1 | 3 | 5 days ago | | ClientCo Consulting | 🔴 Suspended | MEMBER | 5 | 10 | 3 weeks ago | **Actions**: * 🔄 **Switch**: Switch to this organization * ⚙️ **Settings**: Edit organization settings (OWNER/ADMIN only) * 👥 **Members**: View/manage team members * 🚪 **Leave**: Leave organization (if not last OWNER) **Organizations You're Invited To But Haven't Joined**: | Organization | Invited By | Role | Invited Date | Actions | | - | - | - | - | - | | NewClient Workspace | [alice@newclient.com](mailto:alice@newclient.com) | MEMBER | Jan 1, 2026 | ✅ Accept / ❌ Decline | | Partner Agency | [bob@partner.com](mailto:bob@partner.com) | ADMIN | Jan 1, 2026 | ✅ Accept / ❌ Decline | **Actions**: * ✅ **Accept**: Join organization with assigned role * ❌ **Decline**: Reject invitation permanently * ℹ️ **Details**: View organization details before accepting **Create Additional Organization**: * Click **"+ Create New Organization"** button * Follow creation wizard * Submit for approval * New organization appears in your list with PENDING status ### Multi-Organization Dashboard **Feature**: Unified view across all your organizations (coming soon) **What It Will Show**: * Total agents across all organizations * Combined session count * Activity timeline across organizations * Quick organization switcher * Notifications from all organizations *** ## Cross-Organization Features ### What's Shared Across Organizations **Shared**: * User ID * Email address * Display name * Avatar * Password/authentication **Not Shared**: * Role (OWNER in org A ≠ OWNER in org B) * Permissions (org-specific) **Shared**: * Platform features (MCP, RAG, Payment) * Global LLM models * Junis platform updates * Documentation and support **Not Shared**: * Organization-specific feature limits * Organization-specific settings ### What's Isolated Per Organization **Complete Isolation**: The following are NEVER shared between organizations: **Completely Isolated**: * Agents (cannot see other org's agents) * Sessions (cannot access other org's conversations) * RAG Datastores (separate knowledge bases) * Agent Templates (organization-specific) * File Uploads (separate storage per org) * Usage Analytics (separate metrics) **Completely Isolated**: * Team members (separate rosters) * Roles and permissions (org-specific) * Invitations (cannot invite to other orgs) * Team settings (separate configurations) **Completely Isolated** (when billing enabled): * Subscription plans (per organization) * Usage charges (per organization) * Payment methods (separate per org) * Invoices (org-specific) * Wallet addresses (for AP2/x402 revenue) ### Global Resources **What Are Global Resources?** Some platform features are shared across all organizations: **Shared Models**: * Anthropic Claude (all versions) * OpenAI GPT (all versions) * Google Gemini * DeepSeek, Qwen, etc. **How It Works**: * Models available to all organizations * Usage tracked per organization * API keys managed per organization **Shared Platforms**: * GitHub, Firecrawl, 커스텀 MCP 등 * Platform definitions shared * Credentials per-user or per-organization **How It Works**: * Platform capabilities available to all * Authentication per user or org * Usage isolated per organization **Shared Tools**: * Brand search tools * Google search * File upload tools * Payment tools (AP2/x402) **How It Works**: * Tool definitions global * Tool usage tracked per organization * Tool results isolated per organization **Shared Resources**: * Platform documentation * API reference * User guides * Support knowledge base **Always Available**: * Same docs for all users * Accessible from any organization *** ## Common Multi-Organization Scenarios ### Scenario 1: Agency Managing Multiple Clients **Setup**: ```mermaid theme={null} graph TB You[Your User Account] --> Org1[Agency Internal] You --> Org2[Client A] You --> Org3[Client B] You --> Org4[Client C] Org1 --> Team1[Your Team: 10 members] Org2 --> Team2[Client A: 3 members] Org3 --> Team3[Client B: 5 members] Org4 --> Team4[Client C: 2 members] ``` **Organization Structure**: | Organization | Role | Members | Purpose | | - | - | - | - | | `myagency-internal` | OWNER | 10 (your team) | Internal agency work, templates, testing | | `clienta-workspace` | OWNER | 3 (you + 2 client staff) | Client A production agents | | `clientb-workspace` | OWNER | 5 (you + 4 client staff) | Client B production agents | | `clientc-workspace` | OWNER | 2 (you + 1 client staff) | Client C production agents | **Best Practices**: * Use agency internal workspace for agent templates and testing * Clone templates to client organizations * Keep client data completely isolated * Transfer ownership when client relationship ends * Use consistent naming: `{clientname}-workspace` ### Scenario 2: Enterprise with Multiple Departments **Setup**: ```mermaid theme={null} graph TB Enterprise[Enterprise Corp] --> Marketing[Marketing Dept] Enterprise --> Sales[Sales Dept] Enterprise --> Eng[Engineering Dept] Enterprise --> Data[Data Science Dept] Marketing --> M1[Marketing Team: 20] Sales --> S1[Sales Team: 15] Eng --> E1[Engineering Team: 50] Data --> D1[Data Team: 10] ``` **Organization Structure**: | Organization | Role | Members | Purpose | | - | - | - | - | | `acme-marketing` | ADMIN | 20 | Marketing campaigns, content generation | | `acme-sales` | ADMIN | 15 | Sales automation, lead qualification | | `acme-engineering` | ADMIN | 50 | Code generation, documentation | | `acme-data-science` | ADMIN | 10 | Data analysis, model training | **Benefits**: * Department autonomy (each dept manages own agents) * Separate billing (cost allocation per department) * Compliance (different data policies per dept) * Analytics (department-specific usage metrics) **Coordination**: * Platform admin has OWNER role in all departments * Shared best practices document * Cross-department agent templates (manual copy) * Monthly usage review meetings ### Scenario 3: Dev/Staging/Production Environments **Setup**: ```mermaid theme={null} graph LR Dev[Development Org] --> Test1[Test Changes] Dev --> Exp[Experiment] Staging[Staging Org] --> UAT[User Testing] Staging --> QA[QA Validation] Prod[Production Org] --> Live[Live Agents] Prod --> Client[Client Use] Dev -.Clone agents.-> Staging Staging -.Promote.-> Prod ``` **Organization Structure**: | Organization | Role | Members | Purpose | Features | | - | - | - | - | - | | `myapp-dev` | OWNER | 5 (dev team) | Development, testing, experimentation | All features enabled, relaxed limits | | `myapp-staging` | ADMIN | 3 (QA team) | Pre-production testing, UAT | Production-like limits | | `myapp-production` | OWNER | 2 (senior devs) | Live production environment | Strict limits, monitoring | **Workflow**: 1. Develop new agents in `myapp-dev` 2. Test thoroughly in dev environment 3. Clone successful agents to `myapp-staging` 4. Perform UAT and QA validation 5. Manually recreate in `myapp-production` (safest) 6. Monitor production carefully **Best Practices**: * Never test in production * Use different API keys per environment * Separate RAG Datastores (dev uses test data) * Different feature flags per environment * Strict access control on production ### Scenario 4: Personal + Professional Workspaces **Setup**: ```mermaid theme={null} graph TB You[Your Account] --> Personal[Personal Workspace] You --> Freelance[Freelance Projects] You --> EmployerA[Employer A] You --> EmployerB[Employer B - Part Time] Personal --> P1[Side projects, learning] Freelance --> F1[Client work] EmployerA --> E1[Full-time job] EmployerB --> E2[Consulting work] ``` **Organization Structure**: | Organization | Role | Billing | Purpose | | - | - | - | - | | `john-doe-personal` | OWNER | Personal credit card | Hobby projects, learning | | `john-doe-freelance` | OWNER | Business credit card | Client deliverables | | `acme-corp` | MEMBER | Company billing | Full-time job work | | `consulting-inc` | ADMIN | Client billing | Part-time consulting | **Benefits**: * Clear work-life separation * Different billing accounts (personal vs business) * Professional work stays with employer * Freelance work is portable **Best Practices**: * Use work email for employer organizations * Use personal email for personal workspace * Never mix personal and professional data * Archive freelance organizations after project ends *** ## Organization Naming Best Practices ### Naming Conventions **Pattern**: `{agency-name}-{client-name}` **Examples**: * `innovate-agency-clienta` * `innovate-agency-clientb` * `innovate-agency-internal` **Benefits**: * Consistent naming across clients * Easy to identify your organizations * Alphabetical sorting keeps them grouped **Pattern**: `{company-name}-{department}` **Examples**: * `acme-corp-marketing` * `acme-corp-sales` * `acme-corp-engineering` * `acme-corp-data-science` **Benefits**: * Company prefix keeps all orgs together * Department name is clear * Easy to manage at scale **Pattern**: `{project-name}-{environment}` **Examples**: * `myapp-dev` * `myapp-staging` * `myapp-production` **Benefits**: * Clear environment identification * Prevents accidental production changes * Easy to switch between environments **Pattern**: `{name}-{purpose}` or `{company-name}` **Examples**: * `john-doe-personal` * `john-doe-freelance` * `acme-corp` (employer) * `consulting-inc` (part-time) **Benefits**: * Clear ownership * Professional names for employer orgs * Personal identity for own workspaces ### Slug Best Practices **✅ Good Slugs**: * `acme-corp-marketing` (clear, descriptive) * `clienta-workspace` (simple, purposeful) * `myapp-production` (environment clear) * `john-doe-personal` (ownership clear) **❌ Bad Slugs**: * `org1`, `org2`, `org3` (meaningless) * `test`, `temp`, `sandbox` (too generic, conflicts likely) * `acme_corp` (use hyphens, not underscores) * `AcmeCorp` (lowercase only) * `my-super-long-organization-name-with-many-words` (too long) *** ## Billing and Features Across Organizations ### Per-Organization Billing (When Enabled) **Current Status**: Junis is currently free during beta. Billing features will be enabled in future releases. **How It Will Work**: | Feature | Single Organization | Multiple Organizations | | - | - | - | | **Subscription** | One subscription for all usage | Separate subscription per organization | | **Billing** | One invoice | Separate invoice per organization | | **Payment Method** | One credit card | Different cards per organization | | **Usage Limits** | Shared across all projects | Isolated per organization | | **Overages** | One overage charge | Overage per organization | ### Feature Limits Per Organization Each organization has independent feature limits: **Default Limits (STANDARD Organization)**: ```json theme={null} { "max_agents": 10, // Can create up to 10 agents "max_members": 50, // Can invite up to 50 team members "max_sessions_per_day": 1000, // 1,000 sessions per day "rag_enabled": true, // RAG knowledge base enabled "payment_enabled": true, // AP2/x402 payments enabled "mcp_platforms": true, // MCP integrations enabled "scheduled_tasks": true // Scheduled agent execution enabled } ``` **Example: Multi-Organization Limits**: | Organization | Max Agents | Max Members | Max Sessions/Day | RAG | MCP | | - | - | - | - | - | - | | `myagency-internal` | 10 | 50 | 1,000 | ✅ | ✅ | | `clienta-workspace` | 10 | 50 | 1,000 | ✅ | ✅ | | `clientb-workspace` | 10 | 50 | 1,000 | ✅ | ✅ | | **Total** | **30 agents** | **150 members** | **3,000 sessions/day** | - | - | **Limits Add Up**: If you have 3 organizations, you get 3× the limits (10 agents × 3 = 30 total agents). *** ## Troubleshooting Multi-Organization Issues **Symptoms**: Organization doesn't appear in organization switcher dropdown **Possible Causes**: * Organization status is PENDING (not yet approved) * You were removed from the organization * Organization was deleted * Browser cache issue **Solutions**: 1. Check organization status in **Settings > Organizations** 2. Verify you're still a member (check email for removal notification) 3. Clear browser cache and reload 4. Try logging out and logging back in 5. Contact organization OWNER to confirm membership **Symptoms**: After switching, still see previous organization's agents/sessions **Possible Causes**: * Page didn't fully reload after switch * Browser cache showing stale data * API token not updated **Solutions**: 1. Force refresh page: `Cmd+Shift+R` (Mac) / `Ctrl+Shift+R` (Windows) 2. Clear browser cache 3. Log out and log back in 4. Check organization name in header (confirm switch succeeded) 5. If using API, ensure you're using the new token from switch response **Symptoms**: "Create Organization" button disabled or shows error **Possible Causes**: * Email not verified * Organization limit reached (platform-level restriction) * Slug already taken * Invalid characters in organization name/slug **Solutions**: * Verify email if prompted * Try a different slug (must be globally unique) * Use only lowercase letters, numbers, and hyphens in slug * Contact support if you need more than 10 organizations **Symptoms**: "Access Denied" or "Not Found" when trying to access organization **Possible Causes**: * You were removed from the organization * Organization was suspended or deleted * Your role was changed and you lost permissions * Organization is PENDING approval **Solutions**: * Check email for removal or role change notifications * Verify organization status in your organizations list * Contact organization OWNER or ADMIN * If organization is PENDING, wait for approval **Symptoms**: Organization created but status stays PENDING for > 2 business days **Possible Causes**: * Admin approval backlog * Organization name/purpose requires review * Missing required information **Solutions**: * Wait 1-2 business days after creation * Check email for approval requests or follow-up questions * Edit organization settings to provide more details * Contact Junis support at [contact@junis.ai](mailto:contact@junis.ai) **Symptoms**: Trying to transfer OWNER role but operation fails **Current Status**: Organization ownership transfer not yet implemented **Workaround**: 1. Make the intended new owner an ADMIN 2. Give them full permissions 3. As current OWNER, restrict your own permissions (stay as OWNER but with limited actions) 4. Contact Junis support for manual ownership transfer if needed **Coming Soon**: Self-service ownership transfer feature *** ## API Reference for Multi-Organization Management ### List Your Organizations **Endpoint**: `GET /api/organizations` **Authentication**: Bearer token required **Response**: ```json theme={null} { "organizations": [ { "id": "org-uuid-1234", "name": "Acme Marketing", "slug": "acme-marketing", "status": "ACTIVE", "org_type": "STANDARD", "role": "OWNER", "member_count": 12, "agent_count": 25, "created_at": "2026-01-01T00:00:00Z", "last_active_at": "2026-01-01T16:00:00Z" }, { "id": "org-uuid-5678", "name": "Personal Workspace", "slug": "john-doe-personal", "status": "PENDING", "org_type": "STANDARD", "role": "OWNER", "member_count": 1, "agent_count": 3, "created_at": "2026-01-01T10:00:00Z" } ], "total": 2, "current_organization_id": "org-uuid-1234" } ``` ### Switch Organization **Endpoint**: `POST /api/organizations/{organization_id}/switch` **Authentication**: Bearer token required **Response**: ```json theme={null} { "message": "Switched to organization: Acme Marketing", "organization_id": "org-uuid-1234", "organization_name": "Acme Marketing", "new_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Important**: Use the `new_token` for all subsequent requests. It contains the new organization context. ### Create Organization (Programmatically) See "Organization Creation via API" section above. *** ## What's Next? Manage team members and permissions Learn about the admin dashboard interface *** ## Additional Resources * **Organization API Reference**: Full API documentation for all organization endpoints * **Enterprise Plans**: Contact sales for custom organization limits and features * **Migration Guide**: Moving agents between organizations (coming soon) * **Organization Templates**: Pre-configured organization setups for common use cases **Need Help with Multi-Organization Setup?** Contact Junis support at [contact@junis.ai](mailto:contact@junis.ai) or your dedicated account manager. # Team Management Guide Source: https://docs.junis.ai/guides/organization/team-management Invite members, manage roles, and configure permissions for your organization ## What You'll Learn This guide covers comprehensive team management in Junis: * **Inviting Members**: Add team members via email invitation * **Role Management**: Assign and update roles (OWNER, ADMIN, MEMBER) * **Permission Control**: Configure granular permissions for each member * **Invitation Flow**: Manage pending invitations and acceptance process * **Member Removal**: Remove members with proper safeguards * **Best Practices**: Organize your team structure effectively **Prerequisites**: You must be an **OWNER** or **ADMIN** with `members.invite` permission to invite team members. *** ## Team Member Roles ### Role Hierarchy Junis uses a 3-tier role system with inherited permissions: ```mermaid theme={null} graph TD A[OWNER] --> B[Full Control] C[ADMIN] --> D[Almost Full Control] E[MEMBER] --> F[Limited Access] A --> I[Cannot be removed] A --> J[Can delete organization] C --> K[Cannot delete organization] E --> L[Default role for new members] ``` ### Role Comparison Matrix | Feature | OWNER | ADMIN | MEMBER | | - | - | - | - | | **Agents** | | | | | Create agents | ✅ | ✅ | ❌ | | Edit own agents | ✅ | ✅ | ✅ | | Edit all agents | ✅ | ✅ | ❌ | | Delete agents | ✅ | ✅ | ❌ | | View all agents | ✅ | ✅ | ✅ | | **Members** | | | | | Invite members | ✅ | ✅ | ❌ | | Remove members | ✅ | ✅ | ❌ | | Edit permissions | ✅ | ✅ | ❌ | | **Organization** | | | | | Edit settings | ✅ | ✅ | ❌ | | View analytics | ✅ | ✅ | ❌ | | Delete organization | ✅ | ❌ | ❌ | | **Sessions & Chat** | | | | | Create sessions | ✅ | ✅ | ✅ | | View own sessions | ✅ | ✅ | ✅ | | View all sessions | ✅ | ✅ | ❌ | **OWNER Role Protection**: Every organization must have at least one OWNER. The last OWNER cannot be removed or demoted. *** ## Inviting Team Members ### Email-Based Invitation Junis uses an email-based invitation system: Go to **Team > Members** in your organization dashboard. Click the **"Invite Member"** button in the top-right corner. Fill in the invitation form: * **Email**: Member's email address (required) * **Role**: Select initial role (ADMIN or MEMBER) * **Custom Permissions**: (Optional) Customize permissions for this member Click **"Send Invitation"** to create the invitation. ### Invitation Flow ```mermaid theme={null} sequenceDiagram participant Admin as Admin/Owner participant Platform as Junis Platform participant Invitee as New Member Admin->>Platform: Invite member (email + role) Platform->>Platform: Create PENDING member record Platform-->>Admin: Invitation sent Invitee->>Platform: Sign up with invited email Platform->>Platform: Link user_id to member record Platform->>Platform: Update status to ACTIVE Platform-->>Invitee: Welcome! Access granted Note over Invitee,Platform: Member can now access organization ``` ### Invitation States **Member Invited but Not Signed Up** **What Happens**: * Member record created with email * `user_id` is NULL * Status is `PENDING` * Member cannot access organization yet **What Shows in UI**: * Grey status badge: "Pending Invitation" * Email shown, no avatar * "Resend Invitation" button available * "Cancel Invitation" button available **How to Resolve**: 1. Invitee signs up with the invited email 2. System automatically links `user_id` and activates membership 3. Status changes to `ACTIVE` **Member Has Access** **What Happens**: * Member record linked to user account * `user_id` is set * Status is `ACTIVE` * Member can access organization resources **What Shows in UI**: * Green status badge: "Active" * Avatar and name displayed * "Edit Role" and "Remove" buttons available * Last active timestamp shown **Permissions Apply**: * Member can perform actions based on role and custom permissions **Member Temporarily Blocked** **What Happens**: * Member exists but access is revoked * Status is `SUSPENDED` * Member cannot log in or access resources **What Shows in UI**: * Red status badge: "Suspended" * "Reactivate" button available (OWNER/ADMIN only) * Greyed-out avatar **Use Cases**: * Temporary access removal (e.g., leave of absence) * Security investigation * Pending offboarding *** ## Managing Roles and Permissions ### Changing Member Roles Go to **Team > Members**. Click the **"Edit"** icon next to the member's name. In the edit dialog: * Select new role from dropdown * Choose whether to apply default permissions for the new role * Click **"Save Changes"** Review the confirmation dialog showing permission changes. Click **"Confirm"** to apply. **Role Change Impact**: * Changing role **overwrites custom permissions** if you check "Apply default permissions" * Member will immediately gain/lose access based on new role * Sessions in progress will be affected on next request ### Default Permissions by Role #### OWNER Permissions ```json theme={null} { "agents": { "create": true, "edit": true, "delete": true, "view_all": true }, "members": { "invite": true, "remove": true, "edit_permissions": true }, "organization": { "edit_settings": true, "view_analytics": true, "delete": true } } ``` **OWNER Role**: Cannot be customized. Always has full permissions. #### ADMIN Permissions ```json theme={null} { "agents": { "create": true, "edit": true, "delete": true, "view_all": true }, "members": { "invite": true, "remove": true, "edit_permissions": true }, "organization": { "edit_settings": true, "view_analytics": true, "delete": false // ← Cannot delete organization } } ``` **ADMIN Difference**: Almost identical to OWNER, but cannot delete the organization. #### MEMBER Permissions ```json theme={null} { "agents": { "create": false, // ← Cannot create new agents "edit": true, // ← Can edit own agents "delete": false, // ← Cannot delete agents "view_all": true }, "members": { "invite": false, "remove": false, "edit_permissions": false }, "organization": { "edit_settings": false, "view_analytics": false, "delete": false } } ``` **MEMBER Role**: Default role for new team members. Can edit own agents but cannot create new agents or manage other members. *** ## Custom Permissions ### Granular Permission Control Beyond roles, you can customize individual permissions for specific members. ### Permission Structure ```json theme={null} { "agents": { "create": boolean, // Can create new agents "edit": boolean, // Can edit all agents (not just own) "delete": boolean, // Can delete all agents (not just own) "view_all": boolean // Can view all agents }, "members": { "invite": boolean, // Can invite new members "remove": boolean, // Can remove members "edit_permissions": boolean // Can edit member permissions }, "organization": { "edit_settings": boolean, // Can edit organization settings "view_analytics": boolean, // Can view usage analytics "delete": boolean // Can delete organization (OWNER only) } } ``` ### How to Set Custom Permissions **Steps**: 1. Go to **Team > Members** 2. Click **"Edit"** on member 3. Select role (e.g., MEMBER) 4. Uncheck **"Apply default permissions"** 5. Toggle individual permission switches 6. Click **"Save"** **Example Use Case**: * Member role but allow viewing analytics * Admin role but restrict agent deletion **Endpoint**: `PUT /api/organizations/{org_id}/members/{member_id}` **Request Body**: ```json theme={null} { "role": "MEMBER", "permissions": { "agents": { "create": false, "edit": true, "delete": false, "view_all": true }, "members": { "invite": false, "remove": false, "edit_permissions": false }, "organization": { "edit_settings": false, "view_analytics": true, // ← Custom: Allow analytics "delete": false } } } ``` **Response**: ```json theme={null} { "id": "member-uuid-1234", "email": "analyst@company.com", "role": "MEMBER", "permissions": { /* updated permissions */ }, "status": "ACTIVE" } ``` ### Common Custom Permission Patterns **MEMBER + view\_analytics** * Can edit own agents (default MEMBER permission) * Can view analytics dashboard (custom) * Cannot create new agents * Cannot invite members **Use Case**: Data analysts who need insights but not admin control **MEMBER + create agents** * Can create new agents (custom) * Can edit own agents (default MEMBER permission) * Cannot delete agents * Cannot manage members **Use Case**: Developers who need to create agents but not manage team **MEMBER + invite members** * Can edit own agents (default MEMBER permission) * Can invite new members (custom) * Cannot edit permissions * Cannot edit organization settings **Use Case**: Team leads who can grow the team but not configure org-level settings **ADMIN - delete agents** * Full admin permissions * Cannot delete agents (safety measure) * Can edit settings * Can invite members **Use Case**: Admins in regulated environments where agent deletion requires approval *** ## Managing Pending Invitations ### Viewing Pending Invitations Go to **Team > Members**. Use the status filter dropdown and select **"Pending"**. Pending invitations show: * Email address (no avatar) * Role assigned * "Pending Invitation" badge * Invited date * Invited by (admin name) ### Resending Invitations If an invitee didn't receive the invitation email: Click the **"Resend Invitation"** button next to the member. **Email Delivery**: Invitation emails may take up to 5 minutes to arrive. Check spam folder if not received. ### Canceling Invitations To cancel a pending invitation: Go to **Team > Members** and filter by "Pending". Click the **"Cancel Invitation"** button. Confirm in the dialog. The invitation will be permanently removed. **Permanent Deletion**: Canceling an invitation deletes the member record. You'll need to re-invite if you change your mind. ### Invitee Acceptance Flow When an invitee signs up: ```mermaid theme={null} sequenceDiagram participant Invitee as New User participant Platform as Junis Platform participant Database as Database participant Org as Organization Invitee->>Platform: Sign up with invited email Platform->>Database: Check for pending invitation Database-->>Platform: Found: member record (PENDING) Platform->>Database: Link user_id to member Platform->>Database: Update status to ACTIVE Platform->>Org: Grant access to organization Platform-->>Invitee: Welcome! Redirect to organization Note over Invitee,Org: Member can now access agents, sessions, etc. ``` **Automatic Activation**: No manual approval needed. As soon as the invitee signs up, they gain access. *** ## Removing Members ### Safe Member Removal Go to **Team > Members**. Click the **"Remove"** button next to the member's name. A confirmation dialog shows: * Member name and email * Number of agents they created * Number of active sessions * Warning if last OWNER Click **"Remove Member"** to confirm. ### What Happens When a Member is Removed **Access Revoked**: * Member cannot access organization immediately * Active sessions are terminated on next request * WebSocket connections are closed * API tokens no longer valid for this organization **Data Retention**: * Agents created by member remain (ownership transferred to OWNER) * Sessions created by member remain (for audit trail) * Chat history preserved **Member Record**: * Deleted from `organization_members` table (CASCADE delete) * No longer appears in members list * Cannot be restored (must re-invite) **Related Resources**: * Agents: `created_by` updated to organization OWNER * Sessions: Remain unchanged (for audit) * API Keys: Invalidated if scoped to this member **If Removed Member Rejoins**: * Must be re-invited (new invitation) * Gets new member record (new UUID) * Loses previous custom permissions (new role/permissions assigned) * Previous agent ownership not restored ### Last OWNER Protection **Cannot Remove Last OWNER**: * Every organization must have at least one OWNER * Attempting to remove the last OWNER triggers an error: ```json theme={null} { "error": "Cannot remove the last owner of the organization", "code": "LAST_OWNER_PROTECTION" } ``` **Solution**: Promote another member to OWNER first, then remove the original OWNER. ### Suspending vs Removing | Action | Access Revoked | Data Deleted | Reversible | Use Case | | - | - | - | - | - | | **Suspend** | ✅ Yes | ❌ No | ✅ Yes (Reactivate) | Temporary leave, investigation | | **Remove** | ✅ Yes | ✅ Yes (member record) | ❌ No (must re-invite) | Permanent departure | **Suspension** (coming soon): Use suspension for temporary access removal. Use removal for permanent offboarding. *** ## Team Structure Best Practices ### Recommended Team Sizes **Structure**: * 1 OWNER (founder or technical lead) * 1-2 ADMIN (co-founders or senior engineers) * Remaining as MEMBER (developers, analysts) **Permissions Strategy**: * Keep it simple: use default permissions * Grant custom permissions as needed (e.g., MEMBER + create agents) * Admins handle member management **Example**: ``` OWNER: alice@startup.com (CEO) ADMIN: bob@startup.com (CTO) MEMBER: charlie@startup.com (Engineer, custom: + create agents) MEMBER: diana@startup.com (Data Analyst, custom: + view_analytics) ``` **Structure**: * 1 OWNER (organization admin) * 3-5 ADMIN (team leads) * Most as MEMBER (engineers, analysts, designers) * External stakeholders as MEMBER with read-only custom permissions **Permissions Strategy**: * Use custom permissions for team leads (MEMBER + invite + create agents) * Restrict agent deletion to ADMIN only * Grant read-only permissions to external stakeholders **Example**: ``` OWNER: admin@company.com ADMIN: engineering-lead@company.com ADMIN: data-lead@company.com MEMBER: developer-1@company.com (custom: + invite + create agents) MEMBER: developer-2@company.com (custom: + create agents) MEMBER: analyst-1@company.com (custom: + view_analytics) MEMBER: client@external.com (custom: read-only, no create/edit) ``` **Structure**: * 1 OWNER (platform admin) * 5-10 ADMIN (department leads) * Bulk as MEMBER (department members) * External stakeholders as MEMBER with read-only permissions **Permissions Strategy**: * Use sub-teams (feature: coming soon) * Strict permission controls * Custom roles for specialized access * Dedicated admin for member management **Example**: ``` OWNER: platform-admin@enterprise.com ADMIN: ai-team-lead@enterprise.com ADMIN: data-team-lead@enterprise.com ADMIN: product-lead@enterprise.com MEMBER: ai-engineer-1@enterprise.com (custom: + create agents) MEMBER: ai-engineer-2@enterprise.com (custom: + create agents) MEMBER: data-scientist-1@enterprise.com (custom: + view_analytics) MEMBER: product-manager-1@enterprise.com (custom: + create agents) MEMBER: executive@enterprise.com (custom: read-only) MEMBER: partner@external.com (custom: read-only) ``` ### Role Assignment Guidelines **Assign OWNER to**: * Organization creator * Ultimate decision maker * Person responsible for billing * Most trusted individual **Limit to**: 1-2 people (minimize risk) **Assign ADMIN to**: * Team leads * Senior engineers * Department heads * Trusted power users **Limit to**: 10-15% of team size **Assign MEMBER to**: * Regular engineers * Analysts * Designers * Most contributors * External stakeholders (with custom read-only permissions) **Default role**: 80-90% of team ### Permission Management Tips **✅ DO**: * Start with default permissions for each role * Grant least privilege (minimum permissions needed) * Review permissions quarterly * Use custom permissions sparingly * Document why custom permissions were granted * Revoke permissions immediately upon offboarding **❌ DON'T**: * Give everyone ADMIN role "just in case" * Grant organization deletion permission liberally * Use custom permissions without documentation * Leave pending invitations indefinitely * Forget to remove members who leave *** ## Troubleshooting Common Issues **Symptoms**: Member invited but email not received **Possible Causes**: * Email in spam folder * Email delivery delay (up to 5 minutes) * Typo in email address * Email provider blocking automated emails **Solutions**: 1. Check spam/junk folder 2. Wait 5-10 minutes and check again 3. Verify email address is correct (cancel and re-invite if typo) 4. Resend invitation from members list 5. Contact support if issue persists **Symptoms**: "Remove Member" button is disabled or shows error **Possible Causes**: * Trying to remove last OWNER * Insufficient permissions (not OWNER/ADMIN) * Member is yourself (cannot self-remove OWNER) **Solutions**: * **Last OWNER**: Promote another member to OWNER first * **Permissions**: Ask an ADMIN or OWNER to remove * **Self-removal**: Ask another ADMIN/OWNER to remove you **Symptoms**: Member cannot perform expected actions **Debugging Steps**: 1. Check member's role in **Team > Members** 2. Verify custom permissions (if any) 3. Check organization status (must be ACTIVE) 4. Verify member status is ACTIVE (not PENDING or SUSPENDED) **Solutions**: * Update role to appropriate level * Add custom permissions if needed * Reactivate member if suspended * Wait for organization approval if PENDING **Symptoms**: User signed up but still shows PENDING **Possible Causes**: * User signed up with different email * Email case mismatch ([user@example.com](mailto:user@example.com) vs [User@example.com](mailto:User@example.com)) * Database sync delay **Solutions**: 1. Verify user signed up with exact email from invitation 2. Check for case sensitivity (emails should be case-insensitive) 3. Wait 1-2 minutes for database sync 4. Cancel invitation and re-invite with correct email 5. Contact support if issue persists **Symptoms**: Members list cluttered with old pending invitations **Cleanup Steps**: 1. Go to **Team > Members** 2. Filter by "Pending" status 3. Sort by "Invited Date" (oldest first) 4. Cancel invitations older than 30 days 5. Resend invitations for current team members **Prevention**: * Cancel invitations after 7 days of no response * Follow up via personal email * Use expiration dates (feature: coming soon) *** ## API Reference for Team Management ### List Organization Members **Endpoint**: `GET /api/organizations/{organization_id}/members` **Authentication**: Bearer token required **Query Parameters**: * `status` (optional): Filter by status (PENDING, ACTIVE, SUSPENDED) * `role` (optional): Filter by role (OWNER, ADMIN, MEMBER) **Response**: ```json theme={null} { "members": [ { "id": "member-uuid-1234", "email": "alice@company.com", "user_id": "user-uuid-5678", "role": "OWNER", "status": "ACTIVE", "permissions": { /* full permissions */ }, "invited_by": "system", "invited_at": "2026-01-01T00:00:00Z", "joined_at": "2026-01-01T00:05:00Z", "last_active_at": "2026-01-01T15:30:00Z" }, { "id": "member-uuid-9012", "email": "bob@company.com", "user_id": null, "role": "MEMBER", "status": "PENDING", "permissions": { /* default MEMBER permissions */ }, "invited_by": "user-uuid-5678", "invited_at": "2026-01-01T10:00:00Z", "joined_at": null, "last_active_at": null } ], "total": 2 } ``` ### Invite Member **Endpoint**: `POST /api/organizations/{organization_id}/members` **Authentication**: Bearer token with `members.invite` permission **Request Body**: ```json theme={null} { "email": "newmember@company.com", "role": "MEMBER", "permissions": { // Optional: custom permissions // If omitted, default permissions for role are applied } } ``` **Response**: ```json theme={null} { "id": "member-uuid-3456", "email": "newmember@company.com", "user_id": null, "role": "MEMBER", "status": "PENDING", "permissions": { /* default MEMBER permissions */ }, "invited_by": "user-uuid-5678", "invited_at": "2026-01-01T16:00:00Z" } ``` ### Update Member Role **Endpoint**: `PUT /api/organizations/{organization_id}/members/{member_id}` **Authentication**: Bearer token with `members.edit_permissions` permission **Request Body**: ```json theme={null} { "role": "ADMIN", "permissions": { // Optional: custom permissions // If omitted, default permissions for new role are applied } } ``` **Response**: ```json theme={null} { "id": "member-uuid-3456", "email": "newmember@company.com", "user_id": "user-uuid-7890", "role": "ADMIN", "status": "ACTIVE", "permissions": { /* updated permissions */ }, "updated_at": "2026-01-01T16:30:00Z" } ``` ### Remove Member **Endpoint**: `DELETE /api/organizations/{organization_id}/members/{member_id}` **Authentication**: Bearer token with `members.remove` permission **Response**: ```json theme={null} { "message": "Member removed successfully", "removed_member_id": "member-uuid-3456" } ``` **Error (Last OWNER)**: ```json theme={null} { "error": "Cannot remove the last owner of the organization", "code": "LAST_OWNER_PROTECTION" } ``` *** ## What's Next? Learn how to manage multiple organizations effectively Access admin dashboard for API key and organization settings *** ## Additional Resources * **Organization API Reference**: Full API documentation for all organization endpoints * **Permission Schema**: Detailed permission structure and validation rules * **Security Best Practices**: Guidelines for secure team management * **Audit Logs**: Track member activity and permission changes (feature: coming soon) **Need Help?** Contact your organization OWNER or Junis support at [contact@junis.ai](mailto:contact@junis.ai) for assistance with team management. # Soul & Identity Source: https://docs.junis.ai/guides/soul-identity Define your AI team personality — how your agents think, speak, and interact ## Overview **Soul & Identity** lets you define a unique personality for your entire AI team. Instead of generic AI responses, your agents speak with a consistent voice that reflects your brand, values, and communication style. > Give your AI team a soul — not just instructions, but a worldview and personality. **Organization-Wide**: Soul & Identity settings apply to all agents in your organization. Define once, and every agent inherits the personality. *** ## Two Components Soul & Identity consists of two complementary configuration files: **How Your AI Thinks** Define the philosophy, values, and reasoning style of your AI team. * Core beliefs and worldview * Decision-making principles * Thinking patterns and priorities * Ethical guidelines **How Your AI Communicates** Define the speaking style, tone, and persona of your AI team. * Communication tone (formal, casual, playful) * Language preferences and patterns * Response structure and formatting * Personality traits *** ## Setting Up Soul & Identity ### Via Web Dashboard Go to **Team > Soul & Identity** in the sidebar. Click on **soul.md** and define how your AI thinks: ```markdown theme={null} ## Core Philosophy We believe in clarity over complexity. Every response should empower the user to take action, not just understand theory. ## Decision Principles - Prioritize practical solutions over theoretical perfection - When uncertain, ask rather than assume - Always consider the user's expertise level ``` Click on **identity.md** and define how your AI speaks: ```markdown theme={null} ## Communication Style - Warm but professional tone - Use simple language — avoid jargon unless the user uses it first - Start with the answer, then provide context - Use bullet points for lists of 3+ items ## Personality - Helpful and encouraging - Honest about limitations - Celebrates user achievements ``` Click **Save**. Changes take effect on the next conversation. ### Via AI Agent Admins can update Soul & Identity through conversation: ``` "Update our AI's soul to prioritize data-driven decision making." "Change the identity to use a more casual, friendly tone." ``` ### Via Junis Magic Use [Junis Magic](/guides/junis-magic) MCP tools for programmatic access: | Tool | Purpose | | - | - | | `get_soul_directory` | List all soul files | | `get_soul_content` | Read soul or identity content | | `update_soul_content` | Update soul or identity | *** ## How It Works ```mermaid theme={null} graph TD Soul[soul.md — Philosophy & Thinking] --> Injection[Automatic Injection] Identity[identity.md — Style & Persona] --> Injection Injection --> Orchestrator[Every Agent Conversation] Orchestrator --> Response[Personalized, Consistent Response] ``` When a user starts a conversation: 1. The system loads your organization's Soul & Identity files 2. These are automatically injected into the agent's context 3. The agent's responses naturally reflect the defined personality 4. **No manual configuration per agent** — it's automatic *** ## Examples ### Tech Startup ```markdown theme={null} We're builders at heart. Our AI reflects our startup culture: - Ship fast, iterate often - Technical excellence matters - Empower developers to build, not bureaucrats to block - Data over opinions, experiments over debates ``` ```markdown theme={null} - Direct and concise — developers' time is precious - Use code examples over lengthy explanations - Reference official docs when applicable - Casual but technically precise ``` ### Enterprise Consulting ```markdown theme={null} We deliver strategic clarity. Our AI: - Provides evidence-based recommendations - Considers multiple stakeholder perspectives - Maintains confidentiality as a core value - Balances innovation with risk management ``` ```markdown theme={null} - Professional and polished tone - Structure responses with clear sections - Always cite sources and data - Use "we recommend" rather than "you should" ``` ### Creative Agency ```markdown theme={null} Creativity is our currency. Our AI: - Thinks laterally and explores unconventional ideas - Values originality over convention - Encourages experimentation - Sees constraints as creative catalysts ``` ```markdown theme={null} - Energetic and inspiring tone - Use vivid descriptions and metaphors - Ask thought-provoking questions - Celebrate bold ideas, gently redirect impractical ones ``` ### E-Commerce Brand ```markdown theme={null} Customer satisfaction is everything. Our AI: - Puts the customer experience first - Resolves issues proactively - Recommends products based on actual needs, not upselling - Learns from customer feedback patterns ``` ```markdown theme={null} - Friendly and approachable — like a knowledgeable store associate - Use the customer's name when available - Keep responses concise for quick resolution - Always offer next steps or alternatives ``` *** ## Access Control | Role | Permissions | | - | - | | **Owner** | Full read/write access to Soul & Identity | | **Admin** | Full read/write access to Soul & Identity | | **Member** | Read-only (experiences the personality, cannot change it) | *** ## Best Practices **Recommended**: * Write in **markdown** format for clear structure * Keep Soul focused on **principles** (not specific instructions) * Keep Identity focused on **communication patterns** (not task logic) * Test changes by having a few conversations after updating * Use examples in your Identity to illustrate desired tone **Avoid**: * Don't put task-specific instructions in Soul/Identity (those belong in agent instructions) * Don't make Soul/Identity too long — concise guidelines are more effective * Don't contradict agent-level instructions with Soul/Identity *** ## Soul & Identity vs. Agent Instructions | Aspect | Soul & Identity | Agent Instructions | | - | - | - | | **Scope** | Entire organization | Individual agent | | **Purpose** | Personality & values | Task-specific behavior | | **Who sets it** | Admin/Owner | Admin/Owner | | **Content type** | Philosophy, tone, style | Workflows, rules, tool usage | | **Example** | "Be concise and data-driven" | "When user asks about orders, search the CRM first" | **Think of it this way**: Soul & Identity defines *who* your AI is. Agent instructions define *what* each agent does. *** ## Related Guides User-level memory and personalization Write effective agent instructions Manage Soul & Identity via MCP Learn about agent types *** **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for help defining your AI team's personality. # Tools Overview Source: https://docs.junis.ai/guides/tools/overview Extend your agents with powerful tools and capabilities ## What Are Tools in Junis? Tools are **specialized functions** that agents can call to perform specific actions or access external data. They extend your agent's capabilities beyond text generation, enabling real-world interactions. Think of tools as **skills** your agents can learn: * 🔍 **Search tools**: Find information from web, databases, or documents * 📄 **Document tools**: Process PDF, Excel, Word, PowerPoint files * 🔌 **External connectors**: MCP, Composio OAuth (200+ apps), GitHub, Firecrawl * 🧠 **RAG tools**: Search and create knowledge base documents with AI-powered retrieval * 💾 **Memory tools**: Read and update persistent user memory across conversations * ✨ **Soul tools**: Read and update organization AI personality * 📚 **Wiki tools**: Read and grow your organization's shared knowledge base (second brain) * 🤝 **Squad tools**: Discover and message other squads you have access to * 🖥️ **Code execution**: Run code in an isolated cloud sandbox or on your own connected device **Agent Intelligence**: Agents automatically decide when and how to use tools based on user requests. You don't need to manually trigger them. *** ## Tool Types Junis supports four types of tools, each designed for specific use cases: **Python Functions** Custom async functions you build for your specific needs. *Examples*: Search databases, call APIs, process data **Official Anthropic Tools** Prebuilt document processors powered by Claude. *Examples*: PDF extraction, Excel analysis, PPTX creation **Third-Party Integrations** Connect to external systems and services. *Examples*: Salesforce, DataLab, GitHub, Firecrawl (via MCP) **Document Retrieval** Auto-generated search tools for uploaded documents. *Examples*: Company policies, employee handbooks, product specs *** ## How Agents Use Tools ### 🤖 Automatic Tool Selection Agents use tools intelligently based on: 1. **User Request**: What the user is asking for 2. **Tool Descriptions**: What each tool can do (from docstrings) 3. **Conversation Context**: Previous messages and results **Example Workflow**: ``` User: "Find the latest news about artificial intelligence" ↓ Agent thinks: - User wants to search the web for current information - I have a tool called "google_search" - Its description says it searches the web for real-time information - I should use this tool with query="latest artificial intelligence news" ↓ Agent calls: google_search("latest artificial intelligence news") ↓ Tool returns: { "results": [ { "title": "AI Breakthrough in 2026", "url": "https://example.com/ai-news", "snippet": "Recent developments in AI technology..." } ] } ↓ Agent responds: "I found recent AI news. Here are the latest developments: AI Breakthrough in 2026..." ``` **No Manual Intervention**: Once tools are configured, agents use them automatically without user or admin prompting. ### 📊 Tool Execution Flow ```mermaid theme={null} sequenceDiagram participant User participant Agent participant Tool Registry participant External System User->>Agent: "Analyze this PDF document" Agent->>Tool Registry: Check available tools Tool Registry-->>Agent: claude_pdf_processor Agent->>Tool: claude_pdf_processor(file_url, instruction) Tool->>External System: Claude Skills API External System-->>Tool: Extracted text + analysis Tool-->>Agent: Result object Agent-->>User: "The document contains 15 pages and discusses..." ``` *** ## Available Built-in Tools Junis provides a comprehensive set of prebuilt tools ready to use: ### Document Processing (Claude Skills) **`claude_pdf_processor`** * Extract text from PDFs * Extract tables and data * Create new PDFs * Merge multiple PDFs * Convert to images *Use Case*: "Extract all customer information from this invoice PDF" **`claude_xlsx_processor`** * Analyze spreadsheets * Extract specific columns/rows * Generate charts * Perform calculations * Export filtered data *Use Case*: "Calculate total revenue from this sales spreadsheet" **`claude_pptx_creator`** * Create presentations * Add slides with content * Insert images and charts * Apply templates * Export to PDF *Use Case*: "Create a 5-slide presentation about our Q4 results" **`claude_docx_processor`** * Extract text from Word docs * Create new documents * Track changes * Manage comments * Convert to other formats *Use Case*: "Extract all action items from this meeting notes document" ### Search Tools **`google_search`** Search the web for real-time information using Google. **Parameters**: * `query` (str): Search query **Returns**: Search results with titles, URLs, and snippets **Use Case**: "Find the latest AI news" or "Search for Python tutorials" ### Built-in Web Search (LLM Feature) **NEW**: Agents can now perform web searches automatically during response generation without explicitly calling a tool. **Enable Web Search** in Admin > Agents > Edit Agent: ```yaml theme={null} Agent Configuration: Enable Web Search: ✅ ON ``` **How It Works**: * When enabled, the LLM can automatically search the web during response generation * No explicit tool call required - the model decides when to search * Uses LiteLLM's `web_search_options` with `search_context_size: "medium"` **Difference from google\_search Tool**: | Feature | Built-in Web Search | google\_search Tool | | - | - | - | | **Activation** | Automatic (LLM decides) | Explicit tool call | | **Configuration** | Toggle in Agent settings | Assign tool to agent | | **Best For** | General knowledge queries | Targeted searches | **When to Use**: Enable for agents that need to answer questions about current events, recent news, or real-time information without explicit search requests. ### Memory & Soul Tools (Auto-Injected) **Automatic**: These tools are automatically available to all orchestrator agents. No configuration needed. **`update_user_memory(action, content)`** Read or update the user's persistent profile (name, role, preferences). **Actions**: * `read`: View the user's current profile * `update`: Save updated profile content *Use Case*: Agent learns user's name, role, or communication preferences during conversation **`save_to_eternal_memory(action, content)`** Store permanent facts and standing instructions that persist across all conversations. **Actions**: * `read`: View stored eternal memories * `add`: Append a new memory entry * `update`: Replace all eternal memory content *Use Case*: User says "remember that I always want bullet points" or "don't forget my deadline is Friday" **`update_soul(action, content)`** Read or update the organization's AI thinking philosophy (Admin/Owner only). *Use Case*: Admin asks "update our AI to prioritize data-driven decisions" **`update_identity(action, content)`** Read or update the organization's AI communication style (Admin/Owner only). *Use Case*: Admin asks "make our AI more casual and friendly" Learn more about these systems: [Memory System](/guides/memory-system) | [Soul & Identity](/guides/soul-identity) ### Organization Wiki Tools (Auto-Injected) **Automatic**: Wiki tools are available to all orchestrator agents with no configuration. Every member's agent can read and grow the organization's shared knowledge base. | Tool | What It Does | | - | - | | `wiki_list` | Reads the wiki index — every document with its one-line summary. Agents check this before answering to see if your team already knows the answer | | `wiki_read` | Reads one document's full content | | `wiki_create` | Saves new knowledge as a document ("save this to the wiki") | | `wiki_update` | Replaces a document's content entirely | | `wiki_edit` | Makes a precise partial edit without touching the rest | | `wiki_append` | Adds entries to an existing document over time (FAQ items, meeting notes) | There is **no delete tool** by design — documents can only be deleted by admins in **Team → Wiki**. See the [Organization Wiki guide](/guides/wiki-second-brain) for details. ### Squad Collaboration Tools (Auto-Injected) **Automatic**: Every orchestrator can discover and message other squads you have access to. | Tool | What It Does | | - | - | | `squad_list` | Lists the squads you can reach — your active memberships plus public squads | | `call_another_squad` | Sends a message to another squad's orchestrator and returns its answer into your current chat | See [Squad Collaboration](/guides/orchestration/squad-collaboration) for access rules, billing, and chain limits. ### Code Execution (Cloud Sandbox & Your Device) Agents can run code and shell commands in two very different places: Code-execution **Skills** run in an isolated cloud sandbox — no access to your computer, isolated per organization and session. Output streams into the chat in real time. With the [Desktop Agent (`npx junis`)](/guides/npx-junis), agents can work directly on **your own computer** — files, shell commands, browser automation — with live output in chat. Rule of thumb: use the cloud sandbox for safe, disposable code runs; connect a device when the work needs **your** files and apps. ### RAG Document Upload (Agent Tool — Requires Configuration) **`upload_rag_document(datastore_name, file_name, content, description?)`** Agents can **generate and store their own knowledge base documents** as Markdown files. This is unique to Junis — your AI agents can research, synthesize, and save information for future retrieval. **Configuration Required**: Unlike memory and soul tools (which are auto-injected), `upload_rag_document` must be explicitly assigned to an agent via **Admin > Agents > Edit Agent > Tools**. **How it works**: 1. Agent generates content (e.g., research summary, analysis report) 2. Agent calls `upload_rag_document` with the content as Markdown 3. A `.md` file is created, uploaded to a RAG DataStore, and indexed 4. Future conversations can search and retrieve this knowledge **Use Case**: "Research the latest AI trends and save a summary to our knowledge base" ``` Agent: 1. Searches the web for AI trends 2. Synthesizes findings into a structured document 3. Calls upload_rag_document( datastore_name="Research Library", file_name="ai-trends-q1-2026", content="# AI Trends Q1 2026\n\n## Key Developments..." ) 4. Document is indexed and searchable by all agents ``` **DataStore Auto-Creation**: If the specified DataStore doesn't exist, the tool creates it automatically. The file extension `.md` is auto-appended if not provided. ### Payment Tools (AP2/x402 Protocol) **Premium Feature**: Payment tools enable USDC-based agent usage fees with 80/20 revenue split (Agent Owner 80%, Platform 20%). **`require_payment(agent_id, network?)`** Request payment before agent execution. **When to Use**: * Premium agents that charge per use * Computationally expensive operations * Third-party API costs **Returns**: * Payment requirements (amount, wallet, network) * EIP-3009 nonce for signature * 80/20 split details **`verify_payment(nonce, payment_type)`** Verify EIP-3009 payment signatures. **Payment Types**: * `agent_owner`: 80% to agent owner * `platform_fee`: 20% to platform **Auto-Settlement**: When both payments verified, automatically settles on-chain. *** ## When to Use Tools ### ✅ Use Tools When You Need To: * **Access External Data**: Databases, APIs, file storage * **Perform Complex Operations**: File processing, calculations, analysis * **Integrate Third-Party Services**: Salesforce, GitHub, Firecrawl * **Retrieve Uploaded Documents**: RAG search through company docs ### ❌ Don't Build Tools For: * **Simple Text Generation**: Agents already excel at this * **One-Time Tasks**: Better to do manually * **Highly Variable Logic**: Better in agent instructions * **Duplicate Functionality**: Use existing tools or MCP platforms *** ## Getting Started with Tools ### Step 1: Choose Your Approach **Fastest**: Junis provides 15+ prebuilt tools. 1. Go to **Admin > Agents** 2. Edit your agent 3. Select tools from the Tools section 4. Save Done! Your agent can now use those tools. **Recommended**: Use Model Context Protocol for external services. 1. Go to **Team > MCP** 2. Connect platforms (GitHub, Firecrawl, etc.) 3. Assign to agents See [MCP Integration Guide](/guides/mcp/overview) for details. **For Advanced Use Cases**: Junis supports custom tool development for specialized business logic that isn't covered by built-in tools. **Process**: 1. **Contact Junis Support** at [contact@junis.ai](mailto:contact@junis.ai) 2. Describe your use case and requirements 3. Our team will develop and register the custom tool 4. You assign it to your agents via Admin UI **Note**: Most users don't need custom tools - the built-in library covers common use cases. **For Knowledge Bases**: Upload PDFs, docs, etc. 1. Go to **Team > RAG Knowledge** 2. Create or select DataStore 3. Upload files 4. Assign RAG tool to agent See [RAG Guide](/guides/tools/rag) for details. ### Step 2: Configure Agent Tools **Admin Dashboard** → **Agents** → **Edit Agent** ```yaml theme={null} Agent Configuration: Name: Customer Support Agent Type: LLM_AGENT Tools: Regular Tools: - claude_pdf_processor - google_search RAG Tools: - rag_company-policies_1234567890 MCP Platforms: - GitHub (for repository management) - Firecrawl (for web scraping) ``` ### Step 3: Test with Chat ``` You: "Search for recent AI trends and create a GitHub issue to track them" Agent: 1. Uses google_search("recent AI trends 2026") 2. Formats the results 3. Uses GitHub MCP to create issue Response: "I found the latest AI trends and created a tracking issue in your repository." ``` *** ## Tool Best Practices **✅ DO**: * Choose specific tools for specific tasks * Write clear tool descriptions (docstrings) * Test tools individually before assigning to agents * Monitor tool usage in logs * Handle errors gracefully * Use RAG for frequently accessed documents **❌ DON'T**: * Add too many tools (max 10 per agent recommended) * Use tools with unclear purposes * Forget to update tool descriptions * Ignore tool errors in production * Hard-code sensitive data in tools *** ## Real-World Examples ### 1. Customer Support Agent **Tools**: * `claude_pdf_processor`: Extract info from uploaded docs * GitHub MCP: Create support tickets as issues * `rag_faq_documents`: Search company FAQ **User**: "A customer asked about our return policy" **Agent Flow**: 1. Search `rag_faq_documents` for "return policy" 2. Extract relevant sections 3. Format a friendly response 4. Optionally create GitHub issue for tracking ### 2. Data Analysis Agent **Tools**: * `claude_xlsx_processor`: Analyze Excel files * `google_search`: Find market trends and research * `claude_pptx_creator`: Generate presentation **User**: "Analyze Q4 sales and create a presentation" **Agent Flow**: 1. Use `claude_xlsx_processor` to analyze sales spreadsheet 2. Use `google_search` for market research and context 3. Use `claude_pptx_creator` to generate slides ### 3. Premium Agent with Payments **Tools**: * `require_payment`: Gate agent access with USDC payment * `claude_pdf_processor`: Document processing * `google_search`: Research capabilities **User**: "Run premium analysis workflow" **Agent Flow**: 1. Call `require_payment(agent_id="premium_agent")` 2. Wait for user payment (MetaMask) 3. Verify payment signatures 4. Proceed with premium analysis using available tools *** ## Tool Categories Reference | Category | Tools | Primary Use | | - | - | - | | **Document Processing** | claude\_pdf\_processor, claude\_xlsx\_processor, claude\_pptx\_creator, claude\_docx\_processor | File analysis, creation, extraction | | **Web Search** | google\_search, built-in web search | Real-time web information retrieval | | **Memory** | update\_user\_memory, save\_to\_eternal\_memory | Persistent user memory management | | **Soul & Identity** | update\_soul, update\_identity | Organization AI personality | | **Organization Wiki** | wiki\_list, wiki\_read, wiki\_create, wiki\_update, wiki\_edit, wiki\_append | Shared team knowledge (second brain) | | **Squad Collaboration** | squad\_list, call\_another\_squad | Cross-squad questions and workflows | | **Code Execution** | code-execution skills (cloud sandbox), Desktop Agent tools | Run code in the cloud or on your device | | **RAG Search** | rag\_ (auto-generated) | Document knowledge retrieval | | **RAG Upload** | upload\_rag\_document | Agent-generated knowledge creation | | **Payment** | require\_payment, verify\_payment | Premium agent gating | | **External** | MCP platforms, Composio OAuth | Third-party service integration | *** ## Performance Considerations ### ⚡ Tool Execution Speed | Tool Type | Typical Latency | Notes | | - | - | - | | **Database Query** | 100-500ms | Depends on query complexity | | **External API** | 1-5 seconds | Network dependent | | **Claude Skills** | 2-10 seconds | File processing time | | **RAG Search** | 1-3 seconds | Vertex AI Search latency | | **Payment** | 5-30 seconds | User signature + on-chain settlement | **Optimization Tip**: Group multiple simple queries into batch tools rather than making agents call tools repeatedly. *** ## Troubleshooting **Symptom**: Tool is configured but agent doesn't call it **Possible Causes**: * Tool description (docstring) unclear * Agent instruction doesn't mention tool usage * Tool name not intuitive **Solution**: 1. Review tool docstring for clarity 2. Add tool usage examples to agent instruction 3. Test with explicit request: "Use \[tool\_name] to..." **Symptom**: Tool called but no result returned **Possible Causes**: * Tool has unhandled exception * Missing required parameters * External service down **Solution**: 1. Check agent session logs for errors 2. Test tool directly via API 3. Verify all environment variables set 4. Check external service status **Symptom**: RAG search completes but finds nothing **Possible Causes**: * Documents not indexed yet (still processing) * Search query too specific * DataStore not created properly **Solution**: 1. Go to **Team > RAG Knowledge** and check indexing status 2. Wait for "COMPLETED" status (can take 5-60 minutes) 3. Try broader search terms 4. Re-upload documents if status is "FAILED" **Symptom**: require\_payment call takes too long **Possible Causes**: * Network congestion (Base blockchain) * CDP Facilitator API slow * User hasn't signed MetaMask prompt **Solution**: * Payment tools have 30-second timeout * Check Base network status: [https://status.base.org](https://status.base.org) * Ensure user completes MetaMask signature promptly * For testnet, use Base Sepolia (faster, free) *** ## What's Next? Persistent AI memory across conversations Define your AI team's personality Upload documents and create knowledge bases Connect external services via MCP and Composio *** ## Additional Resources * **Claude Skills**: [https://docs.anthropic.com/en/docs/build-with-claude/tool-use](https://docs.anthropic.com/en/docs/build-with-claude/tool-use) - Official Anthropic documentation on tool use **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for assistance with tool development and custom implementations. # RAG Knowledge Base Source: https://docs.junis.ai/guides/tools/rag Upload documents to enhance AI agent knowledge ## Overview RAG (Retrieval-Augmented Generation) allows your AI agents to access custom knowledge bases. Upload documents and let agents search through your organization's proprietary information. **Prerequisites**: * Admin (or Owner) role in Junis — uploading documents requires Admin access * An agent configured with a **Gemini model** (RAG tools only work with Gemini) * Documents in a supported format (see table below) **Model Restriction**: RAG tools use Google Vertex AI Search (VertexAiSearchTool), which is **only compatible with Gemini models**. Agents using Anthropic Claude or other models cannot use RAG tools. Ensure your agent is configured with a Gemini model (e.g., `gemini/gemini-2.5-flash`) when assigning RAG tools. *** ## Supported File Types | Format | Extension | Max Size | | - | - | - | | Plain Text | .txt | 10MB | | PDF | .pdf | 50MB | | HTML | .html, .htm | 10MB | | JSON | .json | 10MB | | JSONL | .jsonl, .ndjson | 10MB | | Markdown | .md, .markdown | 10MB | | Word | .docx | 50MB | | PowerPoint | .pptx | 10MB | *** ## Uploading Documents ### Step 1: Access RAG Knowledge 1. Navigate to **Team > RAG Knowledge** 2. You must have Admin access to upload documents ### Step 2: Create a DataStore 1. Click **Create DataStore** 2. Enter a name for your knowledge base 3. Select files to upload ### Step 3: Wait for Indexing After upload, documents are processed and indexed: | Status | Description | | - | - | | `pending` | Waiting to start processing | | `uploading` | Files being uploaded to cloud storage | | `indexing` | Documents being processed and indexed | | `processing` | Processing in progress | | `completed` | Knowledge base ready to use | | `failed` | Processing error (can be retried) | Indexing typically takes 1-5 minutes depending on document size and quantity. *** ## Using RAG with Agents Once documents are indexed, they can be assigned to AI agents. ### Admin Configuration 1. Go to **Admin > Agents** 2. Edit an agent 3. In the **RAG Tools** section, select the DataStore 4. Save changes ### How It Works When a user asks a question: 1. The agent searches the knowledge base 2. Relevant document sections are retrieved 3. The agent uses this context to provide accurate answers *** ## Agent-Generated Documents Junis agents can **create their own knowledge base entries** using the `upload_rag_document` tool. This enables powerful workflows where agents research, synthesize, and store knowledge for future use. **Requires Configuration**: The `upload_rag_document` tool is **not auto-injected**. An admin must explicitly assign this tool to agents via **Admin > Agents > Tools**. Agent-generated documents are always saved as Markdown (`.md`) files — this is distinct from manual uploads which support PDF, DOCX, PPTX, and other formats. ### How It Works 1. Agent generates content (research summaries, analysis reports, processed data) 2. Agent calls `upload_rag_document` with the content 3. Document is automatically uploaded and indexed 4. All agents with access to that DataStore can search the new content ### Example Workflow ``` User: "Research the top 5 competitors in our market and save it to our knowledge base." Agent: 1. Searches the web for competitor information 2. Compiles findings into a structured Markdown document 3. Calls upload_rag_document: - datastore_name: "Market Research" - file_name: "competitor-analysis-march-2026" - content: "# Competitor Analysis\n\n## Company A..." 4. Document is indexed and searchable Next conversation: User: "What do we know about our competitors?" Agent: Searches "Market Research" DataStore and retrieves the analysis ``` ### Key Features * **Auto-Create DataStore**: If the named DataStore doesn't exist, it's created automatically * **Markdown Format**: Content is stored as `.md` files for clean indexing * **Immediate Indexing**: Documents begin indexing as soon as they're uploaded * **Cumulative Knowledge**: Agents build an ever-growing knowledge base over time **Pro Tip**: Combine `upload_rag_document` with scheduled tasks to automatically research and store information on a recurring basis. *** ## Best Practices * Group related documents together * Use clear, descriptive filenames * Remove duplicate content * Use clear headings and structure * Break long documents into sections * Include context in document titles * Update documents when information changes * Remove outdated content * Re-index after major updates *** ## Troubleshooting **Possible causes:** * File type not supported * File exceeds size limit * Network connection issue **Solution:** * Check file format and size * Try uploading again * Contact support if issue persists **Possible causes:** * DataStore not assigned to agent * Documents not fully indexed * Query doesn't match document content **Solution:** * Verify DataStore is linked to the agent * Check indexing status is "ready" * Try rephrasing your question *** ## Related Guides Learn about AI agents All available tools including memory and soul Programmatic RAG API access Understand usage costs # Scheduled Tasks Source: https://docs.junis.ai/guides/tools/schedules Automate agent execution with flexible scheduling options ## What You'll Learn This guide shows you how to automate agent workflows using **Scheduled Tasks** powered by **Celery RedBeat**. Your agents can run automatically on specific schedules, saving time and ensuring consistent execution. **Prerequisites**: * Admin role in Junis (for creating schedules) * At least one configured agent (Orchestrator recommended) * Understanding of basic cron syntax (optional, for CRON schedules) *** ## What are Scheduled Tasks? **Scheduled Tasks** enable you to run agent workflows automatically at specific times or intervals without manual triggering. ### Real-World Example **Without Scheduled Tasks**: ``` 8:00 AM: Manually open Junis chat 8:02 AM: Type "Generate daily sales report" 8:05 AM: Wait for agent to complete 8:07 AM: Copy report and send to team ...repeat every day ``` **With Scheduled Tasks**: ``` One-time setup: - Create schedule: "Generate daily sales report" at 8:00 AM - Agent runs automatically every day - Results saved to dedicated session - View report anytime in chat history ``` **Benefits**: * ⏰ **Time savings**: No manual execution needed * 📊 **Consistency**: Same task runs at exact same time * 📝 **History tracking**: All execution results saved in sessions * 🔄 **Flexibility**: Adjust schedules without code changes *** ## How Scheduling Works ### Architecture Overview Junis uses **Celery** (distributed task queue) + **RedBeat** (dynamic scheduler) + **Redis** (storage) for robust scheduling. ```mermaid theme={null} graph TB A[Admin UI] -->|Create Schedule| B[FastAPI API] B -->|Save to DB| C[PostgreSQL] B -->|Register with| D[RedBeat Scheduler] D -->|Store metadata in| E[Redis] D -->|At scheduled time| F[Celery Worker] F -->|Execute| G[Orchestrator Agent] G -->|Save results to| H[Chat Session] H -->|View in| I[Chat UI] ``` ### Key Components **Distributed Task Queue** Manages background task execution across multiple workers. **Dynamic Scheduler** Stores schedules in Redis, allows runtime modification without restart. **Schedule Storage** Stores schedule definitions, execution history, metadata. **Metadata Cache** Stores RedBeat scheduler state, provides fast access. ### Execution Flow Admin creates schedule via UI or API. Schedule saved to PostgreSQL + registered with RedBeat. RedBeat continuously checks Redis for schedules due to run. Uses distributed lock to prevent duplicate execution. When scheduled time arrives, RedBeat triggers Celery worker to execute task. Celery worker calls Orchestrator agent with saved prompt. Agent processes using configured workflow. Agent response saved to chat session. Execution status recorded in `scheduled_task_runs` table. *** ## Schedule Types Junis supports 6 schedule types for different use cases: **One-Time Execution** Run task immediately or at a specific future time. **Use Cases**: * Reminder at specific date/time * One-off report generation * Testing scheduled workflows **Configuration**: ```json theme={null} { "schedule_type": "ONCE", "schedule_config": { "execute_at": "2026-01-01 23:59:00" // Optional } } ``` **Behavior**: * If `execute_at` not provided: Runs immediately * If `execute_at` provided: Runs at specified time * Automatically disabled after execution **Run Every N Minutes** Run task at fixed minute intervals. **Use Cases**: * Real-time monitoring (every 5 minutes) * Frequent data sync (every 15 minutes) * High-frequency alerts (every 1 minute) **Configuration**: ```json theme={null} { "schedule_type": "EVERY_MINUTES", "schedule_config": { "interval": 15 // Run every 15 minutes } } ``` **Example**: `{"interval": 5}` = Run every 5 minutes **Resource Usage**: Frequent schedules (every 1-5 minutes) consume more resources. Use sparingly. **Run Every N Hours** Run task at fixed hour intervals. **Use Cases**: * Hourly reports * Periodic data aggregation * Regular health checks **Configuration**: ```json theme={null} { "schedule_type": "EVERY_HOURS", "schedule_config": { "interval": 2 // Run every 2 hours } } ``` **Example**: `{"interval": 4}` = Run every 4 hours **Daily at Specific Time** Run task every day at a specific hour and minute. **Use Cases**: * Daily reports (8 AM every day) * Nightly data backups (2 AM every day) * End-of-day summaries (6 PM every day) **Configuration**: ```json theme={null} { "schedule_type": "DAILY", "schedule_config": { "hour": 8, // 0-23 "minute": 0 // 0-59 } } ``` **Example**: `{"hour": 8, "minute": 30}` = 8:30 AM every day **Weekly on Specific Day** Run task once per week on a specific day of the week. **Use Cases**: * Weekly status reports (Monday 9 AM) * Weekly team sync summaries (Friday 5 PM) * Weekend data analysis (Saturday midnight) **Configuration**: ```json theme={null} { "schedule_type": "WEEKLY", "schedule_config": { "day_of_week": 1, // 0=Sun, 1=Mon, 2=Tue, ..., 6=Sat "hour": 9, "minute": 0 } } ``` **Example**: `{"day_of_week": 1, "hour": 9, "minute": 0}` = Every Monday at 9:00 AM **Monthly on Specific Date** Run task once per month on a specific day of the month. **Use Cases**: * Monthly financial reports (1st of month) * Monthly performance reviews (Last Friday) * Quarterly business summaries (1st of quarter months) **Configuration**: ```json theme={null} { "schedule_type": "MONTHLY", "schedule_config": { "day_of_month": 1, // 1-31 "hour": 8, "minute": 0 } } ``` **Example**: `{"day_of_month": 1, "hour": 8, "minute": 0}` = 1st of every month at 8:00 AM **Day 29-31**: For months without these days (e.g., February), task will run on last day of month. *** ## Setting Up Scheduled Tasks ### Step-by-Step Guide 1. Log in to Junis as Admin 2. Go to **Team Management** → **Task Planning** 3. Click **"New Schedule"** or **"+ Add Schedule"** button **Schedule Name**: * Descriptive name (e.g., "Daily Sales Report") * Must be unique within your organization * Used for identification in schedule list **Prompt**: * The instruction/question sent to Orchestrator * Same as typing in chat interface * Can include specific parameters, agent names, etc. **Example Prompts**: ``` "Generate daily sales summary for yesterday" "Check all open support tickets and create priority list" "Analyze top 10 trending products and send report via email" "Run DataAnalysisWorkflow for customer insights" ``` Select one of the 5 schedule types: **Immediate or Future**: * Leave blank: Runs immediately * Set date/time: Runs at specific future time **Time Selection**: * Hour: 0-23 (24-hour format) * Minute: 0-59 Example: `8:30` = 8:30 AM every day **Day & Time**: * Day of week: Sunday (0) to Saturday (6) * Hour & Minute Example: `Monday at 9:00 AM` **Date & Time**: * Day of month: 1-31 * Hour & Minute Example: `1st of month at 8:00 AM` **Custom Expression**: * Minute, Hour, Day of week, Day of month, Month * Use wildcards (`*`), ranges (`9-17`), lists (`0,30`) Example: `Every 30 minutes during business hours (9-5, Mon-Fri)` **Two Options**: **Create fresh session for each execution** **Pros**: * ✅ Clean slate each time * ✅ Easy to find specific execution results * ✅ No context pollution from previous runs **Use Cases**: * Daily/weekly/monthly reports * Independent analysis tasks * One-off scheduled actions **Session Naming**: * Auto-generated: `Scheduled: {schedule_name} - {timestamp}` * Example: `Scheduled: Daily Sales Report - 2026-01-01 08:00` **Use same session for all executions** **Pros**: * ✅ Continuous conversation thread * ✅ Agent remembers previous executions * ✅ Single place to view all history **Use Cases**: * Iterative analysis (agent learns over time) * Continuous monitoring (track changes) * Thread-based workflows **Configuration**: * Specify `session_id` of existing session * Session must exist before schedule creation * All executions append to this session **Session Limits**: Using existing session may cause very long conversation history. Consider starting new session periodically. 1. Review all settings 2. Toggle **"Enabled"** switch to ON 3. Click **"Create Schedule"** button **What Happens Next**: * Schedule saved to database * RedBeat registers schedule in Redis * Next run time calculated and displayed * Schedule appears in schedule list *** ## Monitoring Scheduled Tasks ### Schedule List View **Location**: Admin → Schedules **Columns Displayed**: | Column | Description | Example | | - | - | - | | **Name** | Schedule name | "Daily Sales Report" | | **Type** | Schedule type | DAILY, WEEKLY, CRON | | **Status** | Enabled/Disabled | 🟢 Enabled / 🔴 Disabled | | **Next Run** | Next execution time | "2026-01-02 08:00:00" | | **Last Run** | Last execution time | "2026-01-01 08:00:00" | | **Run Count** | Total executions | 142 | | **Actions** | Quick actions | Edit, Delete, Run Now, View History | **Status Indicators**: * 🟢 **Enabled**: Schedule active, will run at next scheduled time * 🔴 **Disabled**: Schedule paused, will not run automatically * ⏳ **Running**: Currently executing * ✅ **Completed**: ONCE schedule that has finished ### Execution History **Viewing History**: 1. Find schedule in list 2. Click **"View History"** or history icon 3. See list of all executions **Execution Record Columns**: | Column | Description | | - | - | | **Status** | PENDING, RUNNING, SUCCESS, FAILED | | **Started At** | Execution start timestamp | | **Completed At** | Execution end timestamp | | **Duration** | Time taken to complete | | **Session Link** | Direct link to chat session with results | | **Error Message** | Error details (if failed) | **Status Meanings**: * ⏳ **PENDING**: Task queued, waiting for worker * ▶️ **RUNNING**: Currently executing * ✅ **COMPLETED**: Completed successfully * ❌ **FAILED**: Execution failed (see error message) ### Viewing Execution Results **Method 1: From Execution History**: 1. Click session link in execution record 2. Opens chat session with that execution's conversation 3. View agent's response and any generated outputs **Method 2: From Chat Sessions List**: 1. Go to **Chat** → **Sessions** 2. Filter by schedule name or date 3. Sessions named `Scheduled: {schedule_name} - {timestamp}` **What You'll See**: * User message: `[Scheduled] {prompt}` * Agent response: Full conversation output * Tool calls: Any tools used during execution * Timestamps: Exact execution time *** ## Managing Scheduled Tasks ### Editing Schedules In schedule list, click **Edit** icon (pencil) next to schedule name. **Can Change**: * Schedule name * Prompt * Schedule type and configuration * Session settings **Cannot Change**: * Organization (schedules are org-specific) * Creator (for audit purposes) Click **"Save"** → Schedule updated in database and RedBeat re-registers with new settings. **Effect of Changes**: * **Next run time recalculated** based on new schedule * **Existing execution history preserved** * **Currently running execution not affected** (runs with old settings) ### Enabling/Disabling Schedules **Toggle Switch**: * In schedule list, use toggle switch in **Status** column * Or click schedule name → Toggle "Enabled" switch in details **When to Disable**: * Temporary pause (e.g., during maintenance) * Seasonal schedules (only needed certain times of year) * Testing/debugging (disable production schedule while testing) **Disabled Schedules**: * ❌ Will not run automatically * ✅ All settings and history preserved * ✅ Can re-enable anytime without reconfiguration ### Running Schedules Manually **"Run Now" Button**: * Executes schedule immediately, ignoring schedule time * Does not affect next scheduled run time * Useful for testing or one-off manual execution **Process**: 1. Click **"Run Now"** button next to schedule 2. Confirmation dialog appears 3. Click **"Confirm"** 4. Task executes immediately 5. New execution record created in history **Use Cases**: * Testing new schedule before activation * Running report outside normal schedule * Recovering from missed execution ### Deleting Schedules **Permanent Action**: Deleting a schedule is irreversible. All execution history will also be deleted. **Steps**: 1. Click **Delete** icon (trash can) next to schedule 2. Confirmation dialog: "Are you sure you want to delete this schedule?" 3. Type schedule name to confirm (security measure) 4. Click **"Delete Permanently"** **What Gets Deleted**: * ✅ Schedule definition from database * ✅ RedBeat entry from Redis * ✅ All execution history records * ❌ Chat sessions (preserved for reference) **Alternative: Disable Instead**: * If you might need the schedule later, **disable** instead of delete * Keeps all history and settings intact *** ## Use Cases & Examples ### 1. Daily Sales Report **Scenario**: Generate sales summary every morning at 8 AM for previous day's data. **Configuration**: ```json theme={null} { "name": "Daily Sales Report", "prompt": "Generate sales summary for yesterday. Include: total revenue, top products, regional breakdown.", "schedule_type": "DAILY", "schedule_config": { "hour": 8, "minute": 0 }, "session_config": { "use_existing": false } } ``` **Workflow**: 1. 8:00 AM: Orchestrator receives prompt 2. Orchestrator calls SalesAnalysisAgent 3. Agent queries sales database for yesterday's data 4. Agent generates formatted report 5. Report saved to new session 6. Team opens chat session to view report **Benefits**: * No manual data gathering every morning * Consistent report format * Historical archive of daily reports in chat sessions ### 2. Weekly Team Sync Summary **Scenario**: Every Friday at 5 PM, summarize week's activities and prepare for Monday meeting. **Configuration**: ```json theme={null} { "name": "Weekly Team Sync Summary", "prompt": "Summarize this week's activities: completed tasks, open issues, blockers. Format as meeting agenda.", "schedule_type": "WEEKLY", "schedule_config": { "day_of_week": 5, // Friday "hour": 17, "minute": 0 }, "session_config": { "use_existing": true, "session_id": "weekly-sync-session-123" } } ``` **Workflow**: 1. Friday 5:00 PM: Orchestrator processes prompt 2. Orchestrator calls ProjectManagementAgent 3. Agent queries GitHub, Jira, Slack for activity data 4. Agent generates meeting agenda 5. Result appended to continuous "Weekly Sync" session 6. Team reviews agenda in chat before Monday meeting **Benefits**: * Automatic weekly meeting preparation * Continuous thread shows historical summaries * Agent learns patterns over time (same session) ### 3. Real-Time Monitoring (Every 15 Minutes) **Scenario**: Monitor website uptime and alert on issues every 15 minutes during business hours. **Configuration**: ```json theme={null} { "name": "Website Uptime Monitor", "prompt": "Check website status: response time, error rate, availability. Alert if any issues.", "schedule_type": "CRON", "schedule_config": { "minute": "*/15", // Every 15 minutes "hour": "9-17", // 9 AM to 5 PM "day_of_week": "1-5" // Monday to Friday }, "session_config": { "use_existing": true, "session_id": "uptime-monitor-session" } } ``` **Workflow**: 1. Every 15 minutes (9 AM-5 PM, Mon-Fri): Check website 2. Orchestrator calls MonitoringAgent 3. Agent makes HTTP requests to check endpoints 4. If issues detected: * Agent sends alert via Slack MCP * Agent creates Jira ticket via GitHub MCP 5. Status logged to continuous monitoring session **Benefits**: * Proactive issue detection * Real-time alerts to team * Complete monitoring history in one session ### 4. Monthly Financial Close **Scenario**: On 1st of each month, generate financial summary for previous month. **Configuration**: ```json theme={null} { "name": "Monthly Financial Close", "prompt": "Generate financial summary for last month: revenue, expenses, profit margin, cash flow. Compare with previous month.", "schedule_type": "MONTHLY", "schedule_config": { "day_of_month": 1, "hour": 6, "minute": 0 }, "session_config": { "use_existing": false } } ``` **Workflow**: 1. 1st of month, 6:00 AM: Orchestrator processes prompt 2. Orchestrator calls FinancialAnalysisAgent 3. Agent queries accounting database for previous month 4. Agent calculates metrics and trends 5. Agent generates PDF report via claude\_pdf\_processor 6. Report saved to new session 7. CFO reviews report in chat or downloads PDF **Benefits**: * Automated monthly close process * Consistent financial reporting * Early morning execution ensures reports ready for business day ### 5. Dynamic Content Curation **Scenario**: Twice daily (8 AM, 6 PM), curate trending industry news and share with team. **Configuration**: ```json theme={null} { "name": "Industry News Curation", "prompt": "Find trending news in our industry. Summarize top 5 stories with key insights and implications for our business.", "schedule_type": "CRON", "schedule_config": { "minute": "0", "hour": "8,18", // 8 AM and 6 PM "day_of_week": "*", "day_of_month": "*", "month_of_year": "*" }, "session_config": { "use_existing": true, "session_id": "news-curation-thread" } } ``` **Workflow**: 1. 8 AM & 6 PM: Orchestrator processes prompt 2. Orchestrator calls ContentCuratorAgent 3. Agent uses Firecrawl MCP to scrape industry news sites 4. Agent uses google\_search\_web to find trending topics 5. Agent summarizes top 5 stories 6. Agent posts summary to Slack via Slack MCP 7. Summary also saved to continuous news curation session **Benefits**: * Team stays informed without manual research * Twice daily updates (morning and evening) * Historical archive of curated news *** ## Troubleshooting **Symptom**: Schedule is enabled but doesn't run at scheduled time **Possible Causes**: 1. **Celery Worker offline**: Worker not running to pick up tasks 2. **RedBeat not running**: Scheduler not monitoring schedules 3. **Redis connection issue**: RedBeat can't access schedule metadata 4. **Timezone mismatch**: Schedule configured in different timezone **Solution**: **Check System Services**: 1. Go to Admin Dashboard → System Health 2. Check "Celery Worker Status" (should show "Active") 3. Check "Schedule Processor Status" (should show "Running") 4. Check "Redis Connection" (should show "Connected") **Verify Schedule Registration**: 1. Admin → Schedules 2. Find the schedule in the list 3. Check "Status" column shows "Enabled" 4. Check "Last Run At" has recent timestamp **Timezone Consideration**: * All schedules use **UTC timezone** by default * If schedule should run at 8 AM local time (e.g., KST = UTC+9): * Set hour to `-1` from local (7 for 8 AM KST, which is 23 UTC previous day) * Or use cron with timezone awareness (future feature) **Symptom**: Execution status shows FAILED in history **Possible Causes**: 1. **Prompt error**: Invalid or malformed prompt 2. **Agent not found**: Referenced agent doesn't exist 3. **Permission issue**: Agent lacks required permissions/tools 4. **External service down**: Tool API unavailable (Salesforce, DataLab, etc.) 5. **Timeout**: Execution took too long **Solution**: **Check Error Message**: 1. Go to Schedule → View History 2. Click failed execution record 3. Read error message in details **Common Errors**: | Error Message | Cause | Solution | | - | - | - | | "Agent 'XYZ' not found" | Agent deleted or renamed | Update schedule with correct agent name | | "Tool 'ABC' not available" | Agent doesn't have required tool | Add tool to agent configuration | | "Timeout after 300s" | Execution took too long | Simplify prompt or increase timeout | | "API rate limit exceeded" | Too many API calls | Reduce schedule frequency or batch requests | | "Permission denied" | Agent lacks permissions | Check agent tools and API keys | **Test Manually**: 1. Copy prompt from schedule 2. Open chat with Orchestrator 3. Paste prompt manually 4. Observe execution and error details 5. Fix issues before re-enabling schedule **Symptom**: Schedule runs multiple times at same scheduled time **Possible Causes**: 1. **Multiple Celery Workers**: Different workers pick up same task 2. **RedBeat lock issue**: Distributed lock not working 3. **Schedule duplicated**: Multiple schedules with same name **Solution**: **Identify and Remove Duplicate Schedules**: 1. Admin → Schedules 2. Look for schedules with the same name but different IDs 3. Select duplicates and delete all but one 4. Click "Save Changes" **Verify Schedule Lock**: 1. Admin Dashboard → System Health 2. Check "Schedule Locking" status (should show "Enabled") 3. If disabled, contact support to re-enable **Check Worker Configuration**: 1. Admin Dashboard → Workers 2. Verify only 1 Celery Worker is registered (recommended for schedule processing) 3. If multiple workers, reduce to single worker via Admin settings **Symptom**: `next_run_at` stays same after execution **Possible Causes**: 1. **ONCE schedule**: Single execution completed, schedule disabled 2. **Database update failure**: DB transaction failed after execution 3. **Schedule calculation error**: Next run time calculation bug **Solution**: **Check Schedule Type**: * ONCE schedules: Expected behavior (automatically disabled) * Other types: Should update to next scheduled time **Re-enable Schedule to Recalculate**: 1. Admin → Schedules 2. Find the schedule with stale `next_run_at` 3. Click Edit 4. Toggle "Enabled" to OFF, then click Save 5. Toggle "Enabled" to ON again, then click Save 6. System automatically recalculates `next_run_at` based on schedule type **Need Manual Fix?**: If the above steps don't work, contact support with: * Schedule name * Current `next_run_at` value * Expected next run time * Support team will update schedule metadata **Symptom**: Execution shows SUCCESS but can't find results in chat **Possible Causes**: 1. **Session ID mismatch**: Results saved to unexpected session 2. **Organization filter**: Viewing different organization's chat 3. **Session not created**: New session creation failed **Solution**: **Find Session from Execution Record**: 1. Admin → Schedules → View History 2. Find successful execution 3. Click session link → Opens exact session with results **Verify Session Creation**: 1. Admin → Schedules → View History 2. Find the execution record 3. Check "Session ID" field (should be non-empty) 4. If empty, execution created no session (check execution error in details) 5. If filled, click the session link to view chat history **Verify Organization**: * Ensure you're viewing correct organization in UI * Use organization switcher (top right) to change if needed * Schedule executions only visible to same organization **Check Agent Logs**: ```bash theme={null} # View Orchestrator logs for session activity kubectl logs -n {namespace} deployment/fastapi-app --tail=100 | grep "session_id" ``` *** ## Best Practices ### Schedule Design **✅ DO**: * Use descriptive schedule names ("Daily Sales Report" not "Report1") * Write clear, specific prompts (avoid ambiguity) * Test prompts manually in chat before scheduling * Start with ONCE type to test, then convert to recurring * Use new sessions for independent tasks (reports, analysis) * Use existing sessions for continuous threads (monitoring, iterative analysis) * Set reasonable execution frequency (avoid excessive API usage) * Document complex schedules in schedule description **❌ DON'T**: * Create too many schedules (start simple, add as needed) * Use extremely frequent schedules (every minute) without reason * Schedule long-running tasks during peak hours * Rely on schedules for time-critical operations (use webhooks instead) * Forget to monitor execution history regularly * Create duplicate schedules (check existing first) * Use ambiguous prompts that may yield unpredictable results ### Prompt Writing Tips **Effective Scheduled Prompts**: 1. **Be Specific**: ``` ❌ "Analyze sales" ✅ "Generate sales summary for yesterday: total revenue, top 10 products, regional breakdown" ``` 2. **Include Context**: ``` ❌ "Check issues" ✅ "Check all open support tickets tagged 'urgent'. Create priority list with customer impact assessment." ``` 3. **Specify Output Format**: ``` ❌ "Analyze data" ✅ "Run DataAnalysisWorkflow for sales metrics. Output as PDF and send summary via email." ``` 4. **Use Relative Time**: ``` ✅ "yesterday", "last week", "previous month" (dynamic) ❌ "2026-01-01" (hardcoded date, not useful for recurring schedules) ``` 5. **Chain Actions**: ``` ✅ "Analyze sales data, generate PDF report, upload to Google Drive, send notification to #sales-team Slack channel" ``` ### Monitoring & Maintenance **Regular Checks** (Weekly): * Review execution history for failures * Check success rate (aim for >95%) * Monitor execution duration (watch for increasing times) * Review session list for outdated sessions * Audit schedules for relevance (disable unused) **When to Adjust Schedules**: * ⬆️ **Increase frequency**: If insights/reports needed sooner * ⬇️ **Decrease frequency**: If data doesn't change often enough * 🔄 **Change timing**: If current time causes conflicts or peak load * ⏸️ **Disable temporarily**: During maintenance, holidays, or testing ### Security Considerations **Sensitive Data**: * Scheduled prompts visible to all admins in organization * Execution results saved to chat sessions * Don't include passwords, API keys, or secrets in prompts * Use environment variables or secure storage for credentials **Access Control**: * Only admins can create/edit/delete schedules * Schedule executions run with creator's permissions * Agents inherit organization's tool and MCP access * Execution results visible to all organization members (via chat) **Audit Trail**: * All schedule creations/modifications logged * Execution history preserved (start time, end time, status) * Session links provide full conversation history * Review audit logs regularly for unusual activity *** ## Advanced Configuration ### Conditional Scheduling (Workaround) **Goal**: Run schedule only if certain condition met (e.g., only if new data available). **Approach**: Use agent instruction to check condition first. **Example Prompt**: ``` Check if new sales data available for yesterday. If yes, generate report. If no, skip execution and respond "No new data." ``` **Agent Logic**: * Agent checks data source * If data exists: Proceeds with analysis * If no data: Returns early with "No new data" message * Execution still recorded (SUCCESS status) but no actual work done **Alternative**: Use webhook-triggered execution instead of time-based schedule (future feature). ### Multi-Organization Schedules **Challenge**: Run same schedule across multiple organizations. **Current Limitation**: Schedules are organization-scoped, not global. **Workaround**: 1. Create schedule in Organization A 2. Export schedule configuration (copy JSON) 3. Switch to Organization B 4. Create new schedule with same configuration 5. Repeat for other organizations **Future Feature**: Template-based schedule deployment for multi-org. ### Scheduled Workflows with Sub-Agents **Use Case**: Schedule complex workflow involving multiple agents. **Example**: ```json theme={null} { "name": "Complete Customer Analysis Workflow", "prompt": "Run full customer analysis: 1) Search customer in CRM, 2) Get sales data, 3) Analyze engagement metrics, 4) Generate insights report, 5) Send email summary.", "schedule_type": "WEEKLY", "schedule_config": { "day_of_week": 1, "hour": 9, "minute": 0 } } ``` **Orchestrator handles**: * Routing to appropriate sub-agents (Sequential workflow) * Passing context between agents * Error handling for each step * Final result aggregation **Benefits**: * Single schedule triggers entire workflow * Orchestrator manages complexity * Results appear in one chat session *** ## Performance & Scalability ### Schedule Limits **Recommended Limits** (per organization): | Schedule Type | Recommended Max | Notes | | - | - | - | | **CRON (frequent)** | 10 | Every minute/15 minutes | | **DAILY** | 50 | Once per day | | **WEEKLY** | 20 | Once per week | | **MONTHLY** | 10 | Once per month | | **ONCE** | Unlimited | Auto-disabled after execution | **Why Limits Matter**: * Celery Worker has finite capacity * Concurrent executions may slow down system * Redis storage grows with more schedules * Database queries increase with execution history ### Optimization Tips **Performance Best Practices**: * Stagger schedules (avoid all at 8:00 AM, spread across minutes) * Use longer intervals for non-critical schedules * Clean up old execution history periodically * Disable unused schedules instead of deleting (can re-enable) * Monitor Celery Worker resource usage * Use CRON wisely (avoid excessive frequency) **Staggering Example**: ``` ❌ Bad: - Report A: Daily at 8:00 AM - Report B: Daily at 8:00 AM - Report C: Daily at 8:00 AM All compete for resources simultaneously ✅ Good: - Report A: Daily at 8:00 AM - Report B: Daily at 8:05 AM - Report C: Daily at 8:10 AM Spread across time, smooth execution ``` ### Monitoring Celery Health **Check Worker Status via Admin UI**: 1. Admin Dashboard → System Health 2. View "Active Workers" section showing worker count and status 3. View "Registered Tasks" count 4. View "Worker Memory Usage" graphs **Monitor System Resources**: 1. Admin Dashboard → Metrics 2. Check "Cache Memory Usage" (Redis) 3. Check "Queue Size" showing pending schedules 4. Check "Execution Success Rate" showing schedule health **Data Maintenance**: To manage execution history or need detailed execution reports, contact support with: * Date range * Schedule name (optional) * Type of report needed (execution history, timeline, resource usage) *** ## What's Next? Learn about tools that agents can use Upload documents for agent knowledge *** ## Additional Resources * **Celery Documentation**: [docs.celeryproject.org](https://docs.celeryproject.org/) * **RedBeat Scheduler**: [redbeat.readthedocs.io](https://redbeat.readthedocs.io/) * **Cron Expression Generator**: [crontab.guru](https://crontab.guru/) * **Junis API Reference**: [api.junis.ai/docs](https://api.junis.ai/docs) **Support**: For scheduling issues, contact Junis support at [contact@junis.ai](mailto:contact@junis.ai). # Widget Embed Source: https://docs.junis.ai/guides/widget-embed Put your AI team on any website with a single iframe — visitors chat without logging in The Junis embed widget places your organization's AI team on any website with one iframe snippet. Visitors chat with your orchestrator — with streaming responses, file uploads, and Markdown rendering — without creating a Junis account. Usage is billed to your organization, and per-visitor rate limits plus an origin whitelist keep the widget under your control. **Prerequisites**: * **OWNER** or **ADMIN** role in your Junis organization (widget settings live in Organization Settings → Publish tab) * A website where you can paste an ` ``` Your AI team is live on your website. Keep the `allow` attribute. `microphone` is what lets visitors dictate a message with the mic button in the input box — without it the browser refuses the request and the widget falls back to attaching a recording. `on-device-speech-recognition` lets Chrome recognize speech locally, so the audio never leaves the visitor's device. *** ## How do billing and rate limits work? * **Billing goes to the organization owner.** Anonymous visitors never pay — every message a visitor sends is billed to your organization, exactly like your own usage. * **Per-visitor message limit.** Each visitor can send a limited number of messages per 24 hours — **default 20**, adjustable between **1 and 500** in the Publish tab. This caps your worst-case cost per visitor. * **Organization-wide daily caps.** Independently of the per-visitor limit, each organization has a daily ceiling on new anonymous conversations and on anonymous messages, so a burst of new visitors cannot multiply cost. The defaults are generous (1,000 new conversations and 10,000 messages per day); contact us if your traffic needs more. * **Returning visitors continue their conversation.** When a visitor comes back, the widget resumes their previous session instead of starting from scratch. Start with the default limit of 20 messages per visitor and raise it once you have observed real traffic and cost in your usage dashboard. *** ## Can I restrict what the widget can do? Some capabilities are **always** off in anonymous conversations regardless of settings: image and video generation, code execution, wiki and memory writes, database queries, and the knowledge-graph reasoning tools. Beyond that, you can **block specific tools** for widget conversations at the organization level, so anonymous visitors get answers without triggering sensitive or expensive capabilities (for example, tools that write to internal systems). Your members' normal chat sessions are unaffected. *** ## Widget vs. team link — what's the difference? | | Embed widget (`/embed/{slug}`) | Team link (`/team/public/{slug}`) | | - | - | - | | **Login required** | No (anonymous mode) | Yes — visitors must sign in | | **Works inside an iframe** | Yes — designed for it | No — opens in a new tab only | | **Where it lives** | Your own website | Junis-hosted team page | Use the **widget** to put your AI team on your website. Use the **team link** to share a login-gated page hosted on Junis. *** ## Why does the widget say my domain is not allowed? The widget verifies the **parent page's origin as reported by the visitor's browser** against your allowed-domains list. If a whitelisted site is still blocked, check these in order: 1. **Origins must match exactly** — scheme, subdomain, and port all count. `https://www.example.com`, `https://example.com`, and `http://example.com` are three different origins; whitelist each one you actually serve the widget from. 2. **Nested iframes** — the widget verifies **every** parent frame, not just the immediate one. If a page builder, preview tool, or another site wraps your page in its own iframe, that wrapper's origin must also be on the list — otherwise the widget is blocked on purpose. 3. **Very old browsers** — browsers that expose neither `location.ancestorOrigins` nor a referrer cannot prove where the widget is embedded, so those visitors are blocked by design. All current Chrome, Edge, Safari, and Firefox versions are supported. The origin check is enforced when the visitor's session is created — it cannot be satisfied by the iframe's own network headers (those always point to the widget itself), so simply proxying or copying the iframe tag onto an unlisted site will not work in a browser. *** ## Next Steps Give your widget answers from your team's shared knowledge base Design the AI team that powers your widget Control which tools your agents can use Manage roles — widget settings require OWNER or ADMIN *** **Questions?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for help setting up your embedded AI team. # Organization Wiki (Second Brain) Source: https://docs.junis.ai/guides/wiki-second-brain Your organization's shared knowledge base that every agent reads and writes automatically ## What is the Organization Wiki? The **Organization Wiki** is your team's **second brain** — a shared, persistent knowledge base that every agent in your organization can read and write during conversations. Product specs, compliance rules, buyer profiles, FAQs, meeting outcomes, research findings — anything your team learns once becomes knowledge every agent can use forever. > Tell your agent *"save this to the wiki"* — and from that moment, every agent in your organization knows it. Unlike personal memory, wiki knowledge belongs to the **organization**. Every orchestrator agent reads the same wiki. Wiki tools are automatically available to all orchestrator agents. No configuration, no tool assignment needed. Any member can grow the wiki just by chatting. Agents save research results, decisions, and facts as they work. The wiki index is generated automatically from your documents — it never goes stale and needs no manual upkeep. *** ## How Agents Use the Wiki During any conversation, your orchestrator agent can: | Action | What Happens | | - | - | | **Check the index** | Before answering, the agent scans the wiki index (every document + its one-line summary) to see if your organization already knows the answer | | **Read a document** | Opens the full content of a relevant document and uses it to answer | | **Create a document** | Saves new knowledge when you ask ("save this to the wiki", "remember this for the team") or after completing research | | **Update / edit** | Replaces a document or makes a precise partial edit without touching the rest | | **Append** | Adds new entries to an existing document over time — FAQ items, meeting notes, new cases | **Deletion is human-only.** Agents can create and edit wiki documents, but only admins can delete them — from the **Team → Wiki** page. This protects your knowledge base from accidental loss. ### Example Conversations ``` You: "우리 제품 반품 정책이 뭐였지?" Agent: (checks wiki index → reads policy/returns) "반품은 수령 후 14일 이내..." You: "Research the top 3 competitors and save a summary to the wiki." Agent: (researches, then creates competitors/overview with a one-line summary) "Saved to the wiki — every agent in your team can now reference this." You: "이번 회의 결론 위키의 회의록에 추가해줘." Agent: (appends to meetings/notes) "Added today's decisions to the meeting notes." ``` *** ## Managing the Wiki (Team → Wiki) Admins and owners can manage wiki documents directly in the dashboard at **Team → Wiki**: The left sidebar lists all documents with their keys and one-line summaries. Select any document to read it rendered as Markdown, with its last-updated time. Click **New Document** to add knowledge manually — give it a document key (e.g. `product/faq`), Markdown content, and a one-line summary for the index. **Edit** updates content in place. **Delete** removes a document permanently (agents will no longer see it). Deletion is only possible here — never through chat. Changes made in the dashboard are visible to agents **immediately** — the next message in any conversation already uses the updated knowledge. ### Document Keys Documents are organized with hierarchical keys — `category/topic` — in any language: ``` product/faq compliance/광고심의 customers/enterprise-buyers meetings/notes ``` Each document also carries a **one-line summary** shown in the index, which is how agents decide what to read. *** ## Who Can Do What | Action | Members | Admins / Owners | | - | :-: | :-: | | Agents read wiki in chat | ✅ | ✅ | | Agents save/edit wiki via chat | ✅ | ✅ | | Manage documents in **Team → Wiki** | ❌ | ✅ | | Delete documents | ❌ | ✅ (dashboard only) | The wiki is **isolated per organization** — one squad's wiki is never visible to another squad. *** ## Wiki vs Memory vs Soul Junis has three complementary knowledge systems. Use the right one for the right job: | | **Memory** | **Soul & Identity** | **Wiki (Second Brain)** | | - | - | - | - | | **Scope** | Per user (personal) | Per organization | Per organization | | **Answers** | "Who is this user?" | "Who is our AI?" | "What does our team know?" | | **Contains** | Your profile, preferences, standing instructions | AI philosophy, personality, communication style | Product specs, policies, FAQs, research, decisions | | **Managed at** | Your **Memory** page | **Team → Soul** | **Team → Wiki** | | **Grown by** | Your own conversations | Admins | Every member's conversations | Rule of thumb: facts about **you** → Memory. How the AI should **behave** → Soul & Identity. Facts your **whole team** should share → Wiki. *** ## Best Practices **Recommended**: * Ask agents to save reusable findings: "위키에 저장해줘" after research, analysis, or decisions * Use consistent key prefixes (`product/`, `policy/`, `customers/`) so knowledge stays organized * Keep each document focused on one topic — agents read whole documents * Review the wiki periodically in **Team → Wiki** and prune outdated documents **Avoid**: * Storing personal preferences in the wiki (use Memory instead) * One giant document for everything — smaller focused documents retrieve better * Storing secrets or credentials — the wiki is readable by all organization members' agents *** ## What's Next? Personal, per-user persistent memory Define your AI team's personality All built-in agent capabilities Upload large document sets for semantic search # Composio Integration Source: https://docs.junis.ai/integrations/composio Connect 200+ OAuth apps to your AI agents — Gmail, Slack, GitHub, and more ## Overview **Composio** enables your Junis agents to interact with 200+ third-party applications through secure OAuth connections. Instead of managing API keys and webhooks manually, Composio handles authentication and provides ready-to-use tools for your agents. > Connect once, use everywhere. Your agents get native access to Gmail, Slack, GitHub, Jira, and 200+ more services. **No Code Required**: Connect apps through the Junis dashboard. Your agents automatically gain access to the connected tools. *** ## Supported Applications Composio supports 200+ integrations across major categories: Gmail, Slack, Microsoft Teams, Discord Google Drive, Notion, Jira, Linear, Asana GitHub, GitLab, Azure DevOps, Bitbucket Salesforce, HubSpot, Pipedrive Twitter/X, LinkedIn, Instagram Google Drive, Dropbox, OneDrive *** ## Connection Types ### Personal OAuth Connect your **personal accounts** to your agents. Only you have access to these connections. **Use cases**: * Personal Gmail for email workflows * Personal GitHub for repository management * Individual calendar access **Who can create**: Any team member ### Organization OAuth Connect **shared team accounts** that all agents in the organization can access. **Use cases**: * Company Slack workspace * Team GitHub organization * Shared CRM access **Who can create**: Admin or Owner only **Priority**: When both personal and organization connections exist for the same app, your **personal connection** takes priority — ensuring your agents use your own credentials for sensitive operations. *** ## Setting Up Composio Go to **Team > Integrations** in the sidebar. Browse available integrations and click **Connect** on the app you want (e.g., Gmail). You'll be redirected to the application's OAuth page. Sign in and grant the requested permissions. Back in Junis, you'll see the connection status change to **Active**. Go to **Admin > Agents**, edit your agent, and add the Composio integration in the **Integrations** section. *** ## How Agents Use Composio Tools Once an integration is connected and linked to an agent, the agent can use it automatically: ### Example: Gmail Agent ``` User: "Check my inbox for any urgent emails from the marketing team." Agent: 1. Connects to Gmail via Composio 2. Searches for recent emails from marketing team addresses 3. Identifies urgent/flagged messages 4. Summarizes findings for the user Response: "You have 3 unread emails from the marketing team. The most urgent is about the Q4 budget deadline tomorrow..." ``` ### Example: GitHub + Slack Workflow ``` User: "Review my open PRs and notify the team on Slack." Agent: 1. Fetches open pull requests from GitHub 2. Analyzes each PR's status and review state 3. Sends a formatted summary to the team's Slack channel Response: "I found 5 open PRs. Summary posted to #dev-updates on Slack." ``` ### Example: CRM Data Lookup ``` User: "Find all deals closing this month in our pipeline." Agent: 1. Queries the CRM for deals with close dates in the current month 2. Filters by pipeline stage 3. Calculates total expected revenue Response: "There are 12 deals expected to close this month, with a total pipeline value of $340K..." ``` *** ## Managing Connections ### Connection Status | Status | Meaning | | - | - | | **Active** | Connection is working and available to agents | | **Pending** | OAuth authorization not yet completed | | **Expired** | Token expired — re-authorize to restore access | | **Failed** | Connection error — try reconnecting | ### Disconnect an Application 1. Go to **Team > Integrations** 2. Find the active connection 3. Click **Disconnect** Disconnecting an integration immediately removes agent access to that application's tools. Active workflows using that integration will be affected. *** ## Composio via Junis Magic You can also manage Composio integrations through [Junis Magic](/guides/junis-magic) MCP tools: | Tool | Purpose | | - | - | | `list_composio_integrations` | Browse available integrations and their status | | `link_agent_composio` | Connect an integration to a specific agent | | `list_composio_connections` | Check active OAuth connections | | `get_agent_composio_status` | Verify agent's integration readiness | *** ## Best Practices **Recommended**: * Use personal connections for individual accounts (personal Gmail, GitHub) * Use organization connections for shared team resources (company Slack, team CRM) * Only connect integrations that your agents actually need * Regularly review active connections in the Integrations dashboard * Grant minimum necessary OAuth permissions (least privilege) **Important**: * Re-authorize connections promptly when tokens expire * Don't share personal connection credentials with team members — use organization connections for shared access * Test integrations after connecting before deploying to production workflows *** ## Troubleshooting **Cause**: OAuth authorization was not completed. **Solution**: Click the integration again to re-open the OAuth page and complete authorization. **Cause**: Integration not linked to the agent. **Solution**: Go to **Admin > Agents**, edit the agent, and add the Composio integration in the **Integrations** section. **Cause**: OAuth tokens have a limited lifespan. **Solution**: Go to **Team > Integrations** and re-authorize the connection. The agent will resume access automatically. **Cause**: Temporary service issue with the connected application. **Solution**: Check the application's status page. If the issue persists, disconnect and reconnect the integration. *** ## Related Guides Learn about MCP platforms Manage integrations via MCP Configure agents Slack-specific setup guide *** **Need Help?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for assistance with Composio integration setup. # Slack Integration Source: https://docs.junis.ai/integrations/slack Connect Junis AI to your Slack workspace for seamless team collaboration ## Overview Junis Slack Integration brings the power of AI directly into your team's Slack workspace. Once connected, you can: * **Chat with Junis** - Mention `@Junis` in any channel or send direct messages * **Access Multiple Organizations** - Switch between different Junis organizations using a simple pattern * **Maintain Conversation Context** - Junis remembers your conversation history within threads * **Real-time Streaming** - Get instant, streaming responses directly in Slack *** ## Quick Start ### Step 1: Add Junis to Your Workspace Visit [junis.ai/slack](https://junis.ai/slack) while logged into your Junis account. You must be logged in and belong to at least one Junis organization to install the Slack app. Click the **Add to Slack** button to begin the OAuth authorization process. Review and approve the requested permissions in the Slack authorization screen: * Send messages as Junis * View messages in channels where Junis is mentioned * Receive direct messages After authorization, you'll be redirected to a success page. Enter your **Junis API Key** to complete the setup. Don't have an API key? Go to **Admin → API Keys** in Junis to create one. ### Step 2: Start Chatting Once installed, you can interact with Junis in two ways: Type `@Junis` followed by your message in any channel where Junis is added. ``` @Junis What's the latest sales report? ``` Send a direct message to the Junis app for private conversations. ``` Create a summary of our Q4 objectives ``` *** ## Multi-Organization Support If you belong to multiple Junis organizations, you can specify which organization to use by including the organization name in asterisks: ```text Channel Message theme={null} @Junis *TooEasy* What are our pending tasks? ``` ```text Direct Message theme={null} *MyCompany* Generate a weekly report ``` **Format**: `*OrganizationName* your message here` The organization name should match exactly as it appears in your Junis account (case-sensitive). ### How It Works 1. **No organization specified** → Uses the default organization linked during setup 2. **Organization specified** → Uses the specified organization for that message 3. **Invalid organization** → Falls back to the default organization *** ## Thread Conversations Junis maintains conversation context within Slack threads. When you reply in a thread: * **Previous messages are included** - Up to 20 recent messages in the thread * **Context is preserved** - Follow-up questions work naturally * **Each thread is independent** - Different threads have separate contexts ``` You: @Junis What's our Q4 revenue? Junis: Your Q4 revenue was $2.4M, a 15% increase from Q3... You: (in thread) What were the top contributing products? Junis: Based on the Q4 data I just mentioned, the top products were... ``` *** ## Managing Your Integration ### View Connected Workspaces 1. Go to **Junis Admin Dashboard** 2. Navigate to **Team Settings → Slack Integrations** 3. View all connected Slack workspaces for your organization ### Link or Update API Key In Junis Admin, go to **Team Settings → Slack Integrations** Locate the Slack workspace you want to update Click **Link API Key** and enter your new or updated API key If you revoke or delete your API key, the Slack integration will stop working. Make sure to link a new valid API key. ### Remove Integration To disconnect Junis from your Slack workspace: 1. Go to **Team Settings → Slack Integrations** in Junis Admin 2. Click **Remove** next to the workspace you want to disconnect 3. Confirm the removal Removing the integration will: * Stop Junis from responding to mentions and DMs * Delete the stored OAuth tokens * Keep your conversation history in Junis *** ## Best Practices Provide context in your questions for better responses: ✅ Good: `@Junis What were our top 5 selling products in January 2026?` ❌ Less effective: `@Junis What sold well?` Reply in threads to maintain context and keep channels organized: * Start a new message for unrelated topics * Reply in thread for follow-up questions * Junis remembers up to 20 messages in the thread If you work with multiple organizations, explicitly specify which one: ``` @Junis *ClientCo* What's the status of Project Alpha? ``` Make sure Junis is invited to channels where you want to use it: 1. Open the channel 2. Type `/invite @Junis` 3. Junis can now respond to mentions in that channel *** ## Security & Privacy Industry-standard secure authentication protocol with CSRF protection. All access tokens are encrypted at rest using AES-256 encryption. Each organization's data is isolated and cannot be accessed by others. Only messages from workspaces with valid API keys are processed. ### Data Handling | Data Type | Storage | Access | | - | - | - | | Slack messages | Not stored by Junis | Real-time processing only | | Conversation history | Stored in Junis | Used for context | | OAuth tokens | Encrypted storage | Automatic refresh | | User identifiers | Stored for session management | No personal data shared | *** ## Troubleshooting **Possible causes:** * Junis is not invited to the channel * API key is not linked or has been revoked * The mention format is incorrect **Solutions:** 1. Invite Junis: `/invite @Junis` 2. Check API key in Admin → Team Settings → Slack Integrations 3. Use the exact format: `@Junis your message` **Cause:** The organization name in `*OrgName*` doesn't match any of your organizations. **Solution:** Check the exact organization name in your Junis account settings. **Possible causes:** * Complex queries requiring more processing time * Network connectivity issues **Solutions:** 1. For complex requests, wait a bit longer (up to 30 seconds) 2. Check your internet connection 3. Try breaking down complex questions into simpler ones **Steps:** 1. Remove the integration from Junis Admin 2. Visit [junis.ai/slack](https://junis.ai/slack) 3. Click "Add to Slack" and complete the authorization 4. Link your API key *** ## API Key Scopes When linking an API key to your Slack integration, ensure it has the appropriate scopes: | Scope | Description | Required | | - | - | - | | `orchestrator:invoke` | Send messages to Junis AI | ✅ Yes | | `orchestrator:stream` | Enable real-time streaming responses | ✅ Yes | | `sessions:read` | View conversation history | ✅ Yes | | `sessions:messages` | Access session messages | ✅ Yes | When you create an API key in Junis Admin, all required scopes are automatically included. *** ## Need Help? Email our support team for personalized assistance. Browse our full documentation for more guides and tutorials. # Welcome to Junis Source: https://docs.junis.ai/introduction Build your AI Team - Connect and orchestrate all generative AI models in harmony ## What is Junis? Junis is an AI workforce platform where organizations build, orchestrate, and deploy teams of AI agents without writing code. Each team combines 4 agent types (LLM, Sequential, Parallel, Loop) with MCP integrations, 200+ Composio OAuth apps, a shared organization wiki, persistent memory, an embeddable chat widget, and an OpenAI-compatible API. * 🤖 **AI Team Orchestration** - Connect all generative AI models through Sequential, Parallel, and Loop patterns * 🪄 **Junis Magic** - Manage your entire platform from Claude Desktop or Cursor via MCP, using natural language * 🧠 **Memory, Soul & Wiki** - Persistent user memory, organization-level AI personality, and a shared organization wiki that every agent reads and writes * 🤝 **Squad Collaboration** - One squad's orchestrator can ask another squad a question and relay the answer back * 🖼️ **Widget Embed** - Put your AI team on any website with a single iframe — visitors chat without logging in * 🔌 **Universal Integration** - MCP, Composio (200+ OAuth apps), Claude Skills, and custom tools * 📚 **Knowledge Enhancement** - RAG with agent-powered document creation and semantic search * 💻 **Desktop Agent** - Give agents full control of your device with `npx junis` * ⏰ **Workflow Automation** - Scheduled tasks and intelligent workflows Get up and running in 5 minutes with your first AI agent Integrate Junis into your applications with our OpenAI-compatible API Learn how to build powerful agent workflows step-by-step Connect to external services with Model Context Protocol Manage your platform from Claude Desktop or Cursor Persistent AI memory that learns and evolves over time Connect 200+ OAuth apps like Gmail, Slack, and GitHub A second brain shared by every agent — say "save this to the wiki" once Squads ask other squads questions and relay the answers back Embed your AI team on any website with a single iframe ## What are Junis's key features? Build complex workflows with **4 agent types**: * **LLM Agents**: Individual AI workers with specific skills * **Sequential Agents**: Execute tasks step-by-step in order * **Parallel Agents**: Run multiple tasks simultaneously * **Loop Agents**: Iterate until conditions are met Each agent can call sub-agents, creating powerful hierarchical workflows. Manage your entire Junis platform from **Claude Desktop, Cursor**, or any MCP-compatible client: * Full platform control — agents, tools, schedules, RAG, and organizations * Build and configure agent teams with **natural language** * **Team Builder** for batch creation of entire agent architectures * Cross-organization management with a single API key [Learn more](/guides/junis-magic) Give your AI agents **persistent memory**, **unique personality**, and a **shared second brain** — all injected automatically, with zero setup: * **Hierarchical memory**: Daily, monthly, quarterly, yearly consolidation * **User profiles**: Agents remember preferences, roles, and instructions * **Soul & Identity**: Define organization-wide AI personality and communication style * **Organization Wiki**: Say *"save this to the wiki"* once — every agent in your organization knows it from then on [Memory System](/guides/memory-system) | [Soul & Identity](/guides/soul-identity) | [Organization Wiki](/guides/wiki-second-brain) Let squads (AI teams) talk to **each other**: * One squad's orchestrator can discover and message another squad mid-conversation * Chain calls up to a **depth of 3**, with automatic circular-call protection * Every cross-squad call is attributed and visible in the target squad's session list [Squad Collaboration](/guides/orchestration/squad-collaboration) Put your AI team on any website with a **single iframe**: * Anonymous visitors chat without a Junis account * Origin whitelist plus per-visitor 24-hour message limits (default 20, adjustable 1–500) * Full chat experience: file uploads, Markdown, real-time streaming [Widget Embed](/guides/widget-embed) Build your own product on top of Junis: * **OpenAI-compatible API** for chat completions and streaming * **User provisioning** to create and manage end users programmatically * Billing separation so partner usage is attributed correctly [API Reference](/api-reference/external-overview) | [User Provisioning](/api-reference/user-provisioning) Connect to external services using **Model Context Protocol** and **Composio OAuth**: * **MCP**: GitHub, Firecrawl, and custom MCP servers * **Composio**: 200+ OAuth integrations (Gmail, Slack, Jira, Salesforce, etc.) * **Desktop Agent**: Full device control with `npx junis` * Supports **Remote MCP** (HTTP/SSE) and **Local MCP** (subprocess) [MCP Guide](/guides/mcp/overview) | [Composio](/integrations/composio) Enhance agents with your own knowledge: * Upload **PDF, DOCX, PPTX, TXT, HTML, JSON, Markdown** files * Powered by **Vertex AI Search** with semantic retrieval * **Agent-generated documents**: Agents can create and store their own knowledge via `upload_rag_document` * Automatic document chunking and indexing * Organization-scoped datastores Monetize your agents with on-chain payments: * **USDC-based** micropayments * **AP2/x402** protocol implementation * **MetaMask** integration * Automatic settlement and tracking * Platform fee (20%) + Agent owner (80%) split Automate workflows with flexible scheduling: * **ONCE**: Run at specific time * **DAILY**: Repeat every day * **WEEKLY**: Run on specific weekdays * **MONTHLY**: Monthly execution * **CRON**: Custom cron expressions Stream responses in real-time: * Token-by-token streaming * Agent routing events * Pipeline visualization * File upload support * Voice recorder integration ## How does Junis work? ```mermaid theme={null} graph TB User[User/Client] -->|REST API / WebSocket| Orchestrator[Orchestrator Agent] Orchestrator -->|Sequential| SeqAgent[Sequential Agent] Orchestrator -->|Parallel| ParAgent[Parallel Agent] Orchestrator -->|Loop| LoopAgent[Loop Agent] SeqAgent --> LLM1[LLM Agent 1] SeqAgent --> LLM2[LLM Agent 2] ParAgent --> LLM3[LLM Agent 3] ParAgent --> LLM4[LLM Agent 4] LoopAgent --> LLM5[LLM Agent 5] LLM1 --> Tools[Tools & Skills] LLM2 --> MCP[MCP Platforms] LLM3 --> RAG[RAG Datastores] LLM4 --> Composio[Composio OAuth] Tools --> Functions[Python Functions] MCP --> GitHub[GitHub] MCP --> Firecrawl[Firecrawl] MCP --> JunisMagic[Junis Magic] RAG --> VertexAI[Vertex AI Search] Composio --> OAuth[200+ Apps] Orchestrator -.->|Auto-injected| Memory[Memory, Soul & Wiki] Memory --> UserMem[User Memory] Memory --> Soul[Soul & Identity] Memory --> Wiki[Organization Wiki] ``` ## Who Uses Junis? Build and deploy AI agents with minimal code using our OpenAI-compatible API Automate workflows, integrate with existing tools, and scale operations Create and monetize specialized AI agents with built-in payment systems ## Support & Resources Contact us directly for enterprise inquiries and support Comprehensive guides and tutorials ## Next Steps Sign up at [junis.ai](https://junis.ai) with your Google account Build your first agent in 5 minutes following our [quickstart guide](/quickstart) Learn about [Junis Magic](/guides/junis-magic), [Memory System](/guides/memory-system), [MCP Integration](/guides/mcp/overview), and [Tools](/guides/tools/overview) Integrate Junis into your applications with our [API Reference](/api-reference/authentication) *** **Need help?** Our team is here to assist you. Contact us at [contact@junis.ai](mailto:contact@junis.ai). # Quickstart Source: https://docs.junis.ai/quickstart Build your first AI agent in 5 minutes ## What will you build? Junis is an AI workforce platform where you build and deploy a team of AI agents without writing code. This quickstart takes you from signup to a working agent in about 5 minutes: create an account, build an agent in the web interface, chat with it, and optionally call it through the OpenAI-compatible API. This quickstart guide will help you: 1. **Create an account** and set up your organization 2. **Build your first agent** using the web interface 3. **Test your agent** in the chat interface 4. **(Optional) Integrate via API** using the OpenAI-compatible endpoint **Prerequisites**: A Google account for authentication. No coding required for steps 1-3! *** ## Step 1: Create Your Account Go to [junis.ai](https://junis.ai) and click **"Sign in with Google"** You'll be redirected to the onboarding page. Fill in: * **Organization Name**: Your company or personal workspace name * **Display Name**: How you want to be addressed You'll land on the main dashboard. This is your command center! *** ## Step 2: Create Your First Agent Click **"Team Management"** dropdown in the Navigation Bar, then select **"Agent Orchestration"** Click the **"New Agent"** button Fill in the form: ```yaml theme={null} Name: MyFirstAgent Type: LLM Agent Model: anthropic/claude-sonnet-5 Description: A helpful AI assistant ``` In the **"Instructions"** field, write: ```text theme={null} You are a helpful AI assistant. When users ask questions: 1. Provide clear, concise answers 2. Use friendly, professional tone 3. Ask clarifying questions when needed Always be helpful and informative. ``` Good instructions are key to agent performance! Be specific about behavior and tone. Click **"Create"** to save your agent Agent created successfully! *** ## Step 3: Test Your Agent Click **"Chat"** in the sidebar Type a message and hit **Enter**: ```text theme={null} Hello! What can you help me with? ``` Watch as your agent responds in real-time! 🚀 Try these features: * **File Upload**: Click 📎 to attach documents * **Voice Input**: Click 🎤 to speak your message * **Session History**: See past conversations in the sidebar **Congratulations!** You've created and tested your first Junis agent! 🎉 *** ## Step 4: Integrate via API (Optional) Now let's call your agent programmatically using our OpenAI-compatible API. 1. Navigate to **"Dashboard"** → **"API Keys"** 2. Click **"Create New API Key"** 3. Enter: * **Name**: My First API Key * **Description**: Testing quickstart * **Scopes**: (leave default) 4. **IMPORTANT**: Copy the key immediately - it won't be shown again! Open your terminal and run: ```bash theme={null} curl -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "Hello from API!"} ], "stream": false }' ``` You should receive a response from your agent! For real-time token-by-token responses: ```bash theme={null} curl -N -X POST https://api.junis.ai/api/external/v1/chat/completions \ -H "X-API-Key: YOUR_API_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "user", "content": "Tell me a story"} ], "stream": true }' ``` *** ## Next Steps Learn about Sequential, Parallel, and Loop agents Integrate GitHub, Firecrawl, and more via MCP Automate workflows with flexible scheduling Explore all available tools and features *** ## Common Issues **Solutions**: 1. Check if agent is **active** (toggle in admin panel) 2. Verify LLM model is available (check status page) 3. Refresh the page and try again 4. Check browser console for errors (F12) **Solutions**: 1. Ensure you're using the correct header: `X-API-Key` 2. Check if key is still **active** in admin panel 3. Verify you're calling the correct endpoint: `/api/external/v1/chat/completions` 4. Check for typos in the API key **Possible causes**: * **Model**: Claude Opus is slower than Gemini Flash. Switch to Gemini Flash for faster responses * **RAG**: Large knowledge bases increase latency. Optimize document size * **MCP**: External service calls add time. Use caching when possible * **Network**: Check your internet connection *** ## Need Help? Browse comprehensive guides Contact our team directly Explore full API documentation # Updates & News Source: https://docs.junis.ai/updates Stay updated with the latest Junis platform announcements, features, and insights # Platform Updates **Stay ahead with Junis.** Get the latest news on product launches, feature releases, and platform insights that help you build better AI-powered businesses. *** ## Latest Updates **October 8, 2026** • External API Update Skip the orchestrator's routing call with `target_agent_id`, pre-build agents with `POST /v1/warmup`, and deliver `extra_context` to the agent that answers. Plus SSE keepalives while agents are being built. Read the details → **July 16, 2026** • Product Update Every squad now has a shared knowledge base all agents read and write automatically. Plus: squad-to-squad collaboration, website widget embeds, the white-label partner API, and the new Junis AX program. See what shipped → **January 1, 2026** • Launch Announcement Today, we're launching Junis — the platform that transforms how you build with AI. Create multi-agent orchestrations that execute complex business logic, integrate with your entire tech stack, and scale with your ambitions. Read about our vision, what you can build, and why Junis is different → *** ## What to Expect **New features, improvements, and capabilities** as we evolve the Junis platform. From major releases to quality-of-life enhancements. **New MCP platform integrations** and third-party connections. Expanding what your agents can access and control. **Engineering deep-dives** into how we build Junis. Architecture decisions, performance optimizations, and technical challenges. **Real-world examples** of businesses using Junis. Learn from others building AI-powered workflows at scale. **Where we're heading** and what's coming next. Join us in shaping the future of multi-agent AI platforms. *** ## Stay Connected Learn how to use Junis Integrate Junis into your apps Get in touch with our team *** **Want to be notified?** Important updates are also posted on our [main platform](https://junis.ai). For direct support, reach us at [contact@junis.ai](mailto:contact@junis.ai) # Junis Platform Launch: Build AI-Powered Business, Not Just Chatbots Source: https://docs.junis.ai/updates/2024-11-launch Junis brings enterprise-grade multi-agent AI to everyone. Start building intelligent business workflows today. ## The Future of Business is Agent-Driven **Today, we're launching Junis** — the platform that transforms how you build with AI. While others stop at chatbots, Junis empowers you to **build complete AI-powered businesses**. Create multi-agent orchestrations that don't just answer questions — they execute complex business logic, integrate with your entire tech stack, and scale with your ambitions. ### Why Junis Exists We built Junis because **AI should accelerate business, not complicate it**. Most AI platforms force you to choose: powerful but complex, or simple but limited. Junis refuses that compromise. **Our vision**: Every business, from startups to enterprises, should have access to **production-ready multi-agent AI** without requiring a team of AI engineers. ### What You Can Build with Junis **Automate entire workflows**, not just tasks. Agents that think, decide, and execute across your systems. **Flexible integrations** via Model Context Protocol. GitHub, Firecrawl, and custom services — easily extensible. **Multi-tenant architecture**, role-based access, API keys, usage tracking. Built for real business from day one. **Monetize your agents** with built-in USDC payments. Turn AI into a business model, not just a feature. ### The Junis Difference **Four Types of Agents, Infinite Possibilities**: * **LLM Agents**: Execute AI tasks with Claude, GPT, Gemini, and more * **Sequential Agents**: Multi-stage workflows that think step-by-step * **Parallel Agents**: Process multiple operations simultaneously * **Loop Agents**: Iterative refinement until perfection All orchestrated through a **no-code visual interface** that developers love and business users understand. ### Built on Battle-Tested Technology Junis isn't experimental — it's **production-ready from day one**: * ✅ **Google Agent Development Kit** foundation * ✅ **OpenAI-compatible API** for seamless integration * ✅ **Real-time WebSocket streaming** for instant responses * ✅ **RAG with Vertex AI Search** for knowledge-powered agents * ✅ **Claude Skills integration** for document processing * ✅ **AP2/x402 payment protocol** for agent monetization ### Start Building Today **The question isn't "Can AI help my business?"** The question is: **"What business will you build with AI?"** Junis gives you the answer: **Whatever you imagine.** Build your first agent in 5 minutes Learn advanced workflows and integrations Integrate Junis into your applications *** ## What's Next? This is just the beginning. We're launching with: * **Multi-agent orchestration** for complex workflows * **MCP integration** with 200+ platforms * **Payment system** for agent monetization * **RAG knowledge bases** for domain expertise * **Scheduled tasks** for automation * **Multi-organization** support for teams And we're already working on what's next: Natural voice conversations with your agents. Not just transcription — true voice understanding and generation. Buy, sell, and share agent workflows. Monetize your expertise, accelerate your development. Deep insights into agent performance, user behavior, and business impact. Deploy your own fine-tuned models. Full control, zero vendor lock-in. *** ## Join the AI-First Business Revolution **Junis isn't just another AI tool.** It's the platform that turns AI from a feature into your **competitive advantage**. Whether you're building: * **Customer operations** that never sleep * **Data pipelines** that think * **Content engines** that scale * **Research teams** that process millions of documents * **Trading systems** that react in milliseconds **Junis makes it possible. Today.** **Ready to build?** Sign up at [junis.ai](https://junis.ai) and start creating intelligent business workflows in minutes. Questions? Reach us at [contact@junis.ai](mailto:contact@junis.ai) *** **Stop building chatbots. Start building businesses.** Junis gives you the tools. You bring the vision. **What will you build?** # Your Organization Gets a Second Brain Source: https://docs.junis.ai/updates/2026-07-second-brain-ax Organization Wiki, squad-to-squad collaboration, website widgets, partner API, and the new Junis AX program **July 16, 2026** • Product Update This release is about one idea: knowledge and work should belong to your **organization**, not to individual chat sessions. Here's what shipped. *** ## Organization Wiki — the Second Brain Every squad now has a shared, persistent knowledge base that **all agents read and write automatically**. Say *"save this to the wiki"* in any conversation — from that moment, every agent in your organization knows it. Product specs, policies, FAQs, research findings: learned once, known forever. * Zero setup — wiki tools are auto-injected into every orchestrator * Any member grows it by chatting; admins curate it at **Team → Wiki** * Agents check the wiki index before answering, so your team's answers use your team's knowledge [Read the Organization Wiki guide →](/guides/wiki-second-brain) ## Squad-to-Squad Collaboration Squads can now talk to each other. Ask your squad *"check with the Data squad"* and it discovers, messages, and brings back the answer — across organization boundaries, with loop protection and per-squad billing. [Read the Squad Collaboration guide →](/guides/orchestration/squad-collaboration) ## Embed Your AI Team on Any Website One iframe puts a fully working AI team on your site. Visitors chat **without logging in**, protected by domain whitelisting and per-visitor rate limits. [Read the Widget Embed guide →](/guides/widget-embed) ## Partner API — White-Label AI for Your Product Provision Junis accounts for your own end users by email and act on their behalf through the OpenAI-compatible API — sessions belong to your users, billing stays with you. Build B2B2C AI features without building the AI stack. [Read the User Provisioning reference →](/api-reference/user-provisioning) ## Junis AX Program We opened **Junis AX** — a partnership program that builds your company's second brain and deploys agent squads across the whole business: data ontology, knowledge systems, finance/insight/sales/content squads, dashboards, and security. Compensation is revenue-share — we only earn when you do. [See the AX program →](/consulting/overview) *** Questions about any of these? Reach us at [contact@junis.ai](mailto:contact@junis.ai) # Faster First Turn for Partner Apps Source: https://docs.junis.ai/updates/2026-10-low-latency-first-turn Direct routing with target_agent_id, the warmup endpoint, per-request context delivered to the answering agent, and streaming keepalives during builds **October 8, 2026** • External API Update The first message of a new session used to wait for two things before the first token: the one-time build of your organization's agents, and the orchestrator's routing call that decides which agent should answer. This release lets integrators remove both from the critical path. *** ## `target_agent_id` on Chat Completions When your app already knows which agent should answer, pass its id and the orchestrator's routing LLM call is skipped on that turn. The target agent receives everything the orchestrator would have received — the user's memory and your `extra_context`. Later turns continue with that agent automatically. Requires the `agents:invoke` scope (default keys have it). → [Chat Completions: Low-latency first turn](/api-reference/chat-completions#low-latency-first-turn-direct-routing-warmup-per-request-context) ## `POST /v1/warmup` Build the orchestrator for a user — and optionally one agent — right after provisioning, with no LLM call and no charge. The first message then starts immediately. Cold builds are limited per API key (2 at a time, 30 per minute by default); over the limit you get `429 warmup_rate_limited` with `Retry-After`, and the first message still builds normally. → [Warmup](/api-reference/warmup) ## `extra_context` reaches the answering agent Per-request context is now delivered to the agent that actually answers (the routed sub-agent, the `target_agent_id` target, or the agent called through `/agents/{agent_id}/completions`), not only to the orchestrator. It stays a non-persistent trailing block: never stored in the session, never merged into the system instruction. Organizations that prefer the previous behavior can ask us to turn this off for their organization. ## Streaming keepalives during builds Streaming responses now send SSE comment lines (`: keepalive`) every 15 seconds while the server is still building agents or waiting on tools, so load balancers and HTTP clients with idle timeouts never see a silent connection. Standard SSE clients ignore comment lines.